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