1 package responder
2
3 import (
4 "bytes"
5 "context"
6 "crypto"
7 "encoding/hex"
8 "errors"
9 "fmt"
10 "strings"
11
12 "github.com/jmhodges/clock"
13 "github.com/letsencrypt/boulder/core"
14 "github.com/letsencrypt/boulder/issuance"
15 blog "github.com/letsencrypt/boulder/log"
16 "github.com/prometheus/client_golang/prometheus"
17 "golang.org/x/crypto/ocsp"
18 )
19
20 type responderID struct {
21 nameHash []byte
22 keyHash []byte
23 }
24
25 type filterSource struct {
26 wrapped Source
27 hashAlgorithm crypto.Hash
28 issuers map[issuance.IssuerNameID]responderID
29 serialPrefixes []string
30 counter *prometheus.CounterVec
31 log blog.Logger
32 clk clock.Clock
33 }
34
35
36
37
38 func NewFilterSource(issuerCerts []*issuance.Certificate, serialPrefixes []string, wrapped Source, stats prometheus.Registerer, log blog.Logger, clk clock.Clock) (*filterSource, error) {
39 if len(issuerCerts) < 1 {
40 return nil, errors.New("filter must include at least 1 issuer cert")
41 }
42
43 issuersByNameId := make(map[issuance.IssuerNameID]responderID)
44 for _, issuerCert := range issuerCerts {
45 keyHash := issuerCert.KeyHash()
46 nameHash := issuerCert.NameHash()
47 rid := responderID{
48 keyHash: keyHash[:],
49 nameHash: nameHash[:],
50 }
51 issuersByNameId[issuerCert.NameID()] = rid
52 }
53
54 counter := prometheus.NewCounterVec(prometheus.CounterOpts{
55 Name: "ocsp_filter_responses",
56 Help: "Count of OCSP requests/responses by action taken by the filter",
57 }, []string{"result"})
58 stats.MustRegister(counter)
59
60 return &filterSource{
61 wrapped: wrapped,
62 hashAlgorithm: crypto.SHA1,
63 issuers: issuersByNameId,
64 serialPrefixes: serialPrefixes,
65 counter: counter,
66 log: log,
67 clk: clk,
68 }, nil
69 }
70
71
72
73
74 func (src *filterSource) Response(ctx context.Context, req *ocsp.Request) (*Response, error) {
75 iss, err := src.checkRequest(req)
76 if err != nil {
77 src.log.Debugf("Not responding to filtered OCSP request: %s", err.Error())
78 src.counter.WithLabelValues("request_filtered").Inc()
79 return nil, err
80 }
81
82 resp, err := src.wrapped.Response(ctx, req)
83 if err != nil {
84 src.counter.WithLabelValues("wrapped_error").Inc()
85 return nil, err
86 }
87
88 err = src.checkResponse(iss, resp)
89 if err != nil {
90 src.log.Warningf("OCSP Response not sent for CA=%s, Serial=%s, err: %s", hex.EncodeToString(req.IssuerKeyHash), core.SerialToString(req.SerialNumber), err)
91 src.counter.WithLabelValues("response_filtered").Inc()
92 return nil, err
93 }
94
95 src.counter.WithLabelValues("success").Inc()
96 return resp, nil
97 }
98
99
100
101 func (src *filterSource) checkNextUpdate(resp *Response) error {
102 if src.clk.Now().Before(resp.NextUpdate) {
103 return nil
104 }
105 return errOCSPResponseExpired
106 }
107
108
109
110
111
112 func (src *filterSource) checkRequest(req *ocsp.Request) (issuance.IssuerNameID, error) {
113 if req.HashAlgorithm != src.hashAlgorithm {
114 return 0, fmt.Errorf("unsupported issuer key/name hash algorithm %s: %w", req.HashAlgorithm, ErrNotFound)
115 }
116
117 if len(src.serialPrefixes) > 0 {
118 serialString := core.SerialToString(req.SerialNumber)
119 match := false
120 for _, prefix := range src.serialPrefixes {
121 if strings.HasPrefix(serialString, prefix) {
122 match = true
123 break
124 }
125 }
126 if !match {
127 return 0, fmt.Errorf("unrecognized serial prefix: %w", ErrNotFound)
128 }
129 }
130
131 for nameID, rid := range src.issuers {
132 if bytes.Equal(req.IssuerNameHash, rid.nameHash) && bytes.Equal(req.IssuerKeyHash, rid.keyHash) {
133 return nameID, nil
134 }
135 }
136 return 0, fmt.Errorf("unrecognized issuer key hash %s: %w", hex.EncodeToString(req.IssuerKeyHash), ErrNotFound)
137 }
138
139
140
141
142
143 func (src *filterSource) checkResponse(reqIssuerID issuance.IssuerNameID, resp *Response) error {
144 respIssuerID := issuance.GetOCSPIssuerNameID(resp.Response)
145 if reqIssuerID != respIssuerID {
146
147 return fmt.Errorf("responder name does not match requested issuer name")
148 }
149
150 err := src.checkNextUpdate(resp)
151 if err != nil {
152 return err
153 }
154
155
156
157
158
159
160
161
162 return nil
163 }
164
View as plain text