|  | // Copyright 2016 The Chromium Authors | 
|  | // Use of this source code is governed by a BSD-style license that can be | 
|  | // found in the LICENSE file. | 
|  |  | 
|  | #include "path_builder.h" | 
|  |  | 
|  | #include <cassert> | 
|  | #include <memory> | 
|  | #include <set> | 
|  | #include <unordered_set> | 
|  |  | 
|  | #include <openssl/base.h> | 
|  | #include <openssl/pki/verify_error.h> | 
|  | #include <openssl/sha.h> | 
|  |  | 
|  | #include "cert_issuer_source.h" | 
|  | #include "certificate_policies.h" | 
|  | #include "common_cert_errors.h" | 
|  | #include "parse_certificate.h" | 
|  | #include "parse_name.h"  // For CertDebugString. | 
|  | #include "parser.h" | 
|  | #include "string_util.h" | 
|  | #include "trust_store.h" | 
|  | #include "verify_certificate_chain.h" | 
|  | #include "verify_name_match.h" | 
|  |  | 
|  | BSSL_NAMESPACE_BEGIN | 
|  |  | 
|  | namespace { | 
|  |  | 
|  | using CertIssuerSources = std::vector<CertIssuerSource *>; | 
|  |  | 
|  | // Returns a hex-encoded sha256 of the DER-encoding of |cert|. | 
|  | std::string FingerPrintParsedCertificate(const bssl::ParsedCertificate *cert) { | 
|  | uint8_t digest[SHA256_DIGEST_LENGTH]; | 
|  | SHA256(cert->der_cert().data(), cert->der_cert().size(), digest); | 
|  | return bssl::string_util::HexEncode(digest); | 
|  | } | 
|  |  | 
|  | // TODO(mattm): decide how much debug logging to keep. | 
|  | std::string CertDebugString(const ParsedCertificate *cert) { | 
|  | RDNSequence subject; | 
|  | std::string subject_str; | 
|  | if (!ParseName(cert->tbs().subject_tlv, &subject) || | 
|  | !ConvertToRFC2253(subject, &subject_str)) { | 
|  | subject_str = "???"; | 
|  | } | 
|  |  | 
|  | return FingerPrintParsedCertificate(cert) + " " + subject_str; | 
|  | } | 
|  |  | 
|  | std::string PathDebugString(const ParsedCertificateList &certs) { | 
|  | std::string s; | 
|  | for (const auto &cert : certs) { | 
|  | if (!s.empty()) { | 
|  | s += "\n"; | 
|  | } | 
|  | s += " " + CertDebugString(cert.get()); | 
|  | } | 
|  | return s; | 
|  | } | 
|  |  | 
|  | // This structure describes a certificate and its trust level. Note that |cert| | 
|  | // may be null to indicate an "empty" entry. | 
|  | struct IssuerEntry { | 
|  | std::shared_ptr<const ParsedCertificate> cert; | 
|  | CertificateTrust trust; | 
|  | int trust_and_key_id_match_ordering; | 
|  | }; | 
|  |  | 
|  | enum KeyIdentifierMatch { | 
|  | // |target| has a keyIdentifier and it matches |issuer|'s | 
|  | // subjectKeyIdentifier. | 
|  | kMatch = 0, | 
|  | // |target| does not have authorityKeyIdentifier or |issuer| does not have | 
|  | // subjectKeyIdentifier. | 
|  | kNoData = 1, | 
|  | // |target|'s authorityKeyIdentifier does not match |issuer|. | 
|  | kMismatch = 2, | 
|  | }; | 
|  |  | 
|  | // Returns an integer that represents the relative ordering of |issuer| for | 
|  | // prioritizing certificates in path building based on |issuer|'s | 
|  | // subjectKeyIdentifier and |target|'s authorityKeyIdentifier. Lower return | 
|  | // values indicate higer priority. | 
|  | KeyIdentifierMatch CalculateKeyIdentifierMatch( | 
|  | const ParsedCertificate *target, const ParsedCertificate *issuer) { | 
|  | if (!target->authority_key_identifier()) { | 
|  | return kNoData; | 
|  | } | 
|  |  | 
|  | // TODO(crbug.com/635205): If issuer does not have a subjectKeyIdentifier, | 
|  | // could try synthesizing one using the standard SHA-1 method. Ideally in a | 
|  | // way where any issuers that do have a matching subjectKeyIdentifier could | 
|  | // be tried first before doing the extra work. | 
|  | if (target->authority_key_identifier()->key_identifier && | 
|  | issuer->subject_key_identifier()) { | 
|  | if (target->authority_key_identifier()->key_identifier != | 
|  | issuer->subject_key_identifier().value()) { | 
|  | return kMismatch; | 
|  | } | 
|  | return kMatch; | 
|  | } | 
|  |  | 
|  | return kNoData; | 
|  | } | 
|  |  | 
|  | // Returns an integer that represents the relative ordering of |issuer| based | 
|  | // on |issuer_trust| and authorityKeyIdentifier matching for prioritizing | 
|  | // certificates in path building. Lower return values indicate higer priority. | 
|  | int TrustAndKeyIdentifierMatchToOrder(const ParsedCertificate *target, | 
|  | const ParsedCertificate *issuer, | 
|  | const CertificateTrust &issuer_trust) { | 
|  | enum { | 
|  | kTrustedAndKeyIdMatch = 0, | 
|  | kTrustedAndKeyIdNoData = 1, | 
|  | kKeyIdMatch = 2, | 
|  | kKeyIdNoData = 3, | 
|  | kTrustedAndKeyIdMismatch = 4, | 
|  | kKeyIdMismatch = 5, | 
|  | kDistrustedAndKeyIdMatch = 6, | 
|  | kDistrustedAndKeyIdNoData = 7, | 
|  | kDistrustedAndKeyIdMismatch = 8, | 
|  | }; | 
|  |  | 
|  | KeyIdentifierMatch key_id_match = CalculateKeyIdentifierMatch(target, issuer); | 
|  | switch (issuer_trust.type) { | 
|  | case CertificateTrustType::TRUSTED_ANCHOR: | 
|  | case CertificateTrustType::TRUSTED_ANCHOR_OR_LEAF: | 
|  | switch (key_id_match) { | 
|  | case kMatch: | 
|  | return kTrustedAndKeyIdMatch; | 
|  | case kNoData: | 
|  | return kTrustedAndKeyIdNoData; | 
|  | case kMismatch: | 
|  | return kTrustedAndKeyIdMismatch; | 
|  | } | 
|  | break; | 
|  | case CertificateTrustType::UNSPECIFIED: | 
|  | case CertificateTrustType::TRUSTED_LEAF: | 
|  | switch (key_id_match) { | 
|  | case kMatch: | 
|  | return kKeyIdMatch; | 
|  | case kNoData: | 
|  | return kKeyIdNoData; | 
|  | case kMismatch: | 
|  | return kKeyIdMismatch; | 
|  | } | 
|  | break; | 
|  | case CertificateTrustType::DISTRUSTED: | 
|  | switch (key_id_match) { | 
|  | case kMatch: | 
|  | return kDistrustedAndKeyIdMatch; | 
|  | case kNoData: | 
|  | return kDistrustedAndKeyIdNoData; | 
|  | case kMismatch: | 
|  | return kDistrustedAndKeyIdMismatch; | 
|  | } | 
|  | break; | 
|  | } | 
|  | assert(0);  // NOTREACHED | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | // CertIssuersIter iterates through the intermediates from |cert_issuer_sources| | 
|  | // which may be issuers of |cert|. | 
|  | class CertIssuersIter { | 
|  | public: | 
|  | // Constructs the CertIssuersIter. |*cert_issuer_sources|, and | 
|  | // |*trust_store| must be valid for the lifetime of the CertIssuersIter. | 
|  | CertIssuersIter(std::shared_ptr<const ParsedCertificate> cert, | 
|  | CertIssuerSources *cert_issuer_sources, | 
|  | TrustStore *trust_store); | 
|  |  | 
|  | CertIssuersIter(const CertIssuersIter &) = delete; | 
|  | CertIssuersIter &operator=(const CertIssuersIter &) = delete; | 
|  |  | 
|  | // Gets the next candidate issuer, or clears |*out| when all issuers have been | 
|  | // exhausted. | 
|  | void GetNextIssuer(IssuerEntry *out); | 
|  |  | 
|  | // Returns true if candidate issuers were found for |cert_|. | 
|  | bool had_non_skipped_issuers() const { | 
|  | return issuers_.size() > skipped_issuer_count_; | 
|  | } | 
|  |  | 
|  | void increment_skipped_issuer_count() { skipped_issuer_count_++; } | 
|  |  | 
|  | // Returns the |cert| for which issuers are being retrieved. | 
|  | const ParsedCertificate *cert() const { return cert_.get(); } | 
|  | std::shared_ptr<const ParsedCertificate> reference_cert() const { | 
|  | return cert_; | 
|  | } | 
|  |  | 
|  | private: | 
|  | void AddIssuers(ParsedCertificateList issuers); | 
|  | void DoAsyncIssuerQuery(); | 
|  |  | 
|  | // Returns true if |issuers_| contains unconsumed certificates. | 
|  | bool HasCurrentIssuer() const { return cur_issuer_ < issuers_.size(); } | 
|  |  | 
|  | // Sorts the remaining entries in |issuers_| in the preferred order to | 
|  | // explore. Does not change the ordering for indices before cur_issuer_. | 
|  | void SortRemainingIssuers(); | 
|  |  | 
|  | std::shared_ptr<const ParsedCertificate> cert_; | 
|  | CertIssuerSources *cert_issuer_sources_; | 
|  | TrustStore *trust_store_; | 
|  |  | 
|  | // The list of issuers for |cert_|. This is added to incrementally (first | 
|  | // synchronous results, then possibly multiple times as asynchronous results | 
|  | // arrive.) The issuers may be re-sorted each time new issuers are added, but | 
|  | // only the results from |cur_| onwards should be sorted, since the earlier | 
|  | // results were already returned. | 
|  | // Elements should not be removed from |issuers_| once added, since | 
|  | // |present_issuers_| will point to data owned by the certs. | 
|  | std::vector<IssuerEntry> issuers_; | 
|  | // The index of the next cert in |issuers_| to return. | 
|  | size_t cur_issuer_ = 0; | 
|  | // The number of issuers that were skipped due to the loop checker. | 
|  | size_t skipped_issuer_count_ = 0; | 
|  | // Set to true whenever new issuers are appended at the end, to indicate the | 
|  | // ordering needs to be checked. | 
|  | bool issuers_needs_sort_ = false; | 
|  |  | 
|  | // Set of DER-encoded values for the certs in |issuers_|. Used to prevent | 
|  | // duplicates. This is based on the full DER of the cert to allow different | 
|  | // versions of the same certificate to be tried in different candidate paths. | 
|  | // This points to data owned by |issuers_|. | 
|  | std::unordered_set<std::string_view> present_issuers_; | 
|  |  | 
|  | // Tracks which requests have been made yet. | 
|  | bool did_initial_query_ = false; | 
|  | bool did_async_issuer_query_ = false; | 
|  | // Index into pending_async_requests_ that is the next one to process. | 
|  | size_t cur_async_request_ = 0; | 
|  | // Owns the Request objects for any asynchronous requests so that they will be | 
|  | // cancelled if CertIssuersIter is destroyed. | 
|  | std::vector<std::unique_ptr<CertIssuerSource::Request>> | 
|  | pending_async_requests_; | 
|  | }; | 
|  |  | 
|  | CertIssuersIter::CertIssuersIter( | 
|  | std::shared_ptr<const ParsedCertificate> in_cert, | 
|  | CertIssuerSources *cert_issuer_sources, TrustStore *trust_store) | 
|  | : cert_(std::move(in_cert)), | 
|  | cert_issuer_sources_(cert_issuer_sources), | 
|  | trust_store_(trust_store) {} | 
|  |  | 
|  | void CertIssuersIter::GetNextIssuer(IssuerEntry *out) { | 
|  | if (!did_initial_query_) { | 
|  | did_initial_query_ = true; | 
|  | for (auto *cert_issuer_source : *cert_issuer_sources_) { | 
|  | ParsedCertificateList new_issuers; | 
|  | cert_issuer_source->SyncGetIssuersOf(cert(), &new_issuers); | 
|  | AddIssuers(std::move(new_issuers)); | 
|  | } | 
|  | } | 
|  |  | 
|  | // If there aren't any issuers, block until async results are ready. | 
|  | if (!HasCurrentIssuer()) { | 
|  | if (!did_async_issuer_query_) { | 
|  | // Now issue request(s) for async ones (AIA, etc). | 
|  | DoAsyncIssuerQuery(); | 
|  | } | 
|  |  | 
|  | // TODO(eroman): Rather than blocking on the async requests in FIFO order, | 
|  | // consume in the order they become ready. | 
|  | while (!HasCurrentIssuer() && | 
|  | cur_async_request_ < pending_async_requests_.size()) { | 
|  | ParsedCertificateList new_issuers; | 
|  | pending_async_requests_[cur_async_request_]->GetNext(&new_issuers); | 
|  | if (new_issuers.empty()) { | 
|  | // Request is exhausted, no more results pending from that | 
|  | // CertIssuerSource. | 
|  | pending_async_requests_[cur_async_request_++].reset(); | 
|  | } else { | 
|  | AddIssuers(std::move(new_issuers)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | if (HasCurrentIssuer()) { | 
|  | SortRemainingIssuers(); | 
|  |  | 
|  | // Still have issuers that haven't been returned yet, return the highest | 
|  | // priority one (head of remaining list). A reference to the returned issuer | 
|  | // is retained, since |present_issuers_| points to data owned by it. | 
|  | *out = issuers_[cur_issuer_++]; | 
|  | return; | 
|  | } | 
|  |  | 
|  | // Reached the end of all available issuers. | 
|  | *out = IssuerEntry(); | 
|  | } | 
|  |  | 
|  | void CertIssuersIter::AddIssuers(ParsedCertificateList new_issuers) { | 
|  | for (std::shared_ptr<const ParsedCertificate> &issuer : new_issuers) { | 
|  | if (present_issuers_.find(BytesAsStringView(issuer->der_cert())) != | 
|  | present_issuers_.end()) { | 
|  | continue; | 
|  | } | 
|  | present_issuers_.insert(BytesAsStringView(issuer->der_cert())); | 
|  |  | 
|  | // Look up the trust for this issuer. | 
|  | IssuerEntry entry; | 
|  | entry.cert = std::move(issuer); | 
|  | entry.trust = trust_store_->GetTrust(entry.cert.get()); | 
|  | entry.trust_and_key_id_match_ordering = TrustAndKeyIdentifierMatchToOrder( | 
|  | cert(), entry.cert.get(), entry.trust); | 
|  |  | 
|  | issuers_.push_back(std::move(entry)); | 
|  | issuers_needs_sort_ = true; | 
|  | } | 
|  | } | 
|  |  | 
|  | void CertIssuersIter::DoAsyncIssuerQuery() { | 
|  | BSSL_CHECK(!did_async_issuer_query_); | 
|  | did_async_issuer_query_ = true; | 
|  | cur_async_request_ = 0; | 
|  | for (auto *cert_issuer_source : *cert_issuer_sources_) { | 
|  | std::unique_ptr<CertIssuerSource::Request> request; | 
|  | cert_issuer_source->AsyncGetIssuersOf(cert(), &request); | 
|  | if (request) { | 
|  | pending_async_requests_.push_back(std::move(request)); | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void CertIssuersIter::SortRemainingIssuers() { | 
|  | if (!issuers_needs_sort_) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | std::stable_sort( | 
|  | issuers_.begin() + cur_issuer_, issuers_.end(), | 
|  | [](const IssuerEntry &issuer1, const IssuerEntry &issuer2) { | 
|  | // TODO(crbug.com/635205): Add other prioritization hints. (See big list | 
|  | // of possible sorting hints in RFC 4158.) | 
|  | const bool issuer1_self_issued = issuer1.cert->normalized_subject() == | 
|  | issuer1.cert->normalized_issuer(); | 
|  | const bool issuer2_self_issued = issuer2.cert->normalized_subject() == | 
|  | issuer2.cert->normalized_issuer(); | 
|  | return std::tie(issuer1.trust_and_key_id_match_ordering, | 
|  | issuer2_self_issued, | 
|  | // Newer(larger) notBefore & notAfter dates are | 
|  | // preferred, hence |issuer2| is on the LHS of | 
|  | // the comparison and |issuer1| on the RHS. | 
|  | issuer2.cert->tbs().validity_not_before, | 
|  | issuer2.cert->tbs().validity_not_after) < | 
|  | std::tie(issuer2.trust_and_key_id_match_ordering, | 
|  | issuer1_self_issued, | 
|  | issuer1.cert->tbs().validity_not_before, | 
|  | issuer1.cert->tbs().validity_not_after); | 
|  | }); | 
|  |  | 
|  | issuers_needs_sort_ = false; | 
|  | } | 
|  |  | 
|  | // CertIssuerIterPath tracks which certs are present in the path and prevents | 
|  | // paths from being built which repeat any certs (including different versions | 
|  | // of the same cert, based on Subject+SubjectAltName+SPKI). | 
|  | // (RFC 5280 forbids duplicate certificates per section 6.1, and RFC 4158 | 
|  | // further recommends disallowing the same Subject+SubjectAltName+SPKI in | 
|  | // section 2.4.2.) | 
|  | class CertIssuerIterPath { | 
|  | public: | 
|  | // Returns true if |cert| is already present in the path. | 
|  | bool IsPresent(const ParsedCertificate *cert) const { | 
|  | return present_certs_.find(GetKey(cert)) != present_certs_.end(); | 
|  | } | 
|  |  | 
|  | // Appends |cert_issuers_iter| to the path. The cert referred to by | 
|  | // |cert_issuers_iter| must not be present in the path already. | 
|  | void Append(std::unique_ptr<CertIssuersIter> cert_issuers_iter) { | 
|  | bool added = | 
|  | present_certs_.insert(GetKey(cert_issuers_iter->cert())).second; | 
|  | BSSL_CHECK(added); | 
|  | cur_path_.push_back(std::move(cert_issuers_iter)); | 
|  | } | 
|  |  | 
|  | // Pops the last CertIssuersIter off the path. | 
|  | void Pop() { | 
|  | size_t num_erased = present_certs_.erase(GetKey(cur_path_.back()->cert())); | 
|  | BSSL_CHECK(num_erased == 1U); | 
|  | cur_path_.pop_back(); | 
|  | } | 
|  |  | 
|  | // Copies the ParsedCertificate elements of the current path to |*out_path|. | 
|  | void CopyPath(ParsedCertificateList *out_path) { | 
|  | out_path->clear(); | 
|  | for (const auto &node : cur_path_) { | 
|  | out_path->push_back(node->reference_cert()); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Returns true if the path is empty. | 
|  | bool Empty() const { return cur_path_.empty(); } | 
|  |  | 
|  | // Returns the last CertIssuersIter in the path. | 
|  | CertIssuersIter *back() { return cur_path_.back().get(); } | 
|  |  | 
|  | // Returns the length of the path. | 
|  | size_t Length() const { return cur_path_.size(); } | 
|  |  | 
|  | std::string PathDebugString() { | 
|  | std::string s; | 
|  | for (const auto &node : cur_path_) { | 
|  | if (!s.empty()) { | 
|  | s += "\n"; | 
|  | } | 
|  | s += " " + CertDebugString(node->cert()); | 
|  | } | 
|  | return s; | 
|  | } | 
|  |  | 
|  | private: | 
|  | using Key = std::tuple<std::string_view, std::string_view, std::string_view>; | 
|  |  | 
|  | static Key GetKey(const ParsedCertificate *cert) { | 
|  | // TODO(mattm): ideally this would use a normalized version of | 
|  | // SubjectAltName, but it's not that important just for LoopChecker. | 
|  | // | 
|  | // Note that subject_alt_names_extension().value will be empty if the cert | 
|  | // had no SubjectAltName extension, so there is no need for a condition on | 
|  | // has_subject_alt_names(). | 
|  | return Key(BytesAsStringView(cert->normalized_subject()), | 
|  | BytesAsStringView(cert->subject_alt_names_extension().value), | 
|  | BytesAsStringView(cert->tbs().spki_tlv)); | 
|  | } | 
|  |  | 
|  | std::vector<std::unique_ptr<CertIssuersIter>> cur_path_; | 
|  |  | 
|  | // This refers to data owned by |cur_path_|. | 
|  | // TODO(mattm): use unordered_set. Requires making a hash function for Key. | 
|  | std::set<Key> present_certs_; | 
|  | }; | 
|  |  | 
|  | }  // namespace | 
|  |  | 
|  | const ParsedCertificate *CertPathBuilderResultPath::GetTrustedCert() const { | 
|  | if (certs.empty()) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | switch (last_cert_trust.type) { | 
|  | case CertificateTrustType::TRUSTED_ANCHOR: | 
|  | case CertificateTrustType::TRUSTED_ANCHOR_OR_LEAF: | 
|  | case CertificateTrustType::TRUSTED_LEAF: | 
|  | return certs.back().get(); | 
|  | case CertificateTrustType::UNSPECIFIED: | 
|  | case CertificateTrustType::DISTRUSTED: | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | assert(0);  // NOTREACHED | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | // CertPathIter generates possible paths from |cert| to a trust anchor in | 
|  | // |trust_store|, using intermediates from the |cert_issuer_source| objects if | 
|  | // necessary. | 
|  | class CertPathIter { | 
|  | public: | 
|  | CertPathIter(std::shared_ptr<const ParsedCertificate> cert, | 
|  | TrustStore *trust_store); | 
|  |  | 
|  | CertPathIter(const CertPathIter &) = delete; | 
|  | CertPathIter &operator=(const CertPathIter &) = delete; | 
|  |  | 
|  | // Adds a CertIssuerSource to provide intermediates for use in path building. | 
|  | // The |*cert_issuer_source| must remain valid for the lifetime of the | 
|  | // CertPathIter. | 
|  | void AddCertIssuerSource(CertIssuerSource *cert_issuer_source); | 
|  |  | 
|  | // Gets the next candidate path, and fills it into |out_certs| and | 
|  | // |out_last_cert_trust|. Note that the returned path is unverified and must | 
|  | // still be run through a chain validator. If a candidate path could not be | 
|  | // built, a partial path will be returned and |out_errors| will have an error | 
|  | // added. | 
|  | // If the return value is true, GetNextPath may be called again to backtrack | 
|  | // and continue path building. Once all paths have been exhausted returns | 
|  | // false. If deadline or iteration limit is exceeded, sets |out_certs| to the | 
|  | // current path being explored and returns false. | 
|  | bool GetNextPath(ParsedCertificateList *out_certs, | 
|  | CertificateTrust *out_last_cert_trust, | 
|  | CertPathErrors *out_errors, | 
|  | CertPathBuilderDelegate *delegate, uint32_t *iteration_count, | 
|  | const uint32_t max_iteration_count, | 
|  | const uint32_t max_path_building_depth); | 
|  |  | 
|  | private: | 
|  | // Stores the next candidate issuer, until it is used during the | 
|  | // STATE_GET_NEXT_ISSUER_COMPLETE step. | 
|  | IssuerEntry next_issuer_; | 
|  | // The current path being explored, made up of CertIssuerIters. Each node | 
|  | // keeps track of the state of searching for issuers of that cert, so that | 
|  | // when backtracking it can resume the search where it left off. | 
|  | CertIssuerIterPath cur_path_; | 
|  | // The CertIssuerSources for retrieving candidate issuers. | 
|  | CertIssuerSources cert_issuer_sources_; | 
|  | // The TrustStore for checking if a path ends in a trust anchor. | 
|  | TrustStore *trust_store_; | 
|  | }; | 
|  |  | 
|  | CertPathIter::CertPathIter(std::shared_ptr<const ParsedCertificate> cert, | 
|  | TrustStore *trust_store) | 
|  | : trust_store_(trust_store) { | 
|  | // Initialize |next_issuer_| to the target certificate. | 
|  | next_issuer_.cert = std::move(cert); | 
|  | next_issuer_.trust = trust_store_->GetTrust(next_issuer_.cert.get()); | 
|  | } | 
|  |  | 
|  | void CertPathIter::AddCertIssuerSource(CertIssuerSource *cert_issuer_source) { | 
|  | cert_issuer_sources_.push_back(cert_issuer_source); | 
|  | } | 
|  |  | 
|  | bool CertPathIter::GetNextPath(ParsedCertificateList *out_certs, | 
|  | CertificateTrust *out_last_cert_trust, | 
|  | CertPathErrors *out_errors, | 
|  | CertPathBuilderDelegate *delegate, | 
|  | uint32_t *iteration_count, | 
|  | const uint32_t max_iteration_count, | 
|  | const uint32_t max_path_building_depth) { | 
|  | out_certs->clear(); | 
|  | *out_last_cert_trust = CertificateTrust::ForUnspecified(); | 
|  |  | 
|  | while (true) { | 
|  | if (delegate->IsDeadlineExpired()) { | 
|  | if (cur_path_.Empty()) { | 
|  | // If the deadline is already expired before the first call to | 
|  | // GetNextPath, cur_path_ will be empty. Return the leaf cert in that | 
|  | // case. | 
|  | if (next_issuer_.cert) { | 
|  | out_certs->push_back(next_issuer_.cert); | 
|  | } | 
|  | } else { | 
|  | cur_path_.CopyPath(out_certs); | 
|  | } | 
|  | out_errors->GetOtherErrors()->AddError(cert_errors::kDeadlineExceeded); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // We are not done yet, so if the current path is at the depth limit then | 
|  | // we must backtrack to find an acceptable solution. | 
|  | if (max_path_building_depth > 0 && | 
|  | cur_path_.Length() >= max_path_building_depth) { | 
|  | cur_path_.CopyPath(out_certs); | 
|  | out_errors->GetOtherErrors()->AddError(cert_errors::kDepthLimitExceeded); | 
|  | if (delegate->IsDebugLogEnabled()) { | 
|  | delegate->DebugLog( | 
|  | "CertPathIter reached depth limit. Returning " | 
|  | "partial path and backtracking:\n" + | 
|  | PathDebugString(*out_certs)); | 
|  | } | 
|  | cur_path_.Pop(); | 
|  | return true; | 
|  | } | 
|  |  | 
|  | if (!next_issuer_.cert) { | 
|  | if (cur_path_.Empty()) { | 
|  | if (delegate->IsDebugLogEnabled()) { | 
|  | delegate->DebugLog("CertPathIter exhausted all paths..."); | 
|  | } | 
|  | return false; | 
|  | } | 
|  |  | 
|  | (*iteration_count)++; | 
|  | if (max_iteration_count > 0 && *iteration_count > max_iteration_count) { | 
|  | cur_path_.CopyPath(out_certs); | 
|  | out_errors->GetOtherErrors()->AddError( | 
|  | cert_errors::kIterationLimitExceeded); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | cur_path_.back()->GetNextIssuer(&next_issuer_); | 
|  | if (!next_issuer_.cert) { | 
|  | if (!cur_path_.back()->had_non_skipped_issuers()) { | 
|  | // If the end of a path was reached without finding an anchor, return | 
|  | // the partial path before backtracking. | 
|  | cur_path_.CopyPath(out_certs); | 
|  | out_errors->GetErrorsForCert(out_certs->size() - 1) | 
|  | ->AddError(cert_errors::kNoIssuersFound); | 
|  | if (delegate->IsDebugLogEnabled()) { | 
|  | delegate->DebugLog( | 
|  | "CertPathIter returning partial path and backtracking:\n" + | 
|  | PathDebugString(*out_certs)); | 
|  | } | 
|  | cur_path_.Pop(); | 
|  | return true; | 
|  | } else { | 
|  | // No more issuers for current chain, go back up and see if there are | 
|  | // any more for the previous cert. | 
|  | if (delegate->IsDebugLogEnabled()) { | 
|  | delegate->DebugLog("CertPathIter backtracking..."); | 
|  | } | 
|  | cur_path_.Pop(); | 
|  | continue; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // Overrides for cert with trust appearing in the wrong place for the type | 
|  | // of trust (trusted leaf in non-leaf position, or trust anchor in leaf | 
|  | // position.) | 
|  | switch (next_issuer_.trust.type) { | 
|  | case CertificateTrustType::TRUSTED_ANCHOR: | 
|  | // If the leaf cert is trusted only as an anchor, treat it as having | 
|  | // unspecified trust. This may allow a successful path to be built to a | 
|  | // different root (or to the same cert if it's self-signed). | 
|  | if (cur_path_.Empty()) { | 
|  | if (delegate->IsDebugLogEnabled()) { | 
|  | delegate->DebugLog( | 
|  | "Leaf is a trust anchor, considering as UNSPECIFIED"); | 
|  | } | 
|  | next_issuer_.trust = CertificateTrust::ForUnspecified(); | 
|  | } | 
|  | break; | 
|  | case CertificateTrustType::TRUSTED_LEAF: | 
|  | // If a non-leaf cert is trusted only as a leaf, treat it as having | 
|  | // unspecified trust. This may allow a successful path to be built to a | 
|  | // trusted root. | 
|  | if (!cur_path_.Empty()) { | 
|  | if (delegate->IsDebugLogEnabled()) { | 
|  | delegate->DebugLog( | 
|  | "Issuer is a trust leaf, considering as UNSPECIFIED"); | 
|  | } | 
|  | next_issuer_.trust = CertificateTrust::ForUnspecified(); | 
|  | } | 
|  | break; | 
|  | case CertificateTrustType::DISTRUSTED: | 
|  | case CertificateTrustType::UNSPECIFIED: | 
|  | case CertificateTrustType::TRUSTED_ANCHOR_OR_LEAF: | 
|  | // No override necessary. | 
|  | break; | 
|  | } | 
|  |  | 
|  | // Overrides for trusted leaf cert with require_leaf_selfsigned. If the leaf | 
|  | // isn't actually self-signed, treat it as unspecified. | 
|  | switch (next_issuer_.trust.type) { | 
|  | case CertificateTrustType::TRUSTED_LEAF: | 
|  | case CertificateTrustType::TRUSTED_ANCHOR_OR_LEAF: | 
|  | if (cur_path_.Empty() && next_issuer_.trust.require_leaf_selfsigned && | 
|  | !VerifyCertificateIsSelfSigned(*next_issuer_.cert, | 
|  | delegate->GetVerifyCache(), | 
|  | /*errors=*/nullptr)) { | 
|  | if (delegate->IsDebugLogEnabled()) { | 
|  | delegate->DebugLog( | 
|  | "Leaf is trusted with require_leaf_selfsigned but is " | 
|  | "not self-signed, considering as UNSPECIFIED"); | 
|  | } | 
|  | next_issuer_.trust = CertificateTrust::ForUnspecified(); | 
|  | } | 
|  | break; | 
|  | case CertificateTrustType::TRUSTED_ANCHOR: | 
|  | case CertificateTrustType::DISTRUSTED: | 
|  | case CertificateTrustType::UNSPECIFIED: | 
|  | // No override necessary. | 
|  | break; | 
|  | } | 
|  |  | 
|  | switch (next_issuer_.trust.type) { | 
|  | // If the trust for this issuer is "known" (either because it is | 
|  | // distrusted, or because it is trusted) then stop building and return the | 
|  | // path. | 
|  | case CertificateTrustType::DISTRUSTED: | 
|  | case CertificateTrustType::TRUSTED_ANCHOR: | 
|  | case CertificateTrustType::TRUSTED_ANCHOR_OR_LEAF: | 
|  | case CertificateTrustType::TRUSTED_LEAF: { | 
|  | // If the issuer has a known trust level, can stop building the path. | 
|  | cur_path_.CopyPath(out_certs); | 
|  | out_certs->push_back(std::move(next_issuer_.cert)); | 
|  | if (delegate->IsDebugLogEnabled()) { | 
|  | delegate->DebugLog("CertPathIter returning path:\n" + | 
|  | PathDebugString(*out_certs)); | 
|  | } | 
|  | *out_last_cert_trust = next_issuer_.trust; | 
|  | next_issuer_ = IssuerEntry(); | 
|  | return true; | 
|  | } | 
|  | case CertificateTrustType::UNSPECIFIED: { | 
|  | // Skip this cert if it is already in the chain. | 
|  | if (cur_path_.IsPresent(next_issuer_.cert.get())) { | 
|  | cur_path_.back()->increment_skipped_issuer_count(); | 
|  | if (delegate->IsDebugLogEnabled()) { | 
|  | delegate->DebugLog("CertPathIter skipping dupe cert: " + | 
|  | CertDebugString(next_issuer_.cert.get())); | 
|  | } | 
|  | next_issuer_ = IssuerEntry(); | 
|  | continue; | 
|  | } | 
|  |  | 
|  | cur_path_.Append(std::make_unique<CertIssuersIter>( | 
|  | std::move(next_issuer_.cert), &cert_issuer_sources_, trust_store_)); | 
|  | next_issuer_ = IssuerEntry(); | 
|  | if (delegate->IsDebugLogEnabled()) { | 
|  | delegate->DebugLog("CertPathIter cur_path_ =\n" + | 
|  | cur_path_.PathDebugString()); | 
|  | } | 
|  | // Continue descending the tree. | 
|  | continue; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | CertPathBuilderResultPath::CertPathBuilderResultPath() = default; | 
|  | CertPathBuilderResultPath::~CertPathBuilderResultPath() = default; | 
|  |  | 
|  | bool CertPathBuilderResultPath::IsValid() const { | 
|  | return GetTrustedCert() && !errors.ContainsHighSeverityErrors(); | 
|  | } | 
|  |  | 
|  | VerifyError CertPathBuilderResultPath::GetVerifyError() const { | 
|  | // Diagnostic string is always "everything" about the path. | 
|  | std::string diagnostic = errors.ToDebugString(certs); | 
|  | if (!errors.ContainsHighSeverityErrors()) { | 
|  | // TODO(bbe3): Having to check this after seems awkward: crbug.com/boringssl/713 | 
|  | if (GetTrustedCert()) { | 
|  | return VerifyError(VerifyError::StatusCode::PATH_VERIFIED, 0, | 
|  | std::move(diagnostic)); | 
|  | } else { | 
|  | return VerifyError(VerifyError::StatusCode::VERIFICATION_FAILURE, -1, | 
|  | std::move(diagnostic)); | 
|  | } | 
|  | } | 
|  |  | 
|  | // Check for the presence of things that amount to Internal errors in the | 
|  | // verification code. We deliberately prioritize this to not hide it in | 
|  | // multiple error cases. | 
|  | if (errors.ContainsError(cert_errors::kInternalError) || | 
|  | errors.ContainsError(cert_errors::kChainIsEmpty)) { | 
|  | return VerifyError(VerifyError::StatusCode::VERIFICATION_FAILURE, -1, | 
|  | std::move(diagnostic)); | 
|  | } | 
|  |  | 
|  | // Similarly, for the deadline and limit cases, there will often be other | 
|  | // errors that we probably do not care about, since path building was | 
|  | // aborted. Surface these errors instead of having them hidden in the multiple | 
|  | // error case. | 
|  | // | 
|  | // Normally callers should check for these in the path builder result before | 
|  | // calling this on a single path, but this is here in case they do not and | 
|  | // these errors are actually present on this path. | 
|  | if (errors.ContainsError(cert_errors::kDeadlineExceeded)) { | 
|  | return VerifyError(VerifyError::StatusCode::PATH_DEADLINE_EXCEEDED, -1, | 
|  | std::move(diagnostic)); | 
|  | } | 
|  | if (errors.ContainsError(cert_errors::kIterationLimitExceeded)) { | 
|  | return VerifyError(VerifyError::StatusCode::PATH_ITERATION_COUNT_EXCEEDED, | 
|  | -1, std::move(diagnostic)); | 
|  | } | 
|  | if (errors.ContainsError(cert_errors::kDepthLimitExceeded)) { | 
|  | return VerifyError(VerifyError::StatusCode::PATH_DEPTH_LIMIT_REACHED, -1, | 
|  | std::move(diagnostic)); | 
|  | } | 
|  |  | 
|  | // If the chain has multiple high severity errors, indicate that. | 
|  | ptrdiff_t depth = -1; | 
|  | std::optional<CertErrorId> single_error = | 
|  | errors.FindSingleHighSeverityError(depth); | 
|  | if (!single_error.has_value()) { | 
|  | return VerifyError(VerifyError::StatusCode::PATH_MULTIPLE_ERRORS, -1, | 
|  | std::move(diagnostic)); | 
|  | } | 
|  |  | 
|  | // Otherwise it has a single error, map it appropriately at the | 
|  | // depth it first occurs. | 
|  | if (single_error.value() == cert_errors::kValidityFailedNotAfter) { | 
|  | return VerifyError(VerifyError::StatusCode::CERTIFICATE_EXPIRED, depth, | 
|  | std::move(diagnostic)); | 
|  | } | 
|  | if (single_error.value() == cert_errors::kValidityFailedNotBefore) { | 
|  | return VerifyError(VerifyError::StatusCode::CERTIFICATE_NOT_YET_VALID, | 
|  | depth, std::move(diagnostic)); | 
|  | } | 
|  | if (single_error.value() == cert_errors::kDistrustedByTrustStore || | 
|  | single_error.value() == cert_errors::kCertIsNotTrustAnchor || | 
|  | single_error.value() == cert_errors::kMaxPathLengthViolated || | 
|  | single_error.value() == cert_errors::kSubjectDoesNotMatchIssuer || | 
|  | single_error.value() == cert_errors::kNoIssuersFound) { | 
|  | return VerifyError(VerifyError::StatusCode::PATH_NOT_FOUND, depth, | 
|  | std::move(diagnostic)); | 
|  | } | 
|  | if (single_error.value() == cert_errors::kVerifySignedDataFailed) { | 
|  | return VerifyError(VerifyError::StatusCode::CERTIFICATE_INVALID_SIGNATURE, | 
|  | depth, std::move(diagnostic)); | 
|  | } | 
|  | if (single_error.value() == cert_errors::kUnacceptableSignatureAlgorithm) { | 
|  | return VerifyError( | 
|  | VerifyError::StatusCode::CERTIFICATE_UNSUPPORTED_SIGNATURE_ALGORITHM, | 
|  | depth, std::move(diagnostic)); | 
|  | } | 
|  | if (single_error.value() == cert_errors::kUnacceptablePublicKey) { | 
|  | return VerifyError(VerifyError::StatusCode::CERTIFICATE_UNSUPPORTED_KEY, | 
|  | depth, std::move(diagnostic)); | 
|  | } | 
|  | if (single_error.value() == cert_errors::kEkuLacksServerAuth || | 
|  | single_error.value() == cert_errors::kEkuLacksServerAuthButHasAnyEKU || | 
|  | single_error.value() == cert_errors::kEkuLacksClientAuth || | 
|  | single_error.value() == cert_errors::kEkuLacksClientAuthButHasAnyEKU || | 
|  | single_error.value() == cert_errors::kEkuLacksClientAuthOrServerAuth) { | 
|  | return VerifyError(VerifyError::StatusCode::CERTIFICATE_NO_MATCHING_EKU, | 
|  | depth, std::move(diagnostic)); | 
|  | } | 
|  | if (single_error.value() == cert_errors::kCertificateRevoked) { | 
|  | return VerifyError(VerifyError::StatusCode::CERTIFICATE_REVOKED, depth, | 
|  | std::move(diagnostic)); | 
|  | } | 
|  | if (single_error.value() == cert_errors::kNoRevocationMechanism) { | 
|  | return VerifyError( | 
|  | VerifyError::StatusCode::CERTIFICATE_NO_REVOCATION_MECHANISM, depth, | 
|  | std::move(diagnostic)); | 
|  | } | 
|  | if (single_error.value() == cert_errors::kUnableToCheckRevocation) { | 
|  | return VerifyError( | 
|  | VerifyError::StatusCode::CERTIFICATE_UNABLE_TO_CHECK_REVOCATION, depth, | 
|  | std::move(diagnostic)); | 
|  | } | 
|  | // All other High severity errors map to CERTIFICATE_INVALID if associated | 
|  | // to a certificate, or VERIFICATION_FAILURE if not associated to a | 
|  | // certificate. | 
|  | return VerifyError((depth < 0) ? VerifyError::StatusCode::VERIFICATION_FAILURE | 
|  | : VerifyError::StatusCode::CERTIFICATE_INVALID, | 
|  | depth, std::move(diagnostic)); | 
|  | } | 
|  |  | 
|  |  | 
|  | CertPathBuilder::Result::Result() = default; | 
|  | CertPathBuilder::Result::Result(Result &&) = default; | 
|  | CertPathBuilder::Result::~Result() = default; | 
|  | CertPathBuilder::Result &CertPathBuilder::Result::operator=(Result &&) = | 
|  | default; | 
|  |  | 
|  | bool CertPathBuilder::Result::HasValidPath() const { | 
|  | return GetBestValidPath() != nullptr; | 
|  | } | 
|  |  | 
|  | bool CertPathBuilder::Result::AnyPathContainsError(CertErrorId error_id) const { | 
|  | for (const auto &path : paths) { | 
|  | if (path->errors.ContainsError(error_id)) { | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | const VerifyError CertPathBuilder::Result::GetBestPathVerifyError() const { | 
|  | if (HasValidPath()) { | 
|  | return GetBestValidPath()->GetVerifyError(); | 
|  | } | 
|  | // We can only return one error. Returning the errors corresponding to the | 
|  | // limits if they they appear on any path will make this error prominent even | 
|  | // if there are other paths with different or multiple errors. | 
|  | if (exceeded_iteration_limit) { | 
|  | return VerifyError( | 
|  | VerifyError::StatusCode::PATH_ITERATION_COUNT_EXCEEDED, -1, | 
|  | "Iteration count exceeded, could not find a trusted path."); | 
|  | } | 
|  | if (exceeded_deadline) { | 
|  | return VerifyError(VerifyError::StatusCode::PATH_DEADLINE_EXCEEDED, -1, | 
|  | "Deadline exceeded. Could not find a trusted path."); | 
|  | } | 
|  | if (AnyPathContainsError(cert_errors::kDepthLimitExceeded)) { | 
|  | return VerifyError(VerifyError::StatusCode::PATH_DEPTH_LIMIT_REACHED, -1, | 
|  | "Depth limit reached. Could not find a trusted path."); | 
|  | } | 
|  |  | 
|  | // If there are no paths to report an error on, this probably indicates | 
|  | // something is wrong with this path builder result. | 
|  | if (paths.empty()) { | 
|  | return VerifyError(VerifyError::StatusCode::VERIFICATION_FAILURE, -1, | 
|  | "No paths in path builder result."); | 
|  | } | 
|  |  | 
|  | // If there are paths, report the VerifyError from the best path. | 
|  | CertPathBuilderResultPath *path = paths[best_result_index].get(); | 
|  | return path->GetVerifyError(); | 
|  | } | 
|  |  | 
|  | const CertPathBuilderResultPath *CertPathBuilder::Result::GetBestValidPath() | 
|  | const { | 
|  | const CertPathBuilderResultPath *result_path = GetBestPathPossiblyInvalid(); | 
|  |  | 
|  | if (result_path && result_path->IsValid()) { | 
|  | return result_path; | 
|  | } | 
|  |  | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | const CertPathBuilderResultPath * | 
|  | CertPathBuilder::Result::GetBestPathPossiblyInvalid() const { | 
|  | BSSL_CHECK((paths.empty() && best_result_index == 0) || | 
|  | best_result_index < paths.size()); | 
|  |  | 
|  | if (best_result_index >= paths.size()) { | 
|  | return nullptr; | 
|  | } | 
|  |  | 
|  | return paths[best_result_index].get(); | 
|  | } | 
|  |  | 
|  | CertPathBuilder::CertPathBuilder( | 
|  | std::shared_ptr<const ParsedCertificate> cert, TrustStore *trust_store, | 
|  | CertPathBuilderDelegate *delegate, const der::GeneralizedTime &time, | 
|  | KeyPurpose key_purpose, InitialExplicitPolicy initial_explicit_policy, | 
|  | const std::set<der::Input> &user_initial_policy_set, | 
|  | InitialPolicyMappingInhibit initial_policy_mapping_inhibit, | 
|  | InitialAnyPolicyInhibit initial_any_policy_inhibit) | 
|  | : cert_path_iter_( | 
|  | std::make_unique<CertPathIter>(std::move(cert), trust_store)), | 
|  | delegate_(delegate), | 
|  | time_(time), | 
|  | key_purpose_(key_purpose), | 
|  | initial_explicit_policy_(initial_explicit_policy), | 
|  | user_initial_policy_set_(user_initial_policy_set), | 
|  | initial_policy_mapping_inhibit_(initial_policy_mapping_inhibit), | 
|  | initial_any_policy_inhibit_(initial_any_policy_inhibit) { | 
|  | BSSL_CHECK(delegate); | 
|  | // The TrustStore also implements the CertIssuerSource interface. | 
|  | AddCertIssuerSource(trust_store); | 
|  | } | 
|  |  | 
|  | CertPathBuilder::~CertPathBuilder() = default; | 
|  |  | 
|  | void CertPathBuilder::AddCertIssuerSource( | 
|  | CertIssuerSource *cert_issuer_source) { | 
|  | cert_path_iter_->AddCertIssuerSource(cert_issuer_source); | 
|  | } | 
|  |  | 
|  | void CertPathBuilder::SetIterationLimit(uint32_t limit) { | 
|  | max_iteration_count_ = limit; | 
|  | } | 
|  |  | 
|  | void CertPathBuilder::SetDepthLimit(uint32_t limit) { | 
|  | max_path_building_depth_ = limit; | 
|  | } | 
|  |  | 
|  | void CertPathBuilder::SetValidPathLimit(size_t limit) { | 
|  | valid_path_limit_ = limit; | 
|  | } | 
|  |  | 
|  | void CertPathBuilder::SetExploreAllPaths(bool explore_all_paths) { | 
|  | valid_path_limit_ = explore_all_paths ? 0 : 1; | 
|  | } | 
|  |  | 
|  | CertPathBuilder::Result CertPathBuilder::Run() { | 
|  | uint32_t iteration_count = 0; | 
|  |  | 
|  | while (true) { | 
|  | std::unique_ptr<CertPathBuilderResultPath> result_path = | 
|  | std::make_unique<CertPathBuilderResultPath>(); | 
|  |  | 
|  | if (!cert_path_iter_->GetNextPath( | 
|  | &result_path->certs, &result_path->last_cert_trust, | 
|  | &result_path->errors, delegate_, &iteration_count, | 
|  | max_iteration_count_, max_path_building_depth_)) { | 
|  | // There are no more paths to check or limits were exceeded. | 
|  | if (result_path->errors.ContainsError( | 
|  | cert_errors::kIterationLimitExceeded)) { | 
|  | out_result_.exceeded_iteration_limit = true; | 
|  | } | 
|  | if (result_path->errors.ContainsError(cert_errors::kDeadlineExceeded)) { | 
|  | out_result_.exceeded_deadline = true; | 
|  | } | 
|  | if (!result_path->certs.empty()) { | 
|  | // It shouldn't be possible to get here without adding one of the | 
|  | // errors above, but just in case, add an error if there isn't one | 
|  | // already. | 
|  | if (!result_path->errors.ContainsHighSeverityErrors()) { | 
|  | result_path->errors.GetOtherErrors()->AddError( | 
|  | cert_errors::kInternalError); | 
|  | } | 
|  |  | 
|  | // Allow the delegate to do any processing or logging of the partial | 
|  | // path. (This is for symmetry for the other CheckPathAfterVerification | 
|  | // which also gets called on partial paths.) | 
|  | delegate_->CheckPathAfterVerification(*this, result_path.get()); | 
|  |  | 
|  | AddResultPath(std::move(result_path)); | 
|  | } | 
|  | out_result_.iteration_count = iteration_count; | 
|  | return std::move(out_result_); | 
|  | } | 
|  |  | 
|  | if (result_path->last_cert_trust.HasUnspecifiedTrust()) { | 
|  | // Partial path, don't attempt to verify. Just double check that it is | 
|  | // marked with an error, and move on. | 
|  | if (!result_path->errors.ContainsHighSeverityErrors()) { | 
|  | result_path->errors.GetOtherErrors()->AddError( | 
|  | cert_errors::kInternalError); | 
|  | } | 
|  | } else { | 
|  | // Verify the entire certificate chain. | 
|  | VerifyCertificateChain( | 
|  | result_path->certs, result_path->last_cert_trust, delegate_, time_, | 
|  | key_purpose_, initial_explicit_policy_, user_initial_policy_set_, | 
|  | initial_policy_mapping_inhibit_, initial_any_policy_inhibit_, | 
|  | &result_path->user_constrained_policy_set, &result_path->errors); | 
|  | } | 
|  |  | 
|  | // Give the delegate a chance to add errors to the path. | 
|  | delegate_->CheckPathAfterVerification(*this, result_path.get()); | 
|  |  | 
|  | bool path_is_good = result_path->IsValid(); | 
|  |  | 
|  | AddResultPath(std::move(result_path)); | 
|  |  | 
|  | if (path_is_good) { | 
|  | valid_path_count_++; | 
|  | if (valid_path_limit_ > 0 && valid_path_count_ == valid_path_limit_) { | 
|  | out_result_.iteration_count = iteration_count; | 
|  | // Found enough paths, return immediately. | 
|  | return std::move(out_result_); | 
|  | } | 
|  | } | 
|  | // Path did not verify. Try more paths. | 
|  | } | 
|  | } | 
|  |  | 
|  | void CertPathBuilder::AddResultPath( | 
|  | std::unique_ptr<CertPathBuilderResultPath> result_path) { | 
|  | // TODO(mattm): If there are no valid paths, set best_result_index based on | 
|  | // number or severity of errors. If there are multiple valid paths, could set | 
|  | // best_result_index based on prioritization (since due to AIA and such, the | 
|  | // actual order results were discovered may not match the ideal). | 
|  | if (!out_result_.HasValidPath()) { | 
|  | const CertPathBuilderResultPath *old_best_path = | 
|  | out_result_.GetBestPathPossiblyInvalid(); | 
|  | // If |result_path| is a valid path or if the previous best result did not | 
|  | // end in a trust anchor but the |result_path| does, then update the best | 
|  | // result to the new result. | 
|  | if (result_path->IsValid() || | 
|  | (!result_path->last_cert_trust.HasUnspecifiedTrust() && old_best_path && | 
|  | old_best_path->last_cert_trust.HasUnspecifiedTrust())) { | 
|  | out_result_.best_result_index = out_result_.paths.size(); | 
|  | } | 
|  | } | 
|  | if (result_path->certs.size() > out_result_.max_depth_seen) { | 
|  | out_result_.max_depth_seen = result_path->certs.size(); | 
|  | } | 
|  | out_result_.paths.push_back(std::move(result_path)); | 
|  | } | 
|  |  | 
|  | BSSL_NAMESPACE_END |