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  */