| Index: net/cert/internal/path_builder.cc
|
| diff --git a/net/cert/internal/path_builder.cc b/net/cert/internal/path_builder.cc
|
| index 291bcf5360643b4a7a76bc85273fe430c41cae70..7f4284b2f0fc85226aa44ba4d1f138db80c7eb07 100644
|
| --- a/net/cert/internal/path_builder.cc
|
| +++ b/net/cert/internal/path_builder.cc
|
| @@ -40,24 +40,36 @@ std::string CertDebugString(const ParsedCertificate* cert) {
|
| return subject_str + "(" + issuer_str + ")";
|
| }
|
|
|
| -// This structure contains either a ParsedCertificate or a TrustAnchor. It is
|
| -// used to describe the result of getting a certificate's issuer, which may
|
| -// either be another certificate, or a trust anchor.
|
| -struct CertificateOrTrustAnchor {
|
| - CertificateOrTrustAnchor() {}
|
| -
|
| - explicit CertificateOrTrustAnchor(scoped_refptr<ParsedCertificate> cert)
|
| - : cert(std::move(cert)) {}
|
| +// This structure describes a certificate and its trust level. Note that |cert|
|
| +// may be null to indicate an "empty" entry.
|
| +struct IssuerEntry {
|
| + scoped_refptr<ParsedCertificate> cert;
|
| + CertificateTrust trust;
|
| +};
|
|
|
| - explicit CertificateOrTrustAnchor(scoped_refptr<TrustAnchor> anchor)
|
| - : anchor(std::move(anchor)) {}
|
| +// Simple comparator of IssuerEntry that defines the order in which issuers
|
| +// should be explored. It puts trust anchors ahead of unknown or distrusted
|
| +// ones.
|
| +struct IssuerEntryComparator {
|
| + bool operator()(const IssuerEntry& issuer1, const IssuerEntry& issuer2) {
|
| + return CertificateTrustToOrder(issuer1.trust) <
|
| + CertificateTrustToOrder(issuer2.trust);
|
| + }
|
|
|
| - bool IsTrustAnchor() const { return anchor.get() != nullptr; }
|
| - bool IsCertificate() const { return cert.get() != nullptr; }
|
| - bool IsEmpty() const { return !IsTrustAnchor() && !IsCertificate(); }
|
| + static int CertificateTrustToOrder(const CertificateTrust& trust) {
|
| + switch (trust.type) {
|
| + case CertificateTrustType::TRUSTED_ANCHOR:
|
| + case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS:
|
| + return 1;
|
| + case CertificateTrustType::UNSPECIFIED:
|
| + return 2;
|
| + case CertificateTrustType::DISTRUSTED:
|
| + return 4;
|
| + }
|
|
|
| - scoped_refptr<ParsedCertificate> cert;
|
| - scoped_refptr<TrustAnchor> anchor;
|
| + NOTREACHED();
|
| + return 5;
|
| + }
|
| };
|
|
|
| // CertIssuersIter iterates through the intermediates from |cert_issuer_sources|
|
| @@ -72,7 +84,7 @@ class CertIssuersIter {
|
|
|
| // Gets the next candidate issuer, or clears |*out| when all issuers have been
|
| // exhausted.
|
| - void GetNextIssuer(CertificateOrTrustAnchor* out);
|
| + void GetNextIssuer(IssuerEntry* out);
|
|
|
| // Returns the |cert| for which issuers are being retrieved.
|
| const ParsedCertificate* cert() const { return cert_.get(); }
|
| @@ -85,15 +97,14 @@ class CertIssuersIter {
|
| // 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();
|
| +
|
| scoped_refptr<ParsedCertificate> cert_;
|
| CertIssuerSources* cert_issuer_sources_;
|
| const TrustStore* trust_store_;
|
|
|
| - // The list of trust anchors that match the issuer name for |cert_|.
|
| - TrustAnchors anchors_;
|
| - // The index of the next trust anchor in |anchors_| to return.
|
| - size_t cur_anchor_ = 0;
|
| -
|
| // 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
|
| @@ -101,9 +112,12 @@ class CertIssuersIter {
|
| // results were already returned.
|
| // Elements should not be removed from |issuers_| once added, since
|
| // |present_issuers_| will point to data owned by the certs.
|
| - ParsedCertificateList issuers_;
|
| + std::vector<IssuerEntry> issuers_;
|
| // The index of the next cert in |issuers_| to return.
|
| size_t cur_issuer_ = 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
|
| @@ -133,36 +147,17 @@ CertIssuersIter::CertIssuersIter(scoped_refptr<ParsedCertificate> in_cert,
|
| DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) << ") created";
|
| }
|
|
|
| -void CertIssuersIter::GetNextIssuer(CertificateOrTrustAnchor* out) {
|
| +void CertIssuersIter::GetNextIssuer(IssuerEntry* out) {
|
| if (!did_initial_query_) {
|
| did_initial_query_ = true;
|
| - trust_store_->FindTrustAnchorsForCert(cert_, &anchors_);
|
| -
|
| for (auto* cert_issuer_source : *cert_issuer_sources_) {
|
| ParsedCertificateList new_issuers;
|
| cert_issuer_source->SyncGetIssuersOf(cert(), &new_issuers);
|
| AddIssuers(std::move(new_issuers));
|
| }
|
| - DVLOG(1) << anchors_.size() << " sync anchors, " << issuers_.size()
|
| - << " sync issuers";
|
| - // TODO(mattm): sort by notbefore, etc (eg if cert issuer matches a trust
|
| - // anchor subject (or is a trust anchor), that should be sorted higher too.
|
| - // See big list of possible sorting hints in RFC 4158.)
|
| - // (Update PathBuilderKeyRolloverTest.TestRolloverBothRootsTrusted once that
|
| - // is done)
|
| - }
|
| -
|
| - // Return possible trust anchors first.
|
| - if (cur_anchor_ < anchors_.size()) {
|
| - DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert())
|
| - << "): returning anchor " << cur_anchor_ << " of "
|
| - << anchors_.size();
|
| - // Still have anchors that haven't been returned yet, return one of them.
|
| - *out = CertificateOrTrustAnchor(anchors_[cur_anchor_++]);
|
| - return;
|
| }
|
|
|
| - // If there aren't any issuers left, block until async results are ready.
|
| + // 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).
|
| @@ -186,20 +181,22 @@ void CertIssuersIter::GetNextIssuer(CertificateOrTrustAnchor* out) {
|
| }
|
|
|
| if (HasCurrentIssuer()) {
|
| + SortRemainingIssuers();
|
| +
|
| DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert())
|
| << "): returning issuer " << cur_issuer_ << " of "
|
| << issuers_.size();
|
| - // Still have issuers that haven't been returned yet, return one of them.
|
| - // A reference to the returned issuer is retained, since |present_issuers_|
|
| - // points to data owned by it.
|
| - *out = CertificateOrTrustAnchor(issuers_[cur_issuer_++]);
|
| + // 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;
|
| }
|
|
|
| DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert())
|
| << ") Reached the end of all available issuers.";
|
| // Reached the end of all available issuers.
|
| - *out = CertificateOrTrustAnchor();
|
| + *out = IssuerEntry();
|
| }
|
|
|
| void CertIssuersIter::AddIssuers(ParsedCertificateList new_issuers) {
|
| @@ -208,7 +205,14 @@ void CertIssuersIter::AddIssuers(ParsedCertificateList new_issuers) {
|
| present_issuers_.end())
|
| continue;
|
| present_issuers_.insert(issuer->der_cert().AsStringPiece());
|
| - issuers_.push_back(std::move(issuer));
|
| +
|
| + // Look up the trust for this issuer.
|
| + IssuerEntry entry;
|
| + entry.cert = std::move(issuer);
|
| + trust_store_->GetTrust(entry.cert, &entry.trust);
|
| +
|
| + issuers_.push_back(std::move(entry));
|
| + issuers_needs_sort_ = true;
|
| }
|
| }
|
|
|
| @@ -227,6 +231,21 @@ void CertIssuersIter::DoAsyncIssuerQuery() {
|
| }
|
| }
|
|
|
| +void CertIssuersIter::SortRemainingIssuers() {
|
| + // TODO(mattm): sort by notbefore, etc (eg if cert issuer matches a trust
|
| + // anchor subject (or is a trust anchor), that should be sorted higher too.
|
| + // See big list of possible sorting hints in RFC 4158.)
|
| + // (Update PathBuilderKeyRolloverTest.TestRolloverBothRootsTrusted once that
|
| + // is done)
|
| + if (!issuers_needs_sort_)
|
| + return;
|
| +
|
| + std::stable_sort(issuers_.begin() + cur_issuer_, issuers_.end(),
|
| + IssuerEntryComparator());
|
| +
|
| + 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).
|
| @@ -305,7 +324,7 @@ CertPath::CertPath() = default;
|
| CertPath::~CertPath() = default;
|
|
|
| void CertPath::Clear() {
|
| - trust_anchor = nullptr;
|
| + last_cert_trust = CertificateTrust::ForUnspecified();
|
| certs.clear();
|
| }
|
|
|
| @@ -313,6 +332,23 @@ bool CertPath::IsEmpty() const {
|
| return certs.empty();
|
| }
|
|
|
| +const ParsedCertificate* CertPath::GetTrustedCert() const {
|
| + if (certs.empty())
|
| + return nullptr;
|
| +
|
| + switch (last_cert_trust.type) {
|
| + case CertificateTrustType::TRUSTED_ANCHOR:
|
| + case CertificateTrustType::TRUSTED_ANCHOR_WITH_CONSTRAINTS:
|
| + return certs.back().get();
|
| + case CertificateTrustType::UNSPECIFIED:
|
| + case CertificateTrustType::DISTRUSTED:
|
| + return nullptr;
|
| + }
|
| +
|
| + 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.
|
| @@ -345,7 +381,7 @@ class CertPathIter {
|
|
|
| // Stores the next candidate issuer, until it is used during the
|
| // STATE_GET_NEXT_ISSUER_COMPLETE step.
|
| - CertificateOrTrustAnchor next_issuer_;
|
| + 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.
|
| @@ -365,9 +401,11 @@ class CertPathIter {
|
|
|
| CertPathIter::CertPathIter(scoped_refptr<ParsedCertificate> cert,
|
| const TrustStore* trust_store)
|
| - : next_issuer_(std::move(cert)),
|
| - trust_store_(trust_store),
|
| - next_state_(STATE_GET_NEXT_ISSUER_COMPLETE) {}
|
| + : trust_store_(trust_store), next_state_(STATE_GET_NEXT_ISSUER_COMPLETE) {
|
| + // Initialize |next_issuer_| to the target certificate.
|
| + next_issuer_.cert = std::move(cert);
|
| + trust_store_->GetTrust(next_issuer_.cert, &next_issuer_.trust);
|
| +}
|
|
|
| void CertPathIter::AddCertIssuerSource(CertIssuerSource* cert_issuer_source) {
|
| cert_issuer_sources_.push_back(cert_issuer_source);
|
| @@ -406,42 +444,57 @@ void CertPathIter::GetNextPath(CertPath* path) {
|
| }
|
|
|
| void CertPathIter::DoGetNextIssuer() {
|
| - next_state_ = STATE_GET_NEXT_ISSUER_COMPLETE;
|
| - cur_path_.back()->GetNextIssuer(&next_issuer_);
|
| + if (cur_path_.Empty()) {
|
| + // Exhausted all paths.
|
| + next_state_ = STATE_NONE;
|
| + } else {
|
| + next_state_ = STATE_GET_NEXT_ISSUER_COMPLETE;
|
| + cur_path_.back()->GetNextIssuer(&next_issuer_);
|
| + }
|
| }
|
|
|
| void CertPathIter::DoGetNextIssuerComplete() {
|
| - // If the issuer is a trust anchor signal readiness.
|
| - if (next_issuer_.IsTrustAnchor()) {
|
| - DVLOG(1) << "CertPathIter got anchor("
|
| - << CertDebugString(next_issuer_.anchor->cert().get());
|
| - next_state_ = STATE_RETURN_A_PATH;
|
| - cur_path_.CopyPath(&out_path_->certs);
|
| - out_path_->trust_anchor = std::move(next_issuer_.anchor);
|
| - next_issuer_ = CertificateOrTrustAnchor();
|
| - return;
|
| - }
|
| -
|
| - if (next_issuer_.IsCertificate()) {
|
| - // Skip this cert if it is already in the chain.
|
| - if (cur_path_.IsPresent(next_issuer_.cert.get())) {
|
| - next_state_ = STATE_GET_NEXT_ISSUER;
|
| - return;
|
| - }
|
| -
|
| - cur_path_.Append(base::MakeUnique<CertIssuersIter>(
|
| - std::move(next_issuer_.cert), &cert_issuer_sources_, trust_store_));
|
| - next_issuer_ = CertificateOrTrustAnchor();
|
| - DVLOG(1) << "CertPathIter cur_path_ = " << cur_path_.PathDebugString();
|
| - // Continue descending the tree.
|
| - next_state_ = STATE_GET_NEXT_ISSUER;
|
| - } else {
|
| + if (!next_issuer_.cert) {
|
| // TODO(mattm): should also include such paths in CertPathBuilder::Result,
|
| // maybe with a flag to enable it. Or use a visitor pattern so the caller
|
| // can decide what to do with any failed paths.
|
| // No more issuers for current chain, go back up and see if there are any
|
| // more for the previous cert.
|
| next_state_ = STATE_BACKTRACK;
|
| + return;
|
| + }
|
| +
|
| + switch (next_issuer_.trust.type) {
|
| + // If the trust for this issuer is "known" (either becuase 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_WITH_CONSTRAINTS: {
|
| + // If the issuer has a known trust level, can stop building the path.
|
| + DVLOG(1) << "CertPathIter got anchor: "
|
| + << CertDebugString(next_issuer_.cert.get());
|
| + next_state_ = STATE_RETURN_A_PATH;
|
| + cur_path_.CopyPath(&out_path_->certs);
|
| + out_path_->certs.push_back(std::move(next_issuer_.cert));
|
| + out_path_->last_cert_trust = next_issuer_.trust;
|
| + next_issuer_ = IssuerEntry();
|
| + break;
|
| + }
|
| + case CertificateTrustType::UNSPECIFIED: {
|
| + // Skip this cert if it is already in the chain.
|
| + if (cur_path_.IsPresent(next_issuer_.cert.get())) {
|
| + next_state_ = STATE_GET_NEXT_ISSUER;
|
| + break;
|
| + }
|
| +
|
| + cur_path_.Append(base::MakeUnique<CertIssuersIter>(
|
| + std::move(next_issuer_.cert), &cert_issuer_sources_, trust_store_));
|
| + next_issuer_ = IssuerEntry();
|
| + DVLOG(1) << "CertPathIter cur_path_ = " << cur_path_.PathDebugString();
|
| + // Continue descending the tree.
|
| + next_state_ = STATE_GET_NEXT_ISSUER;
|
| + break;
|
| + }
|
| }
|
| }
|
|
|
| @@ -461,8 +514,7 @@ CertPathBuilder::ResultPath::ResultPath() = default;
|
| CertPathBuilder::ResultPath::~ResultPath() = default;
|
|
|
| bool CertPathBuilder::ResultPath::IsValid() const {
|
| - return !path.certs.empty() && path.trust_anchor &&
|
| - !errors.ContainsHighSeverityErrors();
|
| + return path.GetTrustedCert() && !errors.ContainsHighSeverityErrors();
|
| }
|
|
|
| CertPathBuilder::Result::Result() = default;
|
| @@ -488,7 +540,7 @@ bool CertPathBuilder::Result::HasValidPath() const {
|
| }
|
|
|
| CertPathBuilder::CertPathBuilder(scoped_refptr<ParsedCertificate> cert,
|
| - const TrustStore* trust_store,
|
| + TrustStore* trust_store,
|
| const SignaturePolicy* signature_policy,
|
| const der::GeneralizedTime& time,
|
| KeyPurpose key_purpose,
|
| @@ -498,7 +550,10 @@ CertPathBuilder::CertPathBuilder(scoped_refptr<ParsedCertificate> cert,
|
| time_(time),
|
| key_purpose_(key_purpose),
|
| next_state_(STATE_NONE),
|
| - out_result_(result) {}
|
| + out_result_(result) {
|
| + // The TrustStore also implements the CertIssuerSource interface.
|
| + AddCertIssuerSource(trust_store);
|
| +}
|
|
|
| CertPathBuilder::~CertPathBuilder() {}
|
|
|
| @@ -543,13 +598,15 @@ void CertPathBuilder::DoGetNextPathComplete() {
|
|
|
| // Verify the entire certificate chain.
|
| auto result_path = base::MakeUnique<ResultPath>();
|
| - bool verify_result = VerifyCertificateChain(
|
| - next_path_.certs, next_path_.trust_anchor.get(), signature_policy_, time_,
|
| - key_purpose_, &result_path->errors);
|
| + VerifyCertificateChain(next_path_.certs, next_path_.last_cert_trust,
|
| + signature_policy_, time_, key_purpose_,
|
| + &result_path->errors);
|
| + bool verify_result = !result_path->errors.ContainsHighSeverityErrors();
|
| +
|
| DVLOG(1) << "CertPathBuilder VerifyCertificateChain result = "
|
| - << verify_result;
|
| + << verify_result << "\n"
|
| + << result_path->errors.ToDebugString(next_path_.certs);
|
| result_path->path = next_path_;
|
| - DCHECK_EQ(verify_result, !result_path->errors.ContainsHighSeverityErrors());
|
| AddResultPath(std::move(result_path));
|
|
|
| if (verify_result) {
|
|
|