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