/proc/self/cwd/source/common/tls/ocsp/ocsp.h
Line | Count | Source (jump to first uncovered line) |
1 | | #pragma once |
2 | | |
3 | | #include <iomanip> |
4 | | #include <sstream> |
5 | | #include <vector> |
6 | | |
7 | | #include "envoy/common/exception.h" |
8 | | #include "envoy/common/time.h" |
9 | | #include "envoy/ssl/context.h" |
10 | | |
11 | | #include "absl/status/statusor.h" |
12 | | #include "absl/strings/string_view.h" |
13 | | #include "absl/types/optional.h" |
14 | | #include "openssl/bytestring.h" |
15 | | #include "openssl/ssl.h" |
16 | | |
17 | | /** |
18 | | * Data structures and functions for unmarshaling OCSP responses |
19 | | * according to the RFC6960 B.2 spec. See: https://tools.ietf.org/html/rfc6960#appendix-B |
20 | | * |
21 | | * WARNING: This module is meant to validate that OCSP responses are well-formed |
22 | | * and extract useful fields for OCSP stapling. This assumes that responses are |
23 | | * provided from configs or another trusted source and does not perform |
24 | | * checks necessary to verify responses coming from an upstream server. |
25 | | */ |
26 | | |
27 | | namespace Envoy { |
28 | | namespace Extensions { |
29 | | namespace TransportSockets { |
30 | | namespace Tls { |
31 | | namespace Ocsp { |
32 | | |
33 | | /** |
34 | | * Reflection of the `ASN.1` OcspResponseStatus enumeration. |
35 | | * The possible statuses that can accompany an OCSP response. |
36 | | */ |
37 | | enum class OcspResponseStatus { |
38 | | // OCSPResponseStatus ::= ENUMERATED { |
39 | | // successful (0), -- Response has valid confirmations |
40 | | // malformedRequest (1), -- Illegal confirmation request |
41 | | // internalError (2), -- Internal error in issuer |
42 | | // tryLater (3), -- Try again later |
43 | | // -- (4) is not used |
44 | | // sigRequired (5), -- Must sign the request |
45 | | // unauthorized (6) -- Request unauthorized |
46 | | // } |
47 | | Successful = 0, |
48 | | MalformedRequest = 1, |
49 | | InternalError = 2, |
50 | | TryLater = 3, |
51 | | SigRequired = 5, |
52 | | Unauthorized = 6 |
53 | | }; |
54 | | |
55 | | /** |
56 | | * Partial reflection of the `ASN.1` CertId structure. |
57 | | * Contains the information to identify an SSL Certificate. |
58 | | * Serial numbers are guaranteed to be |
59 | | * unique per issuer but not necessarily universally. |
60 | | */ |
61 | | struct CertId { |
62 | | CertId(std::string serial_number); |
63 | | |
64 | | std::string serial_number_; |
65 | | }; |
66 | | |
67 | | /** |
68 | | * Partial reflection of the `ASN.1` SingleResponse structure. |
69 | | * Contains information about the OCSP status of a single certificate. |
70 | | * An OCSP request may request the status of multiple certificates and |
71 | | * therefore responses may contain multiple SingleResponses. |
72 | | * |
73 | | * this_update_ and next_update_ reflect the validity period for this response. |
74 | | * If next_update_ is not present, the OCSP responder always has new information |
75 | | * available. In this case the response would be considered immediately expired |
76 | | * and invalid for stapling. |
77 | | */ |
78 | | struct SingleResponse { |
79 | | SingleResponse(CertId cert_id, Envoy::SystemTime this_update, |
80 | | absl::optional<Envoy::SystemTime> next_update); |
81 | | |
82 | | const CertId cert_id_; |
83 | | const Envoy::SystemTime this_update_; |
84 | | const absl::optional<Envoy::SystemTime> next_update_; |
85 | | }; |
86 | | |
87 | | /** |
88 | | * Partial reflection of the `ASN.1` ResponseData structure. |
89 | | * Contains an OCSP response for each certificate in a given request |
90 | | * as well as the time at which the response was produced. |
91 | | */ |
92 | | struct ResponseData { |
93 | | ResponseData(std::vector<SingleResponse> single_responses); |
94 | | |
95 | | const std::vector<SingleResponse> single_responses_; |
96 | | }; |
97 | | |
98 | | /** |
99 | | * An abstract type for OCSP response formats. Which variant of `Response` is |
100 | | * used in an `OcspResponse` is indicated by the structure's `OID`. |
101 | | * |
102 | | * Envoy enforces that OCSP responses must be for a single certificate |
103 | | * only. The methods on this class extract the relevant information for the |
104 | | * single certificate contained in the response. |
105 | | */ |
106 | | class Response { |
107 | | public: |
108 | 0 | virtual ~Response() = default; |
109 | | |
110 | | /** |
111 | | * @return The number of certs reported on by this response. |
112 | | */ |
113 | | virtual size_t getNumCerts() PURE; |
114 | | |
115 | | /** |
116 | | * @return The serial number of the certificate. |
117 | | */ |
118 | | virtual const std::string& getCertSerialNumber() PURE; |
119 | | |
120 | | /** |
121 | | * @return The beginning of the validity window for this response. |
122 | | */ |
123 | | virtual const Envoy::SystemTime& getThisUpdate() PURE; |
124 | | |
125 | | /** |
126 | | * The time at which this response is considered to expire. If |
127 | | * `nullopt`, then there is assumed to always be more up-to-date |
128 | | * information available and the response is always considered expired. |
129 | | * |
130 | | * @return The end of the validity window for this response. |
131 | | */ |
132 | | virtual const absl::optional<Envoy::SystemTime>& getNextUpdate() PURE; |
133 | | }; |
134 | | |
135 | | using ResponsePtr = std::unique_ptr<Response>; |
136 | | |
137 | | /** |
138 | | * Reflection of the `ASN.1` BasicOcspResponse structure. |
139 | | * Contains the full data of an OCSP response. |
140 | | * Envoy enforces that OCSP responses contain a response for only |
141 | | * a single certificate. |
142 | | * |
143 | | * BasicOcspResponse is the only supported Response type in RFC 6960. |
144 | | */ |
145 | | class BasicOcspResponse : public Response { |
146 | | public: |
147 | | BasicOcspResponse(ResponseData data); |
148 | | |
149 | | // Response |
150 | 0 | size_t getNumCerts() override { return data_.single_responses_.size(); } |
151 | 0 | const std::string& getCertSerialNumber() override { |
152 | 0 | return data_.single_responses_[0].cert_id_.serial_number_; |
153 | 0 | } |
154 | 0 | const Envoy::SystemTime& getThisUpdate() override { |
155 | 0 | return data_.single_responses_[0].this_update_; |
156 | 0 | } |
157 | 0 | const absl::optional<Envoy::SystemTime>& getNextUpdate() override { |
158 | 0 | return data_.single_responses_[0].next_update_; |
159 | 0 | } |
160 | | |
161 | | // Identified as `id-pkix-ocsp-basic` in |
162 | | // https://tools.ietf.org/html/rfc6960#appendix-B.2 |
163 | | constexpr static absl::string_view OID = "1.3.6.1.5.5.7.48.1.1"; |
164 | | |
165 | | private: |
166 | | const ResponseData data_; |
167 | | }; |
168 | | |
169 | | /** |
170 | | * Reflection of the `ASN.1` OcspResponse structure. |
171 | | * This is the top-level data structure for OCSP responses. |
172 | | */ |
173 | | struct OcspResponse { |
174 | | OcspResponse(OcspResponseStatus status, ResponsePtr response); |
175 | | |
176 | | OcspResponseStatus status_; |
177 | | ResponsePtr response_; |
178 | | }; |
179 | | |
180 | | /** |
181 | | * A wrapper used to own and query an OCSP response in DER-encoded format. |
182 | | */ |
183 | | class OcspResponseWrapperImpl : public Ssl::OcspResponseWrapper { |
184 | | public: |
185 | | OcspResponseWrapperImpl(std::vector<uint8_t> der_response, TimeSource& time_source, |
186 | | std::unique_ptr<OcspResponse>&& response); |
187 | | static absl::StatusOr<std::unique_ptr<OcspResponseWrapperImpl>> |
188 | | create(std::vector<uint8_t> der_response, TimeSource& time_source); |
189 | | |
190 | | /** |
191 | | * @return OcspResponseStatus whether the OCSP response was successfully created |
192 | | * or a status indicating an error in the OCSP process |
193 | | */ |
194 | 0 | OcspResponseStatus getResponseStatus() const { return response_->status_; } |
195 | | |
196 | | /** |
197 | | * @param cert a X509& SSL certificate |
198 | | * @returns bool whether this OCSP response contains the revocation status of `cert` |
199 | | */ |
200 | | bool matchesCertificate(X509& cert) const; |
201 | | |
202 | | // OcspResponseWrapper |
203 | | uint64_t secondsUntilExpiration() const override; |
204 | | Envoy::SystemTime getThisUpdate() const override; |
205 | | Envoy::SystemTime getNextUpdate() const override; |
206 | | bool isExpired() override; |
207 | 0 | const std::vector<uint8_t>& rawBytes() const override { return raw_bytes_; } |
208 | | |
209 | | private: |
210 | | const std::vector<uint8_t> raw_bytes_; |
211 | | const std::unique_ptr<OcspResponse> response_; |
212 | | TimeSource& time_source_; |
213 | | }; |
214 | | |
215 | | /** |
216 | | * `ASN.1` DER-encoded parsing functions similar to `Asn1Utility` but specifically |
217 | | * for structures related to OCSP. |
218 | | * |
219 | | * Each function must advance `cbs` across the element it refers to. |
220 | | */ |
221 | | class Asn1OcspUtility { |
222 | | public: |
223 | | /** |
224 | | * @param `cbs` a CBS& that refers to an `ASN.1` OcspResponse element |
225 | | * @returns std::unique_ptr<OcspResponse> the OCSP response encoded in `cbs` |
226 | | * @throws Envoy::EnvoyException if `cbs` does not contain a well-formed OcspResponse |
227 | | * element. |
228 | | */ |
229 | | static absl::StatusOr<std::unique_ptr<OcspResponse>> parseOcspResponse(CBS& cbs); |
230 | | |
231 | | /** |
232 | | * @param cbs a CBS& that refers to an `ASN.1` OcspResponseStatus element |
233 | | * @returns OcspResponseStatus the OCSP response encoded in `cbs` |
234 | | * @throws Envoy::EnvoyException if `cbs` does not contain a well-formed |
235 | | * OcspResponseStatus element. |
236 | | */ |
237 | | static absl::StatusOr<OcspResponseStatus> parseResponseStatus(CBS& cbs); |
238 | | |
239 | | /** |
240 | | * @param cbs a CBS& that refers to an `ASN.1` Response element |
241 | | * @returns Response containing the content of an OCSP response |
242 | | * @throws Envoy::EnvoyException if `cbs` does not contain a well-formed |
243 | | * structure that is a valid Response type. |
244 | | */ |
245 | | static absl::StatusOr<ResponsePtr> parseResponseBytes(CBS& cbs); |
246 | | |
247 | | /** |
248 | | * @param cbs a CBS& that refers to an `ASN.1` BasicOcspResponse element |
249 | | * @returns BasicOcspResponse containing the content of an OCSP response |
250 | | * @throws Envoy::EnvoyException if `cbs` does not contain a well-formed |
251 | | * BasicOcspResponse element. |
252 | | */ |
253 | | static absl::StatusOr<std::unique_ptr<BasicOcspResponse>> parseBasicOcspResponse(CBS& cbs); |
254 | | |
255 | | /** |
256 | | * @param cbs a CBS& that refers to an `ASN.1` ResponseData element |
257 | | * @returns ResponseData containing the content of an OCSP response relating |
258 | | * to certificate statuses. |
259 | | * @throws Envoy::EnvoyException if `cbs` does not contain a well-formed |
260 | | * ResponseData element. |
261 | | */ |
262 | | static absl::StatusOr<ResponseData> parseResponseData(CBS& cbs); |
263 | | |
264 | | /** |
265 | | * @param cbs a CBS& that refers to an `ASN.1` SingleResponse element |
266 | | * @returns SingleResponse containing the id and revocation status of |
267 | | * a single certificate. |
268 | | * @throws Envoy::EnvoyException if `cbs` does not contain a well-formed |
269 | | * SingleResponse element. |
270 | | */ |
271 | | static absl::StatusOr<SingleResponse> parseSingleResponse(CBS& cbs); |
272 | | |
273 | | /** |
274 | | * @param cbs a CBS& that refers to an `ASN.1` CertId element |
275 | | * @returns CertId containing the information necessary to uniquely identify |
276 | | * an SSL certificate or an error status if |
277 | | * `cbs` does not contain a well-formed CertId element. |
278 | | */ |
279 | | static absl::StatusOr<CertId> parseCertId(CBS& cbs); |
280 | | }; |
281 | | |
282 | | } // namespace Ocsp |
283 | | } // namespace Tls |
284 | | } // namespace TransportSockets |
285 | | } // namespace Extensions |
286 | | } // namespace Envoy |