diff --git a/cmd/ocsp-responder/main.go b/cmd/ocsp-responder/main.go index 3fde81270..e0f8a3c04 100644 --- a/cmd/ocsp-responder/main.go +++ b/cmd/ocsp-responder/main.go @@ -43,9 +43,10 @@ serialNumber field, since we will always query on it. */ type DBSource struct { - dbMap dbSelector - caKeyHash []byte - log blog.Logger + dbMap dbSelector + caKeyHash []byte + reqSerialPrefixes []string + log blog.Logger } // Since the only thing we use from gorp is the SelectOne method on the @@ -58,8 +59,8 @@ type dbSelector interface { // NewSourceFromDatabase produces a DBSource representing the binding of a // given DB schema to a CA key. -func NewSourceFromDatabase(dbMap dbSelector, caKeyHash []byte, log blog.Logger) (src *DBSource, err error) { - src = &DBSource{dbMap: dbMap, caKeyHash: caKeyHash, log: log} +func NewSourceFromDatabase(dbMap dbSelector, caKeyHash []byte, reqSerialPrefixes []string, log blog.Logger) (src *DBSource, err error) { + src = &DBSource{dbMap: dbMap, caKeyHash: caKeyHash, reqSerialPrefixes: reqSerialPrefixes, log: log} return } @@ -77,6 +78,16 @@ func (src *DBSource) Response(req *ocsp.Request) ([]byte, http.Header, error) { } serialString := core.SerialToString(req.SerialNumber) + if len(src.reqSerialPrefixes) > 0 { + match := false + for _, prefix := range src.reqSerialPrefixes { + match = strings.HasPrefix(serialString, prefix) + } + if !match { + return nil, nil, cfocsp.ErrNotFound + } + } + src.log.Debugf("Searching for OCSP issued by us for serial %s", serialString) var response dbResponse @@ -105,7 +116,7 @@ func (src *DBSource) Response(req *ocsp.Request) ([]byte, http.Header, error) { return response.OCSPResponse, nil, nil } -func makeDBSource(dbMap dbSelector, issuerCert string, log blog.Logger) (*DBSource, error) { +func makeDBSource(dbMap dbSelector, issuerCert string, reqSerialPrefixes []string, log blog.Logger) (*DBSource, error) { // Load the CA's key so we can store its SubjectKey in the DB caCertDER, err := cmd.LoadCert(issuerCert) if err != nil { @@ -120,7 +131,7 @@ func makeDBSource(dbMap dbSelector, issuerCert string, log blog.Logger) (*DBSour } // Construct source from DB - return NewSourceFromDatabase(dbMap, caCert.SubjectKeyId, log) + return NewSourceFromDatabase(dbMap, caCert.SubjectKeyId, reqSerialPrefixes, log) } type config struct { @@ -142,6 +153,8 @@ type config struct { ShutdownStopTimeout cmd.ConfigDuration + RequiredSerialPrefixes []string + Features map[string]bool } @@ -201,7 +214,7 @@ as generated by Boulder's single-ocsp command. cmd.FailOnError(err, "Could not connect to database") sa.SetSQLDebug(dbMap, logger) go sa.ReportDbConnCount(dbMap, scope) - source, err = makeDBSource(dbMap, c.Common.IssuerCert, logger) + source, err = makeDBSource(dbMap, c.Common.IssuerCert, c.OCSPResponder.RequiredSerialPrefixes, logger) cmd.FailOnError(err, "Couldn't load OCSP DB") // Export the MaxDBConns dbConnStat := prometheus.NewGauge(prometheus.GaugeOpts{ diff --git a/cmd/ocsp-responder/main_test.go b/cmd/ocsp-responder/main_test.go index 71a7b927f..065a648aa 100644 --- a/cmd/ocsp-responder/main_test.go +++ b/cmd/ocsp-responder/main_test.go @@ -14,6 +14,7 @@ import ( "golang.org/x/crypto/ocsp" cfocsp "github.com/cloudflare/cfssl/ocsp" + "github.com/letsencrypt/boulder/core" blog "github.com/letsencrypt/boulder/log" "github.com/letsencrypt/boulder/metrics" "github.com/letsencrypt/boulder/test" @@ -71,7 +72,7 @@ func TestMux(t *testing.T) { } func TestDBHandler(t *testing.T) { - src, err := makeDBSource(mockSelector{}, "./testdata/test-ca.der.pem", blog.NewMock()) + src, err := makeDBSource(mockSelector{}, "./testdata/test-ca.der.pem", nil, blog.NewMock()) if err != nil { t.Fatalf("makeDBSource: %s", err) } @@ -127,7 +128,7 @@ func (bs brokenSelector) SelectOne(_ interface{}, _ string, _ ...interface{}) er func TestErrorLog(t *testing.T) { mockLog := blog.NewMock() - src, err := makeDBSource(brokenSelector{}, "./testdata/test-ca.der.pem", mockLog) + src, err := makeDBSource(brokenSelector{}, "./testdata/test-ca.der.pem", nil, mockLog) test.AssertNotError(t, err, "Failed to create broken dbMap") ocspReq, err := ocsp.ParseRequest(req) @@ -150,3 +151,22 @@ func mustRead(path string) []byte { } return b } + +func TestRequiredSerialPrefix(t *testing.T) { + mockLog := blog.NewMock() + src, err := makeDBSource(mockSelector{}, "./testdata/test-ca.der.pem", []string{"nope"}, mockLog) + test.AssertNotError(t, err, "failed to create DBSource") + + ocspReq, err := ocsp.ParseRequest(req) + test.AssertNotError(t, err, "Failed to parse OCSP request") + + _, _, err = src.Response(ocspReq) + test.AssertEquals(t, err, cfocsp.ErrNotFound) + + fmt.Println(core.SerialToString(ocspReq.SerialNumber)) + + src, err = makeDBSource(mockSelector{}, "./testdata/test-ca.der.pem", []string{"00"}, mockLog) + test.AssertNotError(t, err, "failed to create DBSource") + _, _, err = src.Response(ocspReq) + test.AssertNotError(t, err, "src.Response failed with acceptable prefix") +} diff --git a/test/config-next/ocsp-responder.json b/test/config-next/ocsp-responder.json index 5b29d96d3..71c72bb7a 100644 --- a/test/config-next/ocsp-responder.json +++ b/test/config-next/ocsp-responder.json @@ -6,7 +6,8 @@ "listenAddress": "0.0.0.0:4002", "maxAge": "10s", "shutdownStopTimeout": "10s", - "debugAddr": ":8005" + "debugAddr": ":8005", + "requiredSerialPrefixes": ["ff"] }, "syslog": {