[822] | 1 | package pq
|
---|
| 2 |
|
---|
| 3 | import (
|
---|
| 4 | "crypto/tls"
|
---|
| 5 | "crypto/x509"
|
---|
| 6 | "io/ioutil"
|
---|
| 7 | "net"
|
---|
| 8 | "os"
|
---|
| 9 | "os/user"
|
---|
| 10 | "path/filepath"
|
---|
| 11 | "strings"
|
---|
| 12 | )
|
---|
| 13 |
|
---|
| 14 | // ssl generates a function to upgrade a net.Conn based on the "sslmode" and
|
---|
| 15 | // related settings. The function is nil when no upgrade should take place.
|
---|
| 16 | func ssl(o values) (func(net.Conn) (net.Conn, error), error) {
|
---|
| 17 | verifyCaOnly := false
|
---|
| 18 | tlsConf := tls.Config{}
|
---|
| 19 | switch mode := o["sslmode"]; mode {
|
---|
| 20 | // "require" is the default.
|
---|
| 21 | case "", "require":
|
---|
| 22 | // We must skip TLS's own verification since it requires full
|
---|
| 23 | // verification since Go 1.3.
|
---|
| 24 | tlsConf.InsecureSkipVerify = true
|
---|
| 25 |
|
---|
| 26 | // From http://www.postgresql.org/docs/current/static/libpq-ssl.html:
|
---|
| 27 | //
|
---|
| 28 | // Note: For backwards compatibility with earlier versions of
|
---|
| 29 | // PostgreSQL, if a root CA file exists, the behavior of
|
---|
| 30 | // sslmode=require will be the same as that of verify-ca, meaning the
|
---|
| 31 | // server certificate is validated against the CA. Relying on this
|
---|
| 32 | // behavior is discouraged, and applications that need certificate
|
---|
| 33 | // validation should always use verify-ca or verify-full.
|
---|
| 34 | if sslrootcert, ok := o["sslrootcert"]; ok {
|
---|
| 35 | if _, err := os.Stat(sslrootcert); err == nil {
|
---|
| 36 | verifyCaOnly = true
|
---|
| 37 | } else {
|
---|
| 38 | delete(o, "sslrootcert")
|
---|
| 39 | }
|
---|
| 40 | }
|
---|
| 41 | case "verify-ca":
|
---|
| 42 | // We must skip TLS's own verification since it requires full
|
---|
| 43 | // verification since Go 1.3.
|
---|
| 44 | tlsConf.InsecureSkipVerify = true
|
---|
| 45 | verifyCaOnly = true
|
---|
| 46 | case "verify-full":
|
---|
| 47 | tlsConf.ServerName = o["host"]
|
---|
| 48 | case "disable":
|
---|
| 49 | return nil, nil
|
---|
| 50 | default:
|
---|
| 51 | return nil, fmterrorf(`unsupported sslmode %q; only "require" (default), "verify-full", "verify-ca", and "disable" supported`, mode)
|
---|
| 52 | }
|
---|
| 53 |
|
---|
| 54 | // Set Server Name Indication (SNI), if enabled by connection parameters.
|
---|
| 55 | // By default SNI is on, any value which is not starting with "1" disables
|
---|
| 56 | // SNI -- that is the same check vanilla libpq uses.
|
---|
| 57 | if sslsni := o["sslsni"]; sslsni == "" || strings.HasPrefix(sslsni, "1") {
|
---|
| 58 | // RFC 6066 asks to not set SNI if the host is a literal IP address (IPv4
|
---|
| 59 | // or IPv6). This check is coded already crypto.tls.hostnameInSNI, so
|
---|
| 60 | // just always set ServerName here and let crypto/tls do the filtering.
|
---|
| 61 | tlsConf.ServerName = o["host"]
|
---|
| 62 | }
|
---|
| 63 |
|
---|
| 64 | err := sslClientCertificates(&tlsConf, o)
|
---|
| 65 | if err != nil {
|
---|
| 66 | return nil, err
|
---|
| 67 | }
|
---|
| 68 | err = sslCertificateAuthority(&tlsConf, o)
|
---|
| 69 | if err != nil {
|
---|
| 70 | return nil, err
|
---|
| 71 | }
|
---|
| 72 |
|
---|
| 73 | // Accept renegotiation requests initiated by the backend.
|
---|
| 74 | //
|
---|
| 75 | // Renegotiation was deprecated then removed from PostgreSQL 9.5, but
|
---|
| 76 | // the default configuration of older versions has it enabled. Redshift
|
---|
| 77 | // also initiates renegotiations and cannot be reconfigured.
|
---|
| 78 | tlsConf.Renegotiation = tls.RenegotiateFreelyAsClient
|
---|
| 79 |
|
---|
| 80 | return func(conn net.Conn) (net.Conn, error) {
|
---|
| 81 | client := tls.Client(conn, &tlsConf)
|
---|
| 82 | if verifyCaOnly {
|
---|
| 83 | err := sslVerifyCertificateAuthority(client, &tlsConf)
|
---|
| 84 | if err != nil {
|
---|
| 85 | return nil, err
|
---|
| 86 | }
|
---|
| 87 | }
|
---|
| 88 | return client, nil
|
---|
| 89 | }, nil
|
---|
| 90 | }
|
---|
| 91 |
|
---|
| 92 | // sslClientCertificates adds the certificate specified in the "sslcert" and
|
---|
| 93 | // "sslkey" settings, or if they aren't set, from the .postgresql directory
|
---|
| 94 | // in the user's home directory. The configured files must exist and have
|
---|
| 95 | // the correct permissions.
|
---|
| 96 | func sslClientCertificates(tlsConf *tls.Config, o values) error {
|
---|
| 97 | sslinline := o["sslinline"]
|
---|
| 98 | if sslinline == "true" {
|
---|
| 99 | cert, err := tls.X509KeyPair([]byte(o["sslcert"]), []byte(o["sslkey"]))
|
---|
| 100 | if err != nil {
|
---|
| 101 | return err
|
---|
| 102 | }
|
---|
| 103 | tlsConf.Certificates = []tls.Certificate{cert}
|
---|
| 104 | return nil
|
---|
| 105 | }
|
---|
| 106 |
|
---|
| 107 | // user.Current() might fail when cross-compiling. We have to ignore the
|
---|
| 108 | // error and continue without home directory defaults, since we wouldn't
|
---|
| 109 | // know from where to load them.
|
---|
| 110 | user, _ := user.Current()
|
---|
| 111 |
|
---|
| 112 | // In libpq, the client certificate is only loaded if the setting is not blank.
|
---|
| 113 | //
|
---|
| 114 | // https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1036-L1037
|
---|
| 115 | sslcert := o["sslcert"]
|
---|
| 116 | if len(sslcert) == 0 && user != nil {
|
---|
| 117 | sslcert = filepath.Join(user.HomeDir, ".postgresql", "postgresql.crt")
|
---|
| 118 | }
|
---|
| 119 | // https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1045
|
---|
| 120 | if len(sslcert) == 0 {
|
---|
| 121 | return nil
|
---|
| 122 | }
|
---|
| 123 | // https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1050:L1054
|
---|
| 124 | if _, err := os.Stat(sslcert); os.IsNotExist(err) {
|
---|
| 125 | return nil
|
---|
| 126 | } else if err != nil {
|
---|
| 127 | return err
|
---|
| 128 | }
|
---|
| 129 |
|
---|
| 130 | // In libpq, the ssl key is only loaded if the setting is not blank.
|
---|
| 131 | //
|
---|
| 132 | // https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1123-L1222
|
---|
| 133 | sslkey := o["sslkey"]
|
---|
| 134 | if len(sslkey) == 0 && user != nil {
|
---|
| 135 | sslkey = filepath.Join(user.HomeDir, ".postgresql", "postgresql.key")
|
---|
| 136 | }
|
---|
| 137 |
|
---|
| 138 | if len(sslkey) > 0 {
|
---|
| 139 | if err := sslKeyPermissions(sslkey); err != nil {
|
---|
| 140 | return err
|
---|
| 141 | }
|
---|
| 142 | }
|
---|
| 143 |
|
---|
| 144 | cert, err := tls.LoadX509KeyPair(sslcert, sslkey)
|
---|
| 145 | if err != nil {
|
---|
| 146 | return err
|
---|
| 147 | }
|
---|
| 148 |
|
---|
| 149 | tlsConf.Certificates = []tls.Certificate{cert}
|
---|
| 150 | return nil
|
---|
| 151 | }
|
---|
| 152 |
|
---|
| 153 | // sslCertificateAuthority adds the RootCA specified in the "sslrootcert" setting.
|
---|
| 154 | func sslCertificateAuthority(tlsConf *tls.Config, o values) error {
|
---|
| 155 | // In libpq, the root certificate is only loaded if the setting is not blank.
|
---|
| 156 | //
|
---|
| 157 | // https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L950-L951
|
---|
| 158 | if sslrootcert := o["sslrootcert"]; len(sslrootcert) > 0 {
|
---|
| 159 | tlsConf.RootCAs = x509.NewCertPool()
|
---|
| 160 |
|
---|
| 161 | sslinline := o["sslinline"]
|
---|
| 162 |
|
---|
| 163 | var cert []byte
|
---|
| 164 | if sslinline == "true" {
|
---|
| 165 | cert = []byte(sslrootcert)
|
---|
| 166 | } else {
|
---|
| 167 | var err error
|
---|
| 168 | cert, err = ioutil.ReadFile(sslrootcert)
|
---|
| 169 | if err != nil {
|
---|
| 170 | return err
|
---|
| 171 | }
|
---|
| 172 | }
|
---|
| 173 |
|
---|
| 174 | if !tlsConf.RootCAs.AppendCertsFromPEM(cert) {
|
---|
| 175 | return fmterrorf("couldn't parse pem in sslrootcert")
|
---|
| 176 | }
|
---|
| 177 | }
|
---|
| 178 |
|
---|
| 179 | return nil
|
---|
| 180 | }
|
---|
| 181 |
|
---|
| 182 | // sslVerifyCertificateAuthority carries out a TLS handshake to the server and
|
---|
| 183 | // verifies the presented certificate against the CA, i.e. the one specified in
|
---|
| 184 | // sslrootcert or the system CA if sslrootcert was not specified.
|
---|
| 185 | func sslVerifyCertificateAuthority(client *tls.Conn, tlsConf *tls.Config) error {
|
---|
| 186 | err := client.Handshake()
|
---|
| 187 | if err != nil {
|
---|
| 188 | return err
|
---|
| 189 | }
|
---|
| 190 | certs := client.ConnectionState().PeerCertificates
|
---|
| 191 | opts := x509.VerifyOptions{
|
---|
| 192 | DNSName: client.ConnectionState().ServerName,
|
---|
| 193 | Intermediates: x509.NewCertPool(),
|
---|
| 194 | Roots: tlsConf.RootCAs,
|
---|
| 195 | }
|
---|
| 196 | for i, cert := range certs {
|
---|
| 197 | if i == 0 {
|
---|
| 198 | continue
|
---|
| 199 | }
|
---|
| 200 | opts.Intermediates.AddCert(cert)
|
---|
| 201 | }
|
---|
| 202 | _, err = certs[0].Verify(opts)
|
---|
| 203 | return err
|
---|
| 204 | }
|
---|