get.pme.sh/pnats@v0.0.0-20240304004023-26bb5a137ed0/server/ocsp_peer.go (about)

     1  // Copyright 2023 The NATS Authors
     2  // Licensed under the Apache License, Version 2.0 (the "License");
     3  // you may not use this file except in compliance with the License.
     4  // You may obtain a copy of the License at
     5  //
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package server
    15  
    16  import (
    17  	"crypto/tls"
    18  	"crypto/x509"
    19  	"errors"
    20  	"fmt"
    21  	"strings"
    22  	"time"
    23  
    24  	"golang.org/x/crypto/ocsp"
    25  
    26  	"get.pme.sh/pnats/server/certidp"
    27  )
    28  
    29  func parseOCSPPeer(v interface{}) (pcfg *certidp.OCSPPeerConfig, retError error) {
    30  	var lt token
    31  	defer convertPanicToError(&lt, &retError)
    32  	tk, v := unwrapValue(v, &lt)
    33  	cm, ok := v.(map[string]interface{})
    34  	if !ok {
    35  		return nil, &configErr{tk, fmt.Sprintf(certidp.ErrIllegalPeerOptsConfig, v)}
    36  	}
    37  	pcfg = certidp.NewOCSPPeerConfig()
    38  	retError = nil
    39  	for mk, mv := range cm {
    40  		tk, mv = unwrapValue(mv, &lt)
    41  		switch strings.ToLower(mk) {
    42  		case "verify":
    43  			verify, ok := mv.(bool)
    44  			if !ok {
    45  				return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldGeneric, mk)}
    46  			}
    47  			pcfg.Verify = verify
    48  		case "allowed_clockskew":
    49  			at := float64(0)
    50  			switch mv := mv.(type) {
    51  			case int64:
    52  				at = float64(mv)
    53  			case float64:
    54  				at = mv
    55  			case string:
    56  				d, err := time.ParseDuration(mv)
    57  				if err != nil {
    58  					return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldTypeConversion, "unexpected type")}
    59  				}
    60  				at = d.Seconds()
    61  			default:
    62  				return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldTypeConversion, "unexpected type")}
    63  			}
    64  			if at >= 0 {
    65  				pcfg.ClockSkew = at
    66  			}
    67  		case "ca_timeout":
    68  			at := float64(0)
    69  			switch mv := mv.(type) {
    70  			case int64:
    71  				at = float64(mv)
    72  			case float64:
    73  				at = mv
    74  			case string:
    75  				d, err := time.ParseDuration(mv)
    76  				if err != nil {
    77  					return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldTypeConversion, err)}
    78  				}
    79  				at = d.Seconds()
    80  			default:
    81  				return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldTypeConversion, "unexpected type")}
    82  			}
    83  			if at >= 0 {
    84  				pcfg.Timeout = at
    85  			}
    86  		case "cache_ttl_when_next_update_unset":
    87  			at := float64(0)
    88  			switch mv := mv.(type) {
    89  			case int64:
    90  				at = float64(mv)
    91  			case float64:
    92  				at = mv
    93  			case string:
    94  				d, err := time.ParseDuration(mv)
    95  				if err != nil {
    96  					return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldTypeConversion, err)}
    97  				}
    98  				at = d.Seconds()
    99  			default:
   100  				return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldTypeConversion, "unexpected type")}
   101  			}
   102  			if at >= 0 {
   103  				pcfg.TTLUnsetNextUpdate = at
   104  			}
   105  		case "warn_only":
   106  			warnOnly, ok := mv.(bool)
   107  			if !ok {
   108  				return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldGeneric, mk)}
   109  			}
   110  			pcfg.WarnOnly = warnOnly
   111  		case "unknown_is_good":
   112  			unknownIsGood, ok := mv.(bool)
   113  			if !ok {
   114  				return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldGeneric, mk)}
   115  			}
   116  			pcfg.UnknownIsGood = unknownIsGood
   117  		case "allow_when_ca_unreachable":
   118  			allowWhenCAUnreachable, ok := mv.(bool)
   119  			if !ok {
   120  				return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldGeneric, mk)}
   121  			}
   122  			pcfg.AllowWhenCAUnreachable = allowWhenCAUnreachable
   123  		default:
   124  			return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldGeneric, mk)}
   125  		}
   126  	}
   127  	return pcfg, nil
   128  }
   129  
   130  func peerFromVerifiedChains(chains [][]*x509.Certificate) *x509.Certificate {
   131  	if len(chains) == 0 || len(chains[0]) == 0 {
   132  		return nil
   133  	}
   134  	return chains[0][0]
   135  }
   136  
   137  // plugTLSOCSPPeer will plug the TLS handshake lifecycle for client mTLS connections and Leaf connections
   138  func (s *Server) plugTLSOCSPPeer(config *tlsConfigKind) (*tls.Config, bool, error) {
   139  	if config == nil || config.tlsConfig == nil {
   140  		return nil, false, errors.New(certidp.ErrUnableToPlugTLSEmptyConfig)
   141  	}
   142  	kind := config.kind
   143  	isSpoke := config.isLeafSpoke
   144  	tcOpts := config.tlsOpts
   145  	if tcOpts == nil || tcOpts.OCSPPeerConfig == nil || !tcOpts.OCSPPeerConfig.Verify {
   146  		return nil, false, nil
   147  	}
   148  	s.Debugf(certidp.DbgPlugTLSForKind, config.kind)
   149  	// peer is a tls client
   150  	if kind == kindStringMap[CLIENT] || (kind == kindStringMap[LEAF] && !isSpoke) {
   151  		if !tcOpts.Verify {
   152  			return nil, false, errors.New(certidp.ErrMTLSRequired)
   153  		}
   154  		return s.plugClientTLSOCSPPeer(config)
   155  	}
   156  	// peer is a tls server
   157  	if kind == kindStringMap[LEAF] && isSpoke {
   158  		return s.plugServerTLSOCSPPeer(config)
   159  	}
   160  	return nil, false, nil
   161  }
   162  
   163  func (s *Server) plugClientTLSOCSPPeer(config *tlsConfigKind) (*tls.Config, bool, error) {
   164  	if config == nil || config.tlsConfig == nil || config.tlsOpts == nil {
   165  		return nil, false, errors.New(certidp.ErrUnableToPlugTLSClient)
   166  	}
   167  	tc := config.tlsConfig
   168  	tcOpts := config.tlsOpts
   169  	kind := config.kind
   170  	if tcOpts.OCSPPeerConfig == nil || !tcOpts.OCSPPeerConfig.Verify {
   171  		return tc, false, nil
   172  	}
   173  	tc.VerifyConnection = func(cs tls.ConnectionState) error {
   174  		if !s.tlsClientOCSPValid(cs.VerifiedChains, tcOpts.OCSPPeerConfig) {
   175  			s.sendOCSPPeerRejectEvent(kind, peerFromVerifiedChains(cs.VerifiedChains), certidp.MsgTLSClientRejectConnection)
   176  			return errors.New(certidp.MsgTLSClientRejectConnection)
   177  		}
   178  		return nil
   179  	}
   180  	return tc, true, nil
   181  }
   182  
   183  func (s *Server) plugServerTLSOCSPPeer(config *tlsConfigKind) (*tls.Config, bool, error) {
   184  	if config == nil || config.tlsConfig == nil || config.tlsOpts == nil {
   185  		return nil, false, errors.New(certidp.ErrUnableToPlugTLSServer)
   186  	}
   187  	tc := config.tlsConfig
   188  	tcOpts := config.tlsOpts
   189  	kind := config.kind
   190  	if tcOpts.OCSPPeerConfig == nil || !tcOpts.OCSPPeerConfig.Verify {
   191  		return tc, false, nil
   192  	}
   193  	tc.VerifyConnection = func(cs tls.ConnectionState) error {
   194  		if !s.tlsServerOCSPValid(cs.VerifiedChains, tcOpts.OCSPPeerConfig) {
   195  			s.sendOCSPPeerRejectEvent(kind, peerFromVerifiedChains(cs.VerifiedChains), certidp.MsgTLSServerRejectConnection)
   196  			return errors.New(certidp.MsgTLSServerRejectConnection)
   197  		}
   198  		return nil
   199  	}
   200  	return tc, true, nil
   201  }
   202  
   203  // tlsServerOCSPValid evaluates verified chains (post successful TLS handshake) against OCSP
   204  // eligibility. A verified chain is considered OCSP Valid if either none of the links are
   205  // OCSP eligible, or current "good" responses from the CA can be obtained for each eligible link.
   206  // Upon first OCSP Valid chain found, the Server is deemed OCSP Valid. If none of the chains are
   207  // OCSP Valid, the Server is deemed OCSP Invalid. A verified self-signed certificate (chain length 1)
   208  // is also considered OCSP Valid.
   209  func (s *Server) tlsServerOCSPValid(chains [][]*x509.Certificate, opts *certidp.OCSPPeerConfig) bool {
   210  	s.Debugf(certidp.DbgNumServerChains, len(chains))
   211  	return s.peerOCSPValid(chains, opts)
   212  }
   213  
   214  // tlsClientOCSPValid evaluates verified chains (post successful TLS handshake) against OCSP
   215  // eligibility. A verified chain is considered OCSP Valid if either none of the links are
   216  // OCSP eligible, or current "good" responses from the CA can be obtained for each eligible link.
   217  // Upon first OCSP Valid chain found, the Client is deemed OCSP Valid. If none of the chains are
   218  // OCSP Valid, the Client is deemed OCSP Invalid. A verified self-signed certificate (chain length 1)
   219  // is also considered OCSP Valid.
   220  func (s *Server) tlsClientOCSPValid(chains [][]*x509.Certificate, opts *certidp.OCSPPeerConfig) bool {
   221  	s.Debugf(certidp.DbgNumClientChains, len(chains))
   222  	return s.peerOCSPValid(chains, opts)
   223  }
   224  
   225  func (s *Server) peerOCSPValid(chains [][]*x509.Certificate, opts *certidp.OCSPPeerConfig) bool {
   226  	peer := peerFromVerifiedChains(chains)
   227  	if peer == nil {
   228  		s.Errorf(certidp.ErrPeerEmptyAutoReject)
   229  		return false
   230  	}
   231  	for ci, chain := range chains {
   232  		s.Debugf(certidp.DbgLinksInChain, ci, len(chain))
   233  		// Self-signed certificate is Client OCSP Valid (no CA)
   234  		if len(chain) == 1 {
   235  			s.Debugf(certidp.DbgSelfSignedValid, ci)
   236  			return true
   237  		}
   238  		// Check if any of the links in the chain are OCSP eligible
   239  		chainEligible := false
   240  		var eligibleLinks []*certidp.ChainLink
   241  		// Iterate over links skipping the root cert which is not OCSP eligible (self == issuer)
   242  		for linkPos := 0; linkPos < len(chain)-1; linkPos++ {
   243  			cert := chain[linkPos]
   244  			link := &certidp.ChainLink{
   245  				Leaf: cert,
   246  			}
   247  			if certidp.CertOCSPEligible(link) {
   248  				chainEligible = true
   249  				issuerCert := certidp.GetLeafIssuerCert(chain, linkPos)
   250  				if issuerCert == nil {
   251  					// unexpected chain condition, reject Client as OCSP Invalid
   252  					return false
   253  				}
   254  				link.Issuer = issuerCert
   255  				eligibleLinks = append(eligibleLinks, link)
   256  			}
   257  		}
   258  		// A trust-store verified chain that is not OCSP eligible is always OCSP Valid
   259  		if !chainEligible {
   260  			s.Debugf(certidp.DbgValidNonOCSPChain, ci)
   261  			return true
   262  		}
   263  		s.Debugf(certidp.DbgChainIsOCSPEligible, ci, len(eligibleLinks))
   264  		// Chain has at least one OCSP eligible link, so check each eligible link;
   265  		// any link with a !good OCSP response chain OCSP Invalid
   266  		chainValid := true
   267  		for _, link := range eligibleLinks {
   268  			// if option selected, good could reflect either ocsp.Good or ocsp.Unknown
   269  			if badReason, good := s.certOCSPGood(link, opts); !good {
   270  				s.Debugf(badReason)
   271  				s.sendOCSPPeerChainlinkInvalidEvent(peer, link.Leaf, badReason)
   272  				chainValid = false
   273  				break
   274  			}
   275  		}
   276  		if chainValid {
   277  			s.Debugf(certidp.DbgChainIsOCSPValid, ci)
   278  			return true
   279  		}
   280  	}
   281  	// If we are here, all chains had OCSP eligible links, but none of the chains achieved OCSP valid
   282  	s.Debugf(certidp.DbgNoOCSPValidChains)
   283  	return false
   284  }
   285  
   286  func (s *Server) certOCSPGood(link *certidp.ChainLink, opts *certidp.OCSPPeerConfig) (string, bool) {
   287  	if link == nil || link.Leaf == nil || link.Issuer == nil || link.OCSPWebEndpoints == nil || len(*link.OCSPWebEndpoints) < 1 {
   288  		return "Empty chainlink found", false
   289  	}
   290  	var err error
   291  	sLogs := &certidp.Log{
   292  		Debugf:  s.Debugf,
   293  		Noticef: s.Noticef,
   294  		Warnf:   s.Warnf,
   295  		Errorf:  s.Errorf,
   296  		Tracef:  s.Tracef,
   297  	}
   298  	fingerprint := certidp.GenerateFingerprint(link.Leaf)
   299  	// Used for debug/operator only, not match
   300  	subj := certidp.GetSubjectDNForm(link.Leaf)
   301  	var rawResp []byte
   302  	var ocspr *ocsp.Response
   303  	var useCachedResp bool
   304  	var rc = s.ocsprc
   305  	var cachedRevocation bool
   306  	// Check our cache before calling out to the CA OCSP responder
   307  	s.Debugf(certidp.DbgCheckingCacheForCert, subj, fingerprint)
   308  	if rawResp = rc.Get(fingerprint, sLogs); len(rawResp) > 0 {
   309  		// Signature validation of CA's OCSP response occurs in ParseResponse
   310  		ocspr, err = ocsp.ParseResponse(rawResp, link.Issuer)
   311  		if err == nil && ocspr != nil {
   312  			// Check if OCSP Response delegation present and if so is valid
   313  			if !certidp.ValidDelegationCheck(link.Issuer, ocspr) {
   314  				// Invalid delegation was already in cache, purge it and don't use it
   315  				s.Debugf(certidp.MsgCachedOCSPResponseInvalid, subj)
   316  				rc.Delete(fingerprint, true, sLogs)
   317  				goto AFTERCACHE
   318  			}
   319  			if certidp.OCSPResponseCurrent(ocspr, opts, sLogs) {
   320  				s.Debugf(certidp.DbgCurrentResponseCached, certidp.GetStatusAssertionStr(ocspr.Status))
   321  				useCachedResp = true
   322  			} else {
   323  				// Cached response is not current, delete it and tidy runtime stats to reflect a miss;
   324  				// if preserve_revoked is enabled, the cache will not delete the cached response
   325  				s.Debugf(certidp.DbgExpiredResponseCached, certidp.GetStatusAssertionStr(ocspr.Status))
   326  				rc.Delete(fingerprint, true, sLogs)
   327  			}
   328  			// Regardless of currency, record a cached revocation found in case AllowWhenCAUnreachable is set
   329  			if ocspr.Status == ocsp.Revoked {
   330  				cachedRevocation = true
   331  			}
   332  		} else {
   333  			// Bogus cached assertion, purge it and don't use it
   334  			s.Debugf(certidp.MsgCachedOCSPResponseInvalid, subj, fingerprint)
   335  			rc.Delete(fingerprint, true, sLogs)
   336  			goto AFTERCACHE
   337  		}
   338  	}
   339  AFTERCACHE:
   340  	if !useCachedResp {
   341  		// CA OCSP responder callout needed
   342  		rawResp, err = certidp.FetchOCSPResponse(link, opts, sLogs)
   343  		if err != nil || rawResp == nil || len(rawResp) == 0 {
   344  			s.Warnf(certidp.ErrCAResponderCalloutFail, subj, err)
   345  			if opts.WarnOnly {
   346  				s.Warnf(certidp.MsgAllowWarnOnlyOccurred, subj)
   347  				return _EMPTY_, true
   348  			}
   349  			if opts.AllowWhenCAUnreachable && !cachedRevocation {
   350  				// Link has no cached history of revocation, so allow it to pass
   351  				s.Warnf(certidp.MsgAllowWhenCAUnreachableOccurred, subj)
   352  				return _EMPTY_, true
   353  			} else if opts.AllowWhenCAUnreachable {
   354  				// Link has cached but expired revocation so reject when CA is unreachable
   355  				s.Warnf(certidp.MsgAllowWhenCAUnreachableOccurredCachedRevoke, subj)
   356  			}
   357  			return certidp.MsgFailedOCSPResponseFetch, false
   358  		}
   359  		// Signature validation of CA's OCSP response occurs in ParseResponse
   360  		ocspr, err = ocsp.ParseResponse(rawResp, link.Issuer)
   361  		if err == nil && ocspr != nil {
   362  			// Check if OCSP Response delegation present and if so is valid
   363  			if !certidp.ValidDelegationCheck(link.Issuer, ocspr) {
   364  				s.Warnf(certidp.MsgOCSPResponseDelegationInvalid, subj)
   365  				if opts.WarnOnly {
   366  					// Can't use bogus assertion, but warn-only set so allow link to pass
   367  					s.Warnf(certidp.MsgAllowWarnOnlyOccurred, subj)
   368  					return _EMPTY_, true
   369  				}
   370  				return fmt.Sprintf(certidp.MsgOCSPResponseDelegationInvalid, subj), false
   371  			}
   372  			if !certidp.OCSPResponseCurrent(ocspr, opts, sLogs) {
   373  				s.Warnf(certidp.ErrNewCAResponseNotCurrent, subj)
   374  				if opts.WarnOnly {
   375  					// Can't use non-effective assertion, but warn-only set so allow link to pass
   376  					s.Warnf(certidp.MsgAllowWarnOnlyOccurred, subj)
   377  					return _EMPTY_, true
   378  				}
   379  				return certidp.MsgOCSPResponseNotEffective, false
   380  			}
   381  		} else {
   382  			s.Errorf(certidp.ErrCAResponseParseFailed, subj, err)
   383  			if opts.WarnOnly {
   384  				// Can't use bogus assertion, but warn-only set so allow link to pass
   385  				s.Warnf(certidp.MsgAllowWarnOnlyOccurred, subj)
   386  				return _EMPTY_, true
   387  			}
   388  			return certidp.MsgFailedOCSPResponseParse, false
   389  		}
   390  		// cache the valid fetched CA OCSP Response
   391  		rc.Put(fingerprint, ocspr, subj, sLogs)
   392  	}
   393  
   394  	// Whether through valid cache response available or newly fetched valid response, now check the status
   395  	if ocspr.Status == ocsp.Revoked || (ocspr.Status == ocsp.Unknown && !opts.UnknownIsGood) {
   396  		s.Warnf(certidp.ErrOCSPInvalidPeerLink, subj, certidp.GetStatusAssertionStr(ocspr.Status))
   397  		if opts.WarnOnly {
   398  			s.Warnf(certidp.MsgAllowWarnOnlyOccurred, subj)
   399  			return _EMPTY_, true
   400  		}
   401  		return fmt.Sprintf(certidp.MsgOCSPResponseInvalidStatus, certidp.GetStatusAssertionStr(ocspr.Status)), false
   402  	}
   403  	s.Debugf(certidp.DbgOCSPValidPeerLink, subj)
   404  	return _EMPTY_, true
   405  }