ssl.go 6.3 KB

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