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
19namespace 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.
63static const unsigned kMaxCacheEntries = 256;
64
65// The number of seconds for which we'll cache a cache entry.
66static const unsigned kTTLSecs = 1800;  // 30 minutes.
67
68namespace {
69
70class DefaultTimeService : public CertVerifier::TimeService {
71 public:
72  // CertVerifier::TimeService methods:
73  virtual base::Time Now() { return base::Time::Now(); }
74};
75
76}  // namespace
77
78CachedCertVerifyResult::CachedCertVerifyResult() : error(ERR_FAILED) {
79}
80
81CachedCertVerifyResult::~CachedCertVerifyResult() {}
82
83bool 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.
88class 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.
123class 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.
235class 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
284CertVerifier::CertVerifier()
285    : time_service_(new DefaultTimeService),
286      requests_(0),
287      cache_hits_(0),
288      inflight_joins_(0) {
289  CertDatabase::AddObserver(this);
290}
291
292CertVerifier::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
300CertVerifier::~CertVerifier() {
301  STLDeleteValues(&inflight_);
302
303  CertDatabase::RemoveObserver(this);
304}
305
306int 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
368void CertVerifier::CancelRequest(RequestHandle req) {
369  DCHECK(CalledOnValidThread());
370  CertVerifierRequest* request = reinterpret_cast<CertVerifierRequest*>(req);
371  request->Cancel();
372}
373
374void CertVerifier::ClearCache() {
375  DCHECK(CalledOnValidThread());
376
377  cache_.clear();
378  // Leaves inflight_ alone.
379}
380
381size_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.
389void 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
438void CertVerifier::OnCertTrustChanged(const X509Certificate* cert) {
439  DCHECK(CalledOnValidThread());
440
441  ClearCache();
442}
443
444/////////////////////////////////////////////////////////////////////
445
446SingleRequestCertVerifier::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
456SingleRequestCertVerifier::~SingleRequestCertVerifier() {
457  if (cur_request_) {
458    cert_verifier_->CancelRequest(cur_request_);
459    cur_request_ = NULL;
460  }
461}
462
463int 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
491void 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
506DISABLE_RUNNABLE_METHOD_REFCOUNT(net::CertVerifierWorker);
507