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