Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(507)

Side by Side Diff: net/cert/internal/path_builder.cc

Issue 1923433002: Certificate path builder for new certificate verification library (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: . Created 4 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(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 <unordered_set>
8
9 #include "base/callback_helpers.h"
10 #include "base/memory/ptr_util.h"
11 #include "net/base/net_errors.h"
12 #include "net/cert/internal/parse_certificate.h"
13 #include "net/cert/internal/parse_name.h" // XXX for DumpPath. Remove.
14 #include "net/cert/internal/verify_name_match.h"
15 #include "net/der/parser.h"
16 #include "net/der/tag.h"
17
18 namespace net {
19
20 namespace {
21
22 // LoopChecker tracks which certs are present in the path and prevents paths
23 // from being built which repeat any certs.
24 class LoopChecker {
25 public:
26 // Attempts to add |cert| to the list of certs to check. If it has not already
27 // been added, true is returned and |cert| is added, such that any future
28 // attempts will return false.
29 // The |cert| data is not copied, Remove must be called before the |cert| data
30 // is destroyed.
31 bool Insert(const CertThing& cert) {
32 if (present_certs_.find(cert.der_cert().AsStringPiece()) !=
33 present_certs_.end())
34 return false;
35 present_certs_.insert(cert.der_cert().AsStringPiece());
36 return true;
37 }
38 // Removes a cert from being disallowed.
39 void Remove(const CertThing& cert) {
40 present_certs_.erase(cert.der_cert().AsStringPiece());
41 }
42
43 private:
44 // TODO: this should be keyed on Name+SAN+SPKI. (Update
45 // PathBuilderKeyRolloverTest.TestRolloverLongChain once that is done)
46 std::unordered_set<base::StringPiece, base::StringPieceHash> present_certs_;
47 };
48
49 // CertIssuersIter iterates through the intermediates from |cert_sources| which
50 // may be issuers of |cert|.
51 // TODO: It should return the issuers in order most likely to succeed.
52 class CertIssuersIter {
53 public:
54 // Constructs the CertIssuersIter. Takes ownership of |cert|. |*cert_sources|
55 // must be valid for the lifetime of the CertIssuersIter.
56 CertIssuersIter(std::unique_ptr<CertThing> cert,
57 CertPathBuilder::CertSources* cert_sources);
58
59 // Gets the next candidate issuer for |cert_|. If one is not ready
60 // synchronously, the return value ERR_IO_PENDING is returned and |callback|
61 // will be called once an issuer is ready.
62 // In either case, the next issuer will be set in |*out_cert|, or if there are
63 // no
64 // more issuers available, |*out_cert| will be cleared.
65 // and it will have been stored in |*out_cert|
66 // it. If an issuer is ready, OK is returned and the cert is stored in
67 // |*out_cert|.
68 // If all issuers have been exhausted, OK is returned and |*out_cert| is
69 // cleared.
70 int GetNextIssuer(std::unique_ptr<CertThing>* out_cert,
71 const base::Closure& callback);
72
73 // Returns the |cert| for which issuers are being retrieved.
74 const CertThing& cert() const { return *cert_; }
75
76 private:
77 void GotAsyncCerts(CertVector certs);
78
79 std::unique_ptr<CertThing> cert_;
80 CertPathBuilder::CertSources* cert_sources_;
81
82 // The list of issuers for |cert_|. This is added to incrementally (first
83 // synchronous, results, then possibly multiple times as asynchronous results
84 // arrive.) The issuers may be re-sorted each time new issuers are added, but
85 // only the results from |cur_| onwards should be sorted, since the earlier
86 // results were already returned.
87 CertVector issuers_;
88 // The index of the next cert in |issuers_| to return.
89 size_t cur_ = 0;
90
91 // Tracks whether asynchronous requests have been made yet.
92 bool did_async_query_ = false;
93 // If asynchronous requests were made, how many of them are still outstanding?
94 int pending_async_results_;
95 // Owns the Request objects for any asynchronous requests so that they will be
96 // cancelled if CertIssuersIter is destroyed.
97 std::vector<std::unique_ptr<CertSource::Request>> pending_async_requests_;
98
99 // When GetNextIssuer was called and returned asynchronously, |out_cert_| is
100 // where the result will be stored, and |callback_| will be run when the
101 // result is ready.
102 std::unique_ptr<CertThing>* out_cert_;
103 base::Closure callback_;
104
105 DISALLOW_COPY_AND_ASSIGN(CertIssuersIter);
106 };
107
108 CertIssuersIter::CertIssuersIter(std::unique_ptr<CertThing> in_cert,
109 CertPathBuilder::CertSources* cert_sources)
110 : cert_(std::move(in_cert)), cert_sources_(cert_sources) {
111 for (auto* cert_source : *cert_sources_) {
112 if (!cert_source->SyncGetIssuersOf(cert(), &issuers_))
113 LOG(ERROR) << "SyncGetIssuersOf error"; // XXX what to do with errors
114 // here? Change SyncGetIssuersOf
115 // to never fail now that it
116 // doesn't do parsing?
117 }
118 // TODO: sort by notbefore, etc (eg if cert issuer matches a trust anchor
119 // subject, that should be sorted higher too. See big list of possible sorting
120 // hints in RFC 4158.)
121 // (Update PathBuilderKeyRolloverTest.TestRolloverBothRootsTrusted once that
122 // is done)
123 }
124
125 int CertIssuersIter::GetNextIssuer(std::unique_ptr<CertThing>* out_cert,
126 const base::Closure& callback) {
127 // Should not be called again while already waiting for an async result.
128 DCHECK(callback_.is_null());
129
130 if (cur_ < issuers_.size()) {
131 DVLOG(1) << "CertIssuersIter: returning item " << cur_ << " of "
132 << issuers_.size();
133 // Still have issuers that haven't been returned yet, return one of them.
134 *out_cert = std::move(issuers_[cur_++]);
135 return OK;
136 }
137 if (did_async_query_) {
138 if (pending_async_results_ == 0) {
139 DVLOG(1) << "CertIssuersIter Reached the end of all available issuers.";
140 // Reached the end of all available issuers.
141 out_cert->reset();
142 return OK;
143 }
144
145 DVLOG(1) << "CertIssuersIter Still waiting for async results from other "
146 "CertSources.";
147 // Still waiting for async results from other CertSources.
148 out_cert_ = out_cert;
149 callback_ = callback;
150 return ERR_IO_PENDING;
151 }
152
153 // Reached the end of synchronously gathered issuers, now issue request(s) for
154 // async ones (AIA, etc).
155
156 did_async_query_ = true;
157 pending_async_results_ = 0;
158 for (auto* cert_source : *cert_sources_) {
159 std::unique_ptr<CertSource::Request> request;
160 int rv = cert_source->AsyncGetIssuersOf(
161 cert(),
162 base::Bind(&CertIssuersIter::GotAsyncCerts, base::Unretained(this)),
163 &request);
164 DVLOG(1) << "AsyncGetIssuersOf v=" << rv;
165 // XXX any other error handling here?
166 if (rv == ERR_IO_PENDING)
167 pending_async_results_++;
168 if (request)
169 pending_async_requests_.push_back(std::move(request));
170 }
171
172 if (pending_async_results_ == 0) {
173 DVLOG(1) << "CertIssuersIter No cert sources have async results.";
174 // No cert sources have async results.
175 out_cert->reset();
176 return OK;
177 }
178
179 DVLOG(1) << "CertIssuersIter issued AsyncGetIssuersOf call(s) (n="
180 << pending_async_results_ << ")";
181 out_cert_ = out_cert;
182 callback_ = callback;
183 return ERR_IO_PENDING;
184 }
185
186 void CertIssuersIter::GotAsyncCerts(CertVector certs) {
187 DVLOG(1) << "CertIssuersIter::GotAsyncCerts n=" << certs.size();
188 pending_async_results_--;
189 for (auto& c : certs)
190 issuers_.push_back(std::move(c));
191
192 // TODO: re-sort remaining elements of issuers_ (remaining elements may be
193 // more than the ones just inserted, depending on |cur_| value).
194
195 // Notify that more results are available, if necessary.
196 if (!callback_.is_null()) {
197 if (cur_ < issuers_.size()) {
198 DVLOG(1) << "CertIssuersIter: async returning item " << cur_ << " of "
199 << issuers_.size();
200 *out_cert_ = std::move(issuers_[cur_++]);
201 base::ResetAndReturn(&callback_).Run();
202 } else if (pending_async_results_ == 0) {
203 DVLOG(1) << "CertIssuersIter: async returning empty result";
204 out_cert_->reset();
205 base::ResetAndReturn(&callback_).Run();
206 } else {
207 DVLOG(1) << "CertIssuersIter: empty result, but other async results "
208 "pending, waiting..";
209 }
210 }
211 }
212
213 } // namespace
214
215 StaticCertsSource::StaticCertsSource() {}
216 StaticCertsSource::~StaticCertsSource() {}
217
218 bool StaticCertsSource::Init(std::vector<der::Input> certs) {
219 for (auto cert_der : certs) {
220 std::unique_ptr<CertThing> cert(CertThing::CreateFromCertificateData(
221 cert_der.UnsafeData(), cert_der.Length(),
222 CertThing::DataSource::EXTERNAL_REFERENCE));
223 if (!cert)
224 return false;
225 intermediates_.insert(
226 std::make_pair(cert->normalized_subject(), std::move(cert)));
227 }
228 return true;
229 }
230
231 bool StaticCertsSource::SyncGetIssuersOf(const CertThing& cert,
232 CertVector* issuers) {
233 auto range = intermediates_.equal_range(cert.normalized_issuer());
234 for (auto it = range.first; it != range.second; ++it)
235 issuers->push_back(it->second->Clone());
236 return true;
237 }
238
239 int StaticCertsSource::AsyncGetIssuersOf(const CertThing& cert,
240 const IssuerCallback& issuers_callback,
241 std::unique_ptr<Request>* out_req) {
242 // StaticCertsSource never returns asynchronous results.
243 return OK;
244 }
245
246 // CertPathIter generates possible paths from |cert| to a trust anchor in
247 // |trust_store|, using intermediates from |cert_sources| if necessary.
248 class CertPathIter {
249 public:
250 CertPathIter(std::unique_ptr<CertThing> cert,
251 const CertPathBuilder::CertSources& cert_sources,
252 const TrustStore& trust_store);
253
254 // Gets the next candidate path. If a path is ready synchronously, OK is
255 // returned and the path is stored in |*path|. If a path is not ready,
256 // ERR_IO_PENDING is returned and |async_ready_callback| will be called once
257 // |*path| has been set.
258 // In either case, if all paths have been exhausted, OK result is returned and
259 // |*path| is cleared.
260 // The cert data referred to in |*path| is only valid until the next
261 // GetNextPath call or until the CertPathIter is destroyed.
262 int GetNextPath(CertVector* path, const CompletionCallback& callback);
263
264 private:
265 enum State {
266 STATE_NONE,
267 STATE_GET_NEXT_ISSUER,
268 STATE_GET_NEXT_ISSUER_COMPLETE,
269 STATE_RETURN_A_PATH,
270 };
271
272 bool IsIssuerTrusted(const CertThing& cert) const;
273
274 int DoLoop();
275
276 int DoGetNextIssuer();
277 int DoGetNextIssuerComplete();
278
279 void HandleGotNextIssuer(void);
280
281 LoopChecker loop_checker_;
282 std::unique_ptr<CertThing> next_cert_;
283 std::vector<std::unique_ptr<CertIssuersIter>> cur_path_;
284 CertPathBuilder::CertSources cert_sources_;
285 const TrustStore& trust_store_;
286 CertVector* out_path_;
287 CompletionCallback callback_;
288 State next_state_;
289
290 DISALLOW_COPY_AND_ASSIGN(CertPathIter);
291 };
292
293 CertPathIter::CertPathIter(std::unique_ptr<CertThing> cert,
294 const CertPathBuilder::CertSources& cert_sources,
295 const TrustStore& trust_store)
296 : next_cert_(std::move(cert)),
297 cert_sources_(cert_sources),
298 trust_store_(trust_store),
299 next_state_(STATE_GET_NEXT_ISSUER_COMPLETE) {}
300
301 int CertPathIter::GetNextPath(CertVector* path,
302 const CompletionCallback& callback) {
303 out_path_ = path;
304 out_path_->clear();
305 int rv = DoLoop();
306 if (rv == ERR_IO_PENDING)
307 callback_ = callback;
308 return rv;
309 }
310
311 bool CertPathIter::IsIssuerTrusted(const CertThing& cert) const {
312 // XXX make trust store do pre-normalization for searching
313 return !trust_store_
314 .FindTrustAnchorsByNormalizedName(cert.normalized_issuer())
315 .empty();
316 }
317
318 int CertPathIter::DoLoop() {
319 int result;
320 do {
321 State state = next_state_;
322 next_state_ = STATE_NONE;
323 switch (state) {
324 case STATE_NONE:
325 NOTREACHED();
326 break;
327 case STATE_GET_NEXT_ISSUER:
328 result = DoGetNextIssuer();
329 break;
330 case STATE_GET_NEXT_ISSUER_COMPLETE:
331 result = DoGetNextIssuerComplete();
332 break;
333 case STATE_RETURN_A_PATH:
334 next_state_ = STATE_GET_NEXT_ISSUER;
335 break;
336 }
337 } while (result != ERR_IO_PENDING && next_state_ != STATE_NONE &&
338 next_state_ != STATE_RETURN_A_PATH);
339
340 return result;
341 }
342
343 int CertPathIter::DoGetNextIssuer() {
344 next_state_ = STATE_GET_NEXT_ISSUER_COMPLETE;
345 int rv = cur_path_.back()->GetNextIssuer(
346 &next_cert_,
347 base::Bind(&CertPathIter::HandleGotNextIssuer, base::Unretained(this)));
348 return rv;
349 }
350
351 std::string DumpPath(
352 const std::vector<std::unique_ptr<CertIssuersIter>>& cur_path) {
353 std::string s;
354 for (const auto& node : cur_path) {
355 const CertThing& cert = node->cert();
356
357 RDNSequence subject, issuer;
358 if (!ParseName(cert.parsed_tbs().subject_tlv, &subject))
359 return std::string();
360 if (!ParseName(cert.parsed_tbs().issuer_tlv, &issuer))
361 return std::string();
362
363 std::string subject_str, issuer_str;
364
365 if (!ConvertToRFC2253(subject, &subject_str))
366 return std::string();
367 if (!ConvertToRFC2253(issuer, &issuer_str))
368 return std::string();
369 if (!s.empty())
370 s += " -> ";
371 s += subject_str + "(" + issuer_str + ")";
372 }
373 return s;
374 }
375
376 // Should we include trust anchors as a CertSource, or handle them separately?
377 //
378 // Pros/cons for handling them as a CertSource:
379 // + CertPathIter will automatically try each one as a separate path
380 // ? Could this cause LoopChecker issues? (preventing finding a trust anchor if
381 // it has same Name+SAN+SPKI as the last cert in chain. Or would there always be
382 // a better path by just removing that last cert and going directly to the trust
383 // anchor?)
384 // - Need special logic to not try to descend past a trust anchor (No reason
385 // to).
386 // - CertIssuersIter would need prioritization logic to put trust anchors first.
387 // + Automatically handles the case where the trust anchor is the end-entity.
388 // (Do we care about that?)
389 //
390 // Not as a Certsource, but Loop through trust anchors separately in
391 // CertPathIter:
392 // - requires another layer of logic to return the sequence of paths. bleh.
393 //
394 // Loop through trust anchors separately in CertPathBuilder:
395 // + pretty straightforward
396 // + doesn't require any special cases in CertPathIter for handling anchors.
397 // ? maybe requires little extra work to make a separate path/result for each?
398 // ? may want to sort/prioritize the anchors before trying them - but could
399 // probably re-use whatever sort function CertIssuersIter uses.
400 // - need to check TrustStore twice, unless CertPathIter returns the list of
401 // matching anchors.
402 // - means we can't just return a const-reference to the cur_path_, instead have
403 // to clone all the entries a new CertVector, since the CertPathBuilder will
404 // need to append the trust anchor to it.
405 //
406 // Conclusion: Loop through trust anchors separately in CertPathBuilder looks
407 // nice, except for having to clone the entries..
408
409 int CertPathIter::DoGetNextIssuerComplete() {
410 if (next_cert_) {
411 // Skip this cert if it is already in the chain.
412 if (!loop_checker_.Insert(*next_cert_)) {
413 next_state_ = STATE_GET_NEXT_ISSUER;
414 return OK;
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 cur_path_.push_back(base::WrapUnique(
420 new CertIssuersIter(std::move(next_cert_), &cert_sources_)));
421 next_cert_.reset();
422 DVLOG(1) << "CertPathIter cur_path_ = " << DumpPath(cur_path_);
423 // If the issuer of the cert matches a trust root, this is a (possible)
424 // // complete path. Signal readiness.
425 // Note that if this path does not verify, further subpaths will still
426 // be checked. This is needed to handle cases like key rollover.
427 if (IsIssuerTrusted(cur_path_.back()->cert())) {
428 DVLOG(1) << "CertPathIter IsIssuerTrusted = true";
429 next_state_ = STATE_RETURN_A_PATH;
430 for (const auto& node : cur_path_)
431 out_path_->push_back(node->cert().Clone()); // XXX see if we can clone
432 // without copying? Or just
433 // return a ref/pointer to
434 // the out_path_?
435 return OK;
436 }
437 // Continue descending the tree.
438 next_state_ = STATE_GET_NEXT_ISSUER;
439 } else {
440 DVLOG(1) << "CertPathIter backtracking...";
441 // XXX should also include such paths in CertPathBuilder::Result?
442 // No more issuers for current chain, go back up and see if there are any
443 // more for the previous cert.
444 loop_checker_.Remove(cur_path_.back()->cert());
445 cur_path_.pop_back();
446 if (cur_path_.empty()) {
447 // Exhausted all paths.
448 next_state_ = STATE_NONE;
449 } else {
450 next_state_ = STATE_GET_NEXT_ISSUER;
451 }
452 }
453 return OK;
454 }
455
456 void CertPathIter::HandleGotNextIssuer(void) {
457 DCHECK(!callback_.is_null());
458 int rv = DoLoop();
459 if (rv != ERR_IO_PENDING)
460 base::ResetAndReturn(&callback_).Run(rv);
461 }
462
463 CertPathBuilder::ResultPath::ResultPath() {}
464 CertPathBuilder::ResultPath::~ResultPath() {}
465 CertPathBuilder::Result::Result() {}
466 CertPathBuilder::Result::~Result() {}
467
468 CertPathBuilder::CertPathBuilder(std::unique_ptr<CertThing> cert,
469 const CertSources& cert_sources,
470 const TrustStore& trust_store,
471 const SignaturePolicy* signature_policy,
472 const der::GeneralizedTime& time,
473 Result* result)
474 : cert_path_iter_(
475 new CertPathIter(std::move(cert), cert_sources, trust_store)),
476 trust_store_(trust_store),
477 signature_policy_(signature_policy),
478 time_(time),
479 next_state_(STATE_NONE),
480 out_result_(result) {}
481
482 CertPathBuilder::~CertPathBuilder() {}
483
484 int CertPathBuilder::Run(const CompletionCallback& callback) {
485 DCHECK_EQ(STATE_NONE, next_state_);
486 next_state_ = STATE_GET_NEXT_PATH;
487 int rv = DoLoop(OK);
488
489 if (rv == ERR_IO_PENDING)
490 callback_ = callback;
491
492 return rv;
493 }
494
495 int CertPathBuilder::DoLoop(int result) {
496 do {
497 State state = next_state_;
498 next_state_ = STATE_NONE;
499 switch (state) {
500 case STATE_NONE:
501 NOTREACHED();
502 break;
503 case STATE_GET_NEXT_PATH:
504 DCHECK_EQ(OK, result);
505 result = DoGetNextPath();
506 break;
507 case STATE_GET_NEXT_PATH_COMPLETE:
508 result = DoGetNextPathComplete(result);
509 break;
510 }
511 } while (result != ERR_IO_PENDING && next_state_ != STATE_NONE);
512
513 return result;
514 }
515
516 int CertPathBuilder::DoGetNextPath() {
517 next_state_ = STATE_GET_NEXT_PATH_COMPLETE;
518 int rv = cert_path_iter_->GetNextPath(
519 &next_path_,
520 base::Bind(&CertPathBuilder::HandleGotNextPath, base::Unretained(this)));
521 return rv;
522 }
523
524 void CertPathBuilder::HandleGotNextPath(int result) {
525 DCHECK(!callback_.is_null());
526 int rv = DoLoop(result);
527 if (rv != ERR_IO_PENDING)
528 base::ResetAndReturn(&callback_).Run(rv);
529 }
530
531 int CertPathBuilder::DoGetNextPathComplete(int result) {
532 if (next_path_.empty()) {
533 // No more paths to check, cert failed to verify. Return the result code for
534 // the best path that was found.
535 next_state_ = STATE_NONE;
536 return out_result_->result();
537 }
538
539 std::vector<const CertThing*> matching_anchors(
540 trust_store_.FindTrustAnchorsByNormalizedName(
541 next_path_.back()->normalized_issuer()));
542 // TODO: sort/prioritize matching_anchors
543
544 for (auto& trust_anchor : matching_anchors) {
eroman 2016/04/26 22:10:10 How does this work when the target cert being veri
mattm 2016/04/26 22:54:16 Yeah, that would be an issue with this approach.
545 // CertVector full_path(next_path_);
546 // full_path.push_back(trust_anchor->cert());
547
548 next_path_.push_back(trust_anchor->Clone());
549 bool verify_result = VerifyCertificateChainAssumingTrustedRoot(
550 next_path_, trust_store_, signature_policy_, time_);
551 DVLOG(1) << "CertPathBuilder VerifyCertificateChain result = "
552 << verify_result;
553 AddResultPath(next_path_, verify_result);
554 next_path_.pop_back();
eroman 2016/04/26 22:10:10 Here it looks to be popping the trust anchor out o
mattm 2016/04/26 22:54:16 AddResultPath makes a copy of the path which is wh
555
556 if (verify_result) {
557 // Found a valid path, return immediately.
558 // XXX add debug/test mode that tries all possible paths.
559 next_state_ = STATE_NONE;
560 return out_result_->result();
561 }
562 }
563 // Path did not verify. Try more paths. If there are no more paths, the result
564 // will be returned next time DoGetNextPathComplete is called with next_path_
565 // empty.
566 next_state_ = STATE_GET_NEXT_PATH;
567 return OK;
568 }
569
570 void CertPathBuilder::AddResultPath(const CertVector& path, bool result) {
571 std::unique_ptr<ResultPath> rp(new ResultPath());
572 // XXX do better error mapping from VerifyCertificateChain results
573 rp->rv = result ? OK : ERR_CERT_AUTHORITY_INVALID;
574 // XXX set best_path_ if #errors(this) < #errors(best):
575 if (rp->rv == OK)
576 out_result_->best_result_index = out_result_->paths.size();
577 // XXX only return a single path except in debug/test mode.
578 for (const auto& cert : path)
579 rp->path.push_back(cert->Clone());
580 out_result_->paths.push_back(std::move(rp));
581 }
582
583 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698