...

Source file src/google.golang.org/grpc/internal/credentials/xds/handshake_info.go

Documentation: google.golang.org/grpc/internal/credentials/xds

     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/xds/matcher"
    35  	"google.golang.org/grpc/resolver"
    36  )
    37  
    38  func init() {
    39  	internal.GetXDSHandshakeInfoForTesting = GetHandshakeInfo
    40  }
    41  
    42  // handshakeAttrKey is the type used as the key to store HandshakeInfo in
    43  // the Attributes field of resolver.Address.
    44  type handshakeAttrKey struct{}
    45  
    46  // Equal reports whether the handshake info structs are identical.
    47  func (hi *HandshakeInfo) Equal(other *HandshakeInfo) bool {
    48  	if hi == nil && other == nil {
    49  		return true
    50  	}
    51  	if hi == nil || other == nil {
    52  		return false
    53  	}
    54  	if hi.rootProvider != other.rootProvider ||
    55  		hi.identityProvider != other.identityProvider ||
    56  		hi.requireClientCert != other.requireClientCert ||
    57  		len(hi.sanMatchers) != len(other.sanMatchers) {
    58  		return false
    59  	}
    60  	for i := range hi.sanMatchers {
    61  		if !hi.sanMatchers[i].Equal(other.sanMatchers[i]) {
    62  			return false
    63  		}
    64  	}
    65  	return true
    66  }
    67  
    68  // SetHandshakeInfo returns a copy of addr in which the Attributes field is
    69  // updated with hiPtr.
    70  func SetHandshakeInfo(addr resolver.Address, hiPtr *unsafe.Pointer) resolver.Address {
    71  	addr.Attributes = addr.Attributes.WithValue(handshakeAttrKey{}, hiPtr)
    72  	return addr
    73  }
    74  
    75  // GetHandshakeInfo returns a pointer to the *HandshakeInfo stored in attr.
    76  func GetHandshakeInfo(attr *attributes.Attributes) *unsafe.Pointer {
    77  	v := attr.Value(handshakeAttrKey{})
    78  	hi, _ := v.(*unsafe.Pointer)
    79  	return hi
    80  }
    81  
    82  // HandshakeInfo wraps all the security configuration required by client and
    83  // server handshake methods in xds credentials. The xDS implementation will be
    84  // responsible for populating these fields.
    85  type HandshakeInfo struct {
    86  	// All fields written at init time and read only after that, so no
    87  	// synchronization needed.
    88  	rootProvider      certprovider.Provider
    89  	identityProvider  certprovider.Provider
    90  	sanMatchers       []matcher.StringMatcher // Only on the client side.
    91  	requireClientCert bool                    // Only on server side.
    92  }
    93  
    94  // NewHandshakeInfo returns a new handshake info configured with the provided
    95  // options.
    96  func NewHandshakeInfo(rootProvider certprovider.Provider, identityProvider certprovider.Provider, sanMatchers []matcher.StringMatcher, requireClientCert bool) *HandshakeInfo {
    97  	return &HandshakeInfo{
    98  		rootProvider:      rootProvider,
    99  		identityProvider:  identityProvider,
   100  		sanMatchers:       sanMatchers,
   101  		requireClientCert: requireClientCert,
   102  	}
   103  }
   104  
   105  // UseFallbackCreds returns true when fallback credentials are to be used based
   106  // on the contents of the HandshakeInfo.
   107  func (hi *HandshakeInfo) UseFallbackCreds() bool {
   108  	if hi == nil {
   109  		return true
   110  	}
   111  	return hi.identityProvider == nil && hi.rootProvider == nil
   112  }
   113  
   114  // GetSANMatchersForTesting returns the SAN matchers stored in HandshakeInfo.
   115  // To be used only for testing purposes.
   116  func (hi *HandshakeInfo) GetSANMatchersForTesting() []matcher.StringMatcher {
   117  	return append([]matcher.StringMatcher{}, hi.sanMatchers...)
   118  }
   119  
   120  // ClientSideTLSConfig constructs a tls.Config to be used in a client-side
   121  // handshake based on the contents of the HandshakeInfo.
   122  func (hi *HandshakeInfo) ClientSideTLSConfig(ctx context.Context) (*tls.Config, error) {
   123  	// On the client side, rootProvider is mandatory. IdentityProvider is
   124  	// optional based on whether the client is doing TLS or mTLS.
   125  	if hi.rootProvider == nil {
   126  		return nil, errors.New("xds: CertificateProvider to fetch trusted roots is missing, cannot perform TLS handshake. Please check configuration on the management server")
   127  	}
   128  	// Since the call to KeyMaterial() can block, we read the providers under
   129  	// the lock but call the actual function after releasing the lock.
   130  	rootProv, idProv := hi.rootProvider, hi.identityProvider
   131  
   132  	// InsecureSkipVerify needs to be set to true because we need to perform
   133  	// custom verification to check the SAN on the received certificate.
   134  	// Currently the Go stdlib does complete verification of the cert (which
   135  	// includes hostname verification) or none. We are forced to go with the
   136  	// latter and perform the normal cert validation ourselves.
   137  	cfg := &tls.Config{
   138  		InsecureSkipVerify: true,
   139  		NextProtos:         []string{"h2"},
   140  	}
   141  
   142  	km, err := rootProv.KeyMaterial(ctx)
   143  	if err != nil {
   144  		return nil, fmt.Errorf("xds: fetching trusted roots from CertificateProvider failed: %v", err)
   145  	}
   146  	cfg.RootCAs = km.Roots
   147  
   148  	if idProv != nil {
   149  		km, err := idProv.KeyMaterial(ctx)
   150  		if err != nil {
   151  			return nil, fmt.Errorf("xds: fetching identity certificates from CertificateProvider failed: %v", err)
   152  		}
   153  		cfg.Certificates = km.Certs
   154  	}
   155  	return cfg, nil
   156  }
   157  
   158  // ServerSideTLSConfig constructs a tls.Config to be used in a server-side
   159  // handshake based on the contents of the HandshakeInfo.
   160  func (hi *HandshakeInfo) ServerSideTLSConfig(ctx context.Context) (*tls.Config, error) {
   161  	cfg := &tls.Config{
   162  		ClientAuth: tls.NoClientCert,
   163  		NextProtos: []string{"h2"},
   164  	}
   165  	// On the server side, identityProvider is mandatory. RootProvider is
   166  	// optional based on whether the server is doing TLS or mTLS.
   167  	if hi.identityProvider == nil {
   168  		return nil, errors.New("xds: CertificateProvider to fetch identity certificate is missing, cannot perform TLS handshake. Please check configuration on the management server")
   169  	}
   170  	// Since the call to KeyMaterial() can block, we read the providers under
   171  	// the lock but call the actual function after releasing the lock.
   172  	rootProv, idProv := hi.rootProvider, hi.identityProvider
   173  	if hi.requireClientCert {
   174  		cfg.ClientAuth = tls.RequireAndVerifyClientCert
   175  	}
   176  
   177  	// identityProvider is mandatory on the server side.
   178  	km, err := idProv.KeyMaterial(ctx)
   179  	if err != nil {
   180  		return nil, fmt.Errorf("xds: fetching identity certificates from CertificateProvider failed: %v", err)
   181  	}
   182  	cfg.Certificates = km.Certs
   183  
   184  	if rootProv != nil {
   185  		km, err := rootProv.KeyMaterial(ctx)
   186  		if err != nil {
   187  			return nil, fmt.Errorf("xds: fetching trusted roots from CertificateProvider failed: %v", err)
   188  		}
   189  		cfg.ClientCAs = km.Roots
   190  	}
   191  	return cfg, nil
   192  }
   193  
   194  // MatchingSANExists returns true if the SANs contained in cert match the
   195  // criteria enforced by the list of SAN matchers in HandshakeInfo.
   196  //
   197  // If the list of SAN matchers in the HandshakeInfo is empty, this function
   198  // returns true for all input certificates.
   199  func (hi *HandshakeInfo) MatchingSANExists(cert *x509.Certificate) bool {
   200  	if len(hi.sanMatchers) == 0 {
   201  		return true
   202  	}
   203  
   204  	// SANs can be specified in any of these four fields on the parsed cert.
   205  	for _, san := range cert.DNSNames {
   206  		if hi.matchSAN(san, true) {
   207  			return true
   208  		}
   209  	}
   210  	for _, san := range cert.EmailAddresses {
   211  		if hi.matchSAN(san, false) {
   212  			return true
   213  		}
   214  	}
   215  	for _, san := range cert.IPAddresses {
   216  		if hi.matchSAN(san.String(), false) {
   217  			return true
   218  		}
   219  	}
   220  	for _, san := range cert.URIs {
   221  		if hi.matchSAN(san.String(), false) {
   222  			return true
   223  		}
   224  	}
   225  	return false
   226  }
   227  
   228  // Caller must hold mu.
   229  func (hi *HandshakeInfo) matchSAN(san string, isDNS bool) bool {
   230  	for _, matcher := range hi.sanMatchers {
   231  		if em := matcher.ExactMatch(); em != "" && isDNS {
   232  			// This is a special case which is documented in the xDS protos.
   233  			// If the DNS SAN is a wildcard entry, and the match criteria is
   234  			// `exact`, then we need to perform DNS wildcard matching
   235  			// instead of regular string comparison.
   236  			if dnsMatch(em, san) {
   237  				return true
   238  			}
   239  			continue
   240  		}
   241  		if matcher.Match(san) {
   242  			return true
   243  		}
   244  	}
   245  	return false
   246  }
   247  
   248  // dnsMatch implements a DNS wildcard matching algorithm based on RFC2828 and
   249  // grpc-java's implementation in `OkHostnameVerifier` class.
   250  //
   251  // NOTE: Here the `host` argument is the one from the set of string matchers in
   252  // the xDS proto and the `san` argument is a DNS SAN from the certificate, and
   253  // this is the one which can potentially contain a wildcard pattern.
   254  func dnsMatch(host, san string) bool {
   255  	// Add trailing "." and turn them into absolute domain names.
   256  	if !strings.HasSuffix(host, ".") {
   257  		host += "."
   258  	}
   259  	if !strings.HasSuffix(san, ".") {
   260  		san += "."
   261  	}
   262  	// Domain names are case-insensitive.
   263  	host = strings.ToLower(host)
   264  	san = strings.ToLower(san)
   265  
   266  	// If san does not contain a wildcard, do exact match.
   267  	if !strings.Contains(san, "*") {
   268  		return host == san
   269  	}
   270  
   271  	// Wildcard dns matching rules
   272  	// - '*' is only permitted in the left-most label and must be the only
   273  	//   character in that label. For example, *.example.com is permitted, while
   274  	//   *a.example.com, a*.example.com, a*b.example.com, a.*.example.com are
   275  	//   not permitted.
   276  	// - '*' matches a single domain name component. For example, *.example.com
   277  	//   matches test.example.com but does not match sub.test.example.com.
   278  	// - Wildcard patterns for single-label domain names are not permitted.
   279  	if san == "*." || !strings.HasPrefix(san, "*.") || strings.Contains(san[1:], "*") {
   280  		return false
   281  	}
   282  	// Optimization: at this point, we know that the san contains a '*' and
   283  	// is the first domain component of san. So, the host name must be at
   284  	// least as long as the san to be able to match.
   285  	if len(host) < len(san) {
   286  		return false
   287  	}
   288  	// Hostname must end with the non-wildcard portion of san.
   289  	if !strings.HasSuffix(host, san[1:]) {
   290  		return false
   291  	}
   292  	// At this point we know that the hostName and san share the same suffix
   293  	// (the non-wildcard portion of san). Now, we just need to make sure
   294  	// that the '*' does not match across domain components.
   295  	hostPrefix := strings.TrimSuffix(host, san[1:])
   296  	return !strings.Contains(hostPrefix, ".")
   297  }
   298  

View as plain text