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 |