google.golang.org/grpc@v1.74.2/internal/credentials/xds/handshake_info.go (about)

     1  /*
     2   *
     3   * Copyright 2020 gRPC authors.
     4   *
     5   * Licensed under the Apache License, Version 2.0 (the "License");
     6   * you may not use this file except in compliance with the License.
     7   * You may obtain a copy of the License at
     8   *
     9   *     http://www.apache.org/licenses/LICENSE-2.0
    10   *
    11   * Unless required by applicable law or agreed to in writing, software
    12   * distributed under the License is distributed on an "AS IS" BASIS,
    13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    14   * See the License for the specific language governing permissions and
    15   * limitations under the License.
    16   *
    17   */
    18  
    19  // Package xds contains non-user facing functionality of the xds credentials.
    20  package xds
    21  
    22  import (
    23  	"context"
    24  	"crypto/tls"
    25  	"crypto/x509"
    26  	"errors"
    27  	"fmt"
    28  	"strings"
    29  	"unsafe"
    30  
    31  	"google.golang.org/grpc/attributes"
    32  	"google.golang.org/grpc/credentials/tls/certprovider"
    33  	"google.golang.org/grpc/internal"
    34  	"google.golang.org/grpc/internal/credentials/spiffe"
    35  	"google.golang.org/grpc/internal/xds/matcher"
    36  	"google.golang.org/grpc/resolver"
    37  )
    38  
    39  func init() {
    40  	internal.GetXDSHandshakeInfoForTesting = GetHandshakeInfo
    41  }
    42  
    43  // handshakeAttrKey is the type used as the key to store HandshakeInfo in
    44  // the Attributes field of resolver.Address.
    45  type handshakeAttrKey struct{}
    46  
    47  // Equal reports whether the handshake info structs are identical.
    48  func (hi *HandshakeInfo) Equal(other *HandshakeInfo) bool {
    49  	if hi == nil && other == nil {
    50  		return true
    51  	}
    52  	if hi == nil || other == nil {
    53  		return false
    54  	}
    55  	if hi.rootProvider != other.rootProvider ||
    56  		hi.identityProvider != other.identityProvider ||
    57  		hi.requireClientCert != other.requireClientCert ||
    58  		len(hi.sanMatchers) != len(other.sanMatchers) {
    59  		return false
    60  	}
    61  	for i := range hi.sanMatchers {
    62  		if !hi.sanMatchers[i].Equal(other.sanMatchers[i]) {
    63  			return false
    64  		}
    65  	}
    66  	return true
    67  }
    68  
    69  // SetHandshakeInfo returns a copy of addr in which the Attributes field is
    70  // updated with hiPtr.
    71  func SetHandshakeInfo(addr resolver.Address, hiPtr *unsafe.Pointer) resolver.Address {
    72  	addr.Attributes = addr.Attributes.WithValue(handshakeAttrKey{}, hiPtr)
    73  	return addr
    74  }
    75  
    76  // GetHandshakeInfo returns a pointer to the *HandshakeInfo stored in attr.
    77  func GetHandshakeInfo(attr *attributes.Attributes) *unsafe.Pointer {
    78  	v := attr.Value(handshakeAttrKey{})
    79  	hi, _ := v.(*unsafe.Pointer)
    80  	return hi
    81  }
    82  
    83  // HandshakeInfo wraps all the security configuration required by client and
    84  // server handshake methods in xds credentials. The xDS implementation will be
    85  // responsible for populating these fields.
    86  type HandshakeInfo struct {
    87  	// All fields written at init time and read only after that, so no
    88  	// synchronization needed.
    89  	rootProvider      certprovider.Provider
    90  	identityProvider  certprovider.Provider
    91  	sanMatchers       []matcher.StringMatcher // Only on the client side.
    92  	requireClientCert bool                    // Only on server side.
    93  }
    94  
    95  // NewHandshakeInfo returns a new handshake info configured with the provided
    96  // options.
    97  func NewHandshakeInfo(rootProvider certprovider.Provider, identityProvider certprovider.Provider, sanMatchers []matcher.StringMatcher, requireClientCert bool) *HandshakeInfo {
    98  	return &HandshakeInfo{
    99  		rootProvider:      rootProvider,
   100  		identityProvider:  identityProvider,
   101  		sanMatchers:       sanMatchers,
   102  		requireClientCert: requireClientCert,
   103  	}
   104  }
   105  
   106  // UseFallbackCreds returns true when fallback credentials are to be used based
   107  // on the contents of the HandshakeInfo.
   108  func (hi *HandshakeInfo) UseFallbackCreds() bool {
   109  	if hi == nil {
   110  		return true
   111  	}
   112  	return hi.identityProvider == nil && hi.rootProvider == nil
   113  }
   114  
   115  // GetSANMatchersForTesting returns the SAN matchers stored in HandshakeInfo.
   116  // To be used only for testing purposes.
   117  func (hi *HandshakeInfo) GetSANMatchersForTesting() []matcher.StringMatcher {
   118  	return append([]matcher.StringMatcher{}, hi.sanMatchers...)
   119  }
   120  
   121  // ClientSideTLSConfig constructs a tls.Config to be used in a client-side
   122  // handshake based on the contents of the HandshakeInfo.
   123  func (hi *HandshakeInfo) ClientSideTLSConfig(ctx context.Context) (*tls.Config, error) {
   124  	// On the client side, rootProvider is mandatory. IdentityProvider is
   125  	// optional based on whether the client is doing TLS or mTLS.
   126  	if hi.rootProvider == nil {
   127  		return nil, errors.New("xds: CertificateProvider to fetch trusted roots is missing, cannot perform TLS handshake. Please check configuration on the management server")
   128  	}
   129  	// Since the call to KeyMaterial() can block, we read the providers under
   130  	// the lock but call the actual function after releasing the lock.
   131  	rootProv, idProv := hi.rootProvider, hi.identityProvider
   132  
   133  	// InsecureSkipVerify needs to be set to true because we need to perform
   134  	// custom verification to check the SAN on the received certificate.
   135  	// Currently the Go stdlib does complete verification of the cert (which
   136  	// includes hostname verification) or none. We are forced to go with the
   137  	// latter and perform the normal cert validation ourselves.
   138  	cfg := &tls.Config{
   139  		InsecureSkipVerify: true,
   140  		NextProtos:         []string{"h2"},
   141  	}
   142  
   143  	km, err := rootProv.KeyMaterial(ctx)
   144  	if err != nil {
   145  		return nil, fmt.Errorf("xds: fetching trusted roots from CertificateProvider failed: %v", err)
   146  	}
   147  	cfg.RootCAs = km.Roots
   148  	cfg.VerifyPeerCertificate = hi.buildVerifyFunc(km, true)
   149  
   150  	if idProv != nil {
   151  		km, err := idProv.KeyMaterial(ctx)
   152  		if err != nil {
   153  			return nil, fmt.Errorf("xds: fetching identity certificates from CertificateProvider failed: %v", err)
   154  		}
   155  		cfg.Certificates = km.Certs
   156  	}
   157  	return cfg, nil
   158  }
   159  
   160  func (hi *HandshakeInfo) buildVerifyFunc(km *certprovider.KeyMaterial, isClient bool) func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
   161  	return func(rawCerts [][]byte, _ [][]*x509.Certificate) error {
   162  		// Parse all raw certificates presented by the peer.
   163  		var certs []*x509.Certificate
   164  		for _, rc := range rawCerts {
   165  			cert, err := x509.ParseCertificate(rc)
   166  			if err != nil {
   167  				return err
   168  			}
   169  			certs = append(certs, cert)
   170  		}
   171  
   172  		// Build the intermediates list and verify that the leaf certificate is
   173  		// signed by one of the root certificates. If a SPIFFE Bundle Map is
   174  		// configured, it is used to get the root certs. Otherwise, the
   175  		// configured roots in the root provider are used.
   176  		intermediates := x509.NewCertPool()
   177  		for _, cert := range certs[1:] {
   178  			intermediates.AddCert(cert)
   179  		}
   180  		roots := km.Roots
   181  		// If a SPIFFE Bundle Map is configured, find the roots for the trust
   182  		// domain of the leaf certificate.
   183  		if km.SPIFFEBundleMap != nil {
   184  			var err error
   185  			roots, err = spiffe.GetRootsFromSPIFFEBundleMap(km.SPIFFEBundleMap, certs[0])
   186  			if err != nil {
   187  				return err
   188  			}
   189  		}
   190  		opts := x509.VerifyOptions{
   191  			Roots:         roots,
   192  			Intermediates: intermediates,
   193  			KeyUsages:     []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth},
   194  		}
   195  		if isClient {
   196  			opts.KeyUsages = []x509.ExtKeyUsage{x509.ExtKeyUsageServerAuth}
   197  		} else {
   198  			opts.KeyUsages = []x509.ExtKeyUsage{x509.ExtKeyUsageClientAuth}
   199  		}
   200  		if _, err := certs[0].Verify(opts); err != nil {
   201  			return err
   202  		}
   203  		// The SANs sent by the MeshCA are encoded as SPIFFE IDs. We need to
   204  		// only look at the SANs on the leaf cert.
   205  		if cert := certs[0]; !hi.MatchingSANExists(cert) {
   206  			// TODO: Print the complete certificate once the x509 package
   207  			// supports a String() method on the Certificate type.
   208  			return fmt.Errorf("xds: received SANs {DNSNames: %v, EmailAddresses: %v, IPAddresses: %v, URIs: %v} do not match any of the accepted SANs", cert.DNSNames, cert.EmailAddresses, cert.IPAddresses, cert.URIs)
   209  		}
   210  		return nil
   211  	}
   212  }
   213  
   214  // ServerSideTLSConfig constructs a tls.Config to be used in a server-side
   215  // handshake based on the contents of the HandshakeInfo.
   216  func (hi *HandshakeInfo) ServerSideTLSConfig(ctx context.Context) (*tls.Config, error) {
   217  	cfg := &tls.Config{
   218  		ClientAuth: tls.NoClientCert,
   219  		NextProtos: []string{"h2"},
   220  	}
   221  	// On the server side, identityProvider is mandatory. RootProvider is
   222  	// optional based on whether the server is doing TLS or mTLS.
   223  	if hi.identityProvider == nil {
   224  		return nil, errors.New("xds: CertificateProvider to fetch identity certificate is missing, cannot perform TLS handshake. Please check configuration on the management server")
   225  	}
   226  	// Since the call to KeyMaterial() can block, we read the providers under
   227  	// the lock but call the actual function after releasing the lock.
   228  	rootProv, idProv := hi.rootProvider, hi.identityProvider
   229  	if hi.requireClientCert {
   230  		cfg.ClientAuth = tls.RequireAndVerifyClientCert
   231  	}
   232  
   233  	// identityProvider is mandatory on the server side.
   234  	km, err := idProv.KeyMaterial(ctx)
   235  	if err != nil {
   236  		return nil, fmt.Errorf("xds: fetching identity certificates from CertificateProvider failed: %v", err)
   237  	}
   238  	cfg.Certificates = km.Certs
   239  
   240  	if rootProv != nil {
   241  		km, err := rootProv.KeyMaterial(ctx)
   242  		if err != nil {
   243  			return nil, fmt.Errorf("xds: fetching trusted roots from CertificateProvider failed: %v", err)
   244  		}
   245  		if km.SPIFFEBundleMap != nil && hi.requireClientCert {
   246  			// ClientAuth, if set greater than tls.RequireAnyClientCert, must be
   247  			// dropped to tls.RequireAnyClientCert so that custom verification
   248  			// to use SPIFFE Bundles is done.
   249  			cfg.ClientAuth = tls.RequireAnyClientCert
   250  			cfg.VerifyPeerCertificate = hi.buildVerifyFunc(km, false)
   251  		} else {
   252  			cfg.ClientCAs = km.Roots
   253  		}
   254  	}
   255  	return cfg, nil
   256  }
   257  
   258  // MatchingSANExists returns true if the SANs contained in cert match the
   259  // criteria enforced by the list of SAN matchers in HandshakeInfo.
   260  //
   261  // If the list of SAN matchers in the HandshakeInfo is empty, this function
   262  // returns true for all input certificates.
   263  func (hi *HandshakeInfo) MatchingSANExists(cert *x509.Certificate) bool {
   264  	if len(hi.sanMatchers) == 0 {
   265  		return true
   266  	}
   267  
   268  	// SANs can be specified in any of these four fields on the parsed cert.
   269  	for _, san := range cert.DNSNames {
   270  		if hi.matchSAN(san, true) {
   271  			return true
   272  		}
   273  	}
   274  	for _, san := range cert.EmailAddresses {
   275  		if hi.matchSAN(san, false) {
   276  			return true
   277  		}
   278  	}
   279  	for _, san := range cert.IPAddresses {
   280  		if hi.matchSAN(san.String(), false) {
   281  			return true
   282  		}
   283  	}
   284  	for _, san := range cert.URIs {
   285  		if hi.matchSAN(san.String(), false) {
   286  			return true
   287  		}
   288  	}
   289  	return false
   290  }
   291  
   292  // Caller must hold mu.
   293  func (hi *HandshakeInfo) matchSAN(san string, isDNS bool) bool {
   294  	for _, matcher := range hi.sanMatchers {
   295  		if em := matcher.ExactMatch(); em != "" && isDNS {
   296  			// This is a special case which is documented in the xDS protos.
   297  			// If the DNS SAN is a wildcard entry, and the match criteria is
   298  			// `exact`, then we need to perform DNS wildcard matching
   299  			// instead of regular string comparison.
   300  			if dnsMatch(em, san) {
   301  				return true
   302  			}
   303  			continue
   304  		}
   305  		if matcher.Match(san) {
   306  			return true
   307  		}
   308  	}
   309  	return false
   310  }
   311  
   312  // dnsMatch implements a DNS wildcard matching algorithm based on RFC2828 and
   313  // grpc-java's implementation in `OkHostnameVerifier` class.
   314  //
   315  // NOTE: Here the `host` argument is the one from the set of string matchers in
   316  // the xDS proto and the `san` argument is a DNS SAN from the certificate, and
   317  // this is the one which can potentially contain a wildcard pattern.
   318  func dnsMatch(host, san string) bool {
   319  	// Add trailing "." and turn them into absolute domain names.
   320  	if !strings.HasSuffix(host, ".") {
   321  		host += "."
   322  	}
   323  	if !strings.HasSuffix(san, ".") {
   324  		san += "."
   325  	}
   326  	// Domain names are case-insensitive.
   327  	host = strings.ToLower(host)
   328  	san = strings.ToLower(san)
   329  
   330  	// If san does not contain a wildcard, do exact match.
   331  	if !strings.Contains(san, "*") {
   332  		return host == san
   333  	}
   334  
   335  	// Wildcard dns matching rules
   336  	// - '*' is only permitted in the left-most label and must be the only
   337  	//   character in that label. For example, *.example.com is permitted, while
   338  	//   *a.example.com, a*.example.com, a*b.example.com, a.*.example.com are
   339  	//   not permitted.
   340  	// - '*' matches a single domain name component. For example, *.example.com
   341  	//   matches test.example.com but does not match sub.test.example.com.
   342  	// - Wildcard patterns for single-label domain names are not permitted.
   343  	if san == "*." || !strings.HasPrefix(san, "*.") || strings.Contains(san[1:], "*") {
   344  		return false
   345  	}
   346  	// Optimization: at this point, we know that the san contains a '*' and
   347  	// is the first domain component of san. So, the host name must be at
   348  	// least as long as the san to be able to match.
   349  	if len(host) < len(san) {
   350  		return false
   351  	}
   352  	// Hostname must end with the non-wildcard portion of san.
   353  	if !strings.HasSuffix(host, san[1:]) {
   354  		return false
   355  	}
   356  	// At this point we know that the hostName and san share the same suffix
   357  	// (the non-wildcard portion of san). Now, we just need to make sure
   358  	// that the '*' does not match across domain components.
   359  	hostPrefix := strings.TrimSuffix(host, san[1:])
   360  	return !strings.Contains(hostPrefix, ".")
   361  }