• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.
4 
5 #include "net/cert/multi_threaded_cert_verifier.h"
6 
7 #include <algorithm>
8 
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 "net/base/net_errors.h"
19 #include "net/base/net_log.h"
20 #include "net/cert/cert_trust_anchor_provider.h"
21 #include "net/cert/cert_verify_proc.h"
22 #include "net/cert/crl_set.h"
23 #include "net/cert/x509_certificate.h"
24 #include "net/cert/x509_certificate_net_log_param.h"
25 
26 #if defined(USE_NSS) || defined(OS_IOS)
27 #include <private/pprthred.h>  // PR_DetachThread
28 #endif
29 
30 namespace net {
31 
32 ////////////////////////////////////////////////////////////////////////////
33 
34 // Life of a request:
35 //
36 // MultiThreadedCertVerifier  CertVerifierJob  CertVerifierWorker     Request
37 //      |                                         (origin loop)    (worker loop)
38 //      |
39 //   Verify()
40 //      |---->-------------------------------------<creates>
41 //      |
42 //      |---->-------------------<creates>
43 //      |
44 //      |---->-------------------------------------------------------<creates>
45 //      |
46 //      |---->---------------------------------------Start
47 //      |                                              |
48 //      |                                           PostTask
49 //      |
50 //      |                                                     <starts verifying>
51 //      |---->-------------------AddRequest                           |
52 //                                                                    |
53 //                                                                    |
54 //                                                                    |
55 //                                                                  Finish
56 //                                                                    |
57 //                                                                 PostTask
58 //
59 //                                                     |
60 //                                                  DoReply
61 //      |----<-----------------------------------------|
62 //  HandleResult
63 //      |
64 //      |---->------------------HandleResult
65 //                                   |
66 //                                   |------>---------------------------Post
67 //
68 //
69 //
70 // On a cache hit, MultiThreadedCertVerifier::Verify() returns synchronously
71 // without posting a task to a worker thread.
72 
73 namespace {
74 
75 // The default value of max_cache_entries_.
76 const unsigned kMaxCacheEntries = 256;
77 
78 // The number of seconds for which we'll cache a cache entry.
79 const unsigned kTTLSecs = 1800;  // 30 minutes.
80 
81 }  // namespace
82 
CachedResult()83 MultiThreadedCertVerifier::CachedResult::CachedResult() : error(ERR_FAILED) {}
84 
~CachedResult()85 MultiThreadedCertVerifier::CachedResult::~CachedResult() {}
86 
CacheValidityPeriod(const base::Time & now)87 MultiThreadedCertVerifier::CacheValidityPeriod::CacheValidityPeriod(
88     const base::Time& now)
89     : verification_time(now),
90       expiration_time(now) {
91 }
92 
CacheValidityPeriod(const base::Time & now,const base::Time & expiration)93 MultiThreadedCertVerifier::CacheValidityPeriod::CacheValidityPeriod(
94     const base::Time& now,
95     const base::Time& expiration)
96     : verification_time(now),
97       expiration_time(expiration) {
98 }
99 
operator ()(const CacheValidityPeriod & now,const CacheValidityPeriod & expiration) const100 bool MultiThreadedCertVerifier::CacheExpirationFunctor::operator()(
101     const CacheValidityPeriod& now,
102     const CacheValidityPeriod& expiration) const {
103   // Ensure this functor is being used for expiration only, and not strict
104   // weak ordering/sorting. |now| should only ever contain a single
105   // base::Time.
106   // Note: DCHECK_EQ is not used due to operator<< overloading requirements.
107   DCHECK(now.verification_time == now.expiration_time);
108 
109   // |now| contains only a single time (verification_time), while |expiration|
110   // contains the validity range - both when the certificate was verified and
111   // when the verification result should expire.
112   //
113   // If the user receives a "not yet valid" message, and adjusts their clock
114   // foward to the correct time, this will (typically) cause
115   // now.verification_time to advance past expiration.expiration_time, thus
116   // treating the cached result as an expired entry and re-verifying.
117   // If the user receives a "expired" message, and adjusts their clock
118   // backwards to the correct time, this will cause now.verification_time to
119   // be less than expiration_verification_time, thus treating the cached
120   // result as an expired entry and re-verifying.
121   // If the user receives either of those messages, and does not adjust their
122   // clock, then the result will be (typically) be cached until the expiration
123   // TTL.
124   //
125   // This algorithm is only problematic if the user consistently keeps
126   // adjusting their clock backwards in increments smaller than the expiration
127   // TTL, in which case, cached elements continue to be added. However,
128   // because the cache has a fixed upper bound, if no entries are expired, a
129   // 'random' entry will be, thus keeping the memory constraints bounded over
130   // time.
131   return now.verification_time >= expiration.verification_time &&
132          now.verification_time < expiration.expiration_time;
133 };
134 
135 
136 // Represents the output and result callback of a request.
137 class CertVerifierRequest {
138  public:
CertVerifierRequest(const CompletionCallback & callback,CertVerifyResult * verify_result,const BoundNetLog & net_log)139   CertVerifierRequest(const CompletionCallback& callback,
140                       CertVerifyResult* verify_result,
141                       const BoundNetLog& net_log)
142       : callback_(callback),
143         verify_result_(verify_result),
144         net_log_(net_log) {
145     net_log_.BeginEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST);
146   }
147 
~CertVerifierRequest()148   ~CertVerifierRequest() {
149   }
150 
151   // Ensures that the result callback will never be made.
Cancel()152   void Cancel() {
153     callback_.Reset();
154     verify_result_ = NULL;
155     net_log_.AddEvent(NetLog::TYPE_CANCELLED);
156     net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST);
157   }
158 
159   // Copies the contents of |verify_result| to the caller's
160   // CertVerifyResult and calls the callback.
Post(const MultiThreadedCertVerifier::CachedResult & verify_result)161   void Post(const MultiThreadedCertVerifier::CachedResult& verify_result) {
162     if (!callback_.is_null()) {
163       net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST);
164       *verify_result_ = verify_result.result;
165       callback_.Run(verify_result.error);
166     }
167     delete this;
168   }
169 
canceled() const170   bool canceled() const { return callback_.is_null(); }
171 
net_log() const172   const BoundNetLog& net_log() const { return net_log_; }
173 
174  private:
175   CompletionCallback callback_;
176   CertVerifyResult* verify_result_;
177   const BoundNetLog net_log_;
178 };
179 
180 
181 // CertVerifierWorker runs on a worker thread and takes care of the blocking
182 // process of performing the certificate verification.  Deletes itself
183 // eventually if Start() succeeds.
184 class CertVerifierWorker {
185  public:
CertVerifierWorker(CertVerifyProc * verify_proc,X509Certificate * cert,const std::string & hostname,int flags,CRLSet * crl_set,const CertificateList & additional_trust_anchors,MultiThreadedCertVerifier * cert_verifier)186   CertVerifierWorker(CertVerifyProc* verify_proc,
187                      X509Certificate* cert,
188                      const std::string& hostname,
189                      int flags,
190                      CRLSet* crl_set,
191                      const CertificateList& additional_trust_anchors,
192                      MultiThreadedCertVerifier* cert_verifier)
193       : verify_proc_(verify_proc),
194         cert_(cert),
195         hostname_(hostname),
196         flags_(flags),
197         crl_set_(crl_set),
198         additional_trust_anchors_(additional_trust_anchors),
199         origin_loop_(base::MessageLoop::current()),
200         cert_verifier_(cert_verifier),
201         canceled_(false),
202         error_(ERR_FAILED) {
203   }
204 
205   // Returns the certificate being verified. May only be called /before/
206   // Start() is called.
certificate() const207   X509Certificate* certificate() const { return cert_.get(); }
208 
Start()209   bool Start() {
210     DCHECK_EQ(base::MessageLoop::current(), origin_loop_);
211 
212     return base::WorkerPool::PostTask(
213         FROM_HERE, base::Bind(&CertVerifierWorker::Run, base::Unretained(this)),
214         true /* task is slow */);
215   }
216 
217   // Cancel is called from the origin loop when the MultiThreadedCertVerifier is
218   // getting deleted.
Cancel()219   void Cancel() {
220     DCHECK_EQ(base::MessageLoop::current(), origin_loop_);
221     base::AutoLock locked(lock_);
222     canceled_ = true;
223   }
224 
225  private:
Run()226   void Run() {
227     // Runs on a worker thread.
228     error_ = verify_proc_->Verify(cert_.get(),
229                                   hostname_,
230                                   flags_,
231                                   crl_set_.get(),
232                                   additional_trust_anchors_,
233                                   &verify_result_);
234 #if defined(USE_NSS) || defined(OS_IOS)
235     // Detach the thread from NSPR.
236     // Calling NSS functions attaches the thread to NSPR, which stores
237     // the NSPR thread ID in thread-specific data.
238     // The threads in our thread pool terminate after we have called
239     // PR_Cleanup.  Unless we detach them from NSPR, net_unittests gets
240     // segfaults on shutdown when the threads' thread-specific data
241     // destructors run.
242     PR_DetachThread();
243 #endif
244     Finish();
245   }
246 
247   // DoReply runs on the origin thread.
DoReply()248   void DoReply() {
249     DCHECK_EQ(base::MessageLoop::current(), origin_loop_);
250     {
251       // We lock here because the worker thread could still be in Finished,
252       // after the PostTask, but before unlocking |lock_|. If we do not lock in
253       // this case, we will end up deleting a locked Lock, which can lead to
254       // memory leaks or worse errors.
255       base::AutoLock locked(lock_);
256       if (!canceled_) {
257         cert_verifier_->HandleResult(cert_.get(),
258                                      hostname_,
259                                      flags_,
260                                      additional_trust_anchors_,
261                                      error_,
262                                      verify_result_);
263       }
264     }
265     delete this;
266   }
267 
Finish()268   void Finish() {
269     // Runs on the worker thread.
270     // We assume that the origin loop outlives the MultiThreadedCertVerifier. If
271     // the MultiThreadedCertVerifier is deleted, it will call Cancel on us. If
272     // it does so before the Acquire, we'll delete ourselves and return. If it's
273     // trying to do so concurrently, then it'll block on the lock and we'll call
274     // PostTask while the MultiThreadedCertVerifier (and therefore the
275     // MessageLoop) is still alive.
276     // If it does so after this function, we assume that the MessageLoop will
277     // process pending tasks. In which case we'll notice the |canceled_| flag
278     // in DoReply.
279 
280     bool canceled;
281     {
282       base::AutoLock locked(lock_);
283       canceled = canceled_;
284       if (!canceled) {
285         origin_loop_->PostTask(
286             FROM_HERE, base::Bind(
287                 &CertVerifierWorker::DoReply, base::Unretained(this)));
288       }
289     }
290 
291     if (canceled)
292       delete this;
293   }
294 
295   scoped_refptr<CertVerifyProc> verify_proc_;
296   scoped_refptr<X509Certificate> cert_;
297   const std::string hostname_;
298   const int flags_;
299   scoped_refptr<CRLSet> crl_set_;
300   const CertificateList additional_trust_anchors_;
301   base::MessageLoop* const origin_loop_;
302   MultiThreadedCertVerifier* const cert_verifier_;
303 
304   // lock_ protects canceled_.
305   base::Lock lock_;
306 
307   // If canceled_ is true,
308   // * origin_loop_ cannot be accessed by the worker thread,
309   // * cert_verifier_ cannot be accessed by any thread.
310   bool canceled_;
311 
312   int error_;
313   CertVerifyResult verify_result_;
314 
315   DISALLOW_COPY_AND_ASSIGN(CertVerifierWorker);
316 };
317 
318 // A CertVerifierJob is a one-to-one counterpart of a CertVerifierWorker. It
319 // lives only on the CertVerifier's origin message loop.
320 class CertVerifierJob {
321  public:
CertVerifierJob(CertVerifierWorker * worker,const BoundNetLog & net_log)322   CertVerifierJob(CertVerifierWorker* worker,
323                   const BoundNetLog& net_log)
324       : start_time_(base::TimeTicks::Now()),
325         worker_(worker),
326         net_log_(net_log) {
327     net_log_.BeginEvent(
328         NetLog::TYPE_CERT_VERIFIER_JOB,
329         base::Bind(&NetLogX509CertificateCallback,
330                    base::Unretained(worker_->certificate())));
331   }
332 
~CertVerifierJob()333   ~CertVerifierJob() {
334     if (worker_) {
335       net_log_.AddEvent(NetLog::TYPE_CANCELLED);
336       net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_JOB);
337       worker_->Cancel();
338       DeleteAllCanceled();
339     }
340   }
341 
AddRequest(CertVerifierRequest * request)342   void AddRequest(CertVerifierRequest* request) {
343     request->net_log().AddEvent(
344         NetLog::TYPE_CERT_VERIFIER_REQUEST_BOUND_TO_JOB,
345         net_log_.source().ToEventParametersCallback());
346 
347     requests_.push_back(request);
348   }
349 
HandleResult(const MultiThreadedCertVerifier::CachedResult & verify_result)350   void HandleResult(
351       const MultiThreadedCertVerifier::CachedResult& verify_result) {
352     worker_ = NULL;
353     net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_JOB);
354     UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_Job_Latency",
355                                base::TimeTicks::Now() - start_time_,
356                                base::TimeDelta::FromMilliseconds(1),
357                                base::TimeDelta::FromMinutes(10),
358                                100);
359     PostAll(verify_result);
360   }
361 
362  private:
PostAll(const MultiThreadedCertVerifier::CachedResult & verify_result)363   void PostAll(const MultiThreadedCertVerifier::CachedResult& verify_result) {
364     std::vector<CertVerifierRequest*> requests;
365     requests_.swap(requests);
366 
367     for (std::vector<CertVerifierRequest*>::iterator
368          i = requests.begin(); i != requests.end(); i++) {
369       (*i)->Post(verify_result);
370       // Post() causes the CertVerifierRequest to delete itself.
371     }
372   }
373 
DeleteAllCanceled()374   void DeleteAllCanceled() {
375     for (std::vector<CertVerifierRequest*>::iterator
376          i = requests_.begin(); i != requests_.end(); i++) {
377       if ((*i)->canceled()) {
378         delete *i;
379       } else {
380         LOG(DFATAL) << "CertVerifierRequest leaked!";
381       }
382     }
383   }
384 
385   const base::TimeTicks start_time_;
386   std::vector<CertVerifierRequest*> requests_;
387   CertVerifierWorker* worker_;
388   const BoundNetLog net_log_;
389 };
390 
MultiThreadedCertVerifier(CertVerifyProc * verify_proc)391 MultiThreadedCertVerifier::MultiThreadedCertVerifier(
392     CertVerifyProc* verify_proc)
393     : cache_(kMaxCacheEntries),
394       requests_(0),
395       cache_hits_(0),
396       inflight_joins_(0),
397       verify_proc_(verify_proc),
398       trust_anchor_provider_(NULL) {
399   CertDatabase::GetInstance()->AddObserver(this);
400 }
401 
~MultiThreadedCertVerifier()402 MultiThreadedCertVerifier::~MultiThreadedCertVerifier() {
403   STLDeleteValues(&inflight_);
404   CertDatabase::GetInstance()->RemoveObserver(this);
405 }
406 
SetCertTrustAnchorProvider(CertTrustAnchorProvider * trust_anchor_provider)407 void MultiThreadedCertVerifier::SetCertTrustAnchorProvider(
408     CertTrustAnchorProvider* trust_anchor_provider) {
409   DCHECK(CalledOnValidThread());
410   trust_anchor_provider_ = trust_anchor_provider;
411 }
412 
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)413 int MultiThreadedCertVerifier::Verify(X509Certificate* cert,
414                                       const std::string& hostname,
415                                       int flags,
416                                       CRLSet* crl_set,
417                                       CertVerifyResult* verify_result,
418                                       const CompletionCallback& callback,
419                                       RequestHandle* out_req,
420                                       const BoundNetLog& net_log) {
421   DCHECK(CalledOnValidThread());
422 
423   if (callback.is_null() || !verify_result || hostname.empty()) {
424     *out_req = NULL;
425     return ERR_INVALID_ARGUMENT;
426   }
427 
428   requests_++;
429 
430   const CertificateList empty_cert_list;
431   const CertificateList& additional_trust_anchors =
432       trust_anchor_provider_ ?
433           trust_anchor_provider_->GetAdditionalTrustAnchors() : empty_cert_list;
434 
435   const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(),
436                           hostname, flags, additional_trust_anchors);
437   const CertVerifierCache::value_type* cached_entry =
438       cache_.Get(key, CacheValidityPeriod(base::Time::Now()));
439   if (cached_entry) {
440     ++cache_hits_;
441     *out_req = NULL;
442     *verify_result = cached_entry->result;
443     return cached_entry->error;
444   }
445 
446   // No cache hit. See if an identical request is currently in flight.
447   CertVerifierJob* job;
448   std::map<RequestParams, CertVerifierJob*>::const_iterator j;
449   j = inflight_.find(key);
450   if (j != inflight_.end()) {
451     // An identical request is in flight already. We'll just attach our
452     // callback.
453     inflight_joins_++;
454     job = j->second;
455   } else {
456     // Need to make a new request.
457     CertVerifierWorker* worker =
458         new CertVerifierWorker(verify_proc_.get(),
459                                cert,
460                                hostname,
461                                flags,
462                                crl_set,
463                                additional_trust_anchors,
464                                this);
465     job = new CertVerifierJob(
466         worker,
467         BoundNetLog::Make(net_log.net_log(), NetLog::SOURCE_CERT_VERIFIER_JOB));
468     if (!worker->Start()) {
469       delete job;
470       delete worker;
471       *out_req = NULL;
472       // TODO(wtc): log to the NetLog.
473       LOG(ERROR) << "CertVerifierWorker couldn't be started.";
474       return ERR_INSUFFICIENT_RESOURCES;  // Just a guess.
475     }
476     inflight_.insert(std::make_pair(key, job));
477   }
478 
479   CertVerifierRequest* request =
480       new CertVerifierRequest(callback, verify_result, net_log);
481   job->AddRequest(request);
482   *out_req = request;
483   return ERR_IO_PENDING;
484 }
485 
CancelRequest(RequestHandle req)486 void MultiThreadedCertVerifier::CancelRequest(RequestHandle req) {
487   DCHECK(CalledOnValidThread());
488   CertVerifierRequest* request = reinterpret_cast<CertVerifierRequest*>(req);
489   request->Cancel();
490 }
491 
RequestParams(const SHA1HashValue & cert_fingerprint_arg,const SHA1HashValue & ca_fingerprint_arg,const std::string & hostname_arg,int flags_arg,const CertificateList & additional_trust_anchors)492 MultiThreadedCertVerifier::RequestParams::RequestParams(
493     const SHA1HashValue& cert_fingerprint_arg,
494     const SHA1HashValue& ca_fingerprint_arg,
495     const std::string& hostname_arg,
496     int flags_arg,
497     const CertificateList& additional_trust_anchors)
498     : hostname(hostname_arg),
499       flags(flags_arg) {
500   hash_values.reserve(2 + additional_trust_anchors.size());
501   hash_values.push_back(cert_fingerprint_arg);
502   hash_values.push_back(ca_fingerprint_arg);
503   for (size_t i = 0; i < additional_trust_anchors.size(); ++i)
504     hash_values.push_back(additional_trust_anchors[i]->fingerprint());
505 }
506 
~RequestParams()507 MultiThreadedCertVerifier::RequestParams::~RequestParams() {}
508 
operator <(const RequestParams & other) const509 bool MultiThreadedCertVerifier::RequestParams::operator<(
510     const RequestParams& other) const {
511   // |flags| is compared before |cert_fingerprint|, |ca_fingerprint|, and
512   // |hostname| under assumption that integer comparisons are faster than
513   // memory and string comparisons.
514   if (flags != other.flags)
515     return flags < other.flags;
516   if (hostname != other.hostname)
517     return hostname < other.hostname;
518   return std::lexicographical_compare(
519       hash_values.begin(), hash_values.end(),
520       other.hash_values.begin(), other.hash_values.end(),
521       net::SHA1HashValueLessThan());
522 }
523 
524 // HandleResult is called by CertVerifierWorker on the origin message loop.
525 // It deletes CertVerifierJob.
HandleResult(X509Certificate * cert,const std::string & hostname,int flags,const CertificateList & additional_trust_anchors,int error,const CertVerifyResult & verify_result)526 void MultiThreadedCertVerifier::HandleResult(
527     X509Certificate* cert,
528     const std::string& hostname,
529     int flags,
530     const CertificateList& additional_trust_anchors,
531     int error,
532     const CertVerifyResult& verify_result) {
533   DCHECK(CalledOnValidThread());
534 
535   const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(),
536                           hostname, flags, additional_trust_anchors);
537 
538   CachedResult cached_result;
539   cached_result.error = error;
540   cached_result.result = verify_result;
541   base::Time now = base::Time::Now();
542   cache_.Put(
543       key, cached_result, CacheValidityPeriod(now),
544       CacheValidityPeriod(now, now + base::TimeDelta::FromSeconds(kTTLSecs)));
545 
546   std::map<RequestParams, CertVerifierJob*>::iterator j;
547   j = inflight_.find(key);
548   if (j == inflight_.end()) {
549     NOTREACHED();
550     return;
551   }
552   CertVerifierJob* job = j->second;
553   inflight_.erase(j);
554 
555   job->HandleResult(cached_result);
556   delete job;
557 }
558 
OnCACertChanged(const X509Certificate * cert)559 void MultiThreadedCertVerifier::OnCACertChanged(
560     const X509Certificate* cert) {
561   DCHECK(CalledOnValidThread());
562 
563   ClearCache();
564 }
565 
566 }  // namespace net
567