1 // Copyright 2017 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "net/cert/internal/revocation_checker.h"
6
7 #include <optional>
8 #include <string>
9 #include <string_view>
10
11 #include "base/logging.h"
12 #include "crypto/sha2.h"
13 #include "net/cert/cert_net_fetcher.h"
14 #include "third_party/boringssl/src/pki/common_cert_errors.h"
15 #include "third_party/boringssl/src/pki/crl.h"
16 #include "third_party/boringssl/src/pki/ocsp.h"
17 #include "third_party/boringssl/src/pki/ocsp_verify_result.h"
18 #include "third_party/boringssl/src/pki/parsed_certificate.h"
19 #include "third_party/boringssl/src/pki/trust_store.h"
20 #include "url/gurl.h"
21
22 namespace net {
23
24 namespace {
25
MarkCertificateRevoked(bssl::CertErrors * errors)26 void MarkCertificateRevoked(bssl::CertErrors* errors) {
27 // TODO(eroman): Add a parameter to the error indicating which mechanism
28 // caused the revocation (i.e. CRLSet, OCSP, stapled OCSP, etc).
29 errors->AddError(bssl::cert_errors::kCertificateRevoked);
30 }
31
32 // Checks the revocation status of |certs[target_cert_index]| according to
33 // |policy|. If the checks failed, returns false and adds errors to
34 // |cert_errors|.
35 //
36 // TODO(eroman): Make the verification time an input.
CheckCertRevocation(const bssl::ParsedCertificateList & certs,size_t target_cert_index,const RevocationPolicy & policy,base::TimeTicks deadline,std::string_view stapled_ocsp_response,std::optional<int64_t> max_age_seconds,base::Time current_time,CertNetFetcher * net_fetcher,bssl::CertErrors * cert_errors,bssl::OCSPVerifyResult * stapled_ocsp_verify_result)37 bool CheckCertRevocation(const bssl::ParsedCertificateList& certs,
38 size_t target_cert_index,
39 const RevocationPolicy& policy,
40 base::TimeTicks deadline,
41 std::string_view stapled_ocsp_response,
42 std::optional<int64_t> max_age_seconds,
43 base::Time current_time,
44 CertNetFetcher* net_fetcher,
45 bssl::CertErrors* cert_errors,
46 bssl::OCSPVerifyResult* stapled_ocsp_verify_result) {
47 DCHECK_LT(target_cert_index, certs.size());
48 const bssl::ParsedCertificate* cert = certs[target_cert_index].get();
49 const bssl::ParsedCertificate* issuer_cert =
50 target_cert_index + 1 < certs.size() ? certs[target_cert_index + 1].get()
51 : nullptr;
52
53 time_t time_now = current_time.ToTimeT();
54
55 // Check using stapled OCSP, if available.
56 if (!stapled_ocsp_response.empty() && issuer_cert) {
57 bssl::OCSPVerifyResult::ResponseStatus response_details;
58 bssl::OCSPRevocationStatus ocsp_status =
59 bssl::CheckOCSP(stapled_ocsp_response, cert, issuer_cert, time_now,
60 max_age_seconds, &response_details);
61 if (stapled_ocsp_verify_result) {
62 stapled_ocsp_verify_result->response_status = response_details;
63 stapled_ocsp_verify_result->revocation_status = ocsp_status;
64 }
65
66 // TODO(eroman): Save the stapled OCSP response to cache.
67 switch (ocsp_status) {
68 case bssl::OCSPRevocationStatus::REVOKED:
69 MarkCertificateRevoked(cert_errors);
70 return false;
71 case bssl::OCSPRevocationStatus::GOOD:
72 return true;
73 case bssl::OCSPRevocationStatus::UNKNOWN:
74 // TODO(eroman): If the OCSP response was invalid, should we keep
75 // looking or fail?
76 break;
77 }
78 }
79
80 if (!policy.check_revocation) {
81 // TODO(eroman): Should still check CRL/OCSP caches.
82 return true;
83 }
84
85 bool found_revocation_info = false;
86
87 // Check OCSP.
88 if (cert->has_authority_info_access()) {
89 // Try each of the OCSP URIs
90 for (const auto& ocsp_uri : cert->ocsp_uris()) {
91 // Only consider http:// URLs (https:// could create a circular
92 // dependency).
93 GURL parsed_ocsp_url(ocsp_uri);
94 if (!parsed_ocsp_url.is_valid() ||
95 !parsed_ocsp_url.SchemeIs(url::kHttpScheme)) {
96 continue;
97 }
98
99 found_revocation_info = true;
100
101 // Check the deadline after setting found_revocation_info, to not give a
102 // misleading kNoRevocationMechanism failure.
103 if (!deadline.is_null() && base::TimeTicks::Now() > deadline)
104 break;
105
106 if (!policy.networking_allowed)
107 continue;
108
109 if (!net_fetcher) {
110 LOG(ERROR) << "Cannot fetch OCSP as didn't specify a |net_fetcher|";
111 continue;
112 }
113
114 // TODO(eroman): Duplication of work if there are multiple URLs to try.
115 // TODO(eroman): Are there cases where we would need to POST instead?
116 std::optional<std::string> get_url_str =
117 CreateOCSPGetURL(cert, issuer_cert, ocsp_uri);
118 if (!get_url_str.has_value()) {
119 // An unexpected failure from BoringSSL, or the input was too large to
120 // base64-encode.
121 continue;
122 }
123 GURL get_url(get_url_str.value());
124 if (!get_url.is_valid()) {
125 // Invalid URL.
126 continue;
127 }
128
129 // Fetch it over network.
130 //
131 // TODO(eroman): Issue POST instead of GET if request is larger than 255
132 // bytes?
133 // TODO(eroman): Improve interplay with HTTP cache.
134 std::unique_ptr<CertNetFetcher::Request> net_ocsp_request =
135 net_fetcher->FetchOcsp(get_url, CertNetFetcher::DEFAULT,
136 CertNetFetcher::DEFAULT);
137
138 Error net_error;
139 std::vector<uint8_t> ocsp_response_bytes;
140 net_ocsp_request->WaitForResult(&net_error, &ocsp_response_bytes);
141
142 if (net_error != OK)
143 continue;
144
145 bssl::OCSPVerifyResult::ResponseStatus response_details;
146
147 bssl::OCSPRevocationStatus ocsp_status = bssl::CheckOCSP(
148 std::string_view(
149 reinterpret_cast<const char*>(ocsp_response_bytes.data()),
150 ocsp_response_bytes.size()),
151 cert, issuer_cert, time_now, max_age_seconds, &response_details);
152
153 switch (ocsp_status) {
154 case bssl::OCSPRevocationStatus::REVOKED:
155 MarkCertificateRevoked(cert_errors);
156 return false;
157 case bssl::OCSPRevocationStatus::GOOD:
158 return true;
159 case bssl::OCSPRevocationStatus::UNKNOWN:
160 break;
161 }
162 }
163 }
164
165 // Check CRLs.
166 bssl::ParsedExtension crl_dp_extension;
167 if (policy.crl_allowed &&
168 cert->GetExtension(bssl::der::Input(bssl::kCrlDistributionPointsOid),
169 &crl_dp_extension)) {
170 std::vector<bssl::ParsedDistributionPoint> distribution_points;
171 if (ParseCrlDistributionPoints(crl_dp_extension.value,
172 &distribution_points)) {
173 for (const auto& distribution_point : distribution_points) {
174 if (distribution_point.crl_issuer) {
175 // Ignore indirect CRLs (CRL where CRLissuer != cert issuer), which
176 // are optional according to RFC 5280's profile.
177 continue;
178 }
179
180 if (distribution_point.reasons) {
181 // Ignore CRLs that only contain some reasons. RFC 5280's profile
182 // requires that conforming CAs "MUST include at least one
183 // DistributionPoint that points to a CRL that covers the certificate
184 // for all reasons".
185 continue;
186 }
187
188 if (!distribution_point.distribution_point_fullname) {
189 // Only distributionPoints with a fullName containing URIs are
190 // supported.
191 continue;
192 }
193
194 for (const auto& crl_uri :
195 distribution_point.distribution_point_fullname
196 ->uniform_resource_identifiers) {
197 // Only consider http:// URLs (https:// could create a circular
198 // dependency).
199 GURL parsed_crl_url(crl_uri);
200 if (!parsed_crl_url.is_valid() ||
201 !parsed_crl_url.SchemeIs(url::kHttpScheme)) {
202 continue;
203 }
204
205 found_revocation_info = true;
206
207 // Check the deadline after setting found_revocation_info, to not give
208 // a misleading kNoRevocationMechanism failure.
209 if (!deadline.is_null() && base::TimeTicks::Now() > deadline)
210 break;
211
212 if (!policy.networking_allowed)
213 continue;
214
215 if (!net_fetcher) {
216 LOG(ERROR) << "Cannot fetch CRL as didn't specify a |net_fetcher|";
217 continue;
218 }
219
220 // Fetch it over network.
221 //
222 // Note that no attempt is made to refetch without cache if a cached
223 // CRL is too old, nor is there a separate CRL cache. It is assumed
224 // the CRL server will send reasonable HTTP caching headers.
225 std::unique_ptr<CertNetFetcher::Request> net_crl_request =
226 net_fetcher->FetchCrl(parsed_crl_url, CertNetFetcher::DEFAULT,
227 CertNetFetcher::DEFAULT);
228
229 Error net_error;
230 std::vector<uint8_t> crl_response_bytes;
231 net_crl_request->WaitForResult(&net_error, &crl_response_bytes);
232
233 if (net_error != OK)
234 continue;
235
236 bssl::CRLRevocationStatus crl_status = CheckCRL(
237 std::string_view(
238 reinterpret_cast<const char*>(crl_response_bytes.data()),
239 crl_response_bytes.size()),
240 certs, target_cert_index, distribution_point, time_now,
241 max_age_seconds);
242
243 switch (crl_status) {
244 case bssl::CRLRevocationStatus::REVOKED:
245 MarkCertificateRevoked(cert_errors);
246 return false;
247 case bssl::CRLRevocationStatus::GOOD:
248 return true;
249 case bssl::CRLRevocationStatus::UNKNOWN:
250 break;
251 }
252 }
253 }
254 }
255 }
256
257 // Reaching here means that revocation checking was inconclusive. Determine
258 // whether failure to complete revocation checking constitutes an error.
259
260 if (!found_revocation_info) {
261 if (policy.allow_missing_info) {
262 // If the certificate lacked any (recognized) revocation mechanisms, and
263 // the policy permits it, consider revocation checking a success.
264 return true;
265 } else {
266 // If the certificate lacked any (recognized) revocation mechanisms, and
267 // the policy forbids it, fail revocation checking.
268 cert_errors->AddError(bssl::cert_errors::kNoRevocationMechanism);
269 return false;
270 }
271 }
272
273 // In soft-fail mode permit other failures.
274 // TODO(eroman): Add a warning to |cert_errors| indicating the failure.
275 if (policy.allow_unable_to_check)
276 return true;
277
278 // Otherwise the policy doesn't allow revocation checking to fail.
279 cert_errors->AddError(bssl::cert_errors::kUnableToCheckRevocation);
280 return false;
281 }
282
283 } // namespace
284
CheckValidatedChainRevocation(const bssl::ParsedCertificateList & certs,const RevocationPolicy & policy,base::TimeTicks deadline,std::string_view stapled_leaf_ocsp_response,base::Time current_time,CertNetFetcher * net_fetcher,bssl::CertPathErrors * errors,bssl::OCSPVerifyResult * stapled_ocsp_verify_result)285 void CheckValidatedChainRevocation(
286 const bssl::ParsedCertificateList& certs,
287 const RevocationPolicy& policy,
288 base::TimeTicks deadline,
289 std::string_view stapled_leaf_ocsp_response,
290 base::Time current_time,
291 CertNetFetcher* net_fetcher,
292 bssl::CertPathErrors* errors,
293 bssl::OCSPVerifyResult* stapled_ocsp_verify_result) {
294 if (stapled_ocsp_verify_result)
295 *stapled_ocsp_verify_result = bssl::OCSPVerifyResult();
296
297 // Check each certificate for revocation using OCSP/CRL. Checks proceed
298 // from the root certificate towards the leaf certificate. Revocation errors
299 // are added to |errors|.
300 for (size_t reverse_i = 0; reverse_i < certs.size(); ++reverse_i) {
301 size_t i = certs.size() - reverse_i - 1;
302
303 // Trust anchors bypass OCSP/CRL revocation checks. (The only way to revoke
304 // trust anchors is via CRLSet or the built-in SPKI block list). Since
305 // |certs| must be a validated chain, the final cert must be a trust
306 // anchor.
307 if (reverse_i == 0)
308 continue;
309
310 // TODO(eroman): Plumb stapled OCSP for non-leaf certificates from TLS?
311 std::string_view stapled_ocsp =
312 (i == 0) ? stapled_leaf_ocsp_response : std::string_view();
313
314 std::optional<int64_t> max_age_seconds;
315 if (policy.enforce_baseline_requirements) {
316 max_age_seconds = ((i == 0) ? kMaxRevocationLeafUpdateAge
317 : kMaxRevocationIntermediateUpdateAge)
318 .InSeconds();
319 }
320
321 // Check whether this certificate's revocation status complies with the
322 // policy.
323 bool cert_ok = CheckCertRevocation(
324 certs, i, policy, deadline, stapled_ocsp, max_age_seconds, current_time,
325 net_fetcher, errors->GetErrorsForCert(i),
326 (i == 0) ? stapled_ocsp_verify_result : nullptr);
327
328 if (!cert_ok) {
329 // If any certificate in the chain fails revocation checks, the chain is
330 // revoked and no need to check revocation status for the remaining
331 // certificates.
332 DCHECK(errors->GetErrorsForCert(i)->ContainsAnyErrorWithSeverity(
333 bssl::CertError::SEVERITY_HIGH));
334 break;
335 }
336 }
337 }
338
CheckChainRevocationUsingCRLSet(const CRLSet * crl_set,const bssl::ParsedCertificateList & certs,bssl::CertPathErrors * errors)339 CRLSet::Result CheckChainRevocationUsingCRLSet(
340 const CRLSet* crl_set,
341 const bssl::ParsedCertificateList& certs,
342 bssl::CertPathErrors* errors) {
343 // Iterate from the root certificate towards the leaf (the root certificate is
344 // also checked for revocation by CRLSet).
345 std::string issuer_spki_hash;
346 for (size_t reverse_i = 0; reverse_i < certs.size(); ++reverse_i) {
347 size_t i = certs.size() - reverse_i - 1;
348 const bssl::ParsedCertificate* cert = certs[i].get();
349
350 // True if |cert| is the root of the chain.
351 const bool is_root = reverse_i == 0;
352 // True if |cert| is the leaf certificate of the chain.
353 const bool is_target = i == 0;
354
355 // Check for revocation using the certificate's SPKI.
356 std::string spki_hash =
357 crypto::SHA256HashString(cert->tbs().spki_tlv.AsStringView());
358 CRLSet::Result result = crl_set->CheckSPKI(spki_hash);
359
360 // Check for revocation using the certificate's Subject.
361 if (result != CRLSet::REVOKED) {
362 result = crl_set->CheckSubject(cert->tbs().subject_tlv.AsStringView(),
363 spki_hash);
364 }
365
366 // Check for revocation using the certificate's serial number and issuer's
367 // SPKI.
368 if (result != CRLSet::REVOKED && !is_root) {
369 result = crl_set->CheckSerial(cert->tbs().serial_number.AsStringView(),
370 issuer_spki_hash);
371 }
372
373 // Prepare for the next iteration.
374 issuer_spki_hash = std::move(spki_hash);
375
376 switch (result) {
377 case CRLSet::REVOKED:
378 MarkCertificateRevoked(errors->GetErrorsForCert(i));
379 return CRLSet::Result::REVOKED;
380 case CRLSet::UNKNOWN:
381 // If the status is unknown, advance to the subordinate certificate.
382 break;
383 case CRLSet::GOOD:
384 if (is_target && !crl_set->IsExpired()) {
385 // If the target is covered by the CRLSet and known good, consider
386 // the entire chain to be valid (even though the revocation status
387 // of the intermediates may have been UNKNOWN).
388 //
389 // Only the leaf certificate is considered for coverage because some
390 // intermediates have CRLs with no revocations (after filtering) and
391 // those CRLs are pruned from the CRLSet at generation time.
392 return CRLSet::Result::GOOD;
393 }
394 break;
395 }
396 }
397
398 // If no certificate was revoked, and the target was not known good, then
399 // the revocation status is still unknown.
400 return CRLSet::Result::UNKNOWN;
401 }
402
403 } // namespace net
404