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(<, &retError) 32 tk, v := unwrapValue(v, <) 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, <) 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 }