Chromium Code Reviews| 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/base/multi_threaded_cert_verifier.h" | 5 #include "net/base/multi_threaded_cert_verifier.h" |
| 6 | 6 |
| 7 #include <vector> | 7 #include <vector> |
| 8 | 8 |
| 9 #include "base/bind.h" | 9 #include "base/bind.h" |
| 10 #include "base/bind_helpers.h" | 10 #include "base/bind_helpers.h" |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 76 | 76 |
| 77 // The number of seconds for which we'll cache a cache entry. | 77 // The number of seconds for which we'll cache a cache entry. |
| 78 const unsigned kTTLSecs = 1800; // 30 minutes. | 78 const unsigned kTTLSecs = 1800; // 30 minutes. |
| 79 | 79 |
| 80 } // namespace | 80 } // namespace |
| 81 | 81 |
| 82 MultiThreadedCertVerifier::CachedResult::CachedResult() : error(ERR_FAILED) {} | 82 MultiThreadedCertVerifier::CachedResult::CachedResult() : error(ERR_FAILED) {} |
| 83 | 83 |
| 84 MultiThreadedCertVerifier::CachedResult::~CachedResult() {} | 84 MultiThreadedCertVerifier::CachedResult::~CachedResult() {} |
| 85 | 85 |
| 86 MultiThreadedCertVerifier::ValidityRange::ValidityRange(const base::Time& now) | |
| 87 : verification_time(now), | |
| 88 expiration_time(now) { | |
| 89 } | |
| 90 | |
| 91 MultiThreadedCertVerifier::ValidityRange::ValidityRange( | |
| 92 const base::Time& now, | |
| 93 const base::Time& expiration) | |
| 94 : verification_time(now), | |
| 95 expiration_time(expiration) { | |
| 96 } | |
| 97 | |
| 86 // Represents the output and result callback of a request. | 98 // Represents the output and result callback of a request. |
| 87 class CertVerifierRequest { | 99 class CertVerifierRequest { |
| 88 public: | 100 public: |
| 89 CertVerifierRequest(const CompletionCallback& callback, | 101 CertVerifierRequest(const CompletionCallback& callback, |
| 90 CertVerifyResult* verify_result, | 102 CertVerifyResult* verify_result, |
| 91 const BoundNetLog& net_log) | 103 const BoundNetLog& net_log) |
| 92 : callback_(callback), | 104 : callback_(callback), |
| 93 verify_result_(verify_result), | 105 verify_result_(verify_result), |
| 94 net_log_(net_log) { | 106 net_log_(net_log) { |
| 95 net_log_.BeginEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST); | 107 net_log_.BeginEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST); |
| (...skipping 225 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 321 } | 333 } |
| 322 } | 334 } |
| 323 | 335 |
| 324 const base::TimeTicks start_time_; | 336 const base::TimeTicks start_time_; |
| 325 std::vector<CertVerifierRequest*> requests_; | 337 std::vector<CertVerifierRequest*> requests_; |
| 326 CertVerifierWorker* worker_; | 338 CertVerifierWorker* worker_; |
| 327 const BoundNetLog net_log_; | 339 const BoundNetLog net_log_; |
| 328 }; | 340 }; |
| 329 | 341 |
| 330 MultiThreadedCertVerifier::MultiThreadedCertVerifier() | 342 MultiThreadedCertVerifier::MultiThreadedCertVerifier() |
| 331 : cache_(kMaxCacheEntries), | 343 : cache_(kMaxCacheEntries, |
| 344 &MultiThreadedCertVerifier::CompareValidityRange), | |
| 332 requests_(0), | 345 requests_(0), |
| 333 cache_hits_(0), | 346 cache_hits_(0), |
| 334 inflight_joins_(0), | 347 inflight_joins_(0), |
| 335 verify_proc_(CertVerifyProc::CreateDefault()) { | 348 verify_proc_(CertVerifyProc::CreateDefault()) { |
| 336 CertDatabase::AddObserver(this); | 349 CertDatabase::AddObserver(this); |
| 337 } | 350 } |
| 338 | 351 |
| 339 MultiThreadedCertVerifier::~MultiThreadedCertVerifier() { | 352 MultiThreadedCertVerifier::~MultiThreadedCertVerifier() { |
| 340 STLDeleteValues(&inflight_); | 353 STLDeleteValues(&inflight_); |
| 341 | 354 |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 355 if (callback.is_null() || !verify_result || hostname.empty()) { | 368 if (callback.is_null() || !verify_result || hostname.empty()) { |
| 356 *out_req = NULL; | 369 *out_req = NULL; |
| 357 return ERR_INVALID_ARGUMENT; | 370 return ERR_INVALID_ARGUMENT; |
| 358 } | 371 } |
| 359 | 372 |
| 360 requests_++; | 373 requests_++; |
| 361 | 374 |
| 362 const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(), | 375 const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(), |
| 363 hostname, flags); | 376 hostname, flags); |
| 364 const CertVerifierCache::value_type* cached_entry = | 377 const CertVerifierCache::value_type* cached_entry = |
| 365 cache_.Get(key, base::TimeTicks::Now()); | 378 cache_.Get(key, ValidityRange(base::Time::Now())); |
| 366 if (cached_entry) { | 379 if (cached_entry) { |
| 367 ++cache_hits_; | 380 ++cache_hits_; |
| 368 *out_req = NULL; | 381 *out_req = NULL; |
| 369 *verify_result = cached_entry->result; | 382 *verify_result = cached_entry->result; |
| 370 return cached_entry->error; | 383 return cached_entry->error; |
| 371 } | 384 } |
| 372 | 385 |
| 373 // No cache hit. See if an identical request is currently in flight. | 386 // No cache hit. See if an identical request is currently in flight. |
| 374 CertVerifierJob* job; | 387 CertVerifierJob* job; |
| 375 std::map<RequestParams, CertVerifierJob*>::const_iterator j; | 388 std::map<RequestParams, CertVerifierJob*>::const_iterator j; |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 404 *out_req = request; | 417 *out_req = request; |
| 405 return ERR_IO_PENDING; | 418 return ERR_IO_PENDING; |
| 406 } | 419 } |
| 407 | 420 |
| 408 void MultiThreadedCertVerifier::CancelRequest(RequestHandle req) { | 421 void MultiThreadedCertVerifier::CancelRequest(RequestHandle req) { |
| 409 DCHECK(CalledOnValidThread()); | 422 DCHECK(CalledOnValidThread()); |
| 410 CertVerifierRequest* request = reinterpret_cast<CertVerifierRequest*>(req); | 423 CertVerifierRequest* request = reinterpret_cast<CertVerifierRequest*>(req); |
| 411 request->Cancel(); | 424 request->Cancel(); |
| 412 } | 425 } |
| 413 | 426 |
| 427 // static | |
| 428 bool MultiThreadedCertVerifier::CompareValidityRange( | |
| 429 const ValidityRange& now, | |
| 430 const ValidityRange& expiration) { | |
| 431 // |now| contains only a single time (verification_time), while |expiration| | |
| 432 // contains the validity range - both when the certificate was verified and | |
| 433 // when the verification should expire. | |
| 434 // | |
| 435 // If the user receives a "not yet valid" message, and adjusts their clock | |
| 436 // foward to the correct time, this will (typically) cause | |
| 437 // now.verification_time to advance past expiration.expiration_time, thus | |
| 438 // treating the cached result as an expired entry and re-verifying. | |
| 439 // If the user receives a "expired" message, and adjusts their clock backwards | |
| 440 // to the correct time, this will cause now.verification_time to be less than | |
| 441 // expiration_verification_time, thus treating the cached result as an expired | |
|
cbentzel
2012/06/18 19:08:28
Unclear to me: Should now.verification_time < expi
Ryan Sleevi
2012/06/19 00:59:28
It's a cache expiration issue, but not a cert expi
| |
| 442 // entry and re-verifying. | |
| 443 // If the user receives either of those messages, and does not adjust their | |
| 444 // clock, then the result will be (typically) be cached until the expiration | |
| 445 // TTL. | |
| 446 // | |
| 447 // This algorithm is only problematic if the user consistently keeps adjusting | |
| 448 // their clock backwards in increments smaller than the expiration TTL, in | |
| 449 // which case, cached elements continue to be added. However, because the | |
| 450 // cache has a fixed upper bound, if no entries are expired, a 'random' entry | |
| 451 // will be, thus keeping the memory constraints bounded over time. | |
| 452 return now.verification_time >= expiration.verification_time && | |
|
cbentzel
2012/06/18 19:08:28
DCHECK that now.verification_time == now.expiratio
| |
| 453 now.verification_time < expiration.expiration_time; | |
| 454 } | |
| 455 | |
| 414 // HandleResult is called by CertVerifierWorker on the origin message loop. | 456 // HandleResult is called by CertVerifierWorker on the origin message loop. |
| 415 // It deletes CertVerifierJob. | 457 // It deletes CertVerifierJob. |
| 416 void MultiThreadedCertVerifier::HandleResult( | 458 void MultiThreadedCertVerifier::HandleResult( |
| 417 X509Certificate* cert, | 459 X509Certificate* cert, |
| 418 const std::string& hostname, | 460 const std::string& hostname, |
| 419 int flags, | 461 int flags, |
| 420 int error, | 462 int error, |
| 421 const CertVerifyResult& verify_result) { | 463 const CertVerifyResult& verify_result) { |
| 422 DCHECK(CalledOnValidThread()); | 464 DCHECK(CalledOnValidThread()); |
| 423 | 465 |
| 424 const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(), | 466 const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(), |
| 425 hostname, flags); | 467 hostname, flags); |
| 426 | 468 |
| 427 CachedResult cached_result; | 469 CachedResult cached_result; |
| 428 cached_result.error = error; | 470 cached_result.error = error; |
| 429 cached_result.result = verify_result; | 471 cached_result.result = verify_result; |
| 430 cache_.Put(key, cached_result, base::TimeTicks::Now(), | 472 base::Time now = base::Time::Now(); |
| 431 base::TimeDelta::FromSeconds(kTTLSecs)); | 473 cache_.Put(key, cached_result, ValidityRange(now), |
| 474 ValidityRange(now, now + base::TimeDelta::FromSeconds(kTTLSecs))); | |
|
szym
2012/06/18 16:11:37
If the result is successful, shouldn't we take min
Ryan Sleevi
2012/06/18 18:00:46
Possibly. Definitely an edge case. Another edge ca
Ryan Sleevi
2012/06/19 17:57:34
In thinking about it, I think it's probably more c
szym
2012/06/19 18:01:13
This alone convinced me that it's better to leave
| |
| 432 | 475 |
| 433 std::map<RequestParams, CertVerifierJob*>::iterator j; | 476 std::map<RequestParams, CertVerifierJob*>::iterator j; |
| 434 j = inflight_.find(key); | 477 j = inflight_.find(key); |
| 435 if (j == inflight_.end()) { | 478 if (j == inflight_.end()) { |
| 436 NOTREACHED(); | 479 NOTREACHED(); |
| 437 return; | 480 return; |
| 438 } | 481 } |
| 439 CertVerifierJob* job = j->second; | 482 CertVerifierJob* job = j->second; |
| 440 inflight_.erase(j); | 483 inflight_.erase(j); |
| 441 | 484 |
| 442 job->HandleResult(cached_result); | 485 job->HandleResult(cached_result); |
| 443 delete job; | 486 delete job; |
| 444 } | 487 } |
| 445 | 488 |
| 446 void MultiThreadedCertVerifier::OnCertTrustChanged( | 489 void MultiThreadedCertVerifier::OnCertTrustChanged( |
| 447 const X509Certificate* cert) { | 490 const X509Certificate* cert) { |
| 448 DCHECK(CalledOnValidThread()); | 491 DCHECK(CalledOnValidThread()); |
| 449 | 492 |
| 450 ClearCache(); | 493 ClearCache(); |
| 451 } | 494 } |
| 452 | 495 |
| 453 void MultiThreadedCertVerifier::SetCertVerifyProc(CertVerifyProc* verify_proc) { | 496 void MultiThreadedCertVerifier::SetCertVerifyProc(CertVerifyProc* verify_proc) { |
| 454 verify_proc_ = verify_proc; | 497 verify_proc_ = verify_proc; |
| 455 } | 498 } |
| 456 | 499 |
| 457 } // namespace net | 500 } // namespace net |
| OLD | NEW |