Home | History | Annotate | Download | only in base
      1 // Copyright (c) 2011 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/base/cert_verifier.h"
      6 
      7 #include "base/compiler_specific.h"
      8 #include "base/message_loop.h"
      9 #include "base/stl_util-inl.h"
     10 #include "base/synchronization/lock.h"
     11 #include "base/threading/worker_pool.h"
     12 #include "net/base/net_errors.h"
     13 #include "net/base/x509_certificate.h"
     14 
     15 #if defined(USE_NSS)
     16 #include <private/pprthred.h>  // PR_DetachThread
     17 #endif
     18 
     19 namespace net {
     20 
     21 ////////////////////////////////////////////////////////////////////////////
     22 
     23 // Life of a request:
     24 //
     25 // CertVerifier CertVerifierJob       CertVerifierWorker          Request
     26 //      |                       (origin loop)    (worker loop)
     27 //      |
     28 //   Verify()
     29 //      |---->-------------------<creates>
     30 //      |
     31 //      |---->----<creates>
     32 //      |
     33 //      |---->---------------------------------------------------<creates>
     34 //      |
     35 //      |---->--------------------Start
     36 //      |                           |
     37 //      |                        PostTask
     38 //      |
     39 //      |                                     <starts verifying>
     40 //      |---->-----AddRequest                         |
     41 //                                                    |
     42 //                                                    |
     43 //                                                    |
     44 //                                                  Finish
     45 //                                                    |
     46 //                                                 PostTask
     47 //
     48 //                                   |
     49 //                                DoReply
     50 //      |----<-----------------------|
     51 //  HandleResult
     52 //      |
     53 //      |---->-----HandleResult
     54 //                      |
     55 //                      |------>-----------------------------------Post
     56 //
     57 //
     58 //
     59 // On a cache hit, CertVerifier::Verify() returns synchronously without
     60 // posting a task to a worker thread.
     61 
     62 // The number of CachedCertVerifyResult objects that we'll cache.
     63 static const unsigned kMaxCacheEntries = 256;
     64 
     65 // The number of seconds for which we'll cache a cache entry.
     66 static const unsigned kTTLSecs = 1800;  // 30 minutes.
     67 
     68 namespace {
     69 
     70 class DefaultTimeService : public CertVerifier::TimeService {
     71  public:
     72   // CertVerifier::TimeService methods:
     73   virtual base::Time Now() { return base::Time::Now(); }
     74 };
     75 
     76 }  // namespace
     77 
     78 CachedCertVerifyResult::CachedCertVerifyResult() : error(ERR_FAILED) {
     79 }
     80 
     81 CachedCertVerifyResult::~CachedCertVerifyResult() {}
     82 
     83 bool CachedCertVerifyResult::HasExpired(const base::Time current_time) const {
     84   return current_time >= expiry;
     85 }
     86 
     87 // Represents the output and result callback of a request.
     88 class CertVerifierRequest {
     89  public:
     90   CertVerifierRequest(CompletionCallback* callback,
     91                       CertVerifyResult* verify_result)
     92       : callback_(callback),
     93         verify_result_(verify_result) {
     94   }
     95 
     96   // Ensures that the result callback will never be made.
     97   void Cancel() {
     98     callback_ = NULL;
     99     verify_result_ = NULL;
    100   }
    101 
    102   // Copies the contents of |verify_result| to the caller's
    103   // CertVerifyResult and calls the callback.
    104   void Post(const CachedCertVerifyResult& verify_result) {
    105     if (callback_) {
    106       *verify_result_ = verify_result.result;
    107       callback_->Run(verify_result.error);
    108     }
    109     delete this;
    110   }
    111 
    112   bool canceled() const { return !callback_; }
    113 
    114  private:
    115   CompletionCallback* callback_;
    116   CertVerifyResult* verify_result_;
    117 };
    118 
    119 
    120 // CertVerifierWorker runs on a worker thread and takes care of the blocking
    121 // process of performing the certificate verification.  Deletes itself
    122 // eventually if Start() succeeds.
    123 class CertVerifierWorker {
    124  public:
    125   CertVerifierWorker(X509Certificate* cert,
    126                      const std::string& hostname,
    127                      int flags,
    128                      CertVerifier* cert_verifier)
    129       : cert_(cert),
    130         hostname_(hostname),
    131         flags_(flags),
    132         origin_loop_(MessageLoop::current()),
    133         cert_verifier_(cert_verifier),
    134         canceled_(false),
    135         error_(ERR_FAILED) {
    136   }
    137 
    138   bool Start() {
    139     DCHECK_EQ(MessageLoop::current(), origin_loop_);
    140 
    141     return base::WorkerPool::PostTask(
    142         FROM_HERE, NewRunnableMethod(this, &CertVerifierWorker::Run),
    143         true /* task is slow */);
    144   }
    145 
    146   // Cancel is called from the origin loop when the CertVerifier is getting
    147   // deleted.
    148   void Cancel() {
    149     DCHECK_EQ(MessageLoop::current(), origin_loop_);
    150     base::AutoLock locked(lock_);
    151     canceled_ = true;
    152   }
    153 
    154  private:
    155   void Run() {
    156     // Runs on a worker thread.
    157     error_ = cert_->Verify(hostname_, flags_, &verify_result_);
    158 #if defined(USE_NSS)
    159     // Detach the thread from NSPR.
    160     // Calling NSS functions attaches the thread to NSPR, which stores
    161     // the NSPR thread ID in thread-specific data.
    162     // The threads in our thread pool terminate after we have called
    163     // PR_Cleanup.  Unless we detach them from NSPR, net_unittests gets
    164     // segfaults on shutdown when the threads' thread-specific data
    165     // destructors run.
    166     PR_DetachThread();
    167 #endif
    168     Finish();
    169   }
    170 
    171   // DoReply runs on the origin thread.
    172   void DoReply() {
    173     DCHECK_EQ(MessageLoop::current(), origin_loop_);
    174     {
    175       // We lock here because the worker thread could still be in Finished,
    176       // after the PostTask, but before unlocking |lock_|. If we do not lock in
    177       // this case, we will end up deleting a locked Lock, which can lead to
    178       // memory leaks or worse errors.
    179       base::AutoLock locked(lock_);
    180       if (!canceled_) {
    181         cert_verifier_->HandleResult(cert_, hostname_, flags_,
    182                                      error_, verify_result_);
    183       }
    184     }
    185     delete this;
    186   }
    187 
    188   void Finish() {
    189     // Runs on the worker thread.
    190     // We assume that the origin loop outlives the CertVerifier. If the
    191     // CertVerifier is deleted, it will call Cancel on us. If it does so
    192     // before the Acquire, we'll delete ourselves and return. If it's trying to
    193     // do so concurrently, then it'll block on the lock and we'll call PostTask
    194     // while the CertVerifier (and therefore the MessageLoop) is still alive.
    195     // If it does so after this function, we assume that the MessageLoop will
    196     // process pending tasks. In which case we'll notice the |canceled_| flag
    197     // in DoReply.
    198 
    199     bool canceled;
    200     {
    201       base::AutoLock locked(lock_);
    202       canceled = canceled_;
    203       if (!canceled) {
    204         origin_loop_->PostTask(
    205             FROM_HERE, NewRunnableMethod(this, &CertVerifierWorker::DoReply));
    206       }
    207     }
    208 
    209     if (canceled)
    210       delete this;
    211   }
    212 
    213   scoped_refptr<X509Certificate> cert_;
    214   const std::string hostname_;
    215   const int flags_;
    216   MessageLoop* const origin_loop_;
    217   CertVerifier* const cert_verifier_;
    218 
    219   // lock_ protects canceled_.
    220   base::Lock lock_;
    221 
    222   // If canceled_ is true,
    223   // * origin_loop_ cannot be accessed by the worker thread,
    224   // * cert_verifier_ cannot be accessed by any thread.
    225   bool canceled_;
    226 
    227   int error_;
    228   CertVerifyResult verify_result_;
    229 
    230   DISALLOW_COPY_AND_ASSIGN(CertVerifierWorker);
    231 };
    232 
    233 // A CertVerifierJob is a one-to-one counterpart of a CertVerifierWorker. It
    234 // lives only on the CertVerifier's origin message loop.
    235 class CertVerifierJob {
    236  public:
    237   explicit CertVerifierJob(CertVerifierWorker* worker) : worker_(worker) {
    238   }
    239 
    240   ~CertVerifierJob() {
    241     if (worker_) {
    242       worker_->Cancel();
    243       DeleteAllCanceled();
    244     }
    245   }
    246 
    247   void AddRequest(CertVerifierRequest* request) {
    248     requests_.push_back(request);
    249   }
    250 
    251   void HandleResult(const CachedCertVerifyResult& verify_result) {
    252     worker_ = NULL;
    253     PostAll(verify_result);
    254   }
    255 
    256  private:
    257   void PostAll(const CachedCertVerifyResult& verify_result) {
    258     std::vector<CertVerifierRequest*> requests;
    259     requests_.swap(requests);
    260 
    261     for (std::vector<CertVerifierRequest*>::iterator
    262          i = requests.begin(); i != requests.end(); i++) {
    263       (*i)->Post(verify_result);
    264       // Post() causes the CertVerifierRequest to delete itself.
    265     }
    266   }
    267 
    268   void DeleteAllCanceled() {
    269     for (std::vector<CertVerifierRequest*>::iterator
    270          i = requests_.begin(); i != requests_.end(); i++) {
    271       if ((*i)->canceled()) {
    272         delete *i;
    273       } else {
    274         LOG(DFATAL) << "CertVerifierRequest leaked!";
    275       }
    276     }
    277   }
    278 
    279   std::vector<CertVerifierRequest*> requests_;
    280   CertVerifierWorker* worker_;
    281 };
    282 
    283 
    284 CertVerifier::CertVerifier()
    285     : time_service_(new DefaultTimeService),
    286       requests_(0),
    287       cache_hits_(0),
    288       inflight_joins_(0) {
    289   CertDatabase::AddObserver(this);
    290 }
    291 
    292 CertVerifier::CertVerifier(TimeService* time_service)
    293     : time_service_(time_service),
    294       requests_(0),
    295       cache_hits_(0),
    296       inflight_joins_(0) {
    297   CertDatabase::AddObserver(this);
    298 }
    299 
    300 CertVerifier::~CertVerifier() {
    301   STLDeleteValues(&inflight_);
    302 
    303   CertDatabase::RemoveObserver(this);
    304 }
    305 
    306 int CertVerifier::Verify(X509Certificate* cert,
    307                          const std::string& hostname,
    308                          int flags,
    309                          CertVerifyResult* verify_result,
    310                          CompletionCallback* callback,
    311                          RequestHandle* out_req) {
    312   DCHECK(CalledOnValidThread());
    313 
    314   if (!callback || !verify_result || hostname.empty()) {
    315     *out_req = NULL;
    316     return ERR_INVALID_ARGUMENT;
    317   }
    318 
    319   requests_++;
    320 
    321   const RequestParams key = {cert->fingerprint(), hostname, flags};
    322   // First check the cache.
    323   std::map<RequestParams, CachedCertVerifyResult>::iterator i;
    324   i = cache_.find(key);
    325   if (i != cache_.end()) {
    326     if (!i->second.HasExpired(time_service_->Now())) {
    327       cache_hits_++;
    328       *out_req = NULL;
    329       *verify_result = i->second.result;
    330       return i->second.error;
    331     }
    332     // Cache entry has expired.
    333     cache_.erase(i);
    334   }
    335 
    336   // No cache hit. See if an identical request is currently in flight.
    337   CertVerifierJob* job;
    338   std::map<RequestParams, CertVerifierJob*>::const_iterator j;
    339   j = inflight_.find(key);
    340   if (j != inflight_.end()) {
    341     // An identical request is in flight already. We'll just attach our
    342     // callback.
    343     inflight_joins_++;
    344     job = j->second;
    345   } else {
    346     // Need to make a new request.
    347     CertVerifierWorker* worker = new CertVerifierWorker(cert, hostname, flags,
    348                                                         this);
    349     job = new CertVerifierJob(worker);
    350     if (!worker->Start()) {
    351       delete job;
    352       delete worker;
    353       *out_req = NULL;
    354       // TODO(wtc): log to the NetLog.
    355       LOG(ERROR) << "CertVerifierWorker couldn't be started.";
    356       return ERR_INSUFFICIENT_RESOURCES;  // Just a guess.
    357     }
    358     inflight_.insert(std::make_pair(key, job));
    359   }
    360 
    361   CertVerifierRequest* request =
    362       new CertVerifierRequest(callback, verify_result);
    363   job->AddRequest(request);
    364   *out_req = request;
    365   return ERR_IO_PENDING;
    366 }
    367 
    368 void CertVerifier::CancelRequest(RequestHandle req) {
    369   DCHECK(CalledOnValidThread());
    370   CertVerifierRequest* request = reinterpret_cast<CertVerifierRequest*>(req);
    371   request->Cancel();
    372 }
    373 
    374 void CertVerifier::ClearCache() {
    375   DCHECK(CalledOnValidThread());
    376 
    377   cache_.clear();
    378   // Leaves inflight_ alone.
    379 }
    380 
    381 size_t CertVerifier::GetCacheSize() const {
    382   DCHECK(CalledOnValidThread());
    383 
    384   return cache_.size();
    385 }
    386 
    387 // HandleResult is called by CertVerifierWorker on the origin message loop.
    388 // It deletes CertVerifierJob.
    389 void CertVerifier::HandleResult(X509Certificate* cert,
    390                                 const std::string& hostname,
    391                                 int flags,
    392                                 int error,
    393                                 const CertVerifyResult& verify_result) {
    394   DCHECK(CalledOnValidThread());
    395 
    396   const base::Time current_time(time_service_->Now());
    397 
    398   CachedCertVerifyResult cached_result;
    399   cached_result.error = error;
    400   cached_result.result = verify_result;
    401   uint32 ttl = kTTLSecs;
    402   cached_result.expiry = current_time + base::TimeDelta::FromSeconds(ttl);
    403 
    404   const RequestParams key = {cert->fingerprint(), hostname, flags};
    405 
    406   DCHECK_GE(kMaxCacheEntries, 1u);
    407   DCHECK_LE(cache_.size(), kMaxCacheEntries);
    408   if (cache_.size() == kMaxCacheEntries) {
    409     // Need to remove an element of the cache.
    410     std::map<RequestParams, CachedCertVerifyResult>::iterator i, cur;
    411     for (i = cache_.begin(); i != cache_.end(); ) {
    412       cur = i++;
    413       if (cur->second.HasExpired(current_time))
    414         cache_.erase(cur);
    415     }
    416   }
    417   if (cache_.size() == kMaxCacheEntries) {
    418     // If we didn't clear out any expired entries, we just remove the first
    419     // element. Crummy but simple.
    420     cache_.erase(cache_.begin());
    421   }
    422 
    423   cache_.insert(std::make_pair(key, cached_result));
    424 
    425   std::map<RequestParams, CertVerifierJob*>::iterator j;
    426   j = inflight_.find(key);
    427   if (j == inflight_.end()) {
    428     NOTREACHED();
    429     return;
    430   }
    431   CertVerifierJob* job = j->second;
    432   inflight_.erase(j);
    433 
    434   job->HandleResult(cached_result);
    435   delete job;
    436 }
    437 
    438 void CertVerifier::OnCertTrustChanged(const X509Certificate* cert) {
    439   DCHECK(CalledOnValidThread());
    440 
    441   ClearCache();
    442 }
    443 
    444 /////////////////////////////////////////////////////////////////////
    445 
    446 SingleRequestCertVerifier::SingleRequestCertVerifier(
    447     CertVerifier* cert_verifier)
    448     : cert_verifier_(cert_verifier),
    449       cur_request_(NULL),
    450       cur_request_callback_(NULL),
    451       ALLOW_THIS_IN_INITIALIZER_LIST(
    452           callback_(this, &SingleRequestCertVerifier::OnVerifyCompletion)) {
    453   DCHECK(cert_verifier_ != NULL);
    454 }
    455 
    456 SingleRequestCertVerifier::~SingleRequestCertVerifier() {
    457   if (cur_request_) {
    458     cert_verifier_->CancelRequest(cur_request_);
    459     cur_request_ = NULL;
    460   }
    461 }
    462 
    463 int SingleRequestCertVerifier::Verify(X509Certificate* cert,
    464                                       const std::string& hostname,
    465                                       int flags,
    466                                       CertVerifyResult* verify_result,
    467                                       CompletionCallback* callback) {
    468   // Should not be already in use.
    469   DCHECK(!cur_request_ && !cur_request_callback_);
    470 
    471   // Do a synchronous verification.
    472   if (!callback)
    473     return cert->Verify(hostname, flags, verify_result);
    474 
    475   CertVerifier::RequestHandle request = NULL;
    476 
    477   // We need to be notified of completion before |callback| is called, so that
    478   // we can clear out |cur_request_*|.
    479   int rv = cert_verifier_->Verify(
    480       cert, hostname, flags, verify_result, &callback_, &request);
    481 
    482   if (rv == ERR_IO_PENDING) {
    483     // Cleared in OnVerifyCompletion().
    484     cur_request_ = request;
    485     cur_request_callback_ = callback;
    486   }
    487 
    488   return rv;
    489 }
    490 
    491 void SingleRequestCertVerifier::OnVerifyCompletion(int result) {
    492   DCHECK(cur_request_ && cur_request_callback_);
    493 
    494   CompletionCallback* callback = cur_request_callback_;
    495 
    496   // Clear the outstanding request information.
    497   cur_request_ = NULL;
    498   cur_request_callback_ = NULL;
    499 
    500   // Call the user's original callback.
    501   callback->Run(result);
    502 }
    503 
    504 }  // namespace net
    505 
    506 DISABLE_RUNNABLE_METHOD_REFCOUNT(net::CertVerifierWorker);
    507