Home | History | Annotate | Download | only in cert
      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 "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"
     27 
     28 #if defined(USE_NSS) || defined(OS_IOS)
     29 #include <private/pprthred.h>  // PR_DetachThread
     30 #endif
     31 
     32 namespace net {
     33 
     34 ////////////////////////////////////////////////////////////////////////////
     35 
     36 // Life of a request:
     37 //
     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
     60 //
     61 //                                                     |
     62 //                                                  DoReply
     63 //      |----<-----------------------------------------|
     64 //  HandleResult
     65 //      |
     66 //      |---->------------------HandleResult
     67 //                                   |
     68 //                                   |------>---------------------------Post
     69 //
     70 //
     71 //
     72 // On a cache hit, MultiThreadedCertVerifier::Verify() returns synchronously
     73 // without posting a task to a worker thread.
     74 
     75 namespace {
     76 
     77 // The default value of max_cache_entries_.
     78 const unsigned kMaxCacheEntries = 256;
     79 
     80 // The number of seconds for which we'll cache a cache entry.
     81 const unsigned kTTLSecs = 1800;  // 30 minutes.
     82 
     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));
     99 
    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());
    106   }
    107   results->Set("public_key_hashes", hashes);
    108 
    109   return results;
    110 }
    111 
    112 }  // namespace
    113 
    114 MultiThreadedCertVerifier::CachedResult::CachedResult() : error(ERR_FAILED) {}
    115 
    116 MultiThreadedCertVerifier::CachedResult::~CachedResult() {}
    117 
    118 MultiThreadedCertVerifier::CacheValidityPeriod::CacheValidityPeriod(
    119     const base::Time& now)
    120     : verification_time(now),
    121       expiration_time(now) {
    122 }
    123 
    124 MultiThreadedCertVerifier::CacheValidityPeriod::CacheValidityPeriod(
    125     const base::Time& now,
    126     const base::Time& expiration)
    127     : verification_time(now),
    128       expiration_time(expiration) {
    129 }
    130 
    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);
    139 
    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.
    143   //
    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.
    155   //
    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;
    164 };
    165 
    166 
    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);
    177   }
    178 
    179   ~CertVerifierRequest() {
    180   }
    181 
    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);
    188   }
    189 
    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);
    197     }
    198     delete this;
    199   }
    200 
    201   bool canceled() const { return callback_.is_null(); }
    202 
    203   const BoundNetLog& net_log() const { return net_log_; }
    204 
    205  private:
    206   CompletionCallback callback_;
    207   CertVerifyResult* verify_result_;
    208   const BoundNetLog net_log_;
    209 };
    210 
    211 
    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) {
    234   }
    235 
    236   // Returns the certificate being verified. May only be called /before/
    237   // Start() is called.
    238   X509Certificate* certificate() const { return cert_.get(); }
    239 
    240   bool Start() {
    241     DCHECK_EQ(base::MessageLoop::current(), origin_loop_);
    242 
    243     return base::WorkerPool::PostTask(
    244         FROM_HERE, base::Bind(&CertVerifierWorker::Run, base::Unretained(this)),
    245         true /* task is slow */);
    246   }
    247 
    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;
    254   }
    255 
    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();
    276   }
    277 
    278   // DoReply runs on the origin thread.
    279   void DoReply() {
    280     DCHECK_EQ(base::MessageLoop::current(), origin_loop_);
    281     {
    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_);
    294       }
    295     }
    296     delete this;
    297   }
    298 
    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.
    310 
    311     bool canceled;
    312     {
    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)));
    319       }
    320     }
    321 
    322     if (canceled)
    323       delete this;
    324   }
    325 
    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_;
    334 
    335   // lock_ protects canceled_.
    336   base::Lock lock_;
    337 
    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_;
    342 
    343   int error_;
    344   CertVerifyResult verify_result_;
    345 
    346   DISALLOW_COPY_AND_ASSIGN(CertVerifierWorker);
    347 };
    348 
    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())));
    362   }
    363 
    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();
    370     }
    371   }
    372 
    373   void AddRequest(CertVerifierRequest* request) {
    374     request->net_log().AddEvent(
    375         NetLog::TYPE_CERT_VERIFIER_REQUEST_BOUND_TO_JOB,
    376         net_log_.source().ToEventParametersCallback());
    377 
    378     requests_.push_back(request);
    379   }
    380 
    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);
    400     }
    401     PostAll(verify_result);
    402   }
    403 
    404  private:
    405   void PostAll(const MultiThreadedCertVerifier::CachedResult& verify_result) {
    406     std::vector<CertVerifierRequest*> requests;
    407     requests_.swap(requests);
    408 
    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.
    413     }
    414   }
    415 
    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!";
    423       }
    424     }
    425   }
    426 
    427   const base::TimeTicks start_time_;
    428   std::vector<CertVerifierRequest*> requests_;
    429   CertVerifierWorker* worker_;
    430   const BoundNetLog net_log_;
    431 };
    432 
    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);
    443 }
    444 
    445 MultiThreadedCertVerifier::~MultiThreadedCertVerifier() {
    446   STLDeleteValues(&inflight_);
    447   CertDatabase::GetInstance()->RemoveObserver(this);
    448 }
    449 
    450 void MultiThreadedCertVerifier::SetCertTrustAnchorProvider(
    451     CertTrustAnchorProvider* trust_anchor_provider) {
    452   DCHECK(CalledOnValidThread());
    453   trust_anchor_provider_ = trust_anchor_provider;
    454 }
    455 
    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());
    465 
    466   if (callback.is_null() || !verify_result || hostname.empty()) {
    467     *out_req = NULL;
    468     return ERR_INVALID_ARGUMENT;
    469   }
    470 
    471   requests_++;
    472 
    473   const CertificateList empty_cert_list;
    474   const CertificateList& additional_trust_anchors =
    475       trust_anchor_provider_ ?
    476           trust_anchor_provider_->GetAdditionalTrustAnchors() : empty_cert_list;
    477 
    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;
    487   }
    488 
    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.
    518     }
    519     inflight_.insert(std::make_pair(key, job));
    520     if (requests_ == 1) {
    521       // Cleared in HandleResult.
    522       first_job_ = job;
    523     }
    524   }
    525 
    526   CertVerifierRequest* request =
    527       new CertVerifierRequest(callback, verify_result, net_log);
    528   job->AddRequest(request);
    529   *out_req = request;
    530   return ERR_IO_PENDING;
    531 }
    532 
    533 void MultiThreadedCertVerifier::CancelRequest(RequestHandle req) {
    534   DCHECK(CalledOnValidThread());
    535   CertVerifierRequest* request = reinterpret_cast<CertVerifierRequest*>(req);
    536   request->Cancel();
    537 }
    538 
    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());
    552 }
    553 
    554 MultiThreadedCertVerifier::RequestParams::~RequestParams() {}
    555 
    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());
    569 }
    570 
    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());
    581 
    582   const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(),
    583                           hostname, flags, additional_trust_anchors);
    584 
    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)));
    592 
    593   std::map<RequestParams, CertVerifierJob*>::iterator j;
    594   j = inflight_.find(key);
    595   if (j == inflight_.end()) {
    596     NOTREACHED();
    597     return;
    598   }
    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;
    605   }
    606 
    607   job->HandleResult(cached_result, is_first_job);
    608   delete job;
    609 }
    610 
    611 void MultiThreadedCertVerifier::OnCACertChanged(
    612     const X509Certificate* cert) {
    613   DCHECK(CalledOnValidThread());
    614 
    615   ClearCache();
    616 }
    617 
    618 }  // namespace net
    619 
    620