github.com/hashicorp/vault/sdk@v0.11.0/helper/ocsp/client.go (about) 1 // Copyright (c) 2017-2022 Snowflake Computing Inc. All rights reserved. 2 3 package ocsp 4 5 import ( 6 "bytes" 7 "context" 8 "crypto" 9 "crypto/tls" 10 "crypto/x509" 11 "crypto/x509/pkix" 12 "encoding/asn1" 13 "encoding/base64" 14 "errors" 15 "fmt" 16 "io" 17 "math/big" 18 "net" 19 "net/http" 20 "net/url" 21 "strconv" 22 "strings" 23 "sync" 24 "time" 25 26 "github.com/hashicorp/go-hclog" 27 "github.com/hashicorp/go-multierror" 28 "github.com/hashicorp/go-retryablehttp" 29 lru "github.com/hashicorp/golang-lru" 30 "github.com/hashicorp/vault/sdk/helper/certutil" 31 "golang.org/x/crypto/ocsp" 32 ) 33 34 // FailOpenMode is OCSP fail open mode. FailOpenTrue by default and may 35 // set to ocspModeFailClosed for fail closed mode 36 type FailOpenMode uint32 37 38 type requestFunc func(method, urlStr string, body interface{}) (*retryablehttp.Request, error) 39 40 type clientInterface interface { 41 Do(req *retryablehttp.Request) (*http.Response, error) 42 } 43 44 const ( 45 httpHeaderContentType = "Content-Type" 46 httpHeaderAccept = "accept" 47 httpHeaderContentLength = "Content-Length" 48 httpHeaderHost = "Host" 49 ocspRequestContentType = "application/ocsp-request" 50 ocspResponseContentType = "application/ocsp-response" 51 ) 52 53 const ( 54 ocspFailOpenNotSet FailOpenMode = iota 55 // FailOpenTrue represents OCSP fail open mode. 56 FailOpenTrue 57 // FailOpenFalse represents OCSP fail closed mode. 58 FailOpenFalse 59 ) 60 61 const ( 62 ocspModeFailOpen = "FAIL_OPEN" 63 ocspModeFailClosed = "FAIL_CLOSED" 64 ocspModeInsecure = "INSECURE" 65 ) 66 67 const ocspCacheKey = "ocsp_cache" 68 69 const ( 70 // defaultOCSPResponderTimeout is the total timeout for OCSP responder. 71 defaultOCSPResponderTimeout = 10 * time.Second 72 ) 73 74 const ( 75 // cacheExpire specifies cache data expiration time in seconds. 76 cacheExpire = float64(24 * 60 * 60) 77 ) 78 79 type ocspCachedResponse struct { 80 time float64 81 producedAt float64 82 thisUpdate float64 83 nextUpdate float64 84 status ocspStatusCode 85 } 86 87 type Client struct { 88 // caRoot includes the CA certificates. 89 caRoot map[string]*x509.Certificate 90 // certPool includes the CA certificates. 91 certPool *x509.CertPool 92 ocspResponseCache *lru.TwoQueueCache 93 ocspResponseCacheLock sync.RWMutex 94 // cacheUpdated is true if the memory cache is updated 95 cacheUpdated bool 96 logFactory func() hclog.Logger 97 } 98 99 type ocspStatusCode int 100 101 type ocspStatus struct { 102 code ocspStatusCode 103 err error 104 } 105 106 const ( 107 ocspSuccess ocspStatusCode = 0 108 ocspStatusGood ocspStatusCode = -1 109 ocspStatusRevoked ocspStatusCode = -2 110 ocspStatusUnknown ocspStatusCode = -3 111 ocspStatusOthers ocspStatusCode = -4 112 ocspFailedDecomposeRequest ocspStatusCode = -5 113 ocspInvalidValidity ocspStatusCode = -6 114 ocspMissedCache ocspStatusCode = -7 115 ocspCacheExpired ocspStatusCode = -8 116 ) 117 118 // copied from crypto/ocsp.go 119 type certID struct { 120 HashAlgorithm pkix.AlgorithmIdentifier 121 NameHash []byte 122 IssuerKeyHash []byte 123 SerialNumber *big.Int 124 } 125 126 // cache key 127 type certIDKey struct { 128 NameHash string 129 IssuerKeyHash string 130 SerialNumber string 131 } 132 133 // copied from crypto/ocsp 134 var hashOIDs = map[crypto.Hash]asn1.ObjectIdentifier{ 135 crypto.SHA1: asn1.ObjectIdentifier([]int{1, 3, 14, 3, 2, 26}), 136 crypto.SHA256: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 1}), 137 crypto.SHA384: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 2}), 138 crypto.SHA512: asn1.ObjectIdentifier([]int{2, 16, 840, 1, 101, 3, 4, 2, 3}), 139 } 140 141 // copied from crypto/ocsp 142 func getOIDFromHashAlgorithm(target crypto.Hash) (asn1.ObjectIdentifier, error) { 143 for hash, oid := range hashOIDs { 144 if hash == target { 145 return oid, nil 146 } 147 } 148 return nil, fmt.Errorf("no valid OID is found for the hash algorithm: %v", target) 149 } 150 151 func (c *Client) ClearCache() { 152 c.ocspResponseCache.Purge() 153 } 154 155 func (c *Client) getHashAlgorithmFromOID(target pkix.AlgorithmIdentifier) crypto.Hash { 156 for hash, oid := range hashOIDs { 157 if oid.Equal(target.Algorithm) { 158 return hash 159 } 160 } 161 // no valid hash algorithm is found for the oid. Falling back to SHA1 162 return crypto.SHA1 163 } 164 165 // isInValidityRange checks the validity 166 func isInValidityRange(currTime, nextUpdate time.Time) bool { 167 return !nextUpdate.IsZero() && !currTime.After(nextUpdate) 168 } 169 170 func extractCertIDKeyFromRequest(ocspReq []byte) (*certIDKey, *ocspStatus) { 171 r, err := ocsp.ParseRequest(ocspReq) 172 if err != nil { 173 return nil, &ocspStatus{ 174 code: ocspFailedDecomposeRequest, 175 err: err, 176 } 177 } 178 179 // encode CertID, used as a key in the cache 180 encodedCertID := &certIDKey{ 181 base64.StdEncoding.EncodeToString(r.IssuerNameHash), 182 base64.StdEncoding.EncodeToString(r.IssuerKeyHash), 183 r.SerialNumber.String(), 184 } 185 return encodedCertID, &ocspStatus{ 186 code: ocspSuccess, 187 } 188 } 189 190 func (c *Client) encodeCertIDKey(certIDKeyBase64 string) (*certIDKey, error) { 191 r, err := base64.StdEncoding.DecodeString(certIDKeyBase64) 192 if err != nil { 193 return nil, err 194 } 195 var cid certID 196 rest, err := asn1.Unmarshal(r, &cid) 197 if err != nil { 198 // error in parsing 199 return nil, err 200 } 201 if len(rest) > 0 { 202 // extra bytes to the end 203 return nil, err 204 } 205 return &certIDKey{ 206 base64.StdEncoding.EncodeToString(cid.NameHash), 207 base64.StdEncoding.EncodeToString(cid.IssuerKeyHash), 208 cid.SerialNumber.String(), 209 }, nil 210 } 211 212 func (c *Client) checkOCSPResponseCache(encodedCertID *certIDKey, subject, issuer *x509.Certificate) (*ocspStatus, error) { 213 c.ocspResponseCacheLock.RLock() 214 var cacheValue *ocspCachedResponse 215 v, ok := c.ocspResponseCache.Get(*encodedCertID) 216 if ok { 217 cacheValue = v.(*ocspCachedResponse) 218 } 219 c.ocspResponseCacheLock.RUnlock() 220 221 status, err := c.extractOCSPCacheResponseValue(cacheValue, subject, issuer) 222 if err != nil { 223 return nil, err 224 } 225 if !isValidOCSPStatus(status.code) { 226 c.deleteOCSPCache(encodedCertID) 227 } 228 return status, err 229 } 230 231 func (c *Client) deleteOCSPCache(encodedCertID *certIDKey) { 232 c.ocspResponseCacheLock.Lock() 233 c.ocspResponseCache.Remove(*encodedCertID) 234 c.cacheUpdated = true 235 c.ocspResponseCacheLock.Unlock() 236 } 237 238 func validateOCSP(ocspRes *ocsp.Response) (*ocspStatus, error) { 239 curTime := time.Now() 240 241 if ocspRes == nil { 242 return nil, errors.New("OCSP Response is nil") 243 } 244 if !isInValidityRange(curTime, ocspRes.NextUpdate) { 245 return &ocspStatus{ 246 code: ocspInvalidValidity, 247 err: fmt.Errorf("invalid validity: producedAt: %v, thisUpdate: %v, nextUpdate: %v", ocspRes.ProducedAt, ocspRes.ThisUpdate, ocspRes.NextUpdate), 248 }, nil 249 } 250 return returnOCSPStatus(ocspRes), nil 251 } 252 253 func returnOCSPStatus(ocspRes *ocsp.Response) *ocspStatus { 254 switch ocspRes.Status { 255 case ocsp.Good: 256 return &ocspStatus{ 257 code: ocspStatusGood, 258 err: nil, 259 } 260 case ocsp.Revoked: 261 return &ocspStatus{ 262 code: ocspStatusRevoked, 263 } 264 case ocsp.Unknown: 265 return &ocspStatus{ 266 code: ocspStatusUnknown, 267 err: errors.New("OCSP status unknown."), 268 } 269 default: 270 return &ocspStatus{ 271 code: ocspStatusOthers, 272 err: fmt.Errorf("OCSP others. %v", ocspRes.Status), 273 } 274 } 275 } 276 277 // retryOCSP is the second level of retry method if the returned contents are corrupted. It often happens with OCSP 278 // serer and retry helps. 279 func (c *Client) retryOCSP( 280 ctx context.Context, 281 client clientInterface, 282 req requestFunc, 283 ocspHost *url.URL, 284 headers map[string]string, 285 reqBody []byte, 286 issuer *x509.Certificate, 287 ) (ocspRes *ocsp.Response, ocspResBytes []byte, ocspS *ocspStatus, retErr error) { 288 doRequest := func(request *retryablehttp.Request) (*http.Response, error) { 289 if request != nil { 290 request = request.WithContext(ctx) 291 for k, v := range headers { 292 request.Header[k] = append(request.Header[k], v) 293 } 294 } 295 res, err := client.Do(request) 296 if err != nil { 297 return nil, err 298 } 299 c.Logger().Debug("StatusCode from OCSP Server:", "statusCode", res.StatusCode) 300 return res, err 301 } 302 303 for _, method := range []string{"GET", "POST"} { 304 reqUrl := *ocspHost 305 var body []byte 306 307 switch method { 308 case "GET": 309 reqUrl.Path = reqUrl.Path + "/" + base64.StdEncoding.EncodeToString(reqBody) 310 case "POST": 311 body = reqBody 312 default: 313 // Programming error; all request/systems errors are multierror 314 // and appended. 315 return nil, nil, nil, fmt.Errorf("unknown request method: %v", method) 316 } 317 318 var res *http.Response 319 request, err := req(method, reqUrl.String(), bytes.NewBuffer(body)) 320 if err != nil { 321 err = fmt.Errorf("error creating %v request: %w", method, err) 322 retErr = multierror.Append(retErr, err) 323 continue 324 } 325 if res, err = doRequest(request); err != nil { 326 err = fmt.Errorf("error doing %v request: %w", method, err) 327 retErr = multierror.Append(retErr, err) 328 continue 329 } else { 330 defer res.Body.Close() 331 } 332 333 if res.StatusCode != http.StatusOK { 334 err = fmt.Errorf("HTTP code is not OK on %v request. %v: %v", method, res.StatusCode, res.Status) 335 retErr = multierror.Append(retErr, err) 336 continue 337 } 338 339 ocspResBytes, err = io.ReadAll(res.Body) 340 if err != nil { 341 err = fmt.Errorf("error reading %v request body: %w", method, err) 342 retErr = multierror.Append(retErr, err) 343 continue 344 } 345 346 // Reading an OCSP response shouldn't be fatal. A misconfigured 347 // endpoint might return invalid results for e.g., GET but return 348 // valid results for POST on retry. This could happen if e.g., the 349 // server responds with JSON. 350 ocspRes, err = ocsp.ParseResponse(ocspResBytes /*issuer = */, nil /* !!unsafe!! */) 351 if err != nil { 352 err = fmt.Errorf("error parsing %v OCSP response: %w", method, err) 353 retErr = multierror.Append(retErr, err) 354 continue 355 } 356 357 // Above, we use the unsafe issuer=nil parameter to ocsp.ParseResponse 358 // because Go's library does the wrong thing. 359 // 360 // Here, we lack a full chain, but we know we trust the parent issuer, 361 // so if the Go library incorrectly discards useful certificates, we 362 // likely cannot verify this without passing through the full chain 363 // back to the root. 364 // 365 // Instead, take one of two paths: 1. if there is no certificate in 366 // the ocspRes, verify the OCSP response directly with our trusted 367 // issuer certificate, or 2. if there is a certificate, either verify 368 // it directly matches our trusted issuer certificate, or verify it 369 // is signed by our trusted issuer certificate. 370 // 371 // See also: https://github.com/golang/go/issues/59641 372 // 373 // This addresses the !!unsafe!! behavior above. 374 if ocspRes.Certificate == nil { 375 if err := ocspRes.CheckSignatureFrom(issuer); err != nil { 376 err = fmt.Errorf("error directly verifying signature on %v OCSP response: %w", method, err) 377 retErr = multierror.Append(retErr, err) 378 continue 379 } 380 } else { 381 // Because we have at least one certificate here, we know that 382 // Go's ocsp library verified the signature from this certificate 383 // onto the response and it was valid. Now we need to know we trust 384 // this certificate. There's two ways we can do this: 385 // 386 // 1. Via confirming issuer == ocspRes.Certificate, or 387 // 2. Via confirming ocspRes.Certificate.CheckSignatureFrom(issuer). 388 if !bytes.Equal(issuer.Raw, ocspRes.Raw) { 389 // 1 must not hold, so 2 holds; verify the signature. 390 if err := ocspRes.Certificate.CheckSignatureFrom(issuer); err != nil { 391 err = fmt.Errorf("error checking chain of trust on %v OCSP response via %v failed: %w", method, issuer.Subject.String(), err) 392 retErr = multierror.Append(retErr, err) 393 continue 394 } 395 396 // Verify the OCSP responder certificate is still valid and 397 // contains the required EKU since it is a delegated OCSP 398 // responder certificate. 399 if ocspRes.Certificate.NotAfter.Before(time.Now()) { 400 err := fmt.Errorf("error checking delegated OCSP responder on %v OCSP response: certificate has expired", method) 401 retErr = multierror.Append(retErr, err) 402 continue 403 } 404 haveEKU := false 405 for _, ku := range ocspRes.Certificate.ExtKeyUsage { 406 if ku == x509.ExtKeyUsageOCSPSigning { 407 haveEKU = true 408 break 409 } 410 } 411 if !haveEKU { 412 err := fmt.Errorf("error checking delegated OCSP responder on %v OCSP response: certificate lacks the OCSP Signing EKU", method) 413 retErr = multierror.Append(retErr, err) 414 continue 415 } 416 } 417 } 418 419 // While we haven't validated the signature on the OCSP response, we 420 // got what we presume is a definitive answer and simply changing 421 // methods will likely not help us in that regard. Use this status 422 // to return without retrying another method, when it looks definitive. 423 // 424 // We don't accept ocsp.Unknown here: presumably, we could've hit a CDN 425 // with static mapping of request->responses, with a default "unknown" 426 // handler for everything else. By retrying here, we use POST, which 427 // could hit a live OCSP server with fresher data than the cached CDN. 428 if ocspRes.Status == ocsp.Good || ocspRes.Status == ocsp.Revoked { 429 break 430 } 431 432 // Here, we didn't have a valid response. Even though we didn't get an 433 // error, we should inform the user that this (valid-looking) response 434 // wasn't utilized. 435 err = fmt.Errorf("fetched %v OCSP response of status %v; wanted either good (%v) or revoked (%v)", method, ocspRes.Status, ocsp.Good, ocsp.Revoked) 436 retErr = multierror.Append(retErr, err) 437 } 438 439 if ocspRes != nil && ocspResBytes != nil { 440 // Clear retErr, because we have one parseable-but-maybe-not-quite-correct 441 // OCSP response. 442 retErr = nil 443 ocspS = &ocspStatus{ 444 code: ocspSuccess, 445 } 446 } 447 448 return 449 } 450 451 // GetRevocationStatus checks the certificate revocation status for subject using issuer certificate. 452 func (c *Client) GetRevocationStatus(ctx context.Context, subject, issuer *x509.Certificate, conf *VerifyConfig) (*ocspStatus, error) { 453 status, ocspReq, encodedCertID, err := c.validateWithCache(subject, issuer) 454 if err != nil { 455 return nil, err 456 } 457 if isValidOCSPStatus(status.code) { 458 return status, nil 459 } 460 if ocspReq == nil || encodedCertID == nil { 461 return status, nil 462 } 463 c.Logger().Debug("cache missed", "server", subject.OCSPServer) 464 if len(subject.OCSPServer) == 0 && len(conf.OcspServersOverride) == 0 { 465 return nil, fmt.Errorf("no OCSP responder URL: subject: %v", subject.Subject) 466 } 467 ocspHosts := subject.OCSPServer 468 if len(conf.OcspServersOverride) > 0 { 469 ocspHosts = conf.OcspServersOverride 470 } 471 472 var wg sync.WaitGroup 473 474 ocspStatuses := make([]*ocspStatus, len(ocspHosts)) 475 ocspResponses := make([]*ocsp.Response, len(ocspHosts)) 476 errors := make([]error, len(ocspHosts)) 477 478 for i, ocspHost := range ocspHosts { 479 u, err := url.Parse(ocspHost) 480 if err != nil { 481 return nil, err 482 } 483 484 hostname := u.Hostname() 485 486 headers := make(map[string]string) 487 headers[httpHeaderContentType] = ocspRequestContentType 488 headers[httpHeaderAccept] = ocspResponseContentType 489 headers[httpHeaderContentLength] = strconv.Itoa(len(ocspReq)) 490 headers[httpHeaderHost] = hostname 491 timeout := defaultOCSPResponderTimeout 492 493 ocspClient := retryablehttp.NewClient() 494 ocspClient.HTTPClient.Timeout = timeout 495 ocspClient.HTTPClient.Transport = newInsecureOcspTransport(conf.ExtraCas) 496 497 doRequest := func() error { 498 if conf.QueryAllServers { 499 defer wg.Done() 500 } 501 ocspRes, _, ocspS, err := c.retryOCSP( 502 ctx, ocspClient, retryablehttp.NewRequest, u, headers, ocspReq, issuer) 503 ocspResponses[i] = ocspRes 504 if err != nil { 505 errors[i] = err 506 return err 507 } 508 if ocspS.code != ocspSuccess { 509 ocspStatuses[i] = ocspS 510 return nil 511 } 512 513 ret, err := validateOCSP(ocspRes) 514 if err != nil { 515 errors[i] = err 516 return err 517 } 518 if isValidOCSPStatus(ret.code) { 519 ocspStatuses[i] = ret 520 } else if ret.err != nil { 521 // This check needs to occur after the isValidOCSPStatus as the unknown 522 // status also sets an err value within ret. 523 errors[i] = ret.err 524 return ret.err 525 } 526 return nil 527 } 528 if conf.QueryAllServers { 529 wg.Add(1) 530 go doRequest() 531 } else { 532 err = doRequest() 533 if err == nil { 534 break 535 } 536 } 537 } 538 if conf.QueryAllServers { 539 wg.Wait() 540 } 541 // Good by default 542 var ret *ocspStatus 543 ocspRes := ocspResponses[0] 544 var firstError error 545 for i := range ocspHosts { 546 if errors[i] != nil { 547 if firstError == nil { 548 firstError = errors[i] 549 } 550 } else if ocspStatuses[i] != nil { 551 switch ocspStatuses[i].code { 552 case ocspStatusRevoked: 553 ret = ocspStatuses[i] 554 ocspRes = ocspResponses[i] 555 break 556 case ocspStatusGood: 557 // Use this response only if we don't have a status already, or if what we have was unknown 558 if ret == nil || ret.code == ocspStatusUnknown { 559 ret = ocspStatuses[i] 560 ocspRes = ocspResponses[i] 561 } 562 case ocspStatusUnknown: 563 if ret == nil { 564 // We may want to use this as the overall result 565 ret = ocspStatuses[i] 566 ocspRes = ocspResponses[i] 567 } 568 } 569 } 570 } 571 572 // If no server reported the cert revoked, but we did have an error, report it 573 if (ret == nil || ret.code == ocspStatusUnknown) && firstError != nil { 574 return nil, firstError 575 } 576 // An extra safety in case ret and firstError are both nil 577 if ret == nil { 578 return nil, fmt.Errorf("failed to extract a known response code or error from the OCSP server") 579 } 580 581 // otherwise ret should contain a response for the overall request 582 if !isValidOCSPStatus(ret.code) { 583 return ret, nil 584 } 585 v := ocspCachedResponse{ 586 status: ret.code, 587 time: float64(time.Now().UTC().Unix()), 588 producedAt: float64(ocspRes.ProducedAt.UTC().Unix()), 589 thisUpdate: float64(ocspRes.ThisUpdate.UTC().Unix()), 590 nextUpdate: float64(ocspRes.NextUpdate.UTC().Unix()), 591 } 592 593 c.ocspResponseCacheLock.Lock() 594 c.ocspResponseCache.Add(*encodedCertID, &v) 595 c.cacheUpdated = true 596 c.ocspResponseCacheLock.Unlock() 597 return ret, nil 598 } 599 600 func isValidOCSPStatus(status ocspStatusCode) bool { 601 return status == ocspStatusGood || status == ocspStatusRevoked || status == ocspStatusUnknown 602 } 603 604 type VerifyConfig struct { 605 OcspEnabled bool 606 ExtraCas []*x509.Certificate 607 OcspServersOverride []string 608 OcspFailureMode FailOpenMode 609 QueryAllServers bool 610 } 611 612 // VerifyLeafCertificate verifies just the subject against it's direct issuer 613 func (c *Client) VerifyLeafCertificate(ctx context.Context, subject, issuer *x509.Certificate, conf *VerifyConfig) error { 614 results, err := c.GetRevocationStatus(ctx, subject, issuer, conf) 615 if err != nil { 616 return err 617 } 618 if results.code == ocspStatusGood { 619 return nil 620 } else { 621 serial := issuer.SerialNumber 622 serialHex := strings.TrimSpace(certutil.GetHexFormatted(serial.Bytes(), ":")) 623 if results.code == ocspStatusRevoked { 624 return fmt.Errorf("certificate with serial number %s has been revoked", serialHex) 625 } else if conf.OcspFailureMode == FailOpenFalse { 626 return fmt.Errorf("unknown OCSP status for cert with serial number %s", strings.TrimSpace(certutil.GetHexFormatted(serial.Bytes(), ":"))) 627 } else { 628 c.Logger().Warn("could not validate OCSP status for cert, but continuing in fail open mode", "serial", serialHex) 629 } 630 } 631 return nil 632 } 633 634 // VerifyPeerCertificate verifies all of certificate revocation status 635 func (c *Client) VerifyPeerCertificate(ctx context.Context, verifiedChains [][]*x509.Certificate, conf *VerifyConfig) error { 636 for i := 0; i < len(verifiedChains); i++ { 637 // Certificate signed by Root CA. This should be one before the last in the Certificate Chain 638 numberOfNoneRootCerts := len(verifiedChains[i]) - 1 639 if !verifiedChains[i][numberOfNoneRootCerts].IsCA || string(verifiedChains[i][numberOfNoneRootCerts].RawIssuer) != string(verifiedChains[i][numberOfNoneRootCerts].RawSubject) { 640 // Check if the last Non Root Cert is also a CA or is self signed. 641 // if the last certificate is not, add it to the list 642 rca := c.caRoot[string(verifiedChains[i][numberOfNoneRootCerts].RawIssuer)] 643 if rca == nil { 644 return fmt.Errorf("failed to find root CA. pkix.name: %v", verifiedChains[i][numberOfNoneRootCerts].Issuer) 645 } 646 verifiedChains[i] = append(verifiedChains[i], rca) 647 numberOfNoneRootCerts++ 648 } 649 results, err := c.GetAllRevocationStatus(ctx, verifiedChains[i], conf) 650 if err != nil { 651 return err 652 } 653 if r := c.canEarlyExitForOCSP(results, numberOfNoneRootCerts, conf); r != nil { 654 return r.err 655 } 656 } 657 658 return nil 659 } 660 661 func (c *Client) canEarlyExitForOCSP(results []*ocspStatus, chainSize int, conf *VerifyConfig) *ocspStatus { 662 msg := "" 663 if conf.OcspFailureMode == FailOpenFalse { 664 // Fail closed. any error is returned to stop connection 665 for _, r := range results { 666 if r.err != nil { 667 return r 668 } 669 } 670 } else { 671 // Fail open and all results are valid. 672 allValid := len(results) == chainSize 673 for _, r := range results { 674 if !isValidOCSPStatus(r.code) { 675 allValid = false 676 break 677 } 678 } 679 for _, r := range results { 680 if allValid && r.code == ocspStatusRevoked { 681 return r 682 } 683 if r != nil && r.code != ocspStatusGood && r.err != nil { 684 msg += "" + r.err.Error() 685 } 686 } 687 } 688 if len(msg) > 0 { 689 c.Logger().Warn( 690 "OCSP is set to fail-open, and could not retrieve OCSP based revocation checking but proceeding.", "detail", msg) 691 } 692 return nil 693 } 694 695 func (c *Client) validateWithCacheForAllCertificates(verifiedChains []*x509.Certificate) (bool, error) { 696 n := len(verifiedChains) - 1 697 for j := 0; j < n; j++ { 698 subject := verifiedChains[j] 699 issuer := verifiedChains[j+1] 700 status, _, _, err := c.validateWithCache(subject, issuer) 701 if err != nil { 702 return false, err 703 } 704 if !isValidOCSPStatus(status.code) { 705 return false, nil 706 } 707 } 708 return true, nil 709 } 710 711 func (c *Client) validateWithCache(subject, issuer *x509.Certificate) (*ocspStatus, []byte, *certIDKey, error) { 712 ocspReq, err := ocsp.CreateRequest(subject, issuer, &ocsp.RequestOptions{}) 713 if err != nil { 714 return nil, nil, nil, fmt.Errorf("failed to create OCSP request from the certificates: %v", err) 715 } 716 encodedCertID, ocspS := extractCertIDKeyFromRequest(ocspReq) 717 if ocspS.code != ocspSuccess { 718 return nil, nil, nil, fmt.Errorf("failed to extract CertID from OCSP Request: %v", err) 719 } 720 status, err := c.checkOCSPResponseCache(encodedCertID, subject, issuer) 721 if err != nil { 722 return nil, nil, nil, err 723 } 724 return status, ocspReq, encodedCertID, nil 725 } 726 727 func (c *Client) GetAllRevocationStatus(ctx context.Context, verifiedChains []*x509.Certificate, conf *VerifyConfig) ([]*ocspStatus, error) { 728 _, err := c.validateWithCacheForAllCertificates(verifiedChains) 729 if err != nil { 730 return nil, err 731 } 732 n := len(verifiedChains) - 1 733 results := make([]*ocspStatus, n) 734 for j := 0; j < n; j++ { 735 results[j], err = c.GetRevocationStatus(ctx, verifiedChains[j], verifiedChains[j+1], conf) 736 if err != nil { 737 return nil, err 738 } 739 if !isValidOCSPStatus(results[j].code) { 740 return results, nil 741 } 742 } 743 return results, nil 744 } 745 746 // verifyPeerCertificateSerial verifies the certificate revocation status in serial. 747 func (c *Client) verifyPeerCertificateSerial(conf *VerifyConfig) func(_ [][]byte, verifiedChains [][]*x509.Certificate) (err error) { 748 return func(_ [][]byte, verifiedChains [][]*x509.Certificate) error { 749 return c.VerifyPeerCertificate(context.TODO(), verifiedChains, conf) 750 } 751 } 752 753 func (c *Client) extractOCSPCacheResponseValueWithoutSubject(cacheValue ocspCachedResponse) (*ocspStatus, error) { 754 return c.extractOCSPCacheResponseValue(&cacheValue, nil, nil) 755 } 756 757 func (c *Client) extractOCSPCacheResponseValue(cacheValue *ocspCachedResponse, subject, issuer *x509.Certificate) (*ocspStatus, error) { 758 subjectName := "Unknown" 759 if subject != nil { 760 subjectName = subject.Subject.CommonName 761 } 762 763 curTime := time.Now() 764 if cacheValue == nil { 765 return &ocspStatus{ 766 code: ocspMissedCache, 767 err: fmt.Errorf("miss cache data. subject: %v", subjectName), 768 }, nil 769 } 770 currentTime := float64(curTime.UTC().Unix()) 771 if currentTime-cacheValue.time >= cacheExpire { 772 return &ocspStatus{ 773 code: ocspCacheExpired, 774 err: fmt.Errorf("cache expired. current: %v, cache: %v", 775 time.Unix(int64(currentTime), 0).UTC(), time.Unix(int64(cacheValue.time), 0).UTC()), 776 }, nil 777 } 778 779 return validateOCSP(&ocsp.Response{ 780 ProducedAt: time.Unix(int64(cacheValue.producedAt), 0).UTC(), 781 ThisUpdate: time.Unix(int64(cacheValue.thisUpdate), 0).UTC(), 782 NextUpdate: time.Unix(int64(cacheValue.nextUpdate), 0).UTC(), 783 Status: int(cacheValue.status), 784 }) 785 } 786 787 /* 788 // writeOCSPCache writes a OCSP Response cache 789 func (c *Client) writeOCSPCache(ctx context.Context, storage logical.Storage) error { 790 c.Logger().Debug("writing OCSP Response cache") 791 t := time.Now() 792 m := make(map[string][]interface{}) 793 keys := c.ocspResponseCache.Keys() 794 if len(keys) > persistedCacheSize { 795 keys = keys[:persistedCacheSize] 796 } 797 for _, k := range keys { 798 e, ok := c.ocspResponseCache.Get(k) 799 if ok { 800 entry := e.(*ocspCachedResponse) 801 // Don't store if expired 802 if isInValidityRange(t, time.Unix(int64(entry.thisUpdate), 0), time.Unix(int64(entry.nextUpdate), 0)) { 803 key := k.(certIDKey) 804 cacheKeyInBase64, err := decodeCertIDKey(&key) 805 if err != nil { 806 return err 807 } 808 m[cacheKeyInBase64] = []interface{}{entry.status, entry.time, entry.producedAt, entry.thisUpdate, entry.nextUpdate} 809 } 810 } 811 } 812 813 v, err := jsonutil.EncodeJSONAndCompress(m, nil) 814 if err != nil { 815 return err 816 } 817 entry := logical.StorageEntry{ 818 Key: ocspCacheKey, 819 Value: v, 820 } 821 return storage.Put(ctx, &entry) 822 } 823 824 // readOCSPCache reads a OCSP Response cache from storage 825 func (c *Client) readOCSPCache(ctx context.Context, storage logical.Storage) error { 826 c.Logger().Debug("reading OCSP Response cache") 827 828 entry, err := storage.Get(ctx, ocspCacheKey) 829 if err != nil { 830 return err 831 } 832 if entry == nil { 833 return nil 834 } 835 var untypedCache map[string][]interface{} 836 837 err = jsonutil.DecodeJSON(entry.Value, &untypedCache) 838 if err != nil { 839 return errors.New("failed to unmarshal OCSP cache") 840 } 841 842 for k, v := range untypedCache { 843 key, err := c.encodeCertIDKey(k) 844 if err != nil { 845 return err 846 } 847 var times [4]float64 848 for i, t := range v[1:] { 849 if jn, ok := t.(json.Number); ok { 850 times[i], err = jn.Float64() 851 if err != nil { 852 return err 853 } 854 } else { 855 times[i] = t.(float64) 856 } 857 } 858 var status int 859 if jn, ok := v[0].(json.Number); ok { 860 s, err := jn.Int64() 861 if err != nil { 862 return err 863 } 864 status = int(s) 865 } else { 866 status = v[0].(int) 867 } 868 869 c.ocspResponseCache.Add(*key, &ocspCachedResponse{ 870 status: ocspStatusCode(status), 871 time: times[0], 872 producedAt: times[1], 873 thisUpdate: times[2], 874 nextUpdate: times[3], 875 }) 876 } 877 878 return nil 879 } 880 */ 881 882 func New(logFactory func() hclog.Logger, cacheSize int) *Client { 883 if cacheSize < 100 { 884 cacheSize = 100 885 } 886 cache, _ := lru.New2Q(cacheSize) 887 c := Client{ 888 caRoot: make(map[string]*x509.Certificate), 889 ocspResponseCache: cache, 890 logFactory: logFactory, 891 } 892 893 return &c 894 } 895 896 func (c *Client) Logger() hclog.Logger { 897 return c.logFactory() 898 } 899 900 // insecureOcspTransport is the transport object that doesn't do certificate revocation check. 901 func newInsecureOcspTransport(extraCas []*x509.Certificate) *http.Transport { 902 // Get the SystemCertPool, continue with an empty pool on error 903 rootCAs, _ := x509.SystemCertPool() 904 if rootCAs == nil { 905 rootCAs = x509.NewCertPool() 906 } 907 for _, c := range extraCas { 908 rootCAs.AddCert(c) 909 } 910 config := &tls.Config{ 911 RootCAs: rootCAs, 912 } 913 return &http.Transport{ 914 MaxIdleConns: 10, 915 IdleConnTimeout: 30 * time.Minute, 916 Proxy: http.ProxyFromEnvironment, 917 DialContext: (&net.Dialer{ 918 Timeout: 30 * time.Second, 919 KeepAlive: 30 * time.Second, 920 }).DialContext, 921 TLSClientConfig: config, 922 } 923 } 924 925 // NewTransport includes the certificate revocation check with OCSP in sequential. 926 func (c *Client) NewTransport(conf *VerifyConfig) *http.Transport { 927 rootCAs := c.certPool 928 if rootCAs == nil { 929 rootCAs, _ = x509.SystemCertPool() 930 } 931 if rootCAs == nil { 932 rootCAs = x509.NewCertPool() 933 } 934 for _, c := range conf.ExtraCas { 935 rootCAs.AddCert(c) 936 } 937 return &http.Transport{ 938 TLSClientConfig: &tls.Config{ 939 RootCAs: rootCAs, 940 VerifyPeerCertificate: c.verifyPeerCertificateSerial(conf), 941 }, 942 MaxIdleConns: 10, 943 IdleConnTimeout: 30 * time.Minute, 944 Proxy: http.ProxyFromEnvironment, 945 DialContext: (&net.Dialer{ 946 Timeout: 30 * time.Second, 947 KeepAlive: 30 * time.Second, 948 }).DialContext, 949 } 950 } 951 952 /* 953 func (c *Client) WriteCache(ctx context.Context, storage logical.Storage) error { 954 c.ocspResponseCacheLock.Lock() 955 defer c.ocspResponseCacheLock.Unlock() 956 if c.cacheUpdated { 957 err := c.writeOCSPCache(ctx, storage) 958 if err == nil { 959 c.cacheUpdated = false 960 } 961 return err 962 } 963 return nil 964 } 965 966 func (c *Client) ReadCache(ctx context.Context, storage logical.Storage) error { 967 c.ocspResponseCacheLock.Lock() 968 defer c.ocspResponseCacheLock.Unlock() 969 return c.readOCSPCache(ctx, storage) 970 } 971 */ 972 /* 973 Apache License 974 Version 2.0, January 2004 975 http://www.apache.org/licenses/ 976 977 TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 978 979 1. Definitions. 980 981 "License" shall mean the terms and conditions for use, reproduction, 982 and distribution as defined by Sections 1 through 9 of this document. 983 984 "Licensor" shall mean the copyright owner or entity authorized by 985 the copyright owner that is granting the License. 986 987 "Legal Entity" shall mean the union of the acting entity and all 988 other entities that control, are controlled by, or are under common 989 control with that entity. For the purposes of this definition, 990 "control" means (i) the power, direct or indirect, to cause the 991 direction or management of such entity, whether by contract or 992 otherwise, or (ii) ownership of fifty percent (50%) or more of the 993 outstanding shares, or (iii) beneficial ownership of such entity. 994 995 "You" (or "Your") shall mean an individual or Legal Entity 996 exercising permissions granted by this License. 997 998 "Source" form shall mean the preferred form for making modifications, 999 including but not limited to software source code, documentation 1000 source, and configuration files. 1001 1002 "Object" form shall mean any form resulting from mechanical 1003 transformation or translation of a Source form, including but 1004 not limited to compiled object code, generated documentation, 1005 and conversions to other media types. 1006 1007 "Work" shall mean the work of authorship, whether in Source or 1008 Object form, made available under the License, as indicated by a 1009 copyright notice that is included in or attached to the work 1010 (an example is provided in the Appendix below). 1011 1012 "Derivative Works" shall mean any work, whether in Source or Object 1013 form, that is based on (or derived from) the Work and for which the 1014 editorial revisions, annotations, elaborations, or other modifications 1015 represent, as a whole, an original work of authorship. For the purposes 1016 of this License, Derivative Works shall not include works that remain 1017 separable from, or merely link (or bind by name) to the interfaces of, 1018 the Work and Derivative Works thereof. 1019 1020 "Contribution" shall mean any work of authorship, including 1021 the original version of the Work and any modifications or additions 1022 to that Work or Derivative Works thereof, that is intentionally 1023 submitted to Licensor for inclusion in the Work by the copyright owner 1024 or by an individual or Legal Entity authorized to submit on behalf of 1025 the copyright owner. For the purposes of this definition, "submitted" 1026 means any form of electronic, verbal, or written communication sent 1027 to the Licensor or its representatives, including but not limited to 1028 communication on electronic mailing lists, source code control systems, 1029 and issue tracking systems that are managed by, or on behalf of, the 1030 Licensor for the purpose of discussing and improving the Work, but 1031 excluding communication that is conspicuously marked or otherwise 1032 designated in writing by the copyright owner as "Not a Contribution." 1033 1034 "Contributor" shall mean Licensor and any individual or Legal Entity 1035 on behalf of whom a Contribution has been received by Licensor and 1036 subsequently incorporated within the Work. 1037 1038 2. Grant of Copyright License. Subject to the terms and conditions of 1039 this License, each Contributor hereby grants to You a perpetual, 1040 worldwide, non-exclusive, no-charge, royalty-free, irrevocable 1041 copyright license to reproduce, prepare Derivative Works of, 1042 publicly display, publicly perform, sublicense, and distribute the 1043 Work and such Derivative Works in Source or Object form. 1044 1045 3. Grant of Patent License. Subject to the terms and conditions of 1046 this License, each Contributor hereby grants to You a perpetual, 1047 worldwide, non-exclusive, no-charge, royalty-free, irrevocable 1048 (except as stated in this section) patent license to make, have made, 1049 use, offer to sell, sell, import, and otherwise transfer the Work, 1050 where such license applies only to those patent claims licensable 1051 by such Contributor that are necessarily infringed by their 1052 Contribution(s) alone or by combination of their Contribution(s) 1053 with the Work to which such Contribution(s) was submitted. If You 1054 institute patent litigation against any entity (including a 1055 cross-claim or counterclaim in a lawsuit) alleging that the Work 1056 or a Contribution incorporated within the Work constitutes direct 1057 or contributory patent infringement, then any patent licenses 1058 granted to You under this License for that Work shall terminate 1059 as of the date such litigation is filed. 1060 1061 4. Redistribution. You may reproduce and distribute copies of the 1062 Work or Derivative Works thereof in any medium, with or without 1063 modifications, and in Source or Object form, provided that You 1064 meet the following conditions: 1065 1066 (a) You must give any other recipients of the Work or 1067 Derivative Works a copy of this License; and 1068 1069 (b) You must cause any modified files to carry prominent notices 1070 stating that You changed the files; and 1071 1072 (c) You must retain, in the Source form of any Derivative Works 1073 that You distribute, all copyright, patent, trademark, and 1074 attribution notices from the Source form of the Work, 1075 excluding those notices that do not pertain to any part of 1076 the Derivative Works; and 1077 1078 (d) If the Work includes a "NOTICE" text file as part of its 1079 distribution, then any Derivative Works that You distribute must 1080 include a readable copy of the attribution notices contained 1081 within such NOTICE file, excluding those notices that do not 1082 pertain to any part of the Derivative Works, in at least one 1083 of the following places: within a NOTICE text file distributed 1084 as part of the Derivative Works; within the Source form or 1085 documentation, if provided along with the Derivative Works; or, 1086 within a display generated by the Derivative Works, if and 1087 wherever such third-party notices normally appear. The contents 1088 of the NOTICE file are for informational purposes only and 1089 do not modify the License. You may add Your own attribution 1090 notices within Derivative Works that You distribute, alongside 1091 or as an addendum to the NOTICE text from the Work, provided 1092 that such additional attribution notices cannot be construed 1093 as modifying the License. 1094 1095 You may add Your own copyright statement to Your modifications and 1096 may provide additional or different license terms and conditions 1097 for use, reproduction, or distribution of Your modifications, or 1098 for any such Derivative Works as a whole, provided Your use, 1099 reproduction, and distribution of the Work otherwise complies with 1100 the conditions stated in this License. 1101 1102 5. Submission of Contributions. Unless You explicitly state otherwise, 1103 any Contribution intentionally submitted for inclusion in the Work 1104 by You to the Licensor shall be under the terms and conditions of 1105 this License, without any additional terms or conditions. 1106 Notwithstanding the above, nothing herein shall supersede or modify 1107 the terms of any separate license agreement you may have executed 1108 with Licensor regarding such Contributions. 1109 1110 6. Trademarks. This License does not grant permission to use the trade 1111 names, trademarks, service marks, or product names of the Licensor, 1112 except as required for reasonable and customary use in describing the 1113 origin of the Work and reproducing the content of the NOTICE file. 1114 1115 7. Disclaimer of Warranty. Unless required by applicable law or 1116 agreed to in writing, Licensor provides the Work (and each 1117 Contributor provides its Contributions) on an "AS IS" BASIS, 1118 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 1119 implied, including, without limitation, any warranties or conditions 1120 of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 1121 PARTICULAR PURPOSE. You are solely responsible for determining the 1122 appropriateness of using or redistributing the Work and assume any 1123 risks associated with Your exercise of permissions under this License. 1124 1125 8. Limitation of Liability. In no event and under no legal theory, 1126 whether in tort (including negligence), contract, or otherwise, 1127 unless required by applicable law (such as deliberate and grossly 1128 negligent acts) or agreed to in writing, shall any Contributor be 1129 liable to You for damages, including any direct, indirect, special, 1130 incidental, or consequential damages of any character arising as a 1131 result of this License or out of the use or inability to use the 1132 Work (including but not limited to damages for loss of goodwill, 1133 work stoppage, computer failure or malfunction, or any and all 1134 other commercial damages or losses), even if such Contributor 1135 has been advised of the possibility of such damages. 1136 1137 9. Accepting Warranty or Additional Liability. While redistributing 1138 the Work or Derivative Works thereof, You may choose to offer, 1139 and charge a fee for, acceptance of support, warranty, indemnity, 1140 or other liability obligations and/or rights consistent with this 1141 License. However, in accepting such obligations, You may act only 1142 on Your own behalf and on Your sole responsibility, not on behalf 1143 of any other Contributor, and only if You agree to indemnify, 1144 defend, and hold each Contributor harmless for any liability 1145 incurred by, or claims asserted against, such Contributor by reason 1146 of your accepting any such warranty or additional liability. 1147 1148 END OF TERMS AND CONDITIONS 1149 1150 APPENDIX: How to apply the Apache License to your work. 1151 1152 To apply the Apache License to your work, attach the following 1153 boilerplate notice, with the fields enclosed by brackets "{}" 1154 replaced with your own identifying information. (Don't include 1155 the brackets!) The text should be enclosed in the appropriate 1156 comment syntax for the file format. We also recommend that a 1157 file or class name and description of purpose be included on the 1158 same "printed page" as the copyright notice for easier 1159 identification within third-party archives. 1160 1161 Copyright (c) 2017-2022 Snowflake Computing Inc. All rights reserved. 1162 1163 Licensed under the Apache License, Version 2.0 (the "License"); 1164 you may not use this file except in compliance with the License. 1165 You may obtain a copy of the License at 1166 1167 http://www.apache.org/licenses/LICENSE-2.0 1168 1169 Unless required by applicable law or agreed to in writing, software 1170 distributed under the License is distributed on an "AS IS" BASIS, 1171 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 1172 See the License for the specific language governing permissions and 1173 limitations under the License. 1174 */