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