Index: net/base/multi_threaded_cert_verifier.cc |
diff --git a/net/base/multi_threaded_cert_verifier.cc b/net/base/multi_threaded_cert_verifier.cc |
deleted file mode 100644 |
index dc01c0e091ea04489f851d3ea94ab554d856228c..0000000000000000000000000000000000000000 |
--- a/net/base/multi_threaded_cert_verifier.cc |
+++ /dev/null |
@@ -1,554 +0,0 @@ |
-// Copyright (c) 2012 The Chromium Authors. All rights reserved. |
-// Use of this source code is governed by a BSD-style license that can be |
-// found in the LICENSE file. |
- |
-#include "net/base/multi_threaded_cert_verifier.h" |
- |
-#include <algorithm> |
- |
-#include "base/bind.h" |
-#include "base/bind_helpers.h" |
-#include "base/compiler_specific.h" |
-#include "base/message_loop.h" |
-#include "base/metrics/histogram.h" |
-#include "base/stl_util.h" |
-#include "base/synchronization/lock.h" |
-#include "base/time.h" |
-#include "base/threading/worker_pool.h" |
-#include "net/base/cert_trust_anchor_provider.h" |
-#include "net/base/cert_verify_proc.h" |
-#include "net/base/crl_set.h" |
-#include "net/base/net_errors.h" |
-#include "net/base/net_log.h" |
-#include "net/base/x509_certificate.h" |
-#include "net/base/x509_certificate_net_log_param.h" |
- |
-#if defined(USE_NSS) || defined(OS_IOS) |
-#include <private/pprthred.h> // PR_DetachThread |
-#endif |
- |
-namespace net { |
- |
-//////////////////////////////////////////////////////////////////////////// |
- |
-// Life of a request: |
-// |
-// MultiThreadedCertVerifier CertVerifierJob CertVerifierWorker Request |
-// | (origin loop) (worker loop) |
-// | |
-// Verify() |
-// |---->-------------------------------------<creates> |
-// | |
-// |---->-------------------<creates> |
-// | |
-// |---->-------------------------------------------------------<creates> |
-// | |
-// |---->---------------------------------------Start |
-// | | |
-// | PostTask |
-// | |
-// | <starts verifying> |
-// |---->-------------------AddRequest | |
-// | |
-// | |
-// | |
-// Finish |
-// | |
-// PostTask |
-// |
-// | |
-// DoReply |
-// |----<-----------------------------------------| |
-// HandleResult |
-// | |
-// |---->------------------HandleResult |
-// | |
-// |------>---------------------------Post |
-// |
-// |
-// |
-// On a cache hit, MultiThreadedCertVerifier::Verify() returns synchronously |
-// without posting a task to a worker thread. |
- |
-namespace { |
- |
-// The default value of max_cache_entries_. |
-const unsigned kMaxCacheEntries = 256; |
- |
-// The number of seconds for which we'll cache a cache entry. |
-const unsigned kTTLSecs = 1800; // 30 minutes. |
- |
-} // namespace |
- |
-MultiThreadedCertVerifier::CachedResult::CachedResult() : error(ERR_FAILED) {} |
- |
-MultiThreadedCertVerifier::CachedResult::~CachedResult() {} |
- |
-MultiThreadedCertVerifier::CacheValidityPeriod::CacheValidityPeriod( |
- const base::Time& now) |
- : verification_time(now), |
- expiration_time(now) { |
-} |
- |
-MultiThreadedCertVerifier::CacheValidityPeriod::CacheValidityPeriod( |
- const base::Time& now, |
- const base::Time& expiration) |
- : verification_time(now), |
- expiration_time(expiration) { |
-} |
- |
-bool MultiThreadedCertVerifier::CacheExpirationFunctor::operator()( |
- const CacheValidityPeriod& now, |
- const CacheValidityPeriod& expiration) const { |
- // Ensure this functor is being used for expiration only, and not strict |
- // weak ordering/sorting. |now| should only ever contain a single |
- // base::Time. |
- // Note: DCHECK_EQ is not used due to operator<< overloading requirements. |
- DCHECK(now.verification_time == now.expiration_time); |
- |
- // |now| contains only a single time (verification_time), while |expiration| |
- // contains the validity range - both when the certificate was verified and |
- // when the verification result should expire. |
- // |
- // If the user receives a "not yet valid" message, and adjusts their clock |
- // foward to the correct time, this will (typically) cause |
- // now.verification_time to advance past expiration.expiration_time, thus |
- // treating the cached result as an expired entry and re-verifying. |
- // If the user receives a "expired" message, and adjusts their clock |
- // backwards to the correct time, this will cause now.verification_time to |
- // be less than expiration_verification_time, thus treating the cached |
- // result as an expired entry and re-verifying. |
- // If the user receives either of those messages, and does not adjust their |
- // clock, then the result will be (typically) be cached until the expiration |
- // TTL. |
- // |
- // This algorithm is only problematic if the user consistently keeps |
- // adjusting their clock backwards in increments smaller than the expiration |
- // TTL, in which case, cached elements continue to be added. However, |
- // because the cache has a fixed upper bound, if no entries are expired, a |
- // 'random' entry will be, thus keeping the memory constraints bounded over |
- // time. |
- return now.verification_time >= expiration.verification_time && |
- now.verification_time < expiration.expiration_time; |
-}; |
- |
- |
-// Represents the output and result callback of a request. |
-class CertVerifierRequest { |
- public: |
- CertVerifierRequest(const CompletionCallback& callback, |
- CertVerifyResult* verify_result, |
- const BoundNetLog& net_log) |
- : callback_(callback), |
- verify_result_(verify_result), |
- net_log_(net_log) { |
- net_log_.BeginEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST); |
- } |
- |
- ~CertVerifierRequest() { |
- } |
- |
- // Ensures that the result callback will never be made. |
- void Cancel() { |
- callback_.Reset(); |
- verify_result_ = NULL; |
- net_log_.AddEvent(NetLog::TYPE_CANCELLED); |
- net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST); |
- } |
- |
- // Copies the contents of |verify_result| to the caller's |
- // CertVerifyResult and calls the callback. |
- void Post(const MultiThreadedCertVerifier::CachedResult& verify_result) { |
- if (!callback_.is_null()) { |
- net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST); |
- *verify_result_ = verify_result.result; |
- callback_.Run(verify_result.error); |
- } |
- delete this; |
- } |
- |
- bool canceled() const { return callback_.is_null(); } |
- |
- const BoundNetLog& net_log() const { return net_log_; } |
- |
- private: |
- CompletionCallback callback_; |
- CertVerifyResult* verify_result_; |
- const BoundNetLog net_log_; |
-}; |
- |
- |
-// CertVerifierWorker runs on a worker thread and takes care of the blocking |
-// process of performing the certificate verification. Deletes itself |
-// eventually if Start() succeeds. |
-class CertVerifierWorker { |
- public: |
- CertVerifierWorker(CertVerifyProc* verify_proc, |
- X509Certificate* cert, |
- const std::string& hostname, |
- int flags, |
- CRLSet* crl_set, |
- const CertificateList& additional_trust_anchors, |
- MultiThreadedCertVerifier* cert_verifier) |
- : verify_proc_(verify_proc), |
- cert_(cert), |
- hostname_(hostname), |
- flags_(flags), |
- crl_set_(crl_set), |
- additional_trust_anchors_(additional_trust_anchors), |
- origin_loop_(MessageLoop::current()), |
- cert_verifier_(cert_verifier), |
- canceled_(false), |
- error_(ERR_FAILED) { |
- } |
- |
- // Returns the certificate being verified. May only be called /before/ |
- // Start() is called. |
- X509Certificate* certificate() const { return cert_; } |
- |
- bool Start() { |
- DCHECK_EQ(MessageLoop::current(), origin_loop_); |
- |
- return base::WorkerPool::PostTask( |
- FROM_HERE, base::Bind(&CertVerifierWorker::Run, base::Unretained(this)), |
- true /* task is slow */); |
- } |
- |
- // Cancel is called from the origin loop when the MultiThreadedCertVerifier is |
- // getting deleted. |
- void Cancel() { |
- DCHECK_EQ(MessageLoop::current(), origin_loop_); |
- base::AutoLock locked(lock_); |
- canceled_ = true; |
- } |
- |
- private: |
- void Run() { |
- // Runs on a worker thread. |
- error_ = verify_proc_->Verify(cert_, hostname_, flags_, crl_set_, |
- additional_trust_anchors_, &verify_result_); |
-#if defined(USE_NSS) || defined(OS_IOS) |
- // Detach the thread from NSPR. |
- // Calling NSS functions attaches the thread to NSPR, which stores |
- // the NSPR thread ID in thread-specific data. |
- // The threads in our thread pool terminate after we have called |
- // PR_Cleanup. Unless we detach them from NSPR, net_unittests gets |
- // segfaults on shutdown when the threads' thread-specific data |
- // destructors run. |
- PR_DetachThread(); |
-#endif |
- Finish(); |
- } |
- |
- // DoReply runs on the origin thread. |
- void DoReply() { |
- DCHECK_EQ(MessageLoop::current(), origin_loop_); |
- { |
- // We lock here because the worker thread could still be in Finished, |
- // after the PostTask, but before unlocking |lock_|. If we do not lock in |
- // this case, we will end up deleting a locked Lock, which can lead to |
- // memory leaks or worse errors. |
- base::AutoLock locked(lock_); |
- if (!canceled_) { |
- cert_verifier_->HandleResult(cert_, hostname_, flags_, |
- additional_trust_anchors_, error_, |
- verify_result_); |
- } |
- } |
- delete this; |
- } |
- |
- void Finish() { |
- // Runs on the worker thread. |
- // We assume that the origin loop outlives the MultiThreadedCertVerifier. If |
- // the MultiThreadedCertVerifier is deleted, it will call Cancel on us. If |
- // it does so before the Acquire, we'll delete ourselves and return. If it's |
- // trying to do so concurrently, then it'll block on the lock and we'll call |
- // PostTask while the MultiThreadedCertVerifier (and therefore the |
- // MessageLoop) is still alive. |
- // If it does so after this function, we assume that the MessageLoop will |
- // process pending tasks. In which case we'll notice the |canceled_| flag |
- // in DoReply. |
- |
- bool canceled; |
- { |
- base::AutoLock locked(lock_); |
- canceled = canceled_; |
- if (!canceled) { |
- origin_loop_->PostTask( |
- FROM_HERE, base::Bind( |
- &CertVerifierWorker::DoReply, base::Unretained(this))); |
- } |
- } |
- |
- if (canceled) |
- delete this; |
- } |
- |
- scoped_refptr<CertVerifyProc> verify_proc_; |
- scoped_refptr<X509Certificate> cert_; |
- const std::string hostname_; |
- const int flags_; |
- scoped_refptr<CRLSet> crl_set_; |
- const CertificateList additional_trust_anchors_; |
- MessageLoop* const origin_loop_; |
- MultiThreadedCertVerifier* const cert_verifier_; |
- |
- // lock_ protects canceled_. |
- base::Lock lock_; |
- |
- // If canceled_ is true, |
- // * origin_loop_ cannot be accessed by the worker thread, |
- // * cert_verifier_ cannot be accessed by any thread. |
- bool canceled_; |
- |
- int error_; |
- CertVerifyResult verify_result_; |
- |
- DISALLOW_COPY_AND_ASSIGN(CertVerifierWorker); |
-}; |
- |
-// A CertVerifierJob is a one-to-one counterpart of a CertVerifierWorker. It |
-// lives only on the CertVerifier's origin message loop. |
-class CertVerifierJob { |
- public: |
- CertVerifierJob(CertVerifierWorker* worker, |
- const BoundNetLog& net_log) |
- : start_time_(base::TimeTicks::Now()), |
- worker_(worker), |
- net_log_(net_log) { |
- net_log_.BeginEvent( |
- NetLog::TYPE_CERT_VERIFIER_JOB, |
- base::Bind(&NetLogX509CertificateCallback, |
- base::Unretained(worker_->certificate()))); |
- } |
- |
- ~CertVerifierJob() { |
- if (worker_) { |
- net_log_.AddEvent(NetLog::TYPE_CANCELLED); |
- net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_JOB); |
- worker_->Cancel(); |
- DeleteAllCanceled(); |
- } |
- } |
- |
- void AddRequest(CertVerifierRequest* request) { |
- request->net_log().AddEvent( |
- NetLog::TYPE_CERT_VERIFIER_REQUEST_BOUND_TO_JOB, |
- net_log_.source().ToEventParametersCallback()); |
- |
- requests_.push_back(request); |
- } |
- |
- void HandleResult( |
- const MultiThreadedCertVerifier::CachedResult& verify_result) { |
- worker_ = NULL; |
- net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_JOB); |
- UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_Job_Latency", |
- base::TimeTicks::Now() - start_time_, |
- base::TimeDelta::FromMilliseconds(1), |
- base::TimeDelta::FromMinutes(10), |
- 100); |
- PostAll(verify_result); |
- } |
- |
- private: |
- void PostAll(const MultiThreadedCertVerifier::CachedResult& verify_result) { |
- std::vector<CertVerifierRequest*> requests; |
- requests_.swap(requests); |
- |
- for (std::vector<CertVerifierRequest*>::iterator |
- i = requests.begin(); i != requests.end(); i++) { |
- (*i)->Post(verify_result); |
- // Post() causes the CertVerifierRequest to delete itself. |
- } |
- } |
- |
- void DeleteAllCanceled() { |
- for (std::vector<CertVerifierRequest*>::iterator |
- i = requests_.begin(); i != requests_.end(); i++) { |
- if ((*i)->canceled()) { |
- delete *i; |
- } else { |
- LOG(DFATAL) << "CertVerifierRequest leaked!"; |
- } |
- } |
- } |
- |
- const base::TimeTicks start_time_; |
- std::vector<CertVerifierRequest*> requests_; |
- CertVerifierWorker* worker_; |
- const BoundNetLog net_log_; |
-}; |
- |
-MultiThreadedCertVerifier::MultiThreadedCertVerifier( |
- CertVerifyProc* verify_proc) |
- : cache_(kMaxCacheEntries), |
- requests_(0), |
- cache_hits_(0), |
- inflight_joins_(0), |
- verify_proc_(verify_proc), |
- trust_anchor_provider_(NULL) { |
- CertDatabase::GetInstance()->AddObserver(this); |
-} |
- |
-MultiThreadedCertVerifier::~MultiThreadedCertVerifier() { |
- STLDeleteValues(&inflight_); |
- CertDatabase::GetInstance()->RemoveObserver(this); |
-} |
- |
-void MultiThreadedCertVerifier::SetCertTrustAnchorProvider( |
- CertTrustAnchorProvider* trust_anchor_provider) { |
- DCHECK(CalledOnValidThread()); |
- trust_anchor_provider_ = trust_anchor_provider; |
-} |
- |
-int MultiThreadedCertVerifier::Verify(X509Certificate* cert, |
- const std::string& hostname, |
- int flags, |
- CRLSet* crl_set, |
- CertVerifyResult* verify_result, |
- const CompletionCallback& callback, |
- RequestHandle* out_req, |
- const BoundNetLog& net_log) { |
- DCHECK(CalledOnValidThread()); |
- |
- if (callback.is_null() || !verify_result || hostname.empty()) { |
- *out_req = NULL; |
- return ERR_INVALID_ARGUMENT; |
- } |
- |
- requests_++; |
- |
- const CertificateList empty_cert_list; |
- const CertificateList& additional_trust_anchors = |
- trust_anchor_provider_ ? |
- trust_anchor_provider_->GetAdditionalTrustAnchors() : empty_cert_list; |
- |
- const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(), |
- hostname, flags, additional_trust_anchors); |
- const CertVerifierCache::value_type* cached_entry = |
- cache_.Get(key, CacheValidityPeriod(base::Time::Now())); |
- if (cached_entry) { |
- ++cache_hits_; |
- *out_req = NULL; |
- *verify_result = cached_entry->result; |
- return cached_entry->error; |
- } |
- |
- // No cache hit. See if an identical request is currently in flight. |
- CertVerifierJob* job; |
- std::map<RequestParams, CertVerifierJob*>::const_iterator j; |
- j = inflight_.find(key); |
- if (j != inflight_.end()) { |
- // An identical request is in flight already. We'll just attach our |
- // callback. |
- inflight_joins_++; |
- job = j->second; |
- } else { |
- // Need to make a new request. |
- CertVerifierWorker* worker = |
- new CertVerifierWorker(verify_proc_, cert, hostname, flags, crl_set, |
- additional_trust_anchors, this); |
- job = new CertVerifierJob( |
- worker, |
- BoundNetLog::Make(net_log.net_log(), NetLog::SOURCE_CERT_VERIFIER_JOB)); |
- if (!worker->Start()) { |
- delete job; |
- delete worker; |
- *out_req = NULL; |
- // TODO(wtc): log to the NetLog. |
- LOG(ERROR) << "CertVerifierWorker couldn't be started."; |
- return ERR_INSUFFICIENT_RESOURCES; // Just a guess. |
- } |
- inflight_.insert(std::make_pair(key, job)); |
- } |
- |
- CertVerifierRequest* request = |
- new CertVerifierRequest(callback, verify_result, net_log); |
- job->AddRequest(request); |
- *out_req = request; |
- return ERR_IO_PENDING; |
-} |
- |
-void MultiThreadedCertVerifier::CancelRequest(RequestHandle req) { |
- DCHECK(CalledOnValidThread()); |
- CertVerifierRequest* request = reinterpret_cast<CertVerifierRequest*>(req); |
- request->Cancel(); |
-} |
- |
-MultiThreadedCertVerifier::RequestParams::RequestParams( |
- const SHA1HashValue& cert_fingerprint_arg, |
- const SHA1HashValue& ca_fingerprint_arg, |
- const std::string& hostname_arg, |
- int flags_arg, |
- const CertificateList& additional_trust_anchors) |
- : hostname(hostname_arg), |
- flags(flags_arg) { |
- hash_values.reserve(2 + additional_trust_anchors.size()); |
- hash_values.push_back(cert_fingerprint_arg); |
- hash_values.push_back(ca_fingerprint_arg); |
- for (size_t i = 0; i < additional_trust_anchors.size(); ++i) |
- hash_values.push_back(additional_trust_anchors[i]->fingerprint()); |
-} |
- |
-MultiThreadedCertVerifier::RequestParams::~RequestParams() {} |
- |
-bool MultiThreadedCertVerifier::RequestParams::operator<( |
- const RequestParams& other) const { |
- // |flags| is compared before |cert_fingerprint|, |ca_fingerprint|, and |
- // |hostname| under assumption that integer comparisons are faster than |
- // memory and string comparisons. |
- if (flags != other.flags) |
- return flags < other.flags; |
- if (hostname != other.hostname) |
- return hostname < other.hostname; |
- return std::lexicographical_compare( |
- hash_values.begin(), hash_values.end(), |
- other.hash_values.begin(), other.hash_values.end(), |
- net::SHA1HashValueLessThan()); |
-} |
- |
-// HandleResult is called by CertVerifierWorker on the origin message loop. |
-// It deletes CertVerifierJob. |
-void MultiThreadedCertVerifier::HandleResult( |
- X509Certificate* cert, |
- const std::string& hostname, |
- int flags, |
- const CertificateList& additional_trust_anchors, |
- int error, |
- const CertVerifyResult& verify_result) { |
- DCHECK(CalledOnValidThread()); |
- |
- const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(), |
- hostname, flags, additional_trust_anchors); |
- |
- CachedResult cached_result; |
- cached_result.error = error; |
- cached_result.result = verify_result; |
- base::Time now = base::Time::Now(); |
- cache_.Put( |
- key, cached_result, CacheValidityPeriod(now), |
- CacheValidityPeriod(now, now + base::TimeDelta::FromSeconds(kTTLSecs))); |
- |
- std::map<RequestParams, CertVerifierJob*>::iterator j; |
- j = inflight_.find(key); |
- if (j == inflight_.end()) { |
- NOTREACHED(); |
- return; |
- } |
- CertVerifierJob* job = j->second; |
- inflight_.erase(j); |
- |
- job->HandleResult(cached_result); |
- delete job; |
-} |
- |
-void MultiThreadedCertVerifier::OnCertTrustChanged( |
- const X509Certificate* cert) { |
- DCHECK(CalledOnValidThread()); |
- |
- ClearCache(); |
-} |
- |
-} // namespace net |