...

Source file src/github.com/letsencrypt/boulder/grpc/creds/creds.go

Documentation: github.com/letsencrypt/boulder/grpc/creds

     1  package creds
     2  
     3  import (
     4  	"context"
     5  	"crypto/tls"
     6  	"crypto/x509"
     7  	"errors"
     8  	"fmt"
     9  	"net"
    10  
    11  	"google.golang.org/grpc/credentials"
    12  )
    13  
    14  var (
    15  	ErrClientHandshakeNop = errors.New(
    16  		"boulder/grpc/creds: Client-side handshakes are not implemented with " +
    17  			"serverTransportCredentials")
    18  	ErrServerHandshakeNop = errors.New(
    19  		"boulder/grpc/creds: Server-side handshakes are not implemented with " +
    20  			"clientTransportCredentials")
    21  	ErrOverrideServerNameNop = errors.New(
    22  		"boulder/grpc/creds: OverrideServerName() is not implemented")
    23  	ErrNilServerConfig = errors.New(
    24  		"boulder/grpc/creds: `serverConfig` must not be nil")
    25  	ErrEmptyPeerCerts = errors.New(
    26  		"boulder/grpc/creds: validateClient given state with empty PeerCertificates")
    27  )
    28  
    29  type ErrSANNotAccepted struct {
    30  	got, expected []string
    31  }
    32  
    33  func (e ErrSANNotAccepted) Error() string {
    34  	return fmt.Sprintf("boulder/grpc/creds: client certificate SAN was invalid. "+
    35  		"Got %q, expected one of %q.", e.got, e.expected)
    36  }
    37  
    38  // clientTransportCredentials is a grpc/credentials.TransportCredentials which supports
    39  // connecting to, and verifying multiple DNS names
    40  type clientTransportCredentials struct {
    41  	roots   *x509.CertPool
    42  	clients []tls.Certificate
    43  	// If set, this is used as the hostname to validate on certificates, instead
    44  	// of the value passed to ClientHandshake by grpc.
    45  	hostOverride string
    46  }
    47  
    48  // NewClientCredentials returns a new initialized grpc/credentials.TransportCredentials for client usage
    49  func NewClientCredentials(rootCAs *x509.CertPool, clientCerts []tls.Certificate, hostOverride string) credentials.TransportCredentials {
    50  	return &clientTransportCredentials{rootCAs, clientCerts, hostOverride}
    51  }
    52  
    53  // ClientHandshake does the authentication handshake specified by the corresponding
    54  // authentication protocol on rawConn for clients. It returns the authenticated
    55  // connection and the corresponding auth information about the connection.
    56  // Implementations must use the provided context to implement timely cancellation.
    57  func (tc *clientTransportCredentials) ClientHandshake(ctx context.Context, addr string, rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
    58  	var err error
    59  	host := tc.hostOverride
    60  	if host == "" {
    61  		// IMPORTANT: Don't wrap the errors returned from this method. gRPC expects to be
    62  		// able to check err.Temporary to spot temporary errors and reconnect when they happen.
    63  		host, _, err = net.SplitHostPort(addr)
    64  		if err != nil {
    65  			return nil, nil, err
    66  		}
    67  	}
    68  	conn := tls.Client(rawConn, &tls.Config{
    69  		ServerName:   host,
    70  		RootCAs:      tc.roots,
    71  		Certificates: tc.clients,
    72  	})
    73  	err = conn.HandshakeContext(ctx)
    74  	if err != nil {
    75  		_ = rawConn.Close()
    76  		return nil, nil, err
    77  	}
    78  	return conn, nil, nil
    79  }
    80  
    81  // ServerHandshake is not implemented for a `clientTransportCredentials`, use
    82  // a `serverTransportCredentials` if you require `ServerHandshake`.
    83  func (tc *clientTransportCredentials) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
    84  	return nil, nil, ErrServerHandshakeNop
    85  }
    86  
    87  // Info returns information about the transport protocol used
    88  func (tc *clientTransportCredentials) Info() credentials.ProtocolInfo {
    89  	return credentials.ProtocolInfo{
    90  		SecurityProtocol: "tls",
    91  		SecurityVersion:  "1.2", // We *only* support TLS 1.2
    92  	}
    93  }
    94  
    95  // GetRequestMetadata returns nil, nil since TLS credentials do not have metadata.
    96  func (tc *clientTransportCredentials) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
    97  	return nil, nil
    98  }
    99  
   100  // RequireTransportSecurity always returns true because TLS is transport security
   101  func (tc *clientTransportCredentials) RequireTransportSecurity() bool {
   102  	return true
   103  }
   104  
   105  // Clone returns a copy of the clientTransportCredentials
   106  func (tc *clientTransportCredentials) Clone() credentials.TransportCredentials {
   107  	return NewClientCredentials(tc.roots, tc.clients, tc.hostOverride)
   108  }
   109  
   110  // OverrideServerName is not implemented and here only to satisfy the interface
   111  func (tc *clientTransportCredentials) OverrideServerName(serverNameOverride string) error {
   112  	return ErrOverrideServerNameNop
   113  }
   114  
   115  // serverTransportCredentials is a grpc/credentials.TransportCredentials which supports
   116  // filtering acceptable peer connections by a list of accepted client certificate SANs
   117  type serverTransportCredentials struct {
   118  	serverConfig *tls.Config
   119  	acceptedSANs map[string]struct{}
   120  }
   121  
   122  // NewServerCredentials returns a new initialized grpc/credentials.TransportCredentials for server usage
   123  func NewServerCredentials(serverConfig *tls.Config, acceptedSANs map[string]struct{}) (credentials.TransportCredentials, error) {
   124  	if serverConfig == nil {
   125  		return nil, ErrNilServerConfig
   126  	}
   127  
   128  	return &serverTransportCredentials{serverConfig, acceptedSANs}, nil
   129  }
   130  
   131  // validateClient checks a peer's client certificate's SAN entries against
   132  // a list of accepted SANs. If the client certificate does not have a SAN on the
   133  // list it is rejected.
   134  //
   135  // Note 1: This function *only* verifies the SAN entries! Callers are expected to
   136  // have provided the `tls.ConnectionState` returned from a validate (e.g.
   137  // non-error producing) `conn.Handshake()`.
   138  //
   139  // Note 2: We do *not* consider the client certificate subject common name. The
   140  // CN field is deprecated and should be present as a DNS SAN!
   141  func (tc *serverTransportCredentials) validateClient(peerState tls.ConnectionState) error {
   142  	/*
   143  	 * If there's no list of accepted SANs, all clients are OK
   144  	 *
   145  	 * TODO(@cpu): This should be converted to a hard error at initialization time
   146  	 * once we have deployed & updated all gRPC configurations to have an accepted
   147  	 * SAN list configured
   148  	 */
   149  	if len(tc.acceptedSANs) == 0 {
   150  		return nil
   151  	}
   152  
   153  	// If `conn.Handshake()` is called before `validateClient` this should not
   154  	// occur. We return an error in this event primarily for unit tests that may
   155  	// call `validateClient` with manufactured & artificial connection states.
   156  	if len(peerState.PeerCertificates) < 1 {
   157  		return ErrEmptyPeerCerts
   158  	}
   159  
   160  	// Since we call `conn.Handshake()` before `validateClient` and ensure
   161  	// a non-error response we don't need to validate anything except the presence
   162  	// of an acceptable SAN in the leaf entry of `PeerCertificates`. The tls
   163  	// package's `serverHandshake` and in particular, `processCertsFromClient`
   164  	// will address everything else as an error returned from `Handshake()`.
   165  	leaf := peerState.PeerCertificates[0]
   166  
   167  	// Combine both the DNS and IP address subjectAlternativeNames into a single
   168  	// list for checking.
   169  	var receivedSANs []string
   170  	receivedSANs = append(receivedSANs, leaf.DNSNames...)
   171  	for _, ip := range leaf.IPAddresses {
   172  		receivedSANs = append(receivedSANs, ip.String())
   173  	}
   174  
   175  	for _, name := range receivedSANs {
   176  		if _, ok := tc.acceptedSANs[name]; ok {
   177  			return nil
   178  		}
   179  	}
   180  
   181  	// If none of the DNS or IP SANs on the leaf certificate matched the
   182  	// acceptable list, the client isn't valid and we error
   183  	var acceptableSANs []string
   184  	for k := range tc.acceptedSANs {
   185  		acceptableSANs = append(acceptableSANs, k)
   186  	}
   187  	return ErrSANNotAccepted{receivedSANs, acceptableSANs}
   188  }
   189  
   190  // ServerHandshake does the authentication handshake for servers. It returns
   191  // the authenticated connection and the corresponding auth information about
   192  // the connection.
   193  func (tc *serverTransportCredentials) ServerHandshake(rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
   194  	// Perform the server <- client TLS handshake. This will validate the peer's
   195  	// client certificate.
   196  	conn := tls.Server(rawConn, tc.serverConfig)
   197  	err := conn.Handshake()
   198  	if err != nil {
   199  		return nil, nil, err
   200  	}
   201  
   202  	// In addition to the validation from `conn.Handshake()` we apply further
   203  	// constraints on what constitutes a valid peer
   204  	err = tc.validateClient(conn.ConnectionState())
   205  	if err != nil {
   206  		return nil, nil, err
   207  	}
   208  
   209  	return conn, credentials.TLSInfo{State: conn.ConnectionState()}, nil
   210  }
   211  
   212  // ClientHandshake is not implemented for a `serverTransportCredentials`, use
   213  // a `clientTransportCredentials` if you require `ClientHandshake`.
   214  func (tc *serverTransportCredentials) ClientHandshake(ctx context.Context, addr string, rawConn net.Conn) (net.Conn, credentials.AuthInfo, error) {
   215  	return nil, nil, ErrClientHandshakeNop
   216  }
   217  
   218  // Info provides the ProtocolInfo of this TransportCredentials.
   219  func (tc *serverTransportCredentials) Info() credentials.ProtocolInfo {
   220  	return credentials.ProtocolInfo{
   221  		SecurityProtocol: "tls",
   222  		SecurityVersion:  "1.2", // We *only* support TLS 1.2
   223  	}
   224  }
   225  
   226  // GetRequestMetadata returns nil, nil since TLS credentials do not have metadata.
   227  func (tc *serverTransportCredentials) GetRequestMetadata(ctx context.Context, uri ...string) (map[string]string, error) {
   228  	return nil, nil
   229  }
   230  
   231  // RequireTransportSecurity always returns true because TLS is transport security
   232  func (tc *serverTransportCredentials) RequireTransportSecurity() bool {
   233  	return true
   234  }
   235  
   236  // Clone returns a copy of the serverTransportCredentials
   237  func (tc *serverTransportCredentials) Clone() credentials.TransportCredentials {
   238  	clone, _ := NewServerCredentials(tc.serverConfig, tc.acceptedSANs)
   239  	return clone
   240  }
   241  
   242  // OverrideServerName is not implemented and here only to satisfy the interface
   243  func (tc *serverTransportCredentials) OverrideServerName(serverNameOverride string) error {
   244  	return ErrOverrideServerNameNop
   245  }
   246  

View as plain text