mirror of
				https://gitee.com/gitea/gitea
				synced 2025-11-04 08:30:25 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			159 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
			
		
		
	
	
			159 lines
		
	
	
		
			4.9 KiB
		
	
	
	
		
			Go
		
	
	
	
	
	
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["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 sslrootcert, ok := o["sslrootcert"]; ok {
 | 
						|
			if _, err := os.Stat(sslrootcert); err == nil {
 | 
						|
				verifyCaOnly = true
 | 
						|
			} else {
 | 
						|
				delete(o, "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["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) {
 | 
						|
	// user.Current() might fail when cross-compiling. We have to ignore the
 | 
						|
	// error and continue without home directory defaults, since we wouldn't
 | 
						|
	// know from where to load them.
 | 
						|
	user, _ := user.Current()
 | 
						|
 | 
						|
	// 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 := o["sslcert"]
 | 
						|
	if len(sslcert) == 0 && user != nil {
 | 
						|
		sslcert = filepath.Join(user.HomeDir, ".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
 | 
						|
	}
 | 
						|
	// https://github.com/postgres/postgres/blob/REL9_6_2/src/interfaces/libpq/fe-secure-openssl.c#L1050:L1054
 | 
						|
	if _, err := os.Stat(sslcert); os.IsNotExist(err) {
 | 
						|
		return
 | 
						|
	} else if err != nil {
 | 
						|
		panic(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 := o["sslkey"]
 | 
						|
	if len(sslkey) == 0 && user != nil {
 | 
						|
		sslkey = filepath.Join(user.HomeDir, ".postgresql", "postgresql.key")
 | 
						|
	}
 | 
						|
 | 
						|
	if len(sslkey) > 0 {
 | 
						|
		if err := sslKeyPermissions(sslkey); err != nil {
 | 
						|
			panic(err)
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	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) {
 | 
						|
	// 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 := o["sslrootcert"]; len(sslrootcert) > 0 {
 | 
						|
		tlsConf.RootCAs = x509.NewCertPool()
 | 
						|
 | 
						|
		cert, err := ioutil.ReadFile(sslrootcert)
 | 
						|
		if err != nil {
 | 
						|
			panic(err)
 | 
						|
		}
 | 
						|
 | 
						|
		if !tlsConf.RootCAs.AppendCertsFromPEM(cert) {
 | 
						|
			errorf("couldn't parse pem in sslrootcert")
 | 
						|
		}
 | 
						|
	}
 | 
						|
}
 | 
						|
 | 
						|
// 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)
 | 
						|
	}
 | 
						|
}
 |