Enable -Wl,-z,defs for ozone builds
[chromium-blink-merge.git] / net / cert / multi_threaded_cert_verifier.cc
blobfc41fa97c925c7d324c5589ffc1d5a7a22150df7
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/stl_util.h"
15 #include "base/synchronization/lock.h"
16 #include "base/threading/worker_pool.h"
17 #include "base/time/time.h"
18 #include "base/values.h"
19 #include "net/base/hash_value.h"
20 #include "net/base/net_errors.h"
21 #include "net/base/net_log.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"
28 #if defined(USE_NSS) || defined(OS_IOS)
29 #include <private/pprthred.h> // PR_DetachThread
30 #endif
32 namespace net {
34 ////////////////////////////////////////////////////////////////////////////
36 // Life of a request:
38 // MultiThreadedCertVerifier CertVerifierJob CertVerifierWorker Request
39 // | (origin loop) (worker loop)
40 // |
41 // Verify()
42 // |---->-------------------------------------<creates>
43 // |
44 // |---->-------------------<creates>
45 // |
46 // |---->-------------------------------------------------------<creates>
47 // |
48 // |---->---------------------------------------Start
49 // | |
50 // | PostTask
51 // |
52 // | <starts verifying>
53 // |---->-------------------AddRequest |
54 // |
55 // |
56 // |
57 // Finish
58 // |
59 // PostTask
61 // |
62 // DoReply
63 // |----<-----------------------------------------|
64 // HandleResult
65 // |
66 // |---->------------------HandleResult
67 // |
68 // |------>---------------------------Post
72 // On a cache hit, MultiThreadedCertVerifier::Verify() returns synchronously
73 // without posting a task to a worker thread.
75 namespace {
77 // The default value of max_cache_entries_.
78 const unsigned kMaxCacheEntries = 256;
80 // The number of seconds for which we'll cache a cache entry.
81 const unsigned kTTLSecs = 1800; // 30 minutes.
83 base::Value* CertVerifyResultCallback(const CertVerifyResult& verify_result,
84 NetLog::LogLevel log_level) {
85 base::DictionaryValue* results = new base::DictionaryValue();
86 results->SetBoolean("has_md5", verify_result.has_md5);
87 results->SetBoolean("has_md2", verify_result.has_md2);
88 results->SetBoolean("has_md4", verify_result.has_md4);
89 results->SetBoolean("is_issued_by_known_root",
90 verify_result.is_issued_by_known_root);
91 results->SetBoolean("is_issued_by_additional_trust_anchor",
92 verify_result.is_issued_by_additional_trust_anchor);
93 results->SetBoolean("common_name_fallback_used",
94 verify_result.common_name_fallback_used);
95 results->SetInteger("cert_status", verify_result.cert_status);
96 results->Set("verified_cert",
97 NetLogX509CertificateCallback(verify_result.verified_cert.get(),
98 log_level));
100 base::ListValue* hashes = new base::ListValue();
101 for (std::vector<HashValue>::const_iterator it =
102 verify_result.public_key_hashes.begin();
103 it != verify_result.public_key_hashes.end();
104 ++it) {
105 hashes->AppendString(it->ToString());
107 results->Set("public_key_hashes", hashes);
109 return results;
112 } // namespace
114 MultiThreadedCertVerifier::CachedResult::CachedResult() : error(ERR_FAILED) {}
116 MultiThreadedCertVerifier::CachedResult::~CachedResult() {}
118 MultiThreadedCertVerifier::CacheValidityPeriod::CacheValidityPeriod(
119 const base::Time& now)
120 : verification_time(now),
121 expiration_time(now) {
124 MultiThreadedCertVerifier::CacheValidityPeriod::CacheValidityPeriod(
125 const base::Time& now,
126 const base::Time& expiration)
127 : verification_time(now),
128 expiration_time(expiration) {
131 bool MultiThreadedCertVerifier::CacheExpirationFunctor::operator()(
132 const CacheValidityPeriod& now,
133 const CacheValidityPeriod& expiration) const {
134 // Ensure this functor is being used for expiration only, and not strict
135 // weak ordering/sorting. |now| should only ever contain a single
136 // base::Time.
137 // Note: DCHECK_EQ is not used due to operator<< overloading requirements.
138 DCHECK(now.verification_time == now.expiration_time);
140 // |now| contains only a single time (verification_time), while |expiration|
141 // contains the validity range - both when the certificate was verified and
142 // when the verification result should expire.
144 // If the user receives a "not yet valid" message, and adjusts their clock
145 // foward to the correct time, this will (typically) cause
146 // now.verification_time to advance past expiration.expiration_time, thus
147 // treating the cached result as an expired entry and re-verifying.
148 // If the user receives a "expired" message, and adjusts their clock
149 // backwards to the correct time, this will cause now.verification_time to
150 // be less than expiration_verification_time, thus treating the cached
151 // result as an expired entry and re-verifying.
152 // If the user receives either of those messages, and does not adjust their
153 // clock, then the result will be (typically) be cached until the expiration
154 // TTL.
156 // This algorithm is only problematic if the user consistently keeps
157 // adjusting their clock backwards in increments smaller than the expiration
158 // TTL, in which case, cached elements continue to be added. However,
159 // because the cache has a fixed upper bound, if no entries are expired, a
160 // 'random' entry will be, thus keeping the memory constraints bounded over
161 // time.
162 return now.verification_time >= expiration.verification_time &&
163 now.verification_time < expiration.expiration_time;
167 // Represents the output and result callback of a request.
168 class CertVerifierRequest {
169 public:
170 CertVerifierRequest(const CompletionCallback& callback,
171 CertVerifyResult* verify_result,
172 const BoundNetLog& net_log)
173 : callback_(callback),
174 verify_result_(verify_result),
175 net_log_(net_log) {
176 net_log_.BeginEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST);
179 ~CertVerifierRequest() {
182 // Ensures that the result callback will never be made.
183 void Cancel() {
184 callback_.Reset();
185 verify_result_ = NULL;
186 net_log_.AddEvent(NetLog::TYPE_CANCELLED);
187 net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST);
190 // Copies the contents of |verify_result| to the caller's
191 // CertVerifyResult and calls the callback.
192 void Post(const MultiThreadedCertVerifier::CachedResult& verify_result) {
193 if (!callback_.is_null()) {
194 net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST);
195 *verify_result_ = verify_result.result;
196 callback_.Run(verify_result.error);
198 delete this;
201 bool canceled() const { return callback_.is_null(); }
203 const BoundNetLog& net_log() const { return net_log_; }
205 private:
206 CompletionCallback callback_;
207 CertVerifyResult* verify_result_;
208 const BoundNetLog net_log_;
212 // CertVerifierWorker runs on a worker thread and takes care of the blocking
213 // process of performing the certificate verification. Deletes itself
214 // eventually if Start() succeeds.
215 class CertVerifierWorker {
216 public:
217 CertVerifierWorker(CertVerifyProc* verify_proc,
218 X509Certificate* cert,
219 const std::string& hostname,
220 int flags,
221 CRLSet* crl_set,
222 const CertificateList& additional_trust_anchors,
223 MultiThreadedCertVerifier* cert_verifier)
224 : verify_proc_(verify_proc),
225 cert_(cert),
226 hostname_(hostname),
227 flags_(flags),
228 crl_set_(crl_set),
229 additional_trust_anchors_(additional_trust_anchors),
230 origin_loop_(base::MessageLoop::current()),
231 cert_verifier_(cert_verifier),
232 canceled_(false),
233 error_(ERR_FAILED) {
236 // Returns the certificate being verified. May only be called /before/
237 // Start() is called.
238 X509Certificate* certificate() const { return cert_.get(); }
240 bool Start() {
241 DCHECK_EQ(base::MessageLoop::current(), origin_loop_);
243 return base::WorkerPool::PostTask(
244 FROM_HERE, base::Bind(&CertVerifierWorker::Run, base::Unretained(this)),
245 true /* task is slow */);
248 // Cancel is called from the origin loop when the MultiThreadedCertVerifier is
249 // getting deleted.
250 void Cancel() {
251 DCHECK_EQ(base::MessageLoop::current(), origin_loop_);
252 base::AutoLock locked(lock_);
253 canceled_ = true;
256 private:
257 void Run() {
258 // Runs on a worker thread.
259 error_ = verify_proc_->Verify(cert_.get(),
260 hostname_,
261 flags_,
262 crl_set_.get(),
263 additional_trust_anchors_,
264 &verify_result_);
265 #if defined(USE_NSS) || defined(OS_IOS)
266 // Detach the thread from NSPR.
267 // Calling NSS functions attaches the thread to NSPR, which stores
268 // the NSPR thread ID in thread-specific data.
269 // The threads in our thread pool terminate after we have called
270 // PR_Cleanup. Unless we detach them from NSPR, net_unittests gets
271 // segfaults on shutdown when the threads' thread-specific data
272 // destructors run.
273 PR_DetachThread();
274 #endif
275 Finish();
278 // DoReply runs on the origin thread.
279 void DoReply() {
280 DCHECK_EQ(base::MessageLoop::current(), origin_loop_);
282 // We lock here because the worker thread could still be in Finished,
283 // after the PostTask, but before unlocking |lock_|. If we do not lock in
284 // this case, we will end up deleting a locked Lock, which can lead to
285 // memory leaks or worse errors.
286 base::AutoLock locked(lock_);
287 if (!canceled_) {
288 cert_verifier_->HandleResult(cert_.get(),
289 hostname_,
290 flags_,
291 additional_trust_anchors_,
292 error_,
293 verify_result_);
296 delete this;
299 void Finish() {
300 // Runs on the worker thread.
301 // We assume that the origin loop outlives the MultiThreadedCertVerifier. If
302 // the MultiThreadedCertVerifier is deleted, it will call Cancel on us. If
303 // it does so before the Acquire, we'll delete ourselves and return. If it's
304 // trying to do so concurrently, then it'll block on the lock and we'll call
305 // PostTask while the MultiThreadedCertVerifier (and therefore the
306 // MessageLoop) is still alive.
307 // If it does so after this function, we assume that the MessageLoop will
308 // process pending tasks. In which case we'll notice the |canceled_| flag
309 // in DoReply.
311 bool canceled;
313 base::AutoLock locked(lock_);
314 canceled = canceled_;
315 if (!canceled) {
316 origin_loop_->PostTask(
317 FROM_HERE, base::Bind(
318 &CertVerifierWorker::DoReply, base::Unretained(this)));
322 if (canceled)
323 delete this;
326 scoped_refptr<CertVerifyProc> verify_proc_;
327 scoped_refptr<X509Certificate> cert_;
328 const std::string hostname_;
329 const int flags_;
330 scoped_refptr<CRLSet> crl_set_;
331 const CertificateList additional_trust_anchors_;
332 base::MessageLoop* const origin_loop_;
333 MultiThreadedCertVerifier* const cert_verifier_;
335 // lock_ protects canceled_.
336 base::Lock lock_;
338 // If canceled_ is true,
339 // * origin_loop_ cannot be accessed by the worker thread,
340 // * cert_verifier_ cannot be accessed by any thread.
341 bool canceled_;
343 int error_;
344 CertVerifyResult verify_result_;
346 DISALLOW_COPY_AND_ASSIGN(CertVerifierWorker);
349 // A CertVerifierJob is a one-to-one counterpart of a CertVerifierWorker. It
350 // lives only on the CertVerifier's origin message loop.
351 class CertVerifierJob {
352 public:
353 CertVerifierJob(CertVerifierWorker* worker,
354 const BoundNetLog& net_log)
355 : start_time_(base::TimeTicks::Now()),
356 worker_(worker),
357 net_log_(net_log) {
358 net_log_.BeginEvent(
359 NetLog::TYPE_CERT_VERIFIER_JOB,
360 base::Bind(&NetLogX509CertificateCallback,
361 base::Unretained(worker_->certificate())));
364 ~CertVerifierJob() {
365 if (worker_) {
366 net_log_.AddEvent(NetLog::TYPE_CANCELLED);
367 net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_JOB);
368 worker_->Cancel();
369 DeleteAllCanceled();
373 void AddRequest(CertVerifierRequest* request) {
374 request->net_log().AddEvent(
375 NetLog::TYPE_CERT_VERIFIER_REQUEST_BOUND_TO_JOB,
376 net_log_.source().ToEventParametersCallback());
378 requests_.push_back(request);
381 void HandleResult(
382 const MultiThreadedCertVerifier::CachedResult& verify_result,
383 bool is_first_job) {
384 worker_ = NULL;
385 net_log_.EndEvent(
386 NetLog::TYPE_CERT_VERIFIER_JOB,
387 base::Bind(&CertVerifyResultCallback, verify_result.result));
388 base::TimeDelta latency = base::TimeTicks::Now() - start_time_;
389 UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_Job_Latency",
390 latency,
391 base::TimeDelta::FromMilliseconds(1),
392 base::TimeDelta::FromMinutes(10),
393 100);
394 if (is_first_job) {
395 UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_First_Job_Latency",
396 latency,
397 base::TimeDelta::FromMilliseconds(1),
398 base::TimeDelta::FromMinutes(10),
399 100);
401 PostAll(verify_result);
404 private:
405 void PostAll(const MultiThreadedCertVerifier::CachedResult& verify_result) {
406 std::vector<CertVerifierRequest*> requests;
407 requests_.swap(requests);
409 for (std::vector<CertVerifierRequest*>::iterator
410 i = requests.begin(); i != requests.end(); i++) {
411 (*i)->Post(verify_result);
412 // Post() causes the CertVerifierRequest to delete itself.
416 void DeleteAllCanceled() {
417 for (std::vector<CertVerifierRequest*>::iterator
418 i = requests_.begin(); i != requests_.end(); i++) {
419 if ((*i)->canceled()) {
420 delete *i;
421 } else {
422 LOG(DFATAL) << "CertVerifierRequest leaked!";
427 const base::TimeTicks start_time_;
428 std::vector<CertVerifierRequest*> requests_;
429 CertVerifierWorker* worker_;
430 const BoundNetLog net_log_;
433 MultiThreadedCertVerifier::MultiThreadedCertVerifier(
434 CertVerifyProc* verify_proc)
435 : cache_(kMaxCacheEntries),
436 first_job_(NULL),
437 requests_(0),
438 cache_hits_(0),
439 inflight_joins_(0),
440 verify_proc_(verify_proc),
441 trust_anchor_provider_(NULL) {
442 CertDatabase::GetInstance()->AddObserver(this);
445 MultiThreadedCertVerifier::~MultiThreadedCertVerifier() {
446 STLDeleteValues(&inflight_);
447 CertDatabase::GetInstance()->RemoveObserver(this);
450 void MultiThreadedCertVerifier::SetCertTrustAnchorProvider(
451 CertTrustAnchorProvider* trust_anchor_provider) {
452 DCHECK(CalledOnValidThread());
453 trust_anchor_provider_ = trust_anchor_provider;
456 int MultiThreadedCertVerifier::Verify(X509Certificate* cert,
457 const std::string& hostname,
458 int flags,
459 CRLSet* crl_set,
460 CertVerifyResult* verify_result,
461 const CompletionCallback& callback,
462 RequestHandle* out_req,
463 const BoundNetLog& net_log) {
464 DCHECK(CalledOnValidThread());
466 if (callback.is_null() || !verify_result || hostname.empty()) {
467 *out_req = NULL;
468 return ERR_INVALID_ARGUMENT;
471 requests_++;
473 const CertificateList empty_cert_list;
474 const CertificateList& additional_trust_anchors =
475 trust_anchor_provider_ ?
476 trust_anchor_provider_->GetAdditionalTrustAnchors() : empty_cert_list;
478 const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(),
479 hostname, flags, additional_trust_anchors);
480 const CertVerifierCache::value_type* cached_entry =
481 cache_.Get(key, CacheValidityPeriod(base::Time::Now()));
482 if (cached_entry) {
483 ++cache_hits_;
484 *out_req = NULL;
485 *verify_result = cached_entry->result;
486 return cached_entry->error;
489 // No cache hit. See if an identical request is currently in flight.
490 CertVerifierJob* job;
491 std::map<RequestParams, CertVerifierJob*>::const_iterator j;
492 j = inflight_.find(key);
493 if (j != inflight_.end()) {
494 // An identical request is in flight already. We'll just attach our
495 // callback.
496 inflight_joins_++;
497 job = j->second;
498 } else {
499 // Need to make a new request.
500 CertVerifierWorker* worker =
501 new CertVerifierWorker(verify_proc_.get(),
502 cert,
503 hostname,
504 flags,
505 crl_set,
506 additional_trust_anchors,
507 this);
508 job = new CertVerifierJob(
509 worker,
510 BoundNetLog::Make(net_log.net_log(), NetLog::SOURCE_CERT_VERIFIER_JOB));
511 if (!worker->Start()) {
512 delete job;
513 delete worker;
514 *out_req = NULL;
515 // TODO(wtc): log to the NetLog.
516 LOG(ERROR) << "CertVerifierWorker couldn't be started.";
517 return ERR_INSUFFICIENT_RESOURCES; // Just a guess.
519 inflight_.insert(std::make_pair(key, job));
520 if (requests_ == 1) {
521 // Cleared in HandleResult.
522 first_job_ = job;
526 CertVerifierRequest* request =
527 new CertVerifierRequest(callback, verify_result, net_log);
528 job->AddRequest(request);
529 *out_req = request;
530 return ERR_IO_PENDING;
533 void MultiThreadedCertVerifier::CancelRequest(RequestHandle req) {
534 DCHECK(CalledOnValidThread());
535 CertVerifierRequest* request = reinterpret_cast<CertVerifierRequest*>(req);
536 request->Cancel();
539 MultiThreadedCertVerifier::RequestParams::RequestParams(
540 const SHA1HashValue& cert_fingerprint_arg,
541 const SHA1HashValue& ca_fingerprint_arg,
542 const std::string& hostname_arg,
543 int flags_arg,
544 const CertificateList& additional_trust_anchors)
545 : hostname(hostname_arg),
546 flags(flags_arg) {
547 hash_values.reserve(2 + additional_trust_anchors.size());
548 hash_values.push_back(cert_fingerprint_arg);
549 hash_values.push_back(ca_fingerprint_arg);
550 for (size_t i = 0; i < additional_trust_anchors.size(); ++i)
551 hash_values.push_back(additional_trust_anchors[i]->fingerprint());
554 MultiThreadedCertVerifier::RequestParams::~RequestParams() {}
556 bool MultiThreadedCertVerifier::RequestParams::operator<(
557 const RequestParams& other) const {
558 // |flags| is compared before |cert_fingerprint|, |ca_fingerprint|, and
559 // |hostname| under assumption that integer comparisons are faster than
560 // memory and string comparisons.
561 if (flags != other.flags)
562 return flags < other.flags;
563 if (hostname != other.hostname)
564 return hostname < other.hostname;
565 return std::lexicographical_compare(
566 hash_values.begin(), hash_values.end(),
567 other.hash_values.begin(), other.hash_values.end(),
568 net::SHA1HashValueLessThan());
571 // HandleResult is called by CertVerifierWorker on the origin message loop.
572 // It deletes CertVerifierJob.
573 void MultiThreadedCertVerifier::HandleResult(
574 X509Certificate* cert,
575 const std::string& hostname,
576 int flags,
577 const CertificateList& additional_trust_anchors,
578 int error,
579 const CertVerifyResult& verify_result) {
580 DCHECK(CalledOnValidThread());
582 const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(),
583 hostname, flags, additional_trust_anchors);
585 CachedResult cached_result;
586 cached_result.error = error;
587 cached_result.result = verify_result;
588 base::Time now = base::Time::Now();
589 cache_.Put(
590 key, cached_result, CacheValidityPeriod(now),
591 CacheValidityPeriod(now, now + base::TimeDelta::FromSeconds(kTTLSecs)));
593 std::map<RequestParams, CertVerifierJob*>::iterator j;
594 j = inflight_.find(key);
595 if (j == inflight_.end()) {
596 NOTREACHED();
597 return;
599 CertVerifierJob* job = j->second;
600 inflight_.erase(j);
601 bool is_first_job = false;
602 if (first_job_ == job) {
603 is_first_job = true;
604 first_job_ = NULL;
607 job->HandleResult(cached_result, is_first_job);
608 delete job;
611 void MultiThreadedCertVerifier::OnCACertChanged(
612 const X509Certificate* cert) {
613 DCHECK(CalledOnValidThread());
615 ClearCache();
618 } // namespace net