OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/cert/multi_threaded_cert_verifier.h" | 5 #include "net/cert/multi_threaded_cert_verifier.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <memory> | 8 #include <memory> |
9 #include <utility> | 9 #include <utility> |
10 | 10 |
(...skipping 238 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
249 // PR_Cleanup. Unless we detach them from NSPR, net_unittests gets | 249 // PR_Cleanup. Unless we detach them from NSPR, net_unittests gets |
250 // segfaults on shutdown when the threads' thread-specific data | 250 // segfaults on shutdown when the threads' thread-specific data |
251 // destructors run. | 251 // destructors run. |
252 PR_DetachThread(); | 252 PR_DetachThread(); |
253 #endif | 253 #endif |
254 } | 254 } |
255 | 255 |
256 // CertVerifierJob lives only on the verifier's origin message loop. | 256 // CertVerifierJob lives only on the verifier's origin message loop. |
257 class CertVerifierJob { | 257 class CertVerifierJob { |
258 public: | 258 public: |
259 CertVerifierJob(const MultiThreadedCertVerifier::RequestParams& key, | 259 CertVerifierJob(const CertVerifier::RequestParams& key, |
260 NetLog* net_log, | 260 NetLog* net_log, |
261 X509Certificate* cert, | 261 X509Certificate* cert, |
262 MultiThreadedCertVerifier* cert_verifier) | 262 MultiThreadedCertVerifier* cert_verifier) |
263 : key_(key), | 263 : key_(key), |
264 start_time_(base::TimeTicks::Now()), | 264 start_time_(base::TimeTicks::Now()), |
| 265 wall_start_time_(base::Time::Now()), |
265 net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_CERT_VERIFIER_JOB)), | 266 net_log_(BoundNetLog::Make(net_log, NetLog::SOURCE_CERT_VERIFIER_JOB)), |
266 cert_verifier_(cert_verifier), | 267 cert_verifier_(cert_verifier), |
267 is_first_job_(false), | 268 is_first_job_(false), |
268 weak_ptr_factory_(this) { | 269 weak_ptr_factory_(this) { |
269 net_log_.BeginEvent( | 270 net_log_.BeginEvent( |
270 NetLog::TYPE_CERT_VERIFIER_JOB, | 271 NetLog::TYPE_CERT_VERIFIER_JOB, |
271 base::Bind(&NetLogX509CertificateCallback, base::Unretained(cert))); | 272 base::Bind(&NetLogX509CertificateCallback, base::Unretained(cert))); |
272 } | 273 } |
273 | 274 |
274 // Indicates whether this was the first job started by the CertVerifier. This | 275 // Indicates whether this was the first job started by the CertVerifier. This |
275 // is only used for logging certain UMA stats. | 276 // is only used for logging certain UMA stats. |
276 void set_is_first_job(bool is_first_job) { is_first_job_ = is_first_job; } | 277 void set_is_first_job(bool is_first_job) { is_first_job_ = is_first_job; } |
277 | 278 |
278 const MultiThreadedCertVerifier::RequestParams& key() const { return key_; } | 279 const CertVerifier::RequestParams& key() const { return key_; } |
279 | 280 |
280 // Posts a task to the worker pool to do the verification. Once the | 281 // Posts a task to the worker pool to do the verification. Once the |
281 // verification has completed on the worker thread, it will call | 282 // verification has completed on the worker thread, it will call |
282 // OnJobCompleted() on the origin thread. | 283 // OnJobCompleted() on the origin thread. |
283 bool Start(const scoped_refptr<CertVerifyProc>& verify_proc, | 284 bool Start(const scoped_refptr<CertVerifyProc>& verify_proc, |
284 const scoped_refptr<X509Certificate>& cert, | 285 const scoped_refptr<X509Certificate>& cert, |
285 const std::string& hostname, | 286 const std::string& hostname, |
286 const std::string& ocsp_response, | 287 const std::string& ocsp_response, |
287 int flags, | 288 int flags, |
288 const scoped_refptr<CRLSet>& crl_set, | 289 const scoped_refptr<CRLSet>& crl_set, |
(...skipping 72 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
361 } | 362 } |
362 } | 363 } |
363 | 364 |
364 void OnJobCompleted( | 365 void OnJobCompleted( |
365 std::unique_ptr<MultiThreadedCertVerifier::CachedResult> verify_result) { | 366 std::unique_ptr<MultiThreadedCertVerifier::CachedResult> verify_result) { |
366 TRACE_EVENT0("net", "CertVerifierJob::OnJobCompleted"); | 367 TRACE_EVENT0("net", "CertVerifierJob::OnJobCompleted"); |
367 std::unique_ptr<CertVerifierJob> keep_alive = | 368 std::unique_ptr<CertVerifierJob> keep_alive = |
368 cert_verifier_->RemoveJob(this); | 369 cert_verifier_->RemoveJob(this); |
369 | 370 |
370 LogMetrics(*verify_result); | 371 LogMetrics(*verify_result); |
371 cert_verifier_->SaveResultToCache(key_, *verify_result); | 372 cert_verifier_->SaveResultToCache(key_, wall_start_time_, *verify_result); |
372 cert_verifier_ = nullptr; | 373 cert_verifier_ = nullptr; |
373 | 374 |
374 // TODO(eroman): If the cert_verifier_ is deleted from within one of the | 375 // TODO(eroman): If the cert_verifier_ is deleted from within one of the |
375 // callbacks, any remaining requests for that job should be cancelled. Right | 376 // callbacks, any remaining requests for that job should be cancelled. Right |
376 // now they will be called. | 377 // now they will be called. |
377 while (!requests_.empty()) { | 378 while (!requests_.empty()) { |
378 base::LinkNode<CertVerifierRequest>* request = requests_.head(); | 379 base::LinkNode<CertVerifierRequest>* request = requests_.head(); |
379 request->RemoveFromList(); | 380 request->RemoveFromList(); |
380 request->value()->Post(*verify_result); | 381 request->value()->Post(*verify_result); |
381 } | 382 } |
382 } | 383 } |
383 | 384 |
384 const MultiThreadedCertVerifier::RequestParams key_; | 385 const CertVerifier::RequestParams key_; |
| 386 // The tick count of when the job started. This is used to measure how long |
| 387 // the job actually took to complete. |
385 const base::TimeTicks start_time_; | 388 const base::TimeTicks start_time_; |
386 | 389 |
| 390 // The wall time of when the job started. This is to account for situations |
| 391 // where the system clock may have changed after the Job had started, which |
| 392 // could otherwise result in caching the wrong data. |
| 393 const base::Time wall_start_time_; |
| 394 |
387 RequestList requests_; // Non-owned. | 395 RequestList requests_; // Non-owned. |
388 | 396 |
389 const BoundNetLog net_log_; | 397 const BoundNetLog net_log_; |
390 MultiThreadedCertVerifier* cert_verifier_; // Non-owned. | 398 MultiThreadedCertVerifier* cert_verifier_; // Non-owned. |
391 | 399 |
392 bool is_first_job_; | 400 bool is_first_job_; |
393 base::WeakPtrFactory<CertVerifierJob> weak_ptr_factory_; | 401 base::WeakPtrFactory<CertVerifierJob> weak_ptr_factory_; |
394 }; | 402 }; |
395 | 403 |
396 MultiThreadedCertVerifier::MultiThreadedCertVerifier( | 404 MultiThreadedCertVerifier::MultiThreadedCertVerifier( |
(...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
431 if (callback.is_null() || !verify_result || hostname.empty()) | 439 if (callback.is_null() || !verify_result || hostname.empty()) |
432 return ERR_INVALID_ARGUMENT; | 440 return ERR_INVALID_ARGUMENT; |
433 | 441 |
434 requests_++; | 442 requests_++; |
435 | 443 |
436 const CertificateList empty_cert_list; | 444 const CertificateList empty_cert_list; |
437 const CertificateList& additional_trust_anchors = | 445 const CertificateList& additional_trust_anchors = |
438 trust_anchor_provider_ ? | 446 trust_anchor_provider_ ? |
439 trust_anchor_provider_->GetAdditionalTrustAnchors() : empty_cert_list; | 447 trust_anchor_provider_->GetAdditionalTrustAnchors() : empty_cert_list; |
440 | 448 |
441 const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(), hostname, | 449 const CertVerifier::RequestParams key(cert, hostname, flags, ocsp_response, |
442 ocsp_response, flags, additional_trust_anchors); | 450 additional_trust_anchors); |
443 const CertVerifierCache::value_type* cached_entry = | 451 const CertVerifierCache::value_type* cached_entry = |
444 cache_.Get(key, CacheValidityPeriod(base::Time::Now())); | 452 cache_.Get(key, CacheValidityPeriod(base::Time::Now())); |
445 if (cached_entry) { | 453 if (cached_entry) { |
446 ++cache_hits_; | 454 ++cache_hits_; |
447 *verify_result = cached_entry->result; | 455 *verify_result = cached_entry->result; |
448 return cached_entry->error; | 456 return cached_entry->error; |
449 } | 457 } |
450 | 458 |
451 // No cache hit. See if an identical request is currently in flight. | 459 // No cache hit. See if an identical request is currently in flight. |
452 CertVerifierJob* job = FindJob(key); | 460 CertVerifierJob* job = FindJob(key); |
(...skipping 23 matching lines...) Expand all Loading... |
476 std::unique_ptr<CertVerifierRequest> request = | 484 std::unique_ptr<CertVerifierRequest> request = |
477 job->CreateRequest(callback, verify_result, net_log); | 485 job->CreateRequest(callback, verify_result, net_log); |
478 *out_req = std::move(request); | 486 *out_req = std::move(request); |
479 return ERR_IO_PENDING; | 487 return ERR_IO_PENDING; |
480 } | 488 } |
481 | 489 |
482 bool MultiThreadedCertVerifier::SupportsOCSPStapling() { | 490 bool MultiThreadedCertVerifier::SupportsOCSPStapling() { |
483 return verify_proc_->SupportsOCSPStapling(); | 491 return verify_proc_->SupportsOCSPStapling(); |
484 } | 492 } |
485 | 493 |
486 MultiThreadedCertVerifier::RequestParams::RequestParams( | |
487 const SHA1HashValue& cert_fingerprint_arg, | |
488 const SHA1HashValue& ca_fingerprint_arg, | |
489 const std::string& hostname_arg, | |
490 const std::string& ocsp_response_arg, | |
491 int flags_arg, | |
492 const CertificateList& additional_trust_anchors) | |
493 : hostname(hostname_arg), flags(flags_arg), start_time(base::Time::Now()) { | |
494 hash_values.reserve(3 + additional_trust_anchors.size()); | |
495 SHA1HashValue ocsp_hash; | |
496 base::SHA1HashBytes( | |
497 reinterpret_cast<const unsigned char*>(ocsp_response_arg.data()), | |
498 ocsp_response_arg.size(), ocsp_hash.data); | |
499 hash_values.push_back(ocsp_hash); | |
500 hash_values.push_back(cert_fingerprint_arg); | |
501 hash_values.push_back(ca_fingerprint_arg); | |
502 for (size_t i = 0; i < additional_trust_anchors.size(); ++i) | |
503 hash_values.push_back(additional_trust_anchors[i]->fingerprint()); | |
504 } | |
505 | |
506 MultiThreadedCertVerifier::RequestParams::RequestParams( | |
507 const RequestParams& other) = default; | |
508 | |
509 MultiThreadedCertVerifier::RequestParams::~RequestParams() {} | |
510 | |
511 bool MultiThreadedCertVerifier::RequestParams::operator<( | |
512 const RequestParams& other) const { | |
513 // |flags| is compared before |cert_fingerprint|, |ca_fingerprint|, | |
514 // |hostname|, and |ocsp_response|, under assumption that integer comparisons | |
515 // are faster than memory and string comparisons. | |
516 if (flags != other.flags) | |
517 return flags < other.flags; | |
518 if (hostname != other.hostname) | |
519 return hostname < other.hostname; | |
520 return std::lexicographical_compare( | |
521 hash_values.begin(), hash_values.end(), other.hash_values.begin(), | |
522 other.hash_values.end(), SHA1HashValueLessThan()); | |
523 } | |
524 | |
525 bool MultiThreadedCertVerifier::JobComparator::operator()( | 494 bool MultiThreadedCertVerifier::JobComparator::operator()( |
526 const CertVerifierJob* job1, | 495 const CertVerifierJob* job1, |
527 const CertVerifierJob* job2) const { | 496 const CertVerifierJob* job2) const { |
528 return job1->key() < job2->key(); | 497 return job1->key() < job2->key(); |
529 } | 498 } |
530 | 499 |
531 void MultiThreadedCertVerifier::SaveResultToCache(const RequestParams& key, | 500 void MultiThreadedCertVerifier::SaveResultToCache( |
532 const CachedResult& result) { | 501 const CertVerifier::RequestParams& key, |
| 502 const base::Time& start_time, |
| 503 const CachedResult& result) { |
533 DCHECK(CalledOnValidThread()); | 504 DCHECK(CalledOnValidThread()); |
534 | 505 |
535 // When caching, this uses the time that validation started as the | 506 // When caching, this uses the time that validation started as the |
536 // beginning of the validity, rather than the time that it ended (aka | 507 // beginning of the validity, rather than the time that it ended (aka |
537 // base::Time::Now()), to account for the fact that during validation, | 508 // base::Time::Now()), to account for the fact that during validation, |
538 // the clock may have changed. | 509 // the clock may have changed. |
539 // | 510 // |
540 // If the clock has changed significantly, then this result will ideally | 511 // If the clock has changed significantly, then this result will ideally |
541 // be evicted and the next time the certificate is encountered, it will | 512 // be evicted and the next time the certificate is encountered, it will |
542 // be revalidated. | 513 // be revalidated. |
543 // | 514 // |
544 // Because of this, it's possible for situations to arise where the | 515 // Because of this, it's possible for situations to arise where the |
545 // clock was correct at the start of validation, changed to an | 516 // clock was correct at the start of validation, changed to an |
546 // incorrect time during validation (such as too far in the past or | 517 // incorrect time during validation (such as too far in the past or |
547 // future), and then was reset to the correct time. If this happens, | 518 // future), and then was reset to the correct time. If this happens, |
548 // it's likely that the result will not be a valid/correct result, | 519 // it's likely that the result will not be a valid/correct result, |
549 // but will still be used from the cache because the clock was reset | 520 // but will still be used from the cache because the clock was reset |
550 // to the correct time after the (bad) validation result completed. | 521 // to the correct time after the (bad) validation result completed. |
551 // | 522 // |
552 // However, this solution optimizes for the case where the clock is | 523 // However, this solution optimizes for the case where the clock is |
553 // bad at the start of validation, and subsequently is corrected. In | 524 // bad at the start of validation, and subsequently is corrected. In |
554 // that situation, the result is also incorrect, but because the clock | 525 // that situation, the result is also incorrect, but because the clock |
555 // was corrected after validation, if the cache validity period was | 526 // was corrected after validation, if the cache validity period was |
556 // computed at the end of validation, it would continue to serve an | 527 // computed at the end of validation, it would continue to serve an |
557 // invalid result for kTTLSecs. | 528 // invalid result for kTTLSecs. |
558 const base::Time start_time = key.start_time; | |
559 cache_.Put( | 529 cache_.Put( |
560 key, result, CacheValidityPeriod(start_time), | 530 key, result, CacheValidityPeriod(start_time), |
561 CacheValidityPeriod(start_time, | 531 CacheValidityPeriod(start_time, |
562 start_time + base::TimeDelta::FromSeconds(kTTLSecs))); | 532 start_time + base::TimeDelta::FromSeconds(kTTLSecs))); |
563 } | 533 } |
564 | 534 |
565 std::unique_ptr<CertVerifierJob> MultiThreadedCertVerifier::RemoveJob( | 535 std::unique_ptr<CertVerifierJob> MultiThreadedCertVerifier::RemoveJob( |
566 CertVerifierJob* job) { | 536 CertVerifierJob* job) { |
567 DCHECK(CalledOnValidThread()); | 537 DCHECK(CalledOnValidThread()); |
568 bool erased_job = inflight_.erase(job) == 1; | 538 bool erased_job = inflight_.erase(job) == 1; |
569 DCHECK(erased_job); | 539 DCHECK(erased_job); |
570 return base::WrapUnique(job); | 540 return base::WrapUnique(job); |
571 } | 541 } |
572 | 542 |
573 void MultiThreadedCertVerifier::OnCACertChanged( | 543 void MultiThreadedCertVerifier::OnCACertChanged( |
574 const X509Certificate* cert) { | 544 const X509Certificate* cert) { |
575 DCHECK(CalledOnValidThread()); | 545 DCHECK(CalledOnValidThread()); |
576 | 546 |
577 ClearCache(); | 547 ClearCache(); |
578 } | 548 } |
579 | 549 |
580 struct MultiThreadedCertVerifier::JobToRequestParamsComparator { | 550 struct MultiThreadedCertVerifier::JobToRequestParamsComparator { |
581 bool operator()(const CertVerifierJob* job, | 551 bool operator()(const CertVerifierJob* job, |
582 const MultiThreadedCertVerifier::RequestParams& value) const { | 552 const CertVerifier::RequestParams& value) const { |
583 return job->key() < value; | 553 return job->key() < value; |
584 } | 554 } |
585 }; | 555 }; |
586 | 556 |
587 CertVerifierJob* MultiThreadedCertVerifier::FindJob(const RequestParams& key) { | 557 CertVerifierJob* MultiThreadedCertVerifier::FindJob( |
| 558 const CertVerifier::RequestParams& key) { |
588 DCHECK(CalledOnValidThread()); | 559 DCHECK(CalledOnValidThread()); |
589 | 560 |
590 // The JobSet is kept in sorted order so items can be found using binary | 561 // The JobSet is kept in sorted order so items can be found using binary |
591 // search. | 562 // search. |
592 auto it = std::lower_bound(inflight_.begin(), inflight_.end(), key, | 563 auto it = std::lower_bound(inflight_.begin(), inflight_.end(), key, |
593 JobToRequestParamsComparator()); | 564 JobToRequestParamsComparator()); |
594 if (it != inflight_.end() && !(key < (*it)->key())) | 565 if (it != inflight_.end() && !(key < (*it)->key())) |
595 return *it; | 566 return *it; |
596 return nullptr; | 567 return nullptr; |
597 } | 568 } |
598 | 569 |
599 } // namespace net | 570 } // namespace net |
OLD | NEW |