summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/lib/pq/ssl.go
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/github.com/lib/pq/ssl.go')
-rw-r--r--vendor/github.com/lib/pq/ssl.go175
1 files changed, 175 insertions, 0 deletions
diff --git a/vendor/github.com/lib/pq/ssl.go b/vendor/github.com/lib/pq/ssl.go
new file mode 100644
index 000000000..b282ebd92
--- /dev/null
+++ b/vendor/github.com/lib/pq/ssl.go
@@ -0,0 +1,175 @@
+package pq
+
+import (
+ "crypto/tls"
+ "crypto/x509"
+ "io/ioutil"
+ "net"
+ "os"
+ "os/user"
+ "path/filepath"
+)
+
+// ssl generates a function to upgrade a net.Conn based on the "sslmode" and
+// related settings. The function is nil when no upgrade should take place.
+func ssl(o values) func(net.Conn) net.Conn {
+ verifyCaOnly := false
+ tlsConf := tls.Config{}
+ switch mode := o.Get("sslmode"); mode {
+ // "require" is the default.
+ case "", "require":
+ // We must skip TLS's own verification since it requires full
+ // verification since Go 1.3.
+ tlsConf.InsecureSkipVerify = true
+
+ // From http://www.postgresql.org/docs/current/static/libpq-ssl.html:
+ // Note: For backwards compatibility with earlier versions of PostgreSQL, if a
+ // root CA file exists, the behavior of sslmode=require will be the same as
+ // that of verify-ca, meaning the server certificate is validated against the
+ // CA. Relying on this behavior is discouraged, and applications that need
+ // certificate validation should always use verify-ca or verify-full.
+ if _, err := os.Stat(o.Get("sslrootcert")); err == nil {
+ verifyCaOnly = true
+ } else {
+ o.Set("sslrootcert", "")
+ }
+ case "verify-ca":
+ // We must skip TLS's own verification since it requires full
+ // verification since Go 1.3.
+ tlsConf.InsecureSkipVerify = true
+ verifyCaOnly = true
+ case "verify-full":
+ tlsConf.ServerName = o.Get("host")
+ case "disable":
+ return nil
+ default:
+ errorf(`unsupported sslmode %q; only "require" (default), "verify-full", "verify-ca", and "disable" supported`, mode)
+ }
+
+ sslClientCertificates(&tlsConf, o)
+ sslCertificateAuthority(&tlsConf, o)
+ sslRenegotiation(&tlsConf)
+
+ return func(conn net.Conn) net.Conn {
+ client := tls.Client(conn, &tlsConf)
+ if verifyCaOnly {
+ sslVerifyCertificateAuthority(client, &tlsConf)
+ }
+ return client
+ }
+}
+
+// sslClientCertificates adds the certificate specified in the "sslcert" and
+// "sslkey" settings, or if they aren't set, from the .postgresql directory
+// in the user's home directory. The configured files must exist and have
+// the correct permissions.
+func sslClientCertificates(tlsConf *tls.Config, o values) {
+ sslkey := o.Get("sslkey")
+ sslcert := o.Get("sslcert")
+
+ var cinfo, kinfo os.FileInfo
+ var err error
+
+ if sslcert != "" && sslkey != "" {
+ // Check that both files exist. Note that we don't do any more extensive
+ // checks than this (such as checking that the paths aren't directories);
+ // LoadX509KeyPair() will take care of the rest.
+ cinfo, err = os.Stat(sslcert)
+ if err != nil {
+ panic(err)
+ }
+
+ kinfo, err = os.Stat(sslkey)
+ if err != nil {
+ panic(err)
+ }
+ } else {
+ // Automatically find certificates from ~/.postgresql
+ sslcert, sslkey, cinfo, kinfo = sslHomeCertificates()
+
+ if cinfo == nil || kinfo == nil {
+ // No certificates to load
+ return
+ }
+ }
+
+ // The files must also have the correct permissions
+ sslCertificatePermissions(cinfo, kinfo)
+
+ cert, err := tls.LoadX509KeyPair(sslcert, sslkey)
+ if err != nil {
+ panic(err)
+ }
+ tlsConf.Certificates = []tls.Certificate{cert}
+}
+
+// sslCertificateAuthority adds the RootCA specified in the "sslrootcert" setting.
+func sslCertificateAuthority(tlsConf *tls.Config, o values) {
+ if sslrootcert := o.Get("sslrootcert"); sslrootcert != "" {
+ tlsConf.RootCAs = x509.NewCertPool()
+
+ cert, err := ioutil.ReadFile(sslrootcert)
+ if err != nil {
+ panic(err)
+ }
+
+ ok := tlsConf.RootCAs.AppendCertsFromPEM(cert)
+ if !ok {
+ errorf("couldn't parse pem in sslrootcert")
+ }
+ }
+}
+
+// sslHomeCertificates returns the path and stats of certificates in the current
+// user's home directory.
+func sslHomeCertificates() (cert, key string, cinfo, kinfo os.FileInfo) {
+ user, err := user.Current()
+
+ if err != nil {
+ // user.Current() might fail when cross-compiling. We have to ignore the
+ // error and continue without client certificates, since we wouldn't know
+ // from where to load them.
+ return
+ }
+
+ cert = filepath.Join(user.HomeDir, ".postgresql", "postgresql.crt")
+ key = filepath.Join(user.HomeDir, ".postgresql", "postgresql.key")
+
+ cinfo, err = os.Stat(cert)
+ if err != nil {
+ cinfo = nil
+ }
+
+ kinfo, err = os.Stat(key)
+ if err != nil {
+ kinfo = nil
+ }
+
+ return
+}
+
+// sslVerifyCertificateAuthority carries out a TLS handshake to the server and
+// verifies the presented certificate against the CA, i.e. the one specified in
+// sslrootcert or the system CA if sslrootcert was not specified.
+func sslVerifyCertificateAuthority(client *tls.Conn, tlsConf *tls.Config) {
+ err := client.Handshake()
+ if err != nil {
+ panic(err)
+ }
+ certs := client.ConnectionState().PeerCertificates
+ opts := x509.VerifyOptions{
+ DNSName: client.ConnectionState().ServerName,
+ Intermediates: x509.NewCertPool(),
+ Roots: tlsConf.RootCAs,
+ }
+ for i, cert := range certs {
+ if i == 0 {
+ continue
+ }
+ opts.Intermediates.AddCert(cert)
+ }
+ _, err = certs[0].Verify(opts)
+ if err != nil {
+ panic(err)
+ }
+}