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

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

Powered by Google App Engine
This is Rietveld 408576698