From fe6c63ff4b645ae1f66f0f641da982bd04426ce7 Mon Sep 17 00:00:00 2001 From: Jan Dittberner Date: Mon, 14 Mar 2022 11:21:40 +0100 Subject: [PATCH] Ensure RFC-6960 compliance --- .gitignore | 1 + internal/ocspsource/ocspsource.go | 20 ++++++++++++++++++-- internal/ocspsource/opensslcertdb.go | 13 +++++++++++-- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index e0b0323..0d55b6d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,4 @@ +*.der /.idea/ /cacertocsp /config.yaml diff --git a/internal/ocspsource/ocspsource.go b/internal/ocspsource/ocspsource.go index 6e67ff6..e6664d3 100644 --- a/internal/ocspsource/ocspsource.go +++ b/internal/ocspsource/ocspsource.go @@ -23,6 +23,7 @@ import ( "crypto/x509" "crypto/x509/pkix" "encoding/asn1" + "encoding/hex" "errors" "fmt" "math/big" @@ -34,6 +35,8 @@ import ( "golang.org/x/crypto/ocsp" ) +var unknownIssuer = errors.New("issuer key hash does not match any of the known issuers") + // CertificateIssuer is an abstraction for the OCSP responder that knows certificates of a specific CA type CertificateIssuer struct { responderCertificate *x509.Certificate @@ -156,7 +159,16 @@ func WithIssuer(issuer *CertificateIssuer) Option { func (o *OcspSource) Response(r *ocsp.Request) ([]byte, http.Header, error) { issuer, err := o.getIssuer(r.IssuerKeyHash, r.HashAlgorithm) if err != nil { - return nil, nil, fmt.Errorf("cannot answer request: %v", err) + if errors.Is(err, unknownIssuer) { + logrus.Infof("received request for unknown issuer key hash %s", hex.EncodeToString(r.IssuerKeyHash)) + issuer = o.getDefaultIssuer() + response, err := issuer.buildUnknownResponse(r.SerialNumber) + if err != nil { + return nil, nil, fmt.Errorf("could not build OCSP response: %v", err) + } + return response, nil, nil + } + return nil, nil, fmt.Errorf("cannot answer request: %w", err) } response, err := issuer.LookupResponse(r.SerialNumber) @@ -177,7 +189,7 @@ func (o *OcspSource) getIssuer(keyHash []byte, algorithm crypto.Hash) (*Certific return issuer, nil } } - return nil, errors.New("issuer key hash does not match any of the known issuers") + return nil, unknownIssuer } // BackgroundTasks delegates background task handling to the CertificateIssuer instances of the OcspSource @@ -195,3 +207,7 @@ func (o *OcspSource) BackgroundTasks(ctx context.Context) { wg.Wait() logrus.Info("stopped OCSP source background tasks") } + +func (o *OcspSource) getDefaultIssuer() *CertificateIssuer { + return o.issuers[0] +} diff --git a/internal/ocspsource/opensslcertdb.go b/internal/ocspsource/opensslcertdb.go index a420587..ee147f7 100644 --- a/internal/ocspsource/opensslcertdb.go +++ b/internal/ocspsource/opensslcertdb.go @@ -102,7 +102,17 @@ func (o *OpenSSLCertDB) LookupResponseTemplate(number *big.Int) *ocsp.Response { if response, ok := o.content[serial]; ok { return response } - return nil + // RFC https://datatracker.ietf.org/doc/html/rfc6960#section-2.2 states that certificates we cannot answer for + // should be marked as good. This would allow a CRL as sole certificate source. + // + // We might change this to an unknown response when we have a more accurate certificate source like an API or an + // up-to-date openssl index.txt. + logrus.Debugf("received request for certificate %s that is not in our data source", serial) + response := &ocsp.Response{ + Status: ocsp.Good, + SerialNumber: number, + } + return response } // WatchChanges creates file system monitoring for the index.txt file of the OpenSSLCertDB @@ -172,7 +182,6 @@ func parseLine(line string) (string, *ocsp.Response) { } response := ocsp.Response{ - Status: 0, SerialNumber: serialNumber, }