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