| 
									
										
										
										
											2023-05-09 19:19:48 +02:00
										 |  |  | /* | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Copyright 2014 gRPC authors. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Licensed under the Apache License, Version 2.0 (the "License"); | 
					
						
							|  |  |  |  * you may not use this file except in compliance with the License. | 
					
						
							|  |  |  |  * You may obtain a copy of the License at | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  *     http://www.apache.org/licenses/LICENSE-2.0 | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  * Unless required by applicable law or agreed to in writing, software | 
					
						
							|  |  |  |  * distributed under the License is distributed on an "AS IS" BASIS, | 
					
						
							|  |  |  |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | 
					
						
							|  |  |  |  * See the License for the specific language governing permissions and | 
					
						
							|  |  |  |  * limitations under the License. | 
					
						
							|  |  |  |  * | 
					
						
							|  |  |  |  */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | package credentials | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import ( | 
					
						
							|  |  |  | 	"context" | 
					
						
							|  |  |  | 	"crypto/tls" | 
					
						
							|  |  |  | 	"crypto/x509" | 
					
						
							|  |  |  | 	"fmt" | 
					
						
							|  |  |  | 	"net" | 
					
						
							|  |  |  | 	"net/url" | 
					
						
							|  |  |  | 	"os" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	credinternal "google.golang.org/grpc/internal/credentials" | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TLSInfo contains the auth information for a TLS authenticated connection. | 
					
						
							|  |  |  | // It implements the AuthInfo interface. | 
					
						
							|  |  |  | type TLSInfo struct { | 
					
						
							|  |  |  | 	State tls.ConnectionState | 
					
						
							|  |  |  | 	CommonAuthInfo | 
					
						
							|  |  |  | 	// This API is experimental. | 
					
						
							|  |  |  | 	SPIFFEID *url.URL | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // AuthType returns the type of TLSInfo as a string. | 
					
						
							|  |  |  | func (t TLSInfo) AuthType() string { | 
					
						
							|  |  |  | 	return "tls" | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-11 15:34:34 +01:00
										 |  |  | // cipherSuiteLookup returns the string version of a TLS cipher suite ID. | 
					
						
							|  |  |  | func cipherSuiteLookup(cipherSuiteID uint16) string { | 
					
						
							|  |  |  | 	for _, s := range tls.CipherSuites() { | 
					
						
							|  |  |  | 		if s.ID == cipherSuiteID { | 
					
						
							|  |  |  | 			return s.Name | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	for _, s := range tls.InsecureCipherSuites() { | 
					
						
							|  |  |  | 		if s.ID == cipherSuiteID { | 
					
						
							|  |  |  | 			return s.Name | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return fmt.Sprintf("unknown ID: %v", cipherSuiteID) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-09 19:19:48 +02:00
										 |  |  | // GetSecurityValue returns security info requested by channelz. | 
					
						
							|  |  |  | func (t TLSInfo) GetSecurityValue() ChannelzSecurityValue { | 
					
						
							|  |  |  | 	v := &TLSChannelzSecurityValue{ | 
					
						
							| 
									
										
										
										
											2024-03-11 15:34:34 +01:00
										 |  |  | 		StandardName: cipherSuiteLookup(t.State.CipherSuite), | 
					
						
							| 
									
										
										
										
											2023-05-09 19:19:48 +02:00
										 |  |  | 	} | 
					
						
							|  |  |  | 	// Currently there's no way to get LocalCertificate info from tls package. | 
					
						
							|  |  |  | 	if len(t.State.PeerCertificates) > 0 { | 
					
						
							|  |  |  | 		v.RemoteCertificate = t.State.PeerCertificates[0].Raw | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return v | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // tlsCreds is the credentials required for authenticating a connection using TLS. | 
					
						
							|  |  |  | type tlsCreds struct { | 
					
						
							|  |  |  | 	// TLS configuration | 
					
						
							|  |  |  | 	config *tls.Config | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (c tlsCreds) Info() ProtocolInfo { | 
					
						
							|  |  |  | 	return ProtocolInfo{ | 
					
						
							|  |  |  | 		SecurityProtocol: "tls", | 
					
						
							|  |  |  | 		SecurityVersion:  "1.2", | 
					
						
							|  |  |  | 		ServerName:       c.config.ServerName, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (c *tlsCreds) ClientHandshake(ctx context.Context, authority string, rawConn net.Conn) (_ net.Conn, _ AuthInfo, err error) { | 
					
						
							|  |  |  | 	// use local cfg to avoid clobbering ServerName if using multiple endpoints | 
					
						
							|  |  |  | 	cfg := credinternal.CloneTLSConfig(c.config) | 
					
						
							|  |  |  | 	if cfg.ServerName == "" { | 
					
						
							|  |  |  | 		serverName, _, err := net.SplitHostPort(authority) | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			// If the authority had no host port or if the authority cannot be parsed, use it as-is. | 
					
						
							|  |  |  | 			serverName = authority | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		cfg.ServerName = serverName | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	conn := tls.Client(rawConn, cfg) | 
					
						
							|  |  |  | 	errChannel := make(chan error, 1) | 
					
						
							|  |  |  | 	go func() { | 
					
						
							|  |  |  | 		errChannel <- conn.Handshake() | 
					
						
							|  |  |  | 		close(errChannel) | 
					
						
							|  |  |  | 	}() | 
					
						
							|  |  |  | 	select { | 
					
						
							|  |  |  | 	case err := <-errChannel: | 
					
						
							|  |  |  | 		if err != nil { | 
					
						
							|  |  |  | 			conn.Close() | 
					
						
							|  |  |  | 			return nil, nil, err | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	case <-ctx.Done(): | 
					
						
							|  |  |  | 		conn.Close() | 
					
						
							|  |  |  | 		return nil, nil, ctx.Err() | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	tlsInfo := TLSInfo{ | 
					
						
							|  |  |  | 		State: conn.ConnectionState(), | 
					
						
							|  |  |  | 		CommonAuthInfo: CommonAuthInfo{ | 
					
						
							|  |  |  | 			SecurityLevel: PrivacyAndIntegrity, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	id := credinternal.SPIFFEIDFromState(conn.ConnectionState()) | 
					
						
							|  |  |  | 	if id != nil { | 
					
						
							|  |  |  | 		tlsInfo.SPIFFEID = id | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return credinternal.WrapSyscallConn(rawConn, conn), tlsInfo, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (c *tlsCreds) ServerHandshake(rawConn net.Conn) (net.Conn, AuthInfo, error) { | 
					
						
							|  |  |  | 	conn := tls.Server(rawConn, c.config) | 
					
						
							|  |  |  | 	if err := conn.Handshake(); err != nil { | 
					
						
							|  |  |  | 		conn.Close() | 
					
						
							|  |  |  | 		return nil, nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	tlsInfo := TLSInfo{ | 
					
						
							|  |  |  | 		State: conn.ConnectionState(), | 
					
						
							|  |  |  | 		CommonAuthInfo: CommonAuthInfo{ | 
					
						
							|  |  |  | 			SecurityLevel: PrivacyAndIntegrity, | 
					
						
							|  |  |  | 		}, | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	id := credinternal.SPIFFEIDFromState(conn.ConnectionState()) | 
					
						
							|  |  |  | 	if id != nil { | 
					
						
							|  |  |  | 		tlsInfo.SPIFFEID = id | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return credinternal.WrapSyscallConn(rawConn, conn), tlsInfo, nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (c *tlsCreds) Clone() TransportCredentials { | 
					
						
							|  |  |  | 	return NewTLS(c.config) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | func (c *tlsCreds) OverrideServerName(serverNameOverride string) error { | 
					
						
							|  |  |  | 	c.config.ServerName = serverNameOverride | 
					
						
							|  |  |  | 	return nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-03-11 15:34:34 +01:00
										 |  |  | // The following cipher suites are forbidden for use with HTTP/2 by | 
					
						
							|  |  |  | // https://datatracker.ietf.org/doc/html/rfc7540#appendix-A | 
					
						
							|  |  |  | var tls12ForbiddenCipherSuites = map[uint16]struct{}{ | 
					
						
							|  |  |  | 	tls.TLS_RSA_WITH_AES_128_CBC_SHA:         {}, | 
					
						
							|  |  |  | 	tls.TLS_RSA_WITH_AES_256_CBC_SHA:         {}, | 
					
						
							|  |  |  | 	tls.TLS_RSA_WITH_AES_128_GCM_SHA256:      {}, | 
					
						
							|  |  |  | 	tls.TLS_RSA_WITH_AES_256_GCM_SHA384:      {}, | 
					
						
							|  |  |  | 	tls.TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA: {}, | 
					
						
							|  |  |  | 	tls.TLS_ECDHE_ECDSA_WITH_AES_256_CBC_SHA: {}, | 
					
						
							|  |  |  | 	tls.TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:   {}, | 
					
						
							|  |  |  | 	tls.TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:   {}, | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2023-05-09 19:19:48 +02:00
										 |  |  | // NewTLS uses c to construct a TransportCredentials based on TLS. | 
					
						
							|  |  |  | func NewTLS(c *tls.Config) TransportCredentials { | 
					
						
							|  |  |  | 	tc := &tlsCreds{credinternal.CloneTLSConfig(c)} | 
					
						
							|  |  |  | 	tc.config.NextProtos = credinternal.AppendH2ToNextProtos(tc.config.NextProtos) | 
					
						
							| 
									
										
										
										
											2024-03-11 15:34:34 +01:00
										 |  |  | 	// If the user did not configure a MinVersion and did not configure a | 
					
						
							|  |  |  | 	// MaxVersion < 1.2, use MinVersion=1.2, which is required by | 
					
						
							|  |  |  | 	// https://datatracker.ietf.org/doc/html/rfc7540#section-9.2 | 
					
						
							|  |  |  | 	if tc.config.MinVersion == 0 && (tc.config.MaxVersion == 0 || tc.config.MaxVersion >= tls.VersionTLS12) { | 
					
						
							|  |  |  | 		tc.config.MinVersion = tls.VersionTLS12 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	// If the user did not configure CipherSuites, use all "secure" cipher | 
					
						
							|  |  |  | 	// suites reported by the TLS package, but remove some explicitly forbidden | 
					
						
							|  |  |  | 	// by https://datatracker.ietf.org/doc/html/rfc7540#appendix-A | 
					
						
							|  |  |  | 	if tc.config.CipherSuites == nil { | 
					
						
							|  |  |  | 		for _, cs := range tls.CipherSuites() { | 
					
						
							|  |  |  | 			if _, ok := tls12ForbiddenCipherSuites[cs.ID]; !ok { | 
					
						
							|  |  |  | 				tc.config.CipherSuites = append(tc.config.CipherSuites, cs.ID) | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2023-05-09 19:19:48 +02:00
										 |  |  | 	return tc | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewClientTLSFromCert constructs TLS credentials from the provided root | 
					
						
							|  |  |  | // certificate authority certificate(s) to validate server connections. If | 
					
						
							|  |  |  | // certificates to establish the identity of the client need to be included in | 
					
						
							|  |  |  | // the credentials (eg: for mTLS), use NewTLS instead, where a complete | 
					
						
							|  |  |  | // tls.Config can be specified. | 
					
						
							|  |  |  | // serverNameOverride is for testing only. If set to a non empty string, | 
					
						
							|  |  |  | // it will override the virtual host name of authority (e.g. :authority header | 
					
						
							|  |  |  | // field) in requests. | 
					
						
							|  |  |  | func NewClientTLSFromCert(cp *x509.CertPool, serverNameOverride string) TransportCredentials { | 
					
						
							|  |  |  | 	return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewClientTLSFromFile constructs TLS credentials from the provided root | 
					
						
							|  |  |  | // certificate authority certificate file(s) to validate server connections. If | 
					
						
							|  |  |  | // certificates to establish the identity of the client need to be included in | 
					
						
							|  |  |  | // the credentials (eg: for mTLS), use NewTLS instead, where a complete | 
					
						
							|  |  |  | // tls.Config can be specified. | 
					
						
							|  |  |  | // serverNameOverride is for testing only. If set to a non empty string, | 
					
						
							|  |  |  | // it will override the virtual host name of authority (e.g. :authority header | 
					
						
							|  |  |  | // field) in requests. | 
					
						
							|  |  |  | func NewClientTLSFromFile(certFile, serverNameOverride string) (TransportCredentials, error) { | 
					
						
							|  |  |  | 	b, err := os.ReadFile(certFile) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	cp := x509.NewCertPool() | 
					
						
							|  |  |  | 	if !cp.AppendCertsFromPEM(b) { | 
					
						
							|  |  |  | 		return nil, fmt.Errorf("credentials: failed to append certificates") | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return NewTLS(&tls.Config{ServerName: serverNameOverride, RootCAs: cp}), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewServerTLSFromCert constructs TLS credentials from the input certificate for server. | 
					
						
							|  |  |  | func NewServerTLSFromCert(cert *tls.Certificate) TransportCredentials { | 
					
						
							|  |  |  | 	return NewTLS(&tls.Config{Certificates: []tls.Certificate{*cert}}) | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // NewServerTLSFromFile constructs TLS credentials from the input certificate file and key | 
					
						
							|  |  |  | // file for server. | 
					
						
							|  |  |  | func NewServerTLSFromFile(certFile, keyFile string) (TransportCredentials, error) { | 
					
						
							|  |  |  | 	cert, err := tls.LoadX509KeyPair(certFile, keyFile) | 
					
						
							|  |  |  | 	if err != nil { | 
					
						
							|  |  |  | 		return nil, err | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 	return NewTLS(&tls.Config{Certificates: []tls.Certificate{cert}}), nil | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // TLSChannelzSecurityValue defines the struct that TLS protocol should return | 
					
						
							|  |  |  | // from GetSecurityValue(), containing security info like cipher and certificate used. | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // # Experimental | 
					
						
							|  |  |  | // | 
					
						
							|  |  |  | // Notice: This type is EXPERIMENTAL and may be changed or removed in a | 
					
						
							|  |  |  | // later release. | 
					
						
							|  |  |  | type TLSChannelzSecurityValue struct { | 
					
						
							|  |  |  | 	ChannelzSecurityValue | 
					
						
							|  |  |  | 	StandardName      string | 
					
						
							|  |  |  | 	LocalCertificate  []byte | 
					
						
							|  |  |  | 	RemoteCertificate []byte | 
					
						
							|  |  |  | } |