vendor
This commit is contained in:
250
vendor/github.com/lib/pq/ssl.go
generated
vendored
Normal file
250
vendor/github.com/lib/pq/ssl.go
generated
vendored
Normal file
@@ -0,0 +1,250 @@
|
||||
package pq
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"net"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
|
||||
"github.com/lib/pq/internal/pqutil"
|
||||
)
|
||||
|
||||
// Registry for custom tls.Configs
|
||||
var (
|
||||
tlsConfs = make(map[string]*tls.Config)
|
||||
tlsConfsMu sync.RWMutex
|
||||
)
|
||||
|
||||
// RegisterTLSConfig registers a custom [tls.Config]. They are used by using
|
||||
// sslmode=pqgo-«key» in the connection string.
|
||||
//
|
||||
// Set the config to nil to remove a configuration.
|
||||
func RegisterTLSConfig(key string, config *tls.Config) error {
|
||||
key = strings.TrimPrefix(key, "pqgo-")
|
||||
if config == nil {
|
||||
tlsConfsMu.Lock()
|
||||
delete(tlsConfs, key)
|
||||
tlsConfsMu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
tlsConfsMu.Lock()
|
||||
tlsConfs[key] = config
|
||||
tlsConfsMu.Unlock()
|
||||
return nil
|
||||
}
|
||||
|
||||
func hasTLSConfig(key string) bool {
|
||||
tlsConfsMu.RLock()
|
||||
defer tlsConfsMu.RUnlock()
|
||||
_, ok := tlsConfs[key]
|
||||
return ok
|
||||
}
|
||||
|
||||
func getTLSConfigClone(key string) *tls.Config {
|
||||
tlsConfsMu.RLock()
|
||||
defer tlsConfsMu.RUnlock()
|
||||
if v, ok := tlsConfs[key]; ok {
|
||||
return v.Clone()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 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(cfg Config) (func(net.Conn) (net.Conn, error), error) {
|
||||
var (
|
||||
verifyCaOnly = false
|
||||
tlsConf = &tls.Config{}
|
||||
mode = cfg.SSLMode
|
||||
)
|
||||
switch {
|
||||
// "require" is the default.
|
||||
case mode == "" || mode == SSLModeRequire:
|
||||
// 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 cfg.SSLRootCert != "" {
|
||||
if _, err := os.Stat(cfg.SSLRootCert); err == nil {
|
||||
verifyCaOnly = true
|
||||
} else {
|
||||
cfg.SSLRootCert = ""
|
||||
}
|
||||
}
|
||||
case mode == SSLModeVerifyCA:
|
||||
// We must skip TLS's own verification since it requires full
|
||||
// verification since Go 1.3.
|
||||
tlsConf.InsecureSkipVerify = true
|
||||
verifyCaOnly = true
|
||||
case mode == SSLModeVerifyFull:
|
||||
tlsConf.ServerName = cfg.Host
|
||||
case mode == SSLModeDisable:
|
||||
return nil, nil
|
||||
case strings.HasPrefix(string(mode), "pqgo-"):
|
||||
tlsConf = getTLSConfigClone(string(mode[5:]))
|
||||
if tlsConf == nil {
|
||||
return nil, fmt.Errorf(`pq: unknown custom sslmode %q`, mode)
|
||||
}
|
||||
default:
|
||||
return nil, fmt.Errorf(
|
||||
`pq: unsupported sslmode %q; only "require" (default), "verify-full", "verify-ca", and "disable" supported`,
|
||||
mode)
|
||||
}
|
||||
|
||||
// Set Server Name Indication (SNI), if enabled by connection parameters.
|
||||
if cfg.SSLSNI {
|
||||
// RFC 6066 asks to not set SNI if the host is a literal IP address (IPv4
|
||||
// or IPv6). This check is coded already crypto.tls.hostnameInSNI, so
|
||||
// just always set ServerName here and let crypto/tls do the filtering.
|
||||
tlsConf.ServerName = cfg.Host
|
||||
}
|
||||
|
||||
err := sslClientCertificates(tlsConf, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
err = sslCertificateAuthority(tlsConf, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Accept renegotiation requests initiated by the backend.
|
||||
//
|
||||
// Renegotiation was deprecated then removed from PostgreSQL 9.5, but
|
||||
// the default configuration of older versions has it enabled. Redshift
|
||||
// also initiates renegotiations and cannot be reconfigured.
|
||||
tlsConf.Renegotiation = tls.RenegotiateFreelyAsClient
|
||||
|
||||
return func(conn net.Conn) (net.Conn, error) {
|
||||
client := tls.Client(conn, tlsConf)
|
||||
if verifyCaOnly {
|
||||
err := client.Handshake()
|
||||
if err != nil {
|
||||
return client, err
|
||||
}
|
||||
var (
|
||||
certs = client.ConnectionState().PeerCertificates
|
||||
opts = x509.VerifyOptions{Intermediates: x509.NewCertPool(), Roots: tlsConf.RootCAs}
|
||||
)
|
||||
for _, cert := range certs[1:] {
|
||||
opts.Intermediates.AddCert(cert)
|
||||
}
|
||||
_, err = certs[0].Verify(opts)
|
||||
return client, err
|
||||
}
|
||||
return client, nil
|
||||
}, nil
|
||||
}
|
||||
|
||||
// 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, cfg Config) error {
|
||||
if cfg.SSLInline {
|
||||
cert, err := tls.X509KeyPair([]byte(cfg.SSLCert), []byte(cfg.SSLKey))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
tlsConf.Certificates = []tls.Certificate{cert}
|
||||
return nil
|
||||
}
|
||||
|
||||
home := pqutil.Home()
|
||||
|
||||
// In libpq, the client certificate is only loaded if the setting is not blank.
|
||||
//
|
||||
// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1036-L1037
|
||||
sslcert := cfg.SSLCert
|
||||
if len(sslcert) == 0 && home != "" {
|
||||
if runtime.GOOS == "windows" {
|
||||
sslcert = filepath.Join(sslcert, "postgresql.crt")
|
||||
} else {
|
||||
sslcert = filepath.Join(home, ".postgresql/postgresql.crt")
|
||||
}
|
||||
}
|
||||
// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1045
|
||||
if len(sslcert) == 0 {
|
||||
return nil
|
||||
}
|
||||
// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1050:L1054
|
||||
_, err := os.Stat(sslcert)
|
||||
if err != nil {
|
||||
perr := new(os.PathError)
|
||||
if errors.As(err, &perr) && (perr.Err == syscall.ENOENT || perr.Err == syscall.ENOTDIR) {
|
||||
return nil
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// In libpq, the ssl key is only loaded if the setting is not blank.
|
||||
//
|
||||
// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1123-L1222
|
||||
sslkey := cfg.SSLKey
|
||||
if len(sslkey) == 0 && home != "" {
|
||||
if runtime.GOOS == "windows" {
|
||||
sslkey = filepath.Join(home, "postgresql.key")
|
||||
} else {
|
||||
sslkey = filepath.Join(home, ".postgresql/postgresql.key")
|
||||
}
|
||||
}
|
||||
|
||||
if len(sslkey) > 0 {
|
||||
err := pqutil.SSLKeyPermissions(sslkey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
cert, err := tls.LoadX509KeyPair(sslcert, sslkey)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
tlsConf.Certificates = []tls.Certificate{cert}
|
||||
return nil
|
||||
}
|
||||
|
||||
// sslCertificateAuthority adds the RootCA specified in the "sslrootcert" setting.
|
||||
func sslCertificateAuthority(tlsConf *tls.Config, cfg Config) error {
|
||||
// In libpq, the root certificate is only loaded if the setting is not blank.
|
||||
//
|
||||
// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L950-L951
|
||||
if sslrootcert := cfg.SSLRootCert; len(sslrootcert) > 0 {
|
||||
tlsConf.RootCAs = x509.NewCertPool()
|
||||
|
||||
var cert []byte
|
||||
if cfg.SSLInline {
|
||||
cert = []byte(sslrootcert)
|
||||
} else {
|
||||
var err error
|
||||
cert, err = os.ReadFile(sslrootcert)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
if !tlsConf.RootCAs.AppendCertsFromPEM(cert) {
|
||||
return errors.New("pq: couldn't parse pem in sslrootcert")
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
Reference in New Issue
Block a user