Windows GN tests, webcrypto warnings
[chromium-blink-merge.git] / net / cert / multi_threaded_cert_verifier.cc
blob9e78abba9b063c409b20f12c5d8eb9b51039fcb7
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
3 // found in the LICENSE file.
5 #include "net/cert/multi_threaded_cert_verifier.h"
7 #include <algorithm>
9 #include "base/bind.h"
10 #include "base/bind_helpers.h"
11 #include "base/compiler_specific.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/metrics/histogram.h"
14 #include "base/profiler/scoped_tracker.h"
15 #include "base/stl_util.h"
16 #include "base/synchronization/lock.h"
17 #include "base/threading/worker_pool.h"
18 #include "base/time/time.h"
19 #include "base/values.h"
20 #include "net/base/hash_value.h"
21 #include "net/base/net_errors.h"
22 #include "net/cert/cert_trust_anchor_provider.h"
23 #include "net/cert/cert_verify_proc.h"
24 #include "net/cert/crl_set.h"
25 #include "net/cert/x509_certificate.h"
26 #include "net/cert/x509_certificate_net_log_param.h"
27 #include "net/log/net_log.h"
29 #if defined(USE_NSS_CERTS) || defined(OS_IOS)
30 #include <private/pprthred.h> // PR_DetachThread
31 #endif
33 namespace net {
35 ////////////////////////////////////////////////////////////////////////////
37 // Life of a request:
39 // MultiThreadedCertVerifier CertVerifierJob CertVerifierWorker Request
40 // | (origin loop) (worker loop)
41 // |
42 // Verify()
43 // |---->-------------------------------------<creates>
44 // |
45 // |---->-------------------<creates>
46 // |
47 // |---->-------------------------------------------------------<creates>
48 // |
49 // |---->---------------------------------------Start
50 // | |
51 // | PostTask
52 // |
53 // | <starts verifying>
54 // |---->-------------------AddRequest |
55 // |
56 // |
57 // |
58 // Finish
59 // |
60 // PostTask
62 // |
63 // DoReply
64 // |----<-----------------------------------------|
65 // HandleResult
66 // |
67 // |---->------------------HandleResult
68 // |
69 // |------>---------------------------Post
73 // On a cache hit, MultiThreadedCertVerifier::Verify() returns synchronously
74 // without posting a task to a worker thread.
76 namespace {
78 // The default value of max_cache_entries_.
79 const unsigned kMaxCacheEntries = 256;
81 // The number of seconds for which we'll cache a cache entry.
82 const unsigned kTTLSecs = 1800; // 30 minutes.
84 base::Value* CertVerifyResultCallback(const CertVerifyResult& verify_result,
85 NetLog::LogLevel log_level) {
86 base::DictionaryValue* results = new base::DictionaryValue();
87 results->SetBoolean("has_md5", verify_result.has_md5);
88 results->SetBoolean("has_md2", verify_result.has_md2);
89 results->SetBoolean("has_md4", verify_result.has_md4);
90 results->SetBoolean("is_issued_by_known_root",
91 verify_result.is_issued_by_known_root);
92 results->SetBoolean("is_issued_by_additional_trust_anchor",
93 verify_result.is_issued_by_additional_trust_anchor);
94 results->SetBoolean("common_name_fallback_used",
95 verify_result.common_name_fallback_used);
96 results->SetInteger("cert_status", verify_result.cert_status);
97 results->Set("verified_cert",
98 NetLogX509CertificateCallback(verify_result.verified_cert.get(),
99 log_level));
101 base::ListValue* hashes = new base::ListValue();
102 for (std::vector<HashValue>::const_iterator it =
103 verify_result.public_key_hashes.begin();
104 it != verify_result.public_key_hashes.end();
105 ++it) {
106 hashes->AppendString(it->ToString());
108 results->Set("public_key_hashes", hashes);
110 return results;
113 } // namespace
115 MultiThreadedCertVerifier::CachedResult::CachedResult() : error(ERR_FAILED) {}
117 MultiThreadedCertVerifier::CachedResult::~CachedResult() {}
119 MultiThreadedCertVerifier::CacheValidityPeriod::CacheValidityPeriod(
120 const base::Time& now)
121 : verification_time(now),
122 expiration_time(now) {
125 MultiThreadedCertVerifier::CacheValidityPeriod::CacheValidityPeriod(
126 const base::Time& now,
127 const base::Time& expiration)
128 : verification_time(now),
129 expiration_time(expiration) {
132 bool MultiThreadedCertVerifier::CacheExpirationFunctor::operator()(
133 const CacheValidityPeriod& now,
134 const CacheValidityPeriod& expiration) const {
135 // Ensure this functor is being used for expiration only, and not strict
136 // weak ordering/sorting. |now| should only ever contain a single
137 // base::Time.
138 // Note: DCHECK_EQ is not used due to operator<< overloading requirements.
139 DCHECK(now.verification_time == now.expiration_time);
141 // |now| contains only a single time (verification_time), while |expiration|
142 // contains the validity range - both when the certificate was verified and
143 // when the verification result should expire.
145 // If the user receives a "not yet valid" message, and adjusts their clock
146 // foward to the correct time, this will (typically) cause
147 // now.verification_time to advance past expiration.expiration_time, thus
148 // treating the cached result as an expired entry and re-verifying.
149 // If the user receives a "expired" message, and adjusts their clock
150 // backwards to the correct time, this will cause now.verification_time to
151 // be less than expiration_verification_time, thus treating the cached
152 // result as an expired entry and re-verifying.
153 // If the user receives either of those messages, and does not adjust their
154 // clock, then the result will be (typically) be cached until the expiration
155 // TTL.
157 // This algorithm is only problematic if the user consistently keeps
158 // adjusting their clock backwards in increments smaller than the expiration
159 // TTL, in which case, cached elements continue to be added. However,
160 // because the cache has a fixed upper bound, if no entries are expired, a
161 // 'random' entry will be, thus keeping the memory constraints bounded over
162 // time.
163 return now.verification_time >= expiration.verification_time &&
164 now.verification_time < expiration.expiration_time;
168 // Represents the output and result callback of a request.
169 class CertVerifierRequest {
170 public:
171 CertVerifierRequest(const CompletionCallback& callback,
172 CertVerifyResult* verify_result,
173 const BoundNetLog& net_log)
174 : callback_(callback),
175 verify_result_(verify_result),
176 net_log_(net_log) {
177 net_log_.BeginEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST);
180 ~CertVerifierRequest() {
183 // Ensures that the result callback will never be made.
184 void Cancel() {
185 callback_.Reset();
186 verify_result_ = NULL;
187 net_log_.AddEvent(NetLog::TYPE_CANCELLED);
188 net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST);
191 // Copies the contents of |verify_result| to the caller's
192 // CertVerifyResult and calls the callback.
193 void Post(const MultiThreadedCertVerifier::CachedResult& verify_result) {
194 if (!callback_.is_null()) {
195 net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST);
196 *verify_result_ = verify_result.result;
197 callback_.Run(verify_result.error);
199 delete this;
202 bool canceled() const { return callback_.is_null(); }
204 const BoundNetLog& net_log() const { return net_log_; }
206 private:
207 CompletionCallback callback_;
208 CertVerifyResult* verify_result_;
209 const BoundNetLog net_log_;
213 // CertVerifierWorker runs on a worker thread and takes care of the blocking
214 // process of performing the certificate verification. Deletes itself
215 // eventually if Start() succeeds.
216 class CertVerifierWorker {
217 public:
218 CertVerifierWorker(CertVerifyProc* verify_proc,
219 X509Certificate* cert,
220 const std::string& hostname,
221 int flags,
222 CRLSet* crl_set,
223 const CertificateList& additional_trust_anchors,
224 MultiThreadedCertVerifier* cert_verifier)
225 : verify_proc_(verify_proc),
226 cert_(cert),
227 hostname_(hostname),
228 flags_(flags),
229 crl_set_(crl_set),
230 additional_trust_anchors_(additional_trust_anchors),
231 origin_loop_(base::MessageLoop::current()),
232 cert_verifier_(cert_verifier),
233 canceled_(false),
234 error_(ERR_FAILED) {
237 // Returns the certificate being verified. May only be called /before/
238 // Start() is called.
239 X509Certificate* certificate() const { return cert_.get(); }
241 bool Start() {
242 DCHECK_EQ(base::MessageLoop::current(), origin_loop_);
244 return base::WorkerPool::PostTask(
245 FROM_HERE, base::Bind(&CertVerifierWorker::Run, base::Unretained(this)),
246 true /* task is slow */);
249 // Cancel is called from the origin loop when the MultiThreadedCertVerifier is
250 // getting deleted.
251 void Cancel() {
252 DCHECK_EQ(base::MessageLoop::current(), origin_loop_);
253 base::AutoLock locked(lock_);
254 canceled_ = true;
257 private:
258 void Run() {
259 // Runs on a worker thread.
260 error_ = verify_proc_->Verify(cert_.get(),
261 hostname_,
262 flags_,
263 crl_set_.get(),
264 additional_trust_anchors_,
265 &verify_result_);
266 #if defined(USE_NSS_CERTS) || defined(OS_IOS)
267 // Detach the thread from NSPR.
268 // Calling NSS functions attaches the thread to NSPR, which stores
269 // the NSPR thread ID in thread-specific data.
270 // The threads in our thread pool terminate after we have called
271 // PR_Cleanup. Unless we detach them from NSPR, net_unittests gets
272 // segfaults on shutdown when the threads' thread-specific data
273 // destructors run.
274 PR_DetachThread();
275 #endif
276 Finish();
279 // DoReply runs on the origin thread.
280 void DoReply() {
281 // TODO(pkasting): Remove ScopedTracker below once crbug.com/477117 is
282 // fixed.
283 tracked_objects::ScopedTracker tracking_profile(
284 FROM_HERE_WITH_EXPLICIT_FUNCTION("477117 CertVerifierWorker::DoReply"));
285 DCHECK_EQ(base::MessageLoop::current(), origin_loop_);
287 // We lock here because the worker thread could still be in Finished,
288 // after the PostTask, but before unlocking |lock_|. If we do not lock in
289 // this case, we will end up deleting a locked Lock, which can lead to
290 // memory leaks or worse errors.
291 base::AutoLock locked(lock_);
292 if (!canceled_) {
293 cert_verifier_->HandleResult(cert_.get(),
294 hostname_,
295 flags_,
296 additional_trust_anchors_,
297 error_,
298 verify_result_);
301 delete this;
304 void Finish() {
305 // Runs on the worker thread.
306 // We assume that the origin loop outlives the MultiThreadedCertVerifier. If
307 // the MultiThreadedCertVerifier is deleted, it will call Cancel on us. If
308 // it does so before the Acquire, we'll delete ourselves and return. If it's
309 // trying to do so concurrently, then it'll block on the lock and we'll call
310 // PostTask while the MultiThreadedCertVerifier (and therefore the
311 // MessageLoop) is still alive.
312 // If it does so after this function, we assume that the MessageLoop will
313 // process pending tasks. In which case we'll notice the |canceled_| flag
314 // in DoReply.
316 bool canceled;
318 base::AutoLock locked(lock_);
319 canceled = canceled_;
320 if (!canceled) {
321 origin_loop_->PostTask(
322 FROM_HERE, base::Bind(
323 &CertVerifierWorker::DoReply, base::Unretained(this)));
327 if (canceled)
328 delete this;
331 scoped_refptr<CertVerifyProc> verify_proc_;
332 scoped_refptr<X509Certificate> cert_;
333 const std::string hostname_;
334 const int flags_;
335 scoped_refptr<CRLSet> crl_set_;
336 const CertificateList additional_trust_anchors_;
337 base::MessageLoop* const origin_loop_;
338 MultiThreadedCertVerifier* const cert_verifier_;
340 // lock_ protects canceled_.
341 base::Lock lock_;
343 // If canceled_ is true,
344 // * origin_loop_ cannot be accessed by the worker thread,
345 // * cert_verifier_ cannot be accessed by any thread.
346 bool canceled_;
348 int error_;
349 CertVerifyResult verify_result_;
351 DISALLOW_COPY_AND_ASSIGN(CertVerifierWorker);
354 // A CertVerifierJob is a one-to-one counterpart of a CertVerifierWorker. It
355 // lives only on the CertVerifier's origin message loop.
356 class CertVerifierJob {
357 public:
358 CertVerifierJob(CertVerifierWorker* worker,
359 const BoundNetLog& net_log)
360 : start_time_(base::TimeTicks::Now()),
361 worker_(worker),
362 net_log_(net_log) {
363 net_log_.BeginEvent(
364 NetLog::TYPE_CERT_VERIFIER_JOB,
365 base::Bind(&NetLogX509CertificateCallback,
366 base::Unretained(worker_->certificate())));
369 ~CertVerifierJob() {
370 if (worker_) {
371 net_log_.AddEvent(NetLog::TYPE_CANCELLED);
372 net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_JOB);
373 worker_->Cancel();
374 DeleteAllCanceled();
378 void AddRequest(CertVerifierRequest* request) {
379 request->net_log().AddEvent(
380 NetLog::TYPE_CERT_VERIFIER_REQUEST_BOUND_TO_JOB,
381 net_log_.source().ToEventParametersCallback());
383 requests_.push_back(request);
386 void HandleResult(
387 const MultiThreadedCertVerifier::CachedResult& verify_result,
388 bool is_first_job) {
389 worker_ = NULL;
390 net_log_.EndEvent(
391 NetLog::TYPE_CERT_VERIFIER_JOB,
392 base::Bind(&CertVerifyResultCallback, verify_result.result));
393 base::TimeDelta latency = base::TimeTicks::Now() - start_time_;
394 UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_Job_Latency",
395 latency,
396 base::TimeDelta::FromMilliseconds(1),
397 base::TimeDelta::FromMinutes(10),
398 100);
399 if (is_first_job) {
400 UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_First_Job_Latency",
401 latency,
402 base::TimeDelta::FromMilliseconds(1),
403 base::TimeDelta::FromMinutes(10),
404 100);
406 PostAll(verify_result);
409 private:
410 void PostAll(const MultiThreadedCertVerifier::CachedResult& verify_result) {
411 std::vector<CertVerifierRequest*> requests;
412 requests_.swap(requests);
414 for (std::vector<CertVerifierRequest*>::iterator
415 i = requests.begin(); i != requests.end(); i++) {
416 (*i)->Post(verify_result);
417 // Post() causes the CertVerifierRequest to delete itself.
421 void DeleteAllCanceled() {
422 for (std::vector<CertVerifierRequest*>::iterator
423 i = requests_.begin(); i != requests_.end(); i++) {
424 if ((*i)->canceled()) {
425 delete *i;
426 } else {
427 LOG(DFATAL) << "CertVerifierRequest leaked!";
432 const base::TimeTicks start_time_;
433 std::vector<CertVerifierRequest*> requests_;
434 CertVerifierWorker* worker_;
435 const BoundNetLog net_log_;
438 MultiThreadedCertVerifier::MultiThreadedCertVerifier(
439 CertVerifyProc* verify_proc)
440 : cache_(kMaxCacheEntries),
441 first_job_(NULL),
442 requests_(0),
443 cache_hits_(0),
444 inflight_joins_(0),
445 verify_proc_(verify_proc),
446 trust_anchor_provider_(NULL) {
447 CertDatabase::GetInstance()->AddObserver(this);
450 MultiThreadedCertVerifier::~MultiThreadedCertVerifier() {
451 STLDeleteValues(&inflight_);
452 CertDatabase::GetInstance()->RemoveObserver(this);
455 void MultiThreadedCertVerifier::SetCertTrustAnchorProvider(
456 CertTrustAnchorProvider* trust_anchor_provider) {
457 DCHECK(CalledOnValidThread());
458 trust_anchor_provider_ = trust_anchor_provider;
461 int MultiThreadedCertVerifier::Verify(X509Certificate* cert,
462 const std::string& hostname,
463 int flags,
464 CRLSet* crl_set,
465 CertVerifyResult* verify_result,
466 const CompletionCallback& callback,
467 RequestHandle* out_req,
468 const BoundNetLog& net_log) {
469 DCHECK(CalledOnValidThread());
471 if (callback.is_null() || !verify_result || hostname.empty()) {
472 *out_req = NULL;
473 return ERR_INVALID_ARGUMENT;
476 requests_++;
478 const CertificateList empty_cert_list;
479 const CertificateList& additional_trust_anchors =
480 trust_anchor_provider_ ?
481 trust_anchor_provider_->GetAdditionalTrustAnchors() : empty_cert_list;
483 const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(),
484 hostname, flags, additional_trust_anchors);
485 const CertVerifierCache::value_type* cached_entry =
486 cache_.Get(key, CacheValidityPeriod(base::Time::Now()));
487 if (cached_entry) {
488 ++cache_hits_;
489 *out_req = NULL;
490 *verify_result = cached_entry->result;
491 return cached_entry->error;
494 // No cache hit. See if an identical request is currently in flight.
495 CertVerifierJob* job;
496 std::map<RequestParams, CertVerifierJob*>::const_iterator j;
497 j = inflight_.find(key);
498 if (j != inflight_.end()) {
499 // An identical request is in flight already. We'll just attach our
500 // callback.
501 inflight_joins_++;
502 job = j->second;
503 } else {
504 // Need to make a new request.
505 CertVerifierWorker* worker =
506 new CertVerifierWorker(verify_proc_.get(),
507 cert,
508 hostname,
509 flags,
510 crl_set,
511 additional_trust_anchors,
512 this);
513 job = new CertVerifierJob(
514 worker,
515 BoundNetLog::Make(net_log.net_log(), NetLog::SOURCE_CERT_VERIFIER_JOB));
516 if (!worker->Start()) {
517 delete job;
518 delete worker;
519 *out_req = NULL;
520 // TODO(wtc): log to the NetLog.
521 LOG(ERROR) << "CertVerifierWorker couldn't be started.";
522 return ERR_INSUFFICIENT_RESOURCES; // Just a guess.
524 inflight_.insert(std::make_pair(key, job));
525 if (requests_ == 1) {
526 // Cleared in HandleResult.
527 first_job_ = job;
531 CertVerifierRequest* request =
532 new CertVerifierRequest(callback, verify_result, net_log);
533 job->AddRequest(request);
534 *out_req = request;
535 return ERR_IO_PENDING;
538 void MultiThreadedCertVerifier::CancelRequest(RequestHandle req) {
539 DCHECK(CalledOnValidThread());
540 CertVerifierRequest* request = reinterpret_cast<CertVerifierRequest*>(req);
541 request->Cancel();
544 MultiThreadedCertVerifier::RequestParams::RequestParams(
545 const SHA1HashValue& cert_fingerprint_arg,
546 const SHA1HashValue& ca_fingerprint_arg,
547 const std::string& hostname_arg,
548 int flags_arg,
549 const CertificateList& additional_trust_anchors)
550 : hostname(hostname_arg),
551 flags(flags_arg) {
552 hash_values.reserve(2 + additional_trust_anchors.size());
553 hash_values.push_back(cert_fingerprint_arg);
554 hash_values.push_back(ca_fingerprint_arg);
555 for (size_t i = 0; i < additional_trust_anchors.size(); ++i)
556 hash_values.push_back(additional_trust_anchors[i]->fingerprint());
559 MultiThreadedCertVerifier::RequestParams::~RequestParams() {}
561 bool MultiThreadedCertVerifier::RequestParams::operator<(
562 const RequestParams& other) const {
563 // |flags| is compared before |cert_fingerprint|, |ca_fingerprint|, and
564 // |hostname| under assumption that integer comparisons are faster than
565 // memory and string comparisons.
566 if (flags != other.flags)
567 return flags < other.flags;
568 if (hostname != other.hostname)
569 return hostname < other.hostname;
570 return std::lexicographical_compare(
571 hash_values.begin(), hash_values.end(),
572 other.hash_values.begin(), other.hash_values.end(),
573 net::SHA1HashValueLessThan());
576 // HandleResult is called by CertVerifierWorker on the origin message loop.
577 // It deletes CertVerifierJob.
578 void MultiThreadedCertVerifier::HandleResult(
579 X509Certificate* cert,
580 const std::string& hostname,
581 int flags,
582 const CertificateList& additional_trust_anchors,
583 int error,
584 const CertVerifyResult& verify_result) {
585 DCHECK(CalledOnValidThread());
587 const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(),
588 hostname, flags, additional_trust_anchors);
590 CachedResult cached_result;
591 cached_result.error = error;
592 cached_result.result = verify_result;
593 base::Time now = base::Time::Now();
594 cache_.Put(
595 key, cached_result, CacheValidityPeriod(now),
596 CacheValidityPeriod(now, now + base::TimeDelta::FromSeconds(kTTLSecs)));
598 std::map<RequestParams, CertVerifierJob*>::iterator j;
599 j = inflight_.find(key);
600 if (j == inflight_.end()) {
601 NOTREACHED();
602 return;
604 CertVerifierJob* job = j->second;
605 inflight_.erase(j);
606 bool is_first_job = false;
607 if (first_job_ == job) {
608 is_first_job = true;
609 first_job_ = NULL;
612 job->HandleResult(cached_result, is_first_job);
613 delete job;
616 void MultiThreadedCertVerifier::OnCACertChanged(
617 const X509Certificate* cert) {
618 DCHECK(CalledOnValidThread());
620 ClearCache();
623 } // namespace net