| Index: net/cert/internal/path_builder.cc
|
| diff --git a/net/cert/internal/path_builder.cc b/net/cert/internal/path_builder.cc
|
| index 863d8506b22d38673eaf18db08302484d934c34f..6bee524c4744b924d6faa49d9ef0d4e24ccec61e 100644
|
| --- a/net/cert/internal/path_builder.cc
|
| +++ b/net/cert/internal/path_builder.cc
|
| @@ -11,10 +11,12 @@
|
| #include "base/memory/ptr_util.h"
|
| #include "net/base/net_errors.h"
|
| #include "net/cert/internal/cert_issuer_source.h"
|
| +#include "net/cert/internal/cert_issuer_source_collection.h"
|
| #include "net/cert/internal/parse_certificate.h"
|
| #include "net/cert/internal/parse_name.h" // For CertDebugString.
|
| #include "net/cert/internal/signature_policy.h"
|
| #include "net/cert/internal/trust_store.h"
|
| +#include "net/cert/internal/trust_store_collection.h"
|
| #include "net/cert/internal/verify_certificate_chain.h"
|
| #include "net/cert/internal/verify_name_match.h"
|
| #include "net/der/parser.h"
|
| @@ -24,8 +26,6 @@ namespace net {
|
|
|
| namespace {
|
|
|
| -using CertIssuerSources = std::vector<CertIssuerSource*>;
|
| -
|
| // TODO(mattm): decide how much debug logging to keep.
|
| std::string CertDebugString(const ParsedCertificate* cert) {
|
| RDNSequence subject, issuer;
|
| @@ -40,15 +40,14 @@ std::string CertDebugString(const ParsedCertificate* cert) {
|
| return subject_str + "(" + issuer_str + ")";
|
| }
|
|
|
| -// CertIssuersIter iterates through the intermediates from |cert_issuer_sources|
|
| +// CertIssuersIter iterates through the intermediates from |cert_issuer_source|
|
| // which may be issuers of |cert|.
|
| class CertIssuersIter {
|
| public:
|
| - // Constructs the CertIssuersIter. |*cert_issuer_sources| must be valid for
|
| + // Constructs the CertIssuersIter. |*cert_issuer_source| must be valid for
|
| // the lifetime of the CertIssuersIter.
|
| CertIssuersIter(scoped_refptr<ParsedCertificate> cert,
|
| - CertIssuerSources* cert_issuer_sources,
|
| - const TrustStore& trust_store);
|
| + CertIssuerSource* cert_issuer_source);
|
|
|
| // Gets the next candidate issuer. If an issuer is ready synchronously, SYNC
|
| // is returned and the cert is stored in |*out_cert|. If an issuer is not
|
| @@ -67,7 +66,7 @@ class CertIssuersIter {
|
| void GotAsyncCerts(CertIssuerSource::Request* request);
|
|
|
| scoped_refptr<ParsedCertificate> cert_;
|
| - CertIssuerSources* cert_issuer_sources_;
|
| + CertIssuerSource* cert_issuer_source_;
|
|
|
| // The list of issuers for |cert_|. This is added to incrementally (first
|
| // synchronous results, then possibly multiple times as asynchronous results
|
| @@ -87,12 +86,9 @@ class CertIssuersIter {
|
|
|
| // Tracks whether asynchronous requests have been made yet.
|
| bool did_async_query_ = false;
|
| - // If asynchronous requests were made, how many of them are still outstanding?
|
| - size_t pending_async_results_;
|
| - // Owns the Request objects for any asynchronous requests so that they will be
|
| + // Owns the Request object for any asynchronous request so that it will be
|
| // cancelled if CertIssuersIter is destroyed.
|
| - std::vector<std::unique_ptr<CertIssuerSource::Request>>
|
| - pending_async_requests_;
|
| + std::unique_ptr<CertIssuerSource::Request> pending_async_request_;
|
|
|
| // When GetNextIssuer was called and returned asynchronously, |*out_cert_| is
|
| // where the result will be stored, and |callback_| will be run when the
|
| @@ -104,28 +100,17 @@ class CertIssuersIter {
|
| };
|
|
|
| CertIssuersIter::CertIssuersIter(scoped_refptr<ParsedCertificate> in_cert,
|
| - CertIssuerSources* cert_issuer_sources,
|
| - const TrustStore& trust_store)
|
| - : cert_(in_cert), cert_issuer_sources_(cert_issuer_sources) {
|
| + CertIssuerSource* cert_issuer_source)
|
| + : cert_(in_cert), cert_issuer_source_(cert_issuer_source) {
|
| DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) << ") created";
|
| - trust_store.FindTrustAnchorsByNormalizedName(in_cert->normalized_issuer(),
|
| - &issuers_);
|
| - // Insert matching roots into |present_issuers_| in case they also are
|
| - // returned by a CertIssuerSource. It is assumed
|
| - // FindTrustAnchorsByNormalizedName does not itself return dupes.
|
| - for (const auto& root : issuers_)
|
| - present_issuers_.insert(root->der_cert().AsStringPiece());
|
| -
|
| - for (auto* cert_issuer_source : *cert_issuer_sources_) {
|
| - ParsedCertificateList new_issuers;
|
| - cert_issuer_source->SyncGetIssuersOf(cert(), &new_issuers);
|
| - for (scoped_refptr<ParsedCertificate>& issuer : new_issuers) {
|
| - if (present_issuers_.find(issuer->der_cert().AsStringPiece()) !=
|
| - present_issuers_.end())
|
| - continue;
|
| - present_issuers_.insert(issuer->der_cert().AsStringPiece());
|
| - issuers_.push_back(std::move(issuer));
|
| - }
|
| + ParsedCertificateList new_issuers;
|
| + cert_issuer_source->SyncGetIssuersOf(cert(), &new_issuers);
|
| + for (scoped_refptr<ParsedCertificate>& issuer : new_issuers) {
|
| + if (present_issuers_.find(issuer->der_cert().AsStringPiece()) !=
|
| + present_issuers_.end())
|
| + continue;
|
| + present_issuers_.insert(issuer->der_cert().AsStringPiece());
|
| + issuers_.push_back(std::move(issuer));
|
| }
|
| // 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.
|
| @@ -150,7 +135,7 @@ CompletionStatus CertIssuersIter::GetNextIssuer(
|
| return CompletionStatus::SYNC;
|
| }
|
| if (did_async_query_) {
|
| - if (pending_async_results_ == 0) {
|
| + if (!pending_async_request_) {
|
| DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert())
|
| << ") Reached the end of all available issuers.";
|
| // Reached the end of all available issuers.
|
| @@ -159,8 +144,7 @@ CompletionStatus CertIssuersIter::GetNextIssuer(
|
| }
|
|
|
| DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert())
|
| - << ") Still waiting for async results from other "
|
| - "CertIssuerSources.";
|
| + << ") Still waiting for more async results.";
|
| // Still waiting for async results from other CertIssuerSources.
|
| out_cert_ = out_cert;
|
| callback_ = callback;
|
| @@ -176,22 +160,18 @@ CompletionStatus CertIssuersIter::GetNextIssuer(
|
|
|
| // Now issue request(s) for async ones (AIA, etc).
|
| did_async_query_ = true;
|
| - pending_async_results_ = 0;
|
| - for (auto* cert_issuer_source : *cert_issuer_sources_) {
|
| - std::unique_ptr<CertIssuerSource::Request> request;
|
| - cert_issuer_source->AsyncGetIssuersOf(
|
| - cert(),
|
| - base::Bind(&CertIssuersIter::GotAsyncCerts, base::Unretained(this)),
|
| - &request);
|
| - if (request) {
|
| - DVLOG(1) << "AsyncGetIssuersOf(" << CertDebugString(cert())
|
| - << ") pending...";
|
| - pending_async_results_++;
|
| - pending_async_requests_.push_back(std::move(request));
|
| - }
|
| + std::unique_ptr<CertIssuerSource::Request> request;
|
| + cert_issuer_source_->AsyncGetIssuersOf(
|
| + reference_cert(),
|
| + base::Bind(&CertIssuersIter::GotAsyncCerts, base::Unretained(this)),
|
| + &request);
|
| + if (request) {
|
| + DVLOG(1) << "AsyncGetIssuersOf(" << CertDebugString(cert())
|
| + << ") pending...";
|
| + pending_async_request_ = std::move(request);
|
| }
|
|
|
| - if (pending_async_results_ == 0) {
|
| + if (!pending_async_request_) {
|
| DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert())
|
| << ") No cert sources have async results.";
|
| // No cert sources have async results.
|
| @@ -199,9 +179,6 @@ CompletionStatus CertIssuersIter::GetNextIssuer(
|
| return CompletionStatus::SYNC;
|
| }
|
|
|
| - DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert())
|
| - << ") issued AsyncGetIssuersOf call(s) (n=" << pending_async_results_
|
| - << ")";
|
| out_cert_ = out_cert;
|
| callback_ = callback;
|
| return CompletionStatus::ASYNC;
|
| @@ -210,15 +187,15 @@ CompletionStatus CertIssuersIter::GetNextIssuer(
|
| void CertIssuersIter::GotAsyncCerts(CertIssuerSource::Request* request) {
|
| DVLOG(1) << "CertIssuersIter::GotAsyncCerts(" << CertDebugString(cert())
|
| << ")";
|
| + DCHECK_EQ(pending_async_request_.get(), request);
|
| while (true) {
|
| scoped_refptr<ParsedCertificate> cert;
|
| CompletionStatus status = request->GetNext(&cert);
|
| if (!cert) {
|
| if (status == CompletionStatus::SYNC) {
|
| - // Request is exhausted, no more results pending from that
|
| + // Request is exhausted, no more results pending from the
|
| // CertIssuerSource.
|
| - DCHECK_GT(pending_async_results_, 0U);
|
| - pending_async_results_--;
|
| + pending_async_request_.reset();
|
| }
|
| break;
|
| }
|
| @@ -241,7 +218,7 @@ void CertIssuersIter::GotAsyncCerts(CertIssuerSource::Request* request) {
|
| << issuers_.size();
|
| *out_cert_ = std::move(issuers_[cur_++]);
|
| base::ResetAndReturn(&callback_).Run();
|
| - } else if (pending_async_results_ == 0) {
|
| + } else if (!pending_async_request_) {
|
| DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert())
|
| << "): async returning empty result";
|
| *out_cert_ = nullptr;
|
| @@ -333,8 +310,12 @@ class CertIssuerIterPath {
|
| // necessary.
|
| class CertPathIter {
|
| public:
|
| - CertPathIter(scoped_refptr<ParsedCertificate> cert,
|
| - const TrustStore* trust_store);
|
| + explicit CertPathIter(scoped_refptr<ParsedCertificate> cert);
|
| +
|
| + // Adds a TrustStore to check if certificates are trust anchors during path
|
| + // building. The |*trust_store| must remain valid for the lifetime of the
|
| + // CertPathIter.
|
| + void AddTrustStore(TrustStore* trust_store);
|
|
|
| // Adds a CertIssuerSource to provide intermediates for use in path building.
|
| // The |*cert_issuer_source| must remain valid for the lifetime of the
|
| @@ -353,6 +334,7 @@ class CertPathIter {
|
| STATE_NONE,
|
| STATE_GET_NEXT_ISSUER,
|
| STATE_GET_NEXT_ISSUER_COMPLETE,
|
| + STATE_IS_NEXT_CERT_TRUSTED_COMPLETE,
|
| STATE_RETURN_A_PATH,
|
| STATE_BACKTRACK,
|
| };
|
| @@ -360,10 +342,12 @@ class CertPathIter {
|
| CompletionStatus DoLoop(bool allow_async);
|
|
|
| CompletionStatus DoGetNextIssuer(bool allow_async);
|
| - CompletionStatus DoGetNextIssuerComplete();
|
| + CompletionStatus DoGetNextIssuerComplete(bool allow_async);
|
| + CompletionStatus DoIsNextCertTrustedComplete();
|
| CompletionStatus DoBackTrack();
|
|
|
| void HandleGotNextIssuer(void);
|
| + void HandleIsTrustedCertificate(bool is_trusted);
|
|
|
| // Stores the next candidate issuer certificate, until it is used during the
|
| // STATE_GET_NEXT_ISSUER_COMPLETE step.
|
| @@ -373,9 +357,14 @@ class CertPathIter {
|
| // 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.
|
| - const TrustStore* trust_store_;
|
| + CertIssuerSourceCollection cert_issuer_source_collection_;
|
| + // The TrustStores for checking if a path ends in a trust anchor.
|
| + TrustStoreCollection trust_store_collection_;
|
| + // Request handle for querying the TrustStoreCollection.
|
| + std::unique_ptr<TrustStore::Request> trust_request_;
|
| + // Stores the result of trust query for use in the
|
| + // STATE_IS_NEXT_CERT_TRUSTED_COMPLETE state.
|
| + bool is_pending_cert_trusted_;
|
| // The output variable for storing the next candidate path, which the client
|
| // passes in to GetNextPath. Only used for a single path output.
|
| ParsedCertificateList* out_path_;
|
| @@ -387,14 +376,17 @@ class CertPathIter {
|
| DISALLOW_COPY_AND_ASSIGN(CertPathIter);
|
| };
|
|
|
| -CertPathIter::CertPathIter(scoped_refptr<ParsedCertificate> cert,
|
| - const TrustStore* trust_store)
|
| +CertPathIter::CertPathIter(scoped_refptr<ParsedCertificate> cert)
|
| : next_cert_(std::move(cert)),
|
| - trust_store_(trust_store),
|
| next_state_(STATE_GET_NEXT_ISSUER_COMPLETE) {}
|
|
|
| +void CertPathIter::AddTrustStore(TrustStore* trust_store) {
|
| + trust_store_collection_.AddStore(trust_store);
|
| + cert_issuer_source_collection_.AddSource(trust_store);
|
| +}
|
| +
|
| void CertPathIter::AddCertIssuerSource(CertIssuerSource* cert_issuer_source) {
|
| - cert_issuer_sources_.push_back(cert_issuer_source);
|
| + cert_issuer_source_collection_.AddSource(cert_issuer_source);
|
| }
|
|
|
| CompletionStatus CertPathIter::GetNextPath(ParsedCertificateList* path,
|
| @@ -424,7 +416,10 @@ CompletionStatus CertPathIter::DoLoop(bool allow_async) {
|
| result = DoGetNextIssuer(allow_async);
|
| break;
|
| case STATE_GET_NEXT_ISSUER_COMPLETE:
|
| - result = DoGetNextIssuerComplete();
|
| + result = DoGetNextIssuerComplete(allow_async);
|
| + break;
|
| + case STATE_IS_NEXT_CERT_TRUSTED_COMPLETE:
|
| + result = DoIsNextCertTrustedComplete();
|
| break;
|
| case STATE_RETURN_A_PATH:
|
| // If the returned path did not verify, keep looking for other paths
|
| @@ -444,6 +439,12 @@ CompletionStatus CertPathIter::DoLoop(bool allow_async) {
|
| }
|
|
|
| CompletionStatus CertPathIter::DoGetNextIssuer(bool allow_async) {
|
| + if (cur_path_.Empty()) {
|
| + // If the target cert matched a trust root, but did not verify
|
| + // successfully, cur_path_ will be empty.
|
| + next_state_ = STATE_NONE;
|
| + return CompletionStatus::SYNC;
|
| + }
|
| next_state_ = STATE_GET_NEXT_ISSUER_COMPLETE;
|
| CompletionStatus rv = cur_path_.back()->GetNextIssuer(
|
| &next_cert_, allow_async ? base::Bind(&CertPathIter::HandleGotNextIssuer,
|
| @@ -452,32 +453,21 @@ CompletionStatus CertPathIter::DoGetNextIssuer(bool allow_async) {
|
| return rv;
|
| }
|
|
|
| -CompletionStatus CertPathIter::DoGetNextIssuerComplete() {
|
| +CompletionStatus CertPathIter::DoGetNextIssuerComplete(bool allow_async) {
|
| if (next_cert_) {
|
| // Skip this cert if it is already in the chain.
|
| if (cur_path_.IsPresent(next_cert_.get())) {
|
| next_state_ = STATE_GET_NEXT_ISSUER;
|
| return CompletionStatus::SYNC;
|
| }
|
| - // If the cert matches a trust root, this is a (possibly) complete path.
|
| - // Signal readiness. Don't add it to cur_path_, since that would cause an
|
| - // unnecessary lookup of issuers of the trust root.
|
| - if (trust_store_->IsTrustedCertificate(next_cert_.get())) {
|
| - DVLOG(1) << "CertPathIter IsTrustedCertificate("
|
| - << CertDebugString(next_cert_.get()) << ") = true";
|
| - next_state_ = STATE_RETURN_A_PATH;
|
| - cur_path_.CopyPath(out_path_);
|
| - out_path_->push_back(std::move(next_cert_));
|
| - next_cert_ = nullptr;
|
| - return CompletionStatus::SYNC;
|
| - }
|
| -
|
| - cur_path_.Append(base::WrapUnique(new CertIssuersIter(
|
| - std::move(next_cert_), &cert_issuer_sources_, *trust_store_)));
|
| - next_cert_ = nullptr;
|
| - DVLOG(1) << "CertPathIter cur_path_ = " << cur_path_.PathDebugString();
|
| - // Continue descending the tree.
|
| - next_state_ = STATE_GET_NEXT_ISSUER;
|
| + next_state_ = STATE_IS_NEXT_CERT_TRUSTED_COMPLETE;
|
| + trust_store_collection_.IsTrustedCertificate(
|
| + next_cert_.get(),
|
| + allow_async ? base::Bind(&CertPathIter::HandleIsTrustedCertificate,
|
| + base::Unretained(this))
|
| + : TrustStore::TrustCallback(),
|
| + &is_pending_cert_trusted_, &trust_request_);
|
| + return trust_request_ ? CompletionStatus::ASYNC : CompletionStatus::SYNC;
|
| } else {
|
| // 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
|
| @@ -489,6 +479,29 @@ CompletionStatus CertPathIter::DoGetNextIssuerComplete() {
|
| return CompletionStatus::SYNC;
|
| }
|
|
|
| +CompletionStatus CertPathIter::DoIsNextCertTrustedComplete() {
|
| + // If the cert matches a trust root, this is a (possibly) complete path.
|
| + // Signal readiness. Don't add it to cur_path_, since that would cause an
|
| + // unnecessary lookup of issuers of the trust root.
|
| + if (is_pending_cert_trusted_) {
|
| + DVLOG(1) << "CertPathIter IsTrustedCertificate("
|
| + << CertDebugString(next_cert_.get()) << ") = true";
|
| + next_state_ = STATE_RETURN_A_PATH;
|
| + cur_path_.CopyPath(out_path_);
|
| + out_path_->push_back(std::move(next_cert_));
|
| + next_cert_ = nullptr;
|
| + return CompletionStatus::SYNC;
|
| + }
|
| +
|
| + cur_path_.Append(base::WrapUnique(new CertIssuersIter(
|
| + std::move(next_cert_), &cert_issuer_source_collection_)));
|
| + next_cert_ = nullptr;
|
| + DVLOG(1) << "CertPathIter cur_path_ = " << cur_path_.PathDebugString();
|
| + // Continue descending the tree.
|
| + next_state_ = STATE_GET_NEXT_ISSUER;
|
| + return CompletionStatus::SYNC;
|
| +}
|
| +
|
| CompletionStatus CertPathIter::DoBackTrack() {
|
| DVLOG(1) << "CertPathIter backtracking...";
|
| cur_path_.Pop();
|
| @@ -512,18 +525,24 @@ void CertPathIter::HandleGotNextIssuer(void) {
|
| }
|
| }
|
|
|
| +void CertPathIter::HandleIsTrustedCertificate(bool is_trusted) {
|
| + DCHECK(!callback_.is_null());
|
| + is_pending_cert_trusted_ = is_trusted;
|
| + CompletionStatus rv = DoLoop(true /* allow_async */);
|
| + if (rv == CompletionStatus::SYNC)
|
| + base::ResetAndReturn(&callback_).Run();
|
| +}
|
| +
|
| CertPathBuilder::ResultPath::ResultPath() = default;
|
| CertPathBuilder::ResultPath::~ResultPath() = default;
|
| CertPathBuilder::Result::Result() = default;
|
| CertPathBuilder::Result::~Result() = default;
|
|
|
| CertPathBuilder::CertPathBuilder(scoped_refptr<ParsedCertificate> cert,
|
| - const TrustStore* trust_store,
|
| const SignaturePolicy* signature_policy,
|
| const der::GeneralizedTime& time,
|
| Result* result)
|
| - : cert_path_iter_(new CertPathIter(std::move(cert), trust_store)),
|
| - trust_store_(trust_store),
|
| + : cert_path_iter_(new CertPathIter(std::move(cert))),
|
| signature_policy_(signature_policy),
|
| time_(time),
|
| next_state_(STATE_NONE),
|
| @@ -531,6 +550,10 @@ CertPathBuilder::CertPathBuilder(scoped_refptr<ParsedCertificate> cert,
|
|
|
| CertPathBuilder::~CertPathBuilder() {}
|
|
|
| +void CertPathBuilder::AddTrustStore(TrustStore* trust_store) {
|
| + cert_path_iter_->AddTrustStore(trust_store);
|
| +}
|
| +
|
| void CertPathBuilder::AddCertIssuerSource(
|
| CertIssuerSource* cert_issuer_source) {
|
| cert_path_iter_->AddCertIssuerSource(cert_issuer_source);
|
| @@ -593,7 +616,7 @@ CompletionStatus CertPathBuilder::DoGetNextPathComplete() {
|
| }
|
|
|
| bool verify_result = VerifyCertificateChainAssumingTrustedRoot(
|
| - next_path_, *trust_store_, signature_policy_, time_);
|
| + next_path_, signature_policy_, time_);
|
| DVLOG(1) << "CertPathBuilder VerifyCertificateChain result = "
|
| << verify_result;
|
| AddResultPath(next_path_, verify_result);
|
|
|