• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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