You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
	
	
		
			176 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Go
		
	
		
		
			
		
	
	
			176 lines
		
	
	
		
			5.4 KiB
		
	
	
	
		
			Go
		
	
| 
											5 months ago
										 | package gokb | ||
|  | 
 | ||
|  | 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, error), error) { | ||
|  | 	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.kingbase.org/docs/current/static/libkci-ssl.html:
 | ||
|  | 		//
 | ||
|  | 		// Note: For backwards compatibility with earlier versions of
 | ||
|  | 		// Kingbase, 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, nil | ||
|  | 	default: | ||
|  | 		return nil, fmterrorf(`unsupported sslmode %q; only "require" (default), "verify-full", "verify-ca", and "disable" supported`, mode) | ||
|  | 	} | ||
|  | 
 | ||
|  | 	err := sslClientCertificates(&tlsConf, o) | ||
|  | 	if err != nil { | ||
|  | 		return nil, err | ||
|  | 	} | ||
|  | 	err = sslCertificateAuthority(&tlsConf, o) | ||
|  | 	if err != nil { | ||
|  | 		return nil, err | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// Accept renegotiation requests initiated by the backend.
 | ||
|  | 	//
 | ||
|  | 	// Renegotiation was deprecated then removed from Kingbase 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 := sslVerifyCertificateAuthority(client, &tlsConf) | ||
|  | 			if err != nil { | ||
|  | 				return nil, err | ||
|  | 			} | ||
|  | 		} | ||
|  | 		return client, nil | ||
|  | 	}, nil | ||
|  | } | ||
|  | 
 | ||
|  | // sslClientCertificates adds the certificate specified in the "sslcert" and
 | ||
|  | // "sslkey" settings, or if they aren't set, from the .kingbase directory
 | ||
|  | // in the user's home directory. The configured files must exist and have
 | ||
|  | // the correct permissions.
 | ||
|  | func sslClientCertificates(tlsConf *tls.Config, o values) error { | ||
|  | 	// 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 libkci, the client certificate is only loaded if the setting is not blank.
 | ||
|  | 	//
 | ||
|  | 	// https://github.com/kingbase/kingbase/blob/REL9_6_2/src/interfaces/libkci/fe-secure-openssl.c#L1036-L1037
 | ||
|  | 	sslcert := o["sslcert"] | ||
|  | 	if len(sslcert) == 0 && user != nil { | ||
|  | 		sslcert = filepath.Join(user.HomeDir, ".kingbase", "kingbase.crt") | ||
|  | 	} | ||
|  | 	// https://github.com/kingbase/kingbase/blob/REL9_6_2/src/interfaces/libkci/fe-secure-openssl.c#L1045
 | ||
|  | 	if len(sslcert) == 0 { | ||
|  | 		return nil | ||
|  | 	} | ||
|  | 	// https://github.com/kingbase/kingbase/blob/REL9_6_2/src/interfaces/libkci/fe-secure-openssl.c#L1050:L1054
 | ||
|  | 	if _, err := os.Stat(sslcert); os.IsNotExist(err) { | ||
|  | 		return nil | ||
|  | 	} else if err != nil { | ||
|  | 		return err | ||
|  | 	} | ||
|  | 
 | ||
|  | 	// In libkci, the ssl key is only loaded if the setting is not blank.
 | ||
|  | 	//
 | ||
|  | 	// https://github.com/kingbase/kingbase/blob/REL9_6_2/src/interfaces/libkci/fe-secure-openssl.c#L1123-L1222
 | ||
|  | 	sslkey := o["sslkey"] | ||
|  | 	if len(sslkey) == 0 && user != nil { | ||
|  | 		sslkey = filepath.Join(user.HomeDir, ".kingbase", "kingbase.key") | ||
|  | 	} | ||
|  | 
 | ||
|  | 	if len(sslkey) > 0 { | ||
|  | 		if err := sslKeyPermissions(sslkey); 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, o values) error { | ||
|  | 	// In libkci, the root certificate is only loaded if the setting is not blank.
 | ||
|  | 	//
 | ||
|  | 	// https://github.com/kingbase/kingbase/blob/REL9_6_2/src/interfaces/libkci/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 { | ||
|  | 			return err | ||
|  | 		} | ||
|  | 
 | ||
|  | 		if !tlsConf.RootCAs.AppendCertsFromPEM(cert) { | ||
|  | 			return fmterrorf("couldn't parse pem in sslrootcert") | ||
|  | 		} | ||
|  | 	} | ||
|  | 
 | ||
|  | 	return nil | ||
|  | } | ||
|  | 
 | ||
|  | // 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) error { | ||
|  | 	err := client.Handshake() | ||
|  | 	if err != nil { | ||
|  | 		return 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) | ||
|  | 	return err | ||
|  | } |