google.golang.org/grpc@v1.62.1/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/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 }