OLD | NEW |
(Empty) | |
| 1 // Copyright 2016 The Chromium Authors. All rights reserved. |
| 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/path_builder.h" |
| 6 |
| 7 #include <set> |
| 8 #include <unordered_set> |
| 9 |
| 10 #include "base/callback_helpers.h" |
| 11 #include "base/memory/ptr_util.h" |
| 12 #include "net/base/net_errors.h" |
| 13 #include "net/cert/internal/cert_issuer_source.h" |
| 14 #include "net/cert/internal/parse_certificate.h" |
| 15 #include "net/cert/internal/parse_name.h" // For CertDebugString. |
| 16 #include "net/cert/internal/signature_policy.h" |
| 17 #include "net/cert/internal/trust_store.h" |
| 18 #include "net/cert/internal/verify_certificate_chain.h" |
| 19 #include "net/cert/internal/verify_name_match.h" |
| 20 #include "net/der/parser.h" |
| 21 #include "net/der/tag.h" |
| 22 |
| 23 namespace net { |
| 24 |
| 25 namespace { |
| 26 |
| 27 using CertIssuerSources = std::vector<CertIssuerSource*>; |
| 28 |
| 29 // TODO(mattm): decide how much debug logging to keep. |
| 30 std::string CertDebugString(const ParsedCertificate* cert) { |
| 31 RDNSequence subject, issuer; |
| 32 std::string subject_str, issuer_str; |
| 33 if (!ParseName(cert->tbs().subject_tlv, &subject) || |
| 34 !ConvertToRFC2253(subject, &subject_str)) |
| 35 subject_str = "???"; |
| 36 if (!ParseName(cert->tbs().issuer_tlv, &issuer) || |
| 37 !ConvertToRFC2253(issuer, &issuer_str)) |
| 38 issuer_str = "???"; |
| 39 |
| 40 return subject_str + "(" + issuer_str + ")"; |
| 41 } |
| 42 |
| 43 // CertIssuersIter iterates through the intermediates from |cert_issuer_sources| |
| 44 // which may be issuers of |cert|. |
| 45 class CertIssuersIter { |
| 46 public: |
| 47 // Constructs the CertIssuersIter. |*cert_issuer_sources| must be valid for |
| 48 // the lifetime of the CertIssuersIter. |
| 49 CertIssuersIter(scoped_refptr<ParsedCertificate> cert, |
| 50 CertIssuerSources* cert_issuer_sources, |
| 51 const TrustStore& trust_store); |
| 52 |
| 53 // Gets the next candidate issuer. If an issuer is ready synchronously, SYNC |
| 54 // is returned and the cert is stored in |*out_cert|. If an issuer is not |
| 55 // ready, ASYNC is returned and |callback| will be called once |*out_cert| has |
| 56 // been set. If |callback| is null, always completes synchronously. |
| 57 // |
| 58 // In either case, if all issuers have been exhausted, |*out_cert| is cleared. |
| 59 CompletionStatus GetNextIssuer(scoped_refptr<ParsedCertificate>* out_cert, |
| 60 const base::Closure& callback); |
| 61 |
| 62 // Returns the |cert| for which issuers are being retrieved. |
| 63 const ParsedCertificate* cert() const { return cert_.get(); } |
| 64 scoped_refptr<ParsedCertificate> reference_cert() const { return cert_; } |
| 65 |
| 66 private: |
| 67 void GotAsyncCerts(CertIssuerSource::Request* request); |
| 68 |
| 69 scoped_refptr<ParsedCertificate> cert_; |
| 70 CertIssuerSources* cert_issuer_sources_; |
| 71 |
| 72 // The list of issuers for |cert_|. This is added to incrementally (first |
| 73 // synchronous results, then possibly multiple times as asynchronous results |
| 74 // arrive.) The issuers may be re-sorted each time new issuers are added, but |
| 75 // only the results from |cur_| onwards should be sorted, since the earlier |
| 76 // results were already returned. |
| 77 // Elements should not be removed from |issuers_| once added, since |
| 78 // |present_issuers_| will point to data owned by the certs. |
| 79 ParsedCertificateList issuers_; |
| 80 // The index of the next cert in |issuers_| to return. |
| 81 size_t cur_ = 0; |
| 82 // Set of DER-encoded values for the certs in |issuers_|. Used to prevent |
| 83 // duplicates. This is based on the full DER of the cert to allow different |
| 84 // versions of the same certificate to be tried in different candidate paths. |
| 85 // This points to data owned by |issuers_|. |
| 86 std::unordered_set<base::StringPiece, base::StringPieceHash> present_issuers_; |
| 87 |
| 88 // Tracks whether asynchronous requests have been made yet. |
| 89 bool did_async_query_ = false; |
| 90 // If asynchronous requests were made, how many of them are still outstanding? |
| 91 size_t pending_async_results_; |
| 92 // Owns the Request objects for any asynchronous requests so that they will be |
| 93 // cancelled if CertIssuersIter is destroyed. |
| 94 std::vector<std::unique_ptr<CertIssuerSource::Request>> |
| 95 pending_async_requests_; |
| 96 |
| 97 // When GetNextIssuer was called and returned asynchronously, |*out_cert_| is |
| 98 // where the result will be stored, and |callback_| will be run when the |
| 99 // result is ready. |
| 100 scoped_refptr<ParsedCertificate>* out_cert_; |
| 101 base::Closure callback_; |
| 102 |
| 103 DISALLOW_COPY_AND_ASSIGN(CertIssuersIter); |
| 104 }; |
| 105 |
| 106 CertIssuersIter::CertIssuersIter(scoped_refptr<ParsedCertificate> in_cert, |
| 107 CertIssuerSources* cert_issuer_sources, |
| 108 const TrustStore& trust_store) |
| 109 : cert_(in_cert), cert_issuer_sources_(cert_issuer_sources) { |
| 110 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) << ") created"; |
| 111 trust_store.FindTrustAnchorsByNormalizedName(in_cert->normalized_issuer(), |
| 112 &issuers_); |
| 113 // Insert matching roots into |present_issuers_| in case they also are |
| 114 // returned by a CertIssuerSource. It is assumed |
| 115 // FindTrustAnchorsByNormalizedName does not itself return dupes. |
| 116 for (const auto& root : issuers_) |
| 117 present_issuers_.insert(root->der_cert().AsStringPiece()); |
| 118 |
| 119 for (auto* cert_issuer_source : *cert_issuer_sources_) { |
| 120 ParsedCertificateList new_issuers; |
| 121 cert_issuer_source->SyncGetIssuersOf(cert(), &new_issuers); |
| 122 for (scoped_refptr<ParsedCertificate>& issuer : new_issuers) { |
| 123 if (present_issuers_.find(issuer->der_cert().AsStringPiece()) != |
| 124 present_issuers_.end()) |
| 125 continue; |
| 126 present_issuers_.insert(issuer->der_cert().AsStringPiece()); |
| 127 issuers_.push_back(std::move(issuer)); |
| 128 } |
| 129 } |
| 130 // TODO(mattm): sort by notbefore, etc (eg if cert issuer matches a trust |
| 131 // anchor subject (or is a trust anchor), that should be sorted higher too. |
| 132 // See big list of possible sorting hints in RFC 4158.) |
| 133 // (Update PathBuilderKeyRolloverTest.TestRolloverBothRootsTrusted once that |
| 134 // is done) |
| 135 } |
| 136 |
| 137 CompletionStatus CertIssuersIter::GetNextIssuer( |
| 138 scoped_refptr<ParsedCertificate>* out_cert, |
| 139 const base::Closure& callback) { |
| 140 // Should not be called again while already waiting for an async result. |
| 141 DCHECK(callback_.is_null()); |
| 142 |
| 143 if (cur_ < issuers_.size()) { |
| 144 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) |
| 145 << "): returning item " << cur_ << " of " << issuers_.size(); |
| 146 // Still have issuers that haven't been returned yet, return one of them. |
| 147 // A reference to the returned issuer is retained, since |present_issuers_| |
| 148 // points to data owned by it. |
| 149 *out_cert = issuers_[cur_++]; |
| 150 return CompletionStatus::SYNC; |
| 151 } |
| 152 if (did_async_query_) { |
| 153 if (pending_async_results_ == 0) { |
| 154 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) |
| 155 << ") Reached the end of all available issuers."; |
| 156 // Reached the end of all available issuers. |
| 157 *out_cert = nullptr; |
| 158 return CompletionStatus::SYNC; |
| 159 } |
| 160 |
| 161 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) |
| 162 << ") Still waiting for async results from other " |
| 163 "CertIssuerSources."; |
| 164 // Still waiting for async results from other CertIssuerSources. |
| 165 out_cert_ = out_cert; |
| 166 callback_ = callback; |
| 167 return CompletionStatus::ASYNC; |
| 168 } |
| 169 // Reached the end of synchronously gathered issuers. |
| 170 |
| 171 if (callback.is_null()) { |
| 172 // Synchronous-only mode, don't try to query async sources. |
| 173 *out_cert = nullptr; |
| 174 return CompletionStatus::SYNC; |
| 175 } |
| 176 |
| 177 // Now issue request(s) for async ones (AIA, etc). |
| 178 did_async_query_ = true; |
| 179 pending_async_results_ = 0; |
| 180 for (auto* cert_issuer_source : *cert_issuer_sources_) { |
| 181 std::unique_ptr<CertIssuerSource::Request> request; |
| 182 cert_issuer_source->AsyncGetIssuersOf( |
| 183 cert(), |
| 184 base::Bind(&CertIssuersIter::GotAsyncCerts, base::Unretained(this)), |
| 185 &request); |
| 186 if (request) { |
| 187 DVLOG(1) << "AsyncGetIssuersOf(" << CertDebugString(cert()) |
| 188 << ") pending..."; |
| 189 pending_async_results_++; |
| 190 pending_async_requests_.push_back(std::move(request)); |
| 191 } |
| 192 } |
| 193 |
| 194 if (pending_async_results_ == 0) { |
| 195 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) |
| 196 << ") No cert sources have async results."; |
| 197 // No cert sources have async results. |
| 198 *out_cert = nullptr; |
| 199 return CompletionStatus::SYNC; |
| 200 } |
| 201 |
| 202 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) |
| 203 << ") issued AsyncGetIssuersOf call(s) (n=" << pending_async_results_ |
| 204 << ")"; |
| 205 out_cert_ = out_cert; |
| 206 callback_ = callback; |
| 207 return CompletionStatus::ASYNC; |
| 208 } |
| 209 |
| 210 void CertIssuersIter::GotAsyncCerts(CertIssuerSource::Request* request) { |
| 211 DVLOG(1) << "CertIssuersIter::GotAsyncCerts(" << CertDebugString(cert()) |
| 212 << ")"; |
| 213 while (true) { |
| 214 scoped_refptr<ParsedCertificate> cert; |
| 215 CompletionStatus status = request->GetNext(&cert); |
| 216 if (!cert) { |
| 217 if (status == CompletionStatus::SYNC) { |
| 218 // Request is exhausted, no more results pending from that |
| 219 // CertIssuerSource. |
| 220 DCHECK_GT(pending_async_results_, 0U); |
| 221 pending_async_results_--; |
| 222 } |
| 223 break; |
| 224 } |
| 225 DCHECK_EQ(status, CompletionStatus::SYNC); |
| 226 if (present_issuers_.find(cert->der_cert().AsStringPiece()) != |
| 227 present_issuers_.end()) |
| 228 continue; |
| 229 present_issuers_.insert(cert->der_cert().AsStringPiece()); |
| 230 issuers_.push_back(std::move(cert)); |
| 231 } |
| 232 |
| 233 // TODO(mattm): re-sort remaining elements of issuers_ (remaining elements may |
| 234 // be more than the ones just inserted, depending on |cur_| value). |
| 235 |
| 236 // Notify that more results are available, if necessary. |
| 237 if (!callback_.is_null()) { |
| 238 if (cur_ < issuers_.size()) { |
| 239 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) |
| 240 << "): async returning item " << cur_ << " of " |
| 241 << issuers_.size(); |
| 242 *out_cert_ = std::move(issuers_[cur_++]); |
| 243 base::ResetAndReturn(&callback_).Run(); |
| 244 } else if (pending_async_results_ == 0) { |
| 245 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) |
| 246 << "): async returning empty result"; |
| 247 *out_cert_ = nullptr; |
| 248 base::ResetAndReturn(&callback_).Run(); |
| 249 } else { |
| 250 DVLOG(1) << "CertIssuersIter(" << CertDebugString(cert()) |
| 251 << "): empty result, but other async results " |
| 252 "pending, waiting.."; |
| 253 } |
| 254 } |
| 255 } |
| 256 |
| 257 // CertIssuerIterPath tracks which certs are present in the path and prevents |
| 258 // paths from being built which repeat any certs (including different versions |
| 259 // of the same cert, based on Subject+SubjectAltName+SPKI). |
| 260 class CertIssuerIterPath { |
| 261 public: |
| 262 // Returns true if |cert| is already present in the path. |
| 263 bool IsPresent(const ParsedCertificate* cert) const { |
| 264 return present_certs_.find(GetKey(cert)) != present_certs_.end(); |
| 265 } |
| 266 |
| 267 // Appends |cert_issuers_iter| to the path. The cert referred to by |
| 268 // |cert_issuers_iter| must not be present in the path already. |
| 269 void Append(std::unique_ptr<CertIssuersIter> cert_issuers_iter) { |
| 270 bool added = |
| 271 present_certs_.insert(GetKey(cert_issuers_iter->cert())).second; |
| 272 DCHECK(added); |
| 273 cur_path_.push_back(std::move(cert_issuers_iter)); |
| 274 } |
| 275 |
| 276 // Pops the last CertIssuersIter off the path. |
| 277 void Pop() { |
| 278 size_t num_erased = present_certs_.erase(GetKey(cur_path_.back()->cert())); |
| 279 DCHECK_EQ(num_erased, 1U); |
| 280 cur_path_.pop_back(); |
| 281 } |
| 282 |
| 283 // Copies the ParsedCertificate elements of the current path to |*out_path|. |
| 284 void CopyPath(ParsedCertificateList* out_path) { |
| 285 out_path->clear(); |
| 286 for (const auto& node : cur_path_) |
| 287 out_path->push_back(node->reference_cert()); |
| 288 } |
| 289 |
| 290 // Returns true if the path is empty. |
| 291 bool Empty() const { return cur_path_.empty(); } |
| 292 |
| 293 // Returns the last CertIssuersIter in the path. |
| 294 CertIssuersIter* back() { return cur_path_.back().get(); } |
| 295 |
| 296 std::string PathDebugString() { |
| 297 std::string s; |
| 298 for (const auto& node : cur_path_) { |
| 299 if (!s.empty()) |
| 300 s += " <- "; |
| 301 s += CertDebugString(node->cert()); |
| 302 } |
| 303 return s; |
| 304 } |
| 305 |
| 306 private: |
| 307 using Key = |
| 308 std::tuple<base::StringPiece, base::StringPiece, base::StringPiece>; |
| 309 |
| 310 static Key GetKey(const ParsedCertificate* cert) { |
| 311 // TODO(mattm): ideally this would use a normalized version of |
| 312 // SubjectAltName, but it's not that important just for LoopChecker. |
| 313 // |
| 314 // Note that subject_alt_names_extension().value will be empty if the cert |
| 315 // had no SubjectAltName extension, so there is no need for a condition on |
| 316 // has_subject_alt_names(). |
| 317 return Key(cert->normalized_subject().AsStringPiece(), |
| 318 cert->subject_alt_names_extension().value.AsStringPiece(), |
| 319 cert->tbs().spki_tlv.AsStringPiece()); |
| 320 } |
| 321 |
| 322 std::vector<std::unique_ptr<CertIssuersIter>> cur_path_; |
| 323 |
| 324 // This refers to data owned by |cur_path_|. |
| 325 // TODO(mattm): use unordered_set. Requires making a hash function for Key. |
| 326 std::set<Key> present_certs_; |
| 327 }; |
| 328 |
| 329 } // namespace |
| 330 |
| 331 // CertPathIter generates possible paths from |cert| to a trust anchor in |
| 332 // |trust_store|, using intermediates from the |cert_issuer_source| objects if |
| 333 // necessary. |
| 334 class CertPathIter { |
| 335 public: |
| 336 CertPathIter(scoped_refptr<ParsedCertificate> cert, |
| 337 const TrustStore* trust_store); |
| 338 |
| 339 // Adds a CertIssuerSource to provide intermediates for use in path building. |
| 340 // The |*cert_issuer_source| must remain valid for the lifetime of the |
| 341 // CertPathIter. |
| 342 void AddCertIssuerSource(CertIssuerSource* cert_issuer_source); |
| 343 |
| 344 // Gets the next candidate path. If a path is ready synchronously, SYNC is |
| 345 // returned and the path is stored in |*path|. If a path is not ready, |
| 346 // ASYNC is returned and |callback| will be called once |*path| has been set. |
| 347 // In either case, if all paths have been exhausted, |*path| is cleared. |
| 348 CompletionStatus GetNextPath(ParsedCertificateList* path, |
| 349 const base::Closure& callback); |
| 350 |
| 351 private: |
| 352 enum State { |
| 353 STATE_NONE, |
| 354 STATE_GET_NEXT_ISSUER, |
| 355 STATE_GET_NEXT_ISSUER_COMPLETE, |
| 356 STATE_RETURN_A_PATH, |
| 357 STATE_BACKTRACK, |
| 358 }; |
| 359 |
| 360 CompletionStatus DoLoop(bool allow_async); |
| 361 |
| 362 CompletionStatus DoGetNextIssuer(bool allow_async); |
| 363 CompletionStatus DoGetNextIssuerComplete(); |
| 364 CompletionStatus DoBackTrack(); |
| 365 |
| 366 void HandleGotNextIssuer(void); |
| 367 |
| 368 // Stores the next candidate issuer certificate, until it is used during the |
| 369 // STATE_GET_NEXT_ISSUER_COMPLETE step. |
| 370 scoped_refptr<ParsedCertificate> next_cert_; |
| 371 // The current path being explored, made up of CertIssuerIters. Each node |
| 372 // keeps track of the state of searching for issuers of that cert, so that |
| 373 // when backtracking it can resume the search where it left off. |
| 374 CertIssuerIterPath cur_path_; |
| 375 // The CertIssuerSources for retrieving candidate issuers. |
| 376 CertIssuerSources cert_issuer_sources_; |
| 377 // The TrustStore for checking if a path ends in a trust anchor. |
| 378 const TrustStore* trust_store_; |
| 379 // The output variable for storing the next candidate path, which the client |
| 380 // passes in to GetNextPath. Only used for a single path output. |
| 381 ParsedCertificateList* out_path_; |
| 382 // The callback to be called if an async lookup generated a candidate path. |
| 383 base::Closure callback_; |
| 384 // Current state of the state machine. |
| 385 State next_state_; |
| 386 |
| 387 DISALLOW_COPY_AND_ASSIGN(CertPathIter); |
| 388 }; |
| 389 |
| 390 CertPathIter::CertPathIter(scoped_refptr<ParsedCertificate> cert, |
| 391 const TrustStore* trust_store) |
| 392 : next_cert_(std::move(cert)), |
| 393 trust_store_(trust_store), |
| 394 next_state_(STATE_GET_NEXT_ISSUER_COMPLETE) {} |
| 395 |
| 396 void CertPathIter::AddCertIssuerSource(CertIssuerSource* cert_issuer_source) { |
| 397 cert_issuer_sources_.push_back(cert_issuer_source); |
| 398 } |
| 399 |
| 400 CompletionStatus CertPathIter::GetNextPath(ParsedCertificateList* path, |
| 401 const base::Closure& callback) { |
| 402 out_path_ = path; |
| 403 out_path_->clear(); |
| 404 CompletionStatus rv = DoLoop(!callback.is_null()); |
| 405 if (rv == CompletionStatus::ASYNC) { |
| 406 callback_ = callback; |
| 407 } else { |
| 408 // Clear the reference to the output parameter as a precaution. |
| 409 out_path_ = nullptr; |
| 410 } |
| 411 return rv; |
| 412 } |
| 413 |
| 414 CompletionStatus CertPathIter::DoLoop(bool allow_async) { |
| 415 CompletionStatus result = CompletionStatus::SYNC; |
| 416 do { |
| 417 State state = next_state_; |
| 418 next_state_ = STATE_NONE; |
| 419 switch (state) { |
| 420 case STATE_NONE: |
| 421 NOTREACHED(); |
| 422 break; |
| 423 case STATE_GET_NEXT_ISSUER: |
| 424 result = DoGetNextIssuer(allow_async); |
| 425 break; |
| 426 case STATE_GET_NEXT_ISSUER_COMPLETE: |
| 427 result = DoGetNextIssuerComplete(); |
| 428 break; |
| 429 case STATE_RETURN_A_PATH: |
| 430 // If the returned path did not verify, keep looking for other paths |
| 431 // (the trust root is not part of cur_path_, so don't need to |
| 432 // backtrack). |
| 433 next_state_ = STATE_GET_NEXT_ISSUER; |
| 434 result = CompletionStatus::SYNC; |
| 435 break; |
| 436 case STATE_BACKTRACK: |
| 437 result = DoBackTrack(); |
| 438 break; |
| 439 } |
| 440 } while (result == CompletionStatus::SYNC && next_state_ != STATE_NONE && |
| 441 next_state_ != STATE_RETURN_A_PATH); |
| 442 |
| 443 return result; |
| 444 } |
| 445 |
| 446 CompletionStatus CertPathIter::DoGetNextIssuer(bool allow_async) { |
| 447 next_state_ = STATE_GET_NEXT_ISSUER_COMPLETE; |
| 448 CompletionStatus rv = cur_path_.back()->GetNextIssuer( |
| 449 &next_cert_, allow_async ? base::Bind(&CertPathIter::HandleGotNextIssuer, |
| 450 base::Unretained(this)) |
| 451 : base::Closure()); |
| 452 return rv; |
| 453 } |
| 454 |
| 455 CompletionStatus CertPathIter::DoGetNextIssuerComplete() { |
| 456 if (next_cert_) { |
| 457 // Skip this cert if it is already in the chain. |
| 458 if (cur_path_.IsPresent(next_cert_.get())) { |
| 459 next_state_ = STATE_GET_NEXT_ISSUER; |
| 460 return CompletionStatus::SYNC; |
| 461 } |
| 462 // If the cert matches a trust root, this is a (possibly) complete path. |
| 463 // Signal readiness. Don't add it to cur_path_, since that would cause an |
| 464 // unnecessary lookup of issuers of the trust root. |
| 465 if (trust_store_->IsTrustedCertificate(next_cert_.get())) { |
| 466 DVLOG(1) << "CertPathIter IsTrustedCertificate(" |
| 467 << CertDebugString(next_cert_.get()) << ") = true"; |
| 468 next_state_ = STATE_RETURN_A_PATH; |
| 469 cur_path_.CopyPath(out_path_); |
| 470 out_path_->push_back(std::move(next_cert_)); |
| 471 next_cert_ = nullptr; |
| 472 return CompletionStatus::SYNC; |
| 473 } |
| 474 |
| 475 cur_path_.Append(base::WrapUnique(new CertIssuersIter( |
| 476 std::move(next_cert_), &cert_issuer_sources_, *trust_store_))); |
| 477 next_cert_ = nullptr; |
| 478 DVLOG(1) << "CertPathIter cur_path_ = " << cur_path_.PathDebugString(); |
| 479 // Continue descending the tree. |
| 480 next_state_ = STATE_GET_NEXT_ISSUER; |
| 481 } else { |
| 482 // TODO(mattm): should also include such paths in CertPathBuilder::Result, |
| 483 // maybe with a flag to enable it. Or use a visitor pattern so the caller |
| 484 // can decide what to do with any failed paths. |
| 485 // No more issuers for current chain, go back up and see if there are any |
| 486 // more for the previous cert. |
| 487 next_state_ = STATE_BACKTRACK; |
| 488 } |
| 489 return CompletionStatus::SYNC; |
| 490 } |
| 491 |
| 492 CompletionStatus CertPathIter::DoBackTrack() { |
| 493 DVLOG(1) << "CertPathIter backtracking..."; |
| 494 cur_path_.Pop(); |
| 495 if (cur_path_.Empty()) { |
| 496 // Exhausted all paths. |
| 497 next_state_ = STATE_NONE; |
| 498 } else { |
| 499 // Continue exploring issuers of the previous path. |
| 500 next_state_ = STATE_GET_NEXT_ISSUER; |
| 501 } |
| 502 return CompletionStatus::SYNC; |
| 503 } |
| 504 |
| 505 void CertPathIter::HandleGotNextIssuer(void) { |
| 506 DCHECK(!callback_.is_null()); |
| 507 CompletionStatus rv = DoLoop(true /* allow_async */); |
| 508 if (rv == CompletionStatus::SYNC) { |
| 509 // Clear the reference to the output parameter as a precaution. |
| 510 out_path_ = nullptr; |
| 511 base::ResetAndReturn(&callback_).Run(); |
| 512 } |
| 513 } |
| 514 |
| 515 CertPathBuilder::ResultPath::ResultPath() = default; |
| 516 CertPathBuilder::ResultPath::~ResultPath() = default; |
| 517 CertPathBuilder::Result::Result() = default; |
| 518 CertPathBuilder::Result::~Result() = default; |
| 519 |
| 520 CertPathBuilder::CertPathBuilder(scoped_refptr<ParsedCertificate> cert, |
| 521 const TrustStore* trust_store, |
| 522 const SignaturePolicy* signature_policy, |
| 523 const der::GeneralizedTime& time, |
| 524 Result* result) |
| 525 : cert_path_iter_(new CertPathIter(std::move(cert), trust_store)), |
| 526 trust_store_(trust_store), |
| 527 signature_policy_(signature_policy), |
| 528 time_(time), |
| 529 next_state_(STATE_NONE), |
| 530 out_result_(result) {} |
| 531 |
| 532 CertPathBuilder::~CertPathBuilder() {} |
| 533 |
| 534 void CertPathBuilder::AddCertIssuerSource( |
| 535 CertIssuerSource* cert_issuer_source) { |
| 536 cert_path_iter_->AddCertIssuerSource(cert_issuer_source); |
| 537 } |
| 538 |
| 539 CompletionStatus CertPathBuilder::Run(const base::Closure& callback) { |
| 540 DCHECK_EQ(STATE_NONE, next_state_); |
| 541 next_state_ = STATE_GET_NEXT_PATH; |
| 542 CompletionStatus rv = DoLoop(!callback.is_null()); |
| 543 |
| 544 if (rv == CompletionStatus::ASYNC) |
| 545 callback_ = callback; |
| 546 |
| 547 return rv; |
| 548 } |
| 549 |
| 550 CompletionStatus CertPathBuilder::DoLoop(bool allow_async) { |
| 551 CompletionStatus result = CompletionStatus::SYNC; |
| 552 |
| 553 do { |
| 554 State state = next_state_; |
| 555 next_state_ = STATE_NONE; |
| 556 switch (state) { |
| 557 case STATE_NONE: |
| 558 NOTREACHED(); |
| 559 break; |
| 560 case STATE_GET_NEXT_PATH: |
| 561 result = DoGetNextPath(allow_async); |
| 562 break; |
| 563 case STATE_GET_NEXT_PATH_COMPLETE: |
| 564 result = DoGetNextPathComplete(); |
| 565 break; |
| 566 } |
| 567 } while (result == CompletionStatus::SYNC && next_state_ != STATE_NONE); |
| 568 |
| 569 return result; |
| 570 } |
| 571 |
| 572 CompletionStatus CertPathBuilder::DoGetNextPath(bool allow_async) { |
| 573 next_state_ = STATE_GET_NEXT_PATH_COMPLETE; |
| 574 CompletionStatus rv = cert_path_iter_->GetNextPath( |
| 575 &next_path_, allow_async ? base::Bind(&CertPathBuilder::HandleGotNextPath, |
| 576 base::Unretained(this)) |
| 577 : base::Closure()); |
| 578 return rv; |
| 579 } |
| 580 |
| 581 void CertPathBuilder::HandleGotNextPath() { |
| 582 DCHECK(!callback_.is_null()); |
| 583 CompletionStatus rv = DoLoop(true /* allow_async */); |
| 584 if (rv == CompletionStatus::SYNC) |
| 585 base::ResetAndReturn(&callback_).Run(); |
| 586 } |
| 587 |
| 588 CompletionStatus CertPathBuilder::DoGetNextPathComplete() { |
| 589 if (next_path_.empty()) { |
| 590 // No more paths to check, signal completion. |
| 591 next_state_ = STATE_NONE; |
| 592 return CompletionStatus::SYNC; |
| 593 } |
| 594 |
| 595 bool verify_result = VerifyCertificateChainAssumingTrustedRoot( |
| 596 next_path_, *trust_store_, signature_policy_, time_); |
| 597 DVLOG(1) << "CertPathBuilder VerifyCertificateChain result = " |
| 598 << verify_result; |
| 599 AddResultPath(next_path_, verify_result); |
| 600 |
| 601 if (verify_result) { |
| 602 // Found a valid path, return immediately. |
| 603 // TODO(mattm): add debug/test mode that tries all possible paths. |
| 604 next_state_ = STATE_NONE; |
| 605 return CompletionStatus::SYNC; |
| 606 } |
| 607 |
| 608 // Path did not verify. Try more paths. If there are no more paths, the result |
| 609 // will be returned next time DoGetNextPathComplete is called with next_path_ |
| 610 // empty. |
| 611 next_state_ = STATE_GET_NEXT_PATH; |
| 612 return CompletionStatus::SYNC; |
| 613 } |
| 614 |
| 615 void CertPathBuilder::AddResultPath(const ParsedCertificateList& path, |
| 616 bool is_success) { |
| 617 std::unique_ptr<ResultPath> result_path(new ResultPath()); |
| 618 // TODO(mattm): better error reporting. |
| 619 result_path->error = is_success ? OK : ERR_CERT_AUTHORITY_INVALID; |
| 620 // TODO(mattm): set best_result_index based on number or severity of errors. |
| 621 if (result_path->error == OK) |
| 622 out_result_->best_result_index = out_result_->paths.size(); |
| 623 // TODO(mattm): add flag to only return a single path or all attempted paths? |
| 624 result_path->path = path; |
| 625 out_result_->paths.push_back(std::move(result_path)); |
| 626 } |
| 627 |
| 628 } // namespace net |
OLD | NEW |