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