snapshot of https://github.com/nats-io/nats-server.git (main, 2026-09-15; blobs > 131072B omitted)
Some checks failed
NATS Server Tests / Lint (push) Failing after 3s
NATS Server Tests / Build (Latest Go) (push) Has been skipped
NATS Server Tests / Build (Minimum Go) (push) Has been skipped
NATS Server Tests / Build (Minimum Go, windows-2022) (push) Has been skipped
NATS Server Tests / Build (Minimum Go, windows-2025) (push) Has been skipped
NATS Server Tests / Test Stores (push) Has been skipped
NATS Server Tests / Test JetStream (push) Has been skipped
NATS Server Tests / Test Raft (push) Has been skipped
NATS Server Tests / Test JetStream Consumers (push) Has been skipped
NATS Server Tests / Test JetStream Cluster 1 (push) Has been skipped
NATS Server Tests / Test JetStream Cluster 2 (push) Has been skipped
NATS Server Tests / Test JetStream Cluster 3 (push) Has been skipped
NATS Server Tests / Test JetStream Cluster 4 (push) Has been skipped
NATS Server Tests / Test JetStream Supercluster (push) Has been skipped
NATS Server Tests / Test No-Race 1 (push) Has been skipped
NATS Server Tests / Test No-Race 2 (push) Has been skipped
NATS Server Tests / Test MQTT (push) Has been skipped
NATS Server Tests / Test Message Tracing (push) Has been skipped
NATS Server Tests / Test JWT (push) Has been skipped
NATS Server Tests / Test Remaining Server Tests (push) Has been skipped
NATS Server Tests / Test Other Packages (push) Has been skipped
Vulnerability Scan / govulncheck (push) Failing after 12m2s
Some checks failed
NATS Server Tests / Lint (push) Failing after 3s
NATS Server Tests / Build (Latest Go) (push) Has been skipped
NATS Server Tests / Build (Minimum Go) (push) Has been skipped
NATS Server Tests / Build (Minimum Go, windows-2022) (push) Has been skipped
NATS Server Tests / Build (Minimum Go, windows-2025) (push) Has been skipped
NATS Server Tests / Test Stores (push) Has been skipped
NATS Server Tests / Test JetStream (push) Has been skipped
NATS Server Tests / Test Raft (push) Has been skipped
NATS Server Tests / Test JetStream Consumers (push) Has been skipped
NATS Server Tests / Test JetStream Cluster 1 (push) Has been skipped
NATS Server Tests / Test JetStream Cluster 2 (push) Has been skipped
NATS Server Tests / Test JetStream Cluster 3 (push) Has been skipped
NATS Server Tests / Test JetStream Cluster 4 (push) Has been skipped
NATS Server Tests / Test JetStream Supercluster (push) Has been skipped
NATS Server Tests / Test No-Race 1 (push) Has been skipped
NATS Server Tests / Test No-Race 2 (push) Has been skipped
NATS Server Tests / Test MQTT (push) Has been skipped
NATS Server Tests / Test Message Tracing (push) Has been skipped
NATS Server Tests / Test JWT (push) Has been skipped
NATS Server Tests / Test Remaining Server Tests (push) Has been skipped
NATS Server Tests / Test Other Packages (push) Has been skipped
Vulnerability Scan / govulncheck (push) Failing after 12m2s
This commit is contained in:
405
server/ocsp_peer.go
Normal file
405
server/ocsp_peer.go
Normal file
@@ -0,0 +1,405 @@
|
||||
// Copyright 2023-2025 The NATS Authors
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package server
|
||||
|
||||
import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"golang.org/x/crypto/ocsp"
|
||||
|
||||
"github.com/nats-io/nats-server/v2/server/certidp"
|
||||
)
|
||||
|
||||
func parseOCSPPeer(v any) (pcfg *certidp.OCSPPeerConfig, retError error) {
|
||||
var lt token
|
||||
defer convertPanicToError(<, &retError)
|
||||
tk, v := unwrapValue(v, <)
|
||||
cm, ok := v.(map[string]any)
|
||||
if !ok {
|
||||
return nil, &configErr{tk, fmt.Sprintf(certidp.ErrIllegalPeerOptsConfig, v)}
|
||||
}
|
||||
pcfg = certidp.NewOCSPPeerConfig()
|
||||
retError = nil
|
||||
for mk, mv := range cm {
|
||||
tk, mv = unwrapValue(mv, <)
|
||||
switch strings.ToLower(mk) {
|
||||
case "verify":
|
||||
verify, ok := mv.(bool)
|
||||
if !ok {
|
||||
return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldGeneric, mk)}
|
||||
}
|
||||
pcfg.Verify = verify
|
||||
case "allowed_clockskew":
|
||||
at := float64(0)
|
||||
switch mv := mv.(type) {
|
||||
case int64:
|
||||
at = float64(mv)
|
||||
case float64:
|
||||
at = mv
|
||||
case string:
|
||||
d, err := time.ParseDuration(mv)
|
||||
if err != nil {
|
||||
return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldTypeConversion, "unexpected type")}
|
||||
}
|
||||
at = d.Seconds()
|
||||
default:
|
||||
return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldTypeConversion, "unexpected type")}
|
||||
}
|
||||
if at >= 0 {
|
||||
pcfg.ClockSkew = at
|
||||
}
|
||||
case "ca_timeout":
|
||||
at := float64(0)
|
||||
switch mv := mv.(type) {
|
||||
case int64:
|
||||
at = float64(mv)
|
||||
case float64:
|
||||
at = mv
|
||||
case string:
|
||||
d, err := time.ParseDuration(mv)
|
||||
if err != nil {
|
||||
return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldTypeConversion, err)}
|
||||
}
|
||||
at = d.Seconds()
|
||||
default:
|
||||
return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldTypeConversion, "unexpected type")}
|
||||
}
|
||||
if at >= 0 {
|
||||
pcfg.Timeout = at
|
||||
}
|
||||
case "cache_ttl_when_next_update_unset":
|
||||
at := float64(0)
|
||||
switch mv := mv.(type) {
|
||||
case int64:
|
||||
at = float64(mv)
|
||||
case float64:
|
||||
at = mv
|
||||
case string:
|
||||
d, err := time.ParseDuration(mv)
|
||||
if err != nil {
|
||||
return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldTypeConversion, err)}
|
||||
}
|
||||
at = d.Seconds()
|
||||
default:
|
||||
return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldTypeConversion, "unexpected type")}
|
||||
}
|
||||
if at >= 0 {
|
||||
pcfg.TTLUnsetNextUpdate = at
|
||||
}
|
||||
case "warn_only":
|
||||
warnOnly, ok := mv.(bool)
|
||||
if !ok {
|
||||
return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldGeneric, mk)}
|
||||
}
|
||||
pcfg.WarnOnly = warnOnly
|
||||
case "unknown_is_good":
|
||||
unknownIsGood, ok := mv.(bool)
|
||||
if !ok {
|
||||
return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldGeneric, mk)}
|
||||
}
|
||||
pcfg.UnknownIsGood = unknownIsGood
|
||||
case "allow_when_ca_unreachable":
|
||||
allowWhenCAUnreachable, ok := mv.(bool)
|
||||
if !ok {
|
||||
return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldGeneric, mk)}
|
||||
}
|
||||
pcfg.AllowWhenCAUnreachable = allowWhenCAUnreachable
|
||||
default:
|
||||
return nil, &configErr{tk, fmt.Sprintf(certidp.ErrParsingPeerOptFieldGeneric, mk)}
|
||||
}
|
||||
}
|
||||
return pcfg, nil
|
||||
}
|
||||
|
||||
func peerFromVerifiedChains(chains [][]*x509.Certificate) *x509.Certificate {
|
||||
if len(chains) == 0 || len(chains[0]) == 0 {
|
||||
return nil
|
||||
}
|
||||
return chains[0][0]
|
||||
}
|
||||
|
||||
// plugTLSOCSPPeer will plug the TLS handshake lifecycle for client mTLS connections and Leaf connections
|
||||
func (s *Server) plugTLSOCSPPeer(config *tlsConfigKind) (*tls.Config, bool, error) {
|
||||
if config == nil || config.tlsConfig == nil {
|
||||
return nil, false, errors.New(certidp.ErrUnableToPlugTLSEmptyConfig)
|
||||
}
|
||||
kind := config.kind
|
||||
isSpoke := config.isLeafSpoke
|
||||
tcOpts := config.tlsOpts
|
||||
if tcOpts == nil || tcOpts.OCSPPeerConfig == nil || !tcOpts.OCSPPeerConfig.Verify {
|
||||
return nil, false, nil
|
||||
}
|
||||
s.Debugf(certidp.DbgPlugTLSForKind, config.kind)
|
||||
// peer is a tls client
|
||||
if kind == kindStringMap[CLIENT] || (kind == kindStringMap[LEAF] && !isSpoke) {
|
||||
if !tcOpts.Verify {
|
||||
return nil, false, errors.New(certidp.ErrMTLSRequired)
|
||||
}
|
||||
return s.plugClientTLSOCSPPeer(config)
|
||||
}
|
||||
// peer is a tls server
|
||||
if kind == kindStringMap[LEAF] && isSpoke {
|
||||
return s.plugServerTLSOCSPPeer(config)
|
||||
}
|
||||
return nil, false, nil
|
||||
}
|
||||
|
||||
func (s *Server) plugClientTLSOCSPPeer(config *tlsConfigKind) (*tls.Config, bool, error) {
|
||||
if config == nil || config.tlsConfig == nil || config.tlsOpts == nil {
|
||||
return nil, false, errors.New(certidp.ErrUnableToPlugTLSClient)
|
||||
}
|
||||
tc := config.tlsConfig
|
||||
tcOpts := config.tlsOpts
|
||||
kind := config.kind
|
||||
if tcOpts.OCSPPeerConfig == nil || !tcOpts.OCSPPeerConfig.Verify {
|
||||
return tc, false, nil
|
||||
}
|
||||
tc.VerifyConnection = func(cs tls.ConnectionState) error {
|
||||
if !s.tlsClientOCSPValid(cs.VerifiedChains, tcOpts.OCSPPeerConfig) {
|
||||
s.sendOCSPPeerRejectEvent(kind, peerFromVerifiedChains(cs.VerifiedChains), certidp.MsgTLSClientRejectConnection)
|
||||
return errors.New(certidp.MsgTLSClientRejectConnection)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return tc, true, nil
|
||||
}
|
||||
|
||||
func (s *Server) plugServerTLSOCSPPeer(config *tlsConfigKind) (*tls.Config, bool, error) {
|
||||
if config == nil || config.tlsConfig == nil || config.tlsOpts == nil {
|
||||
return nil, false, errors.New(certidp.ErrUnableToPlugTLSServer)
|
||||
}
|
||||
tc := config.tlsConfig
|
||||
tcOpts := config.tlsOpts
|
||||
kind := config.kind
|
||||
if tcOpts.OCSPPeerConfig == nil || !tcOpts.OCSPPeerConfig.Verify {
|
||||
return tc, false, nil
|
||||
}
|
||||
tc.VerifyConnection = func(cs tls.ConnectionState) error {
|
||||
if !s.tlsServerOCSPValid(cs.VerifiedChains, tcOpts.OCSPPeerConfig) {
|
||||
s.sendOCSPPeerRejectEvent(kind, peerFromVerifiedChains(cs.VerifiedChains), certidp.MsgTLSServerRejectConnection)
|
||||
return errors.New(certidp.MsgTLSServerRejectConnection)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
return tc, true, nil
|
||||
}
|
||||
|
||||
// tlsServerOCSPValid evaluates verified chains (post successful TLS handshake) against OCSP
|
||||
// eligibility. A verified chain is considered OCSP Valid if either none of the links are
|
||||
// OCSP eligible, or current "good" responses from the CA can be obtained for each eligible link.
|
||||
// Upon first OCSP Valid chain found, the Server is deemed OCSP Valid. If none of the chains are
|
||||
// OCSP Valid, the Server is deemed OCSP Invalid. A verified self-signed certificate (chain length 1)
|
||||
// is also considered OCSP Valid.
|
||||
func (s *Server) tlsServerOCSPValid(chains [][]*x509.Certificate, opts *certidp.OCSPPeerConfig) bool {
|
||||
s.Debugf(certidp.DbgNumServerChains, len(chains))
|
||||
return s.peerOCSPValid(chains, opts)
|
||||
}
|
||||
|
||||
// tlsClientOCSPValid evaluates verified chains (post successful TLS handshake) against OCSP
|
||||
// eligibility. A verified chain is considered OCSP Valid if either none of the links are
|
||||
// OCSP eligible, or current "good" responses from the CA can be obtained for each eligible link.
|
||||
// Upon first OCSP Valid chain found, the Client is deemed OCSP Valid. If none of the chains are
|
||||
// OCSP Valid, the Client is deemed OCSP Invalid. A verified self-signed certificate (chain length 1)
|
||||
// is also considered OCSP Valid.
|
||||
func (s *Server) tlsClientOCSPValid(chains [][]*x509.Certificate, opts *certidp.OCSPPeerConfig) bool {
|
||||
s.Debugf(certidp.DbgNumClientChains, len(chains))
|
||||
return s.peerOCSPValid(chains, opts)
|
||||
}
|
||||
|
||||
func (s *Server) peerOCSPValid(chains [][]*x509.Certificate, opts *certidp.OCSPPeerConfig) bool {
|
||||
peer := peerFromVerifiedChains(chains)
|
||||
if peer == nil {
|
||||
s.Errorf(certidp.ErrPeerEmptyAutoReject)
|
||||
return false
|
||||
}
|
||||
for ci, chain := range chains {
|
||||
s.Debugf(certidp.DbgLinksInChain, ci, len(chain))
|
||||
// Self-signed certificate is Client OCSP Valid (no CA)
|
||||
if len(chain) == 1 {
|
||||
s.Debugf(certidp.DbgSelfSignedValid, ci)
|
||||
return true
|
||||
}
|
||||
// Check if any of the links in the chain are OCSP eligible
|
||||
chainEligible := false
|
||||
var eligibleLinks []*certidp.ChainLink
|
||||
// Iterate over links skipping the root cert which is not OCSP eligible (self == issuer)
|
||||
for linkPos := 0; linkPos < len(chain)-1; linkPos++ {
|
||||
cert := chain[linkPos]
|
||||
link := &certidp.ChainLink{
|
||||
Leaf: cert,
|
||||
}
|
||||
if certidp.CertOCSPEligible(link) {
|
||||
chainEligible = true
|
||||
issuerCert := certidp.GetLeafIssuerCert(chain, linkPos)
|
||||
if issuerCert == nil {
|
||||
// unexpected chain condition, reject Client as OCSP Invalid
|
||||
return false
|
||||
}
|
||||
link.Issuer = issuerCert
|
||||
eligibleLinks = append(eligibleLinks, link)
|
||||
}
|
||||
}
|
||||
// A trust-store verified chain that is not OCSP eligible is always OCSP Valid
|
||||
if !chainEligible {
|
||||
s.Debugf(certidp.DbgValidNonOCSPChain, ci)
|
||||
return true
|
||||
}
|
||||
s.Debugf(certidp.DbgChainIsOCSPEligible, ci, len(eligibleLinks))
|
||||
// Chain has at least one OCSP eligible link, so check each eligible link;
|
||||
// any link with a !good OCSP response chain OCSP Invalid
|
||||
chainValid := true
|
||||
for _, link := range eligibleLinks {
|
||||
// if option selected, good could reflect either ocsp.Good or ocsp.Unknown
|
||||
if badReason, good := s.certOCSPGood(link, opts); !good {
|
||||
s.Debugf(badReason)
|
||||
s.sendOCSPPeerChainlinkInvalidEvent(peer, link.Leaf, badReason)
|
||||
chainValid = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if chainValid {
|
||||
s.Debugf(certidp.DbgChainIsOCSPValid, ci)
|
||||
return true
|
||||
}
|
||||
}
|
||||
// If we are here, all chains had OCSP eligible links, but none of the chains achieved OCSP valid
|
||||
s.Debugf(certidp.DbgNoOCSPValidChains)
|
||||
return false
|
||||
}
|
||||
|
||||
func (s *Server) certOCSPGood(link *certidp.ChainLink, opts *certidp.OCSPPeerConfig) (string, bool) {
|
||||
if link == nil || link.Leaf == nil || link.Issuer == nil || link.OCSPWebEndpoints == nil || len(*link.OCSPWebEndpoints) < 1 {
|
||||
return "Empty chainlink found", false
|
||||
}
|
||||
var err error
|
||||
sLogs := &certidp.Log{
|
||||
Debugf: s.Debugf,
|
||||
Noticef: s.Noticef,
|
||||
Warnf: s.Warnf,
|
||||
Errorf: s.Errorf,
|
||||
Tracef: s.Tracef,
|
||||
}
|
||||
fingerprint := certidp.GenerateFingerprint(link.Leaf)
|
||||
// Used for debug/operator only, not match
|
||||
subj := certidp.GetSubjectDNForm(link.Leaf)
|
||||
var rawResp []byte
|
||||
var ocspr *ocsp.Response
|
||||
var useCachedResp bool
|
||||
var rc = s.ocsprc
|
||||
var cachedRevocation bool
|
||||
// Check our cache before calling out to the CA OCSP responder
|
||||
s.Debugf(certidp.DbgCheckingCacheForCert, subj, fingerprint)
|
||||
if rawResp = rc.Get(fingerprint, sLogs); len(rawResp) > 0 {
|
||||
// Signature validation of CA's OCSP response occurs in ParseResponse
|
||||
ocspr, err = ocsp.ParseResponse(rawResp, link.Issuer)
|
||||
if err == nil && ocspr != nil {
|
||||
// Check if OCSP Response delegation present and if so is valid
|
||||
if !certidp.ValidDelegationCheck(link.Issuer, ocspr) {
|
||||
// Invalid delegation was already in cache, purge it and don't use it
|
||||
s.Debugf(certidp.MsgCachedOCSPResponseInvalid, subj)
|
||||
rc.Delete(fingerprint, true, sLogs)
|
||||
goto AFTERCACHE
|
||||
}
|
||||
if certidp.OCSPResponseCurrent(ocspr, opts, sLogs) {
|
||||
s.Debugf(certidp.DbgCurrentResponseCached, certidp.GetStatusAssertionStr(ocspr.Status))
|
||||
useCachedResp = true
|
||||
} else {
|
||||
// Cached response is not current, delete it and tidy runtime stats to reflect a miss;
|
||||
// if preserve_revoked is enabled, the cache will not delete the cached response
|
||||
s.Debugf(certidp.DbgExpiredResponseCached, certidp.GetStatusAssertionStr(ocspr.Status))
|
||||
rc.Delete(fingerprint, true, sLogs)
|
||||
}
|
||||
// Regardless of currency, record a cached revocation found in case AllowWhenCAUnreachable is set
|
||||
if ocspr.Status == ocsp.Revoked {
|
||||
cachedRevocation = true
|
||||
}
|
||||
} else {
|
||||
// Bogus cached assertion, purge it and don't use it
|
||||
s.Debugf(certidp.MsgCachedOCSPResponseInvalid, subj, fingerprint)
|
||||
rc.Delete(fingerprint, true, sLogs)
|
||||
goto AFTERCACHE
|
||||
}
|
||||
}
|
||||
AFTERCACHE:
|
||||
if !useCachedResp {
|
||||
// CA OCSP responder callout needed
|
||||
rawResp, err = certidp.FetchOCSPResponse(link, opts, sLogs)
|
||||
if err != nil || rawResp == nil || len(rawResp) == 0 {
|
||||
s.Warnf(certidp.ErrCAResponderCalloutFail, subj, err)
|
||||
if opts.WarnOnly {
|
||||
s.Warnf(certidp.MsgAllowWarnOnlyOccurred, subj)
|
||||
return _EMPTY_, true
|
||||
}
|
||||
if opts.AllowWhenCAUnreachable && !cachedRevocation {
|
||||
// Link has no cached history of revocation, so allow it to pass
|
||||
s.Warnf(certidp.MsgAllowWhenCAUnreachableOccurred, subj)
|
||||
return _EMPTY_, true
|
||||
} else if opts.AllowWhenCAUnreachable {
|
||||
// Link has cached but expired revocation so reject when CA is unreachable
|
||||
s.Warnf(certidp.MsgAllowWhenCAUnreachableOccurredCachedRevoke, subj)
|
||||
}
|
||||
return certidp.MsgFailedOCSPResponseFetch, false
|
||||
}
|
||||
// Signature validation of CA's OCSP response occurs in ParseResponse
|
||||
ocspr, err = ocsp.ParseResponse(rawResp, link.Issuer)
|
||||
if err == nil && ocspr != nil {
|
||||
// Check if OCSP Response delegation present and if so is valid
|
||||
if !certidp.ValidDelegationCheck(link.Issuer, ocspr) {
|
||||
s.Warnf(certidp.MsgOCSPResponseDelegationInvalid, subj)
|
||||
if opts.WarnOnly {
|
||||
// Can't use bogus assertion, but warn-only set so allow link to pass
|
||||
s.Warnf(certidp.MsgAllowWarnOnlyOccurred, subj)
|
||||
return _EMPTY_, true
|
||||
}
|
||||
return fmt.Sprintf(certidp.MsgOCSPResponseDelegationInvalid, subj), false
|
||||
}
|
||||
if !certidp.OCSPResponseCurrent(ocspr, opts, sLogs) {
|
||||
s.Warnf(certidp.ErrNewCAResponseNotCurrent, subj)
|
||||
if opts.WarnOnly {
|
||||
// Can't use non-effective assertion, but warn-only set so allow link to pass
|
||||
s.Warnf(certidp.MsgAllowWarnOnlyOccurred, subj)
|
||||
return _EMPTY_, true
|
||||
}
|
||||
return certidp.MsgOCSPResponseNotEffective, false
|
||||
}
|
||||
} else {
|
||||
s.Errorf(certidp.ErrCAResponseParseFailed, subj, err)
|
||||
if opts.WarnOnly {
|
||||
// Can't use bogus assertion, but warn-only set so allow link to pass
|
||||
s.Warnf(certidp.MsgAllowWarnOnlyOccurred, subj)
|
||||
return _EMPTY_, true
|
||||
}
|
||||
return certidp.MsgFailedOCSPResponseParse, false
|
||||
}
|
||||
// cache the valid fetched CA OCSP Response
|
||||
rc.Put(fingerprint, ocspr, subj, sLogs)
|
||||
}
|
||||
|
||||
// Whether through valid cache response available or newly fetched valid response, now check the status
|
||||
if ocspr.Status == ocsp.Revoked || (ocspr.Status == ocsp.Unknown && !opts.UnknownIsGood) {
|
||||
s.Warnf(certidp.ErrOCSPInvalidPeerLink, subj, certidp.GetStatusAssertionStr(ocspr.Status))
|
||||
if opts.WarnOnly {
|
||||
s.Warnf(certidp.MsgAllowWarnOnlyOccurred, subj)
|
||||
return _EMPTY_, true
|
||||
}
|
||||
return fmt.Sprintf(certidp.MsgOCSPResponseInvalidStatus, certidp.GetStatusAssertionStr(ocspr.Status)), false
|
||||
}
|
||||
s.Debugf(certidp.DbgOCSPValidPeerLink, subj)
|
||||
return _EMPTY_, true
|
||||
}
|
||||
Reference in New Issue
Block a user