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:
Now()73 virtual base::Time Now() { return base::Time::Now(); }
74 };
75
76 } // namespace
77
CachedCertVerifyResult()78 CachedCertVerifyResult::CachedCertVerifyResult() : error(ERR_FAILED) {
79 }
80
~CachedCertVerifyResult()81 CachedCertVerifyResult::~CachedCertVerifyResult() {}
82
HasExpired(const base::Time current_time) const83 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:
CertVerifierRequest(CompletionCallback * callback,CertVerifyResult * verify_result)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.
Cancel()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.
Post(const CachedCertVerifyResult & verify_result)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
canceled() const112 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:
CertVerifierWorker(X509Certificate * cert,const std::string & hostname,int flags,CertVerifier * cert_verifier)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
Start()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.
Cancel()148 void Cancel() {
149 DCHECK_EQ(MessageLoop::current(), origin_loop_);
150 base::AutoLock locked(lock_);
151 canceled_ = true;
152 }
153
154 private:
Run()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.
DoReply()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
Finish()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:
CertVerifierJob(CertVerifierWorker * worker)237 explicit CertVerifierJob(CertVerifierWorker* worker) : worker_(worker) {
238 }
239
~CertVerifierJob()240 ~CertVerifierJob() {
241 if (worker_) {
242 worker_->Cancel();
243 DeleteAllCanceled();
244 }
245 }
246
AddRequest(CertVerifierRequest * request)247 void AddRequest(CertVerifierRequest* request) {
248 requests_.push_back(request);
249 }
250
HandleResult(const CachedCertVerifyResult & verify_result)251 void HandleResult(const CachedCertVerifyResult& verify_result) {
252 worker_ = NULL;
253 PostAll(verify_result);
254 }
255
256 private:
PostAll(const CachedCertVerifyResult & verify_result)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
DeleteAllCanceled()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
CertVerifier()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
CertVerifier(TimeService * time_service)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
~CertVerifier()300 CertVerifier::~CertVerifier() {
301 STLDeleteValues(&inflight_);
302
303 CertDatabase::RemoveObserver(this);
304 }
305
Verify(X509Certificate * cert,const std::string & hostname,int flags,CertVerifyResult * verify_result,CompletionCallback * callback,RequestHandle * out_req)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
CancelRequest(RequestHandle req)368 void CertVerifier::CancelRequest(RequestHandle req) {
369 DCHECK(CalledOnValidThread());
370 CertVerifierRequest* request = reinterpret_cast<CertVerifierRequest*>(req);
371 request->Cancel();
372 }
373
ClearCache()374 void CertVerifier::ClearCache() {
375 DCHECK(CalledOnValidThread());
376
377 cache_.clear();
378 // Leaves inflight_ alone.
379 }
380
GetCacheSize() const381 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.
HandleResult(X509Certificate * cert,const std::string & hostname,int flags,int error,const CertVerifyResult & verify_result)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
OnCertTrustChanged(const X509Certificate * cert)438 void CertVerifier::OnCertTrustChanged(const X509Certificate* cert) {
439 DCHECK(CalledOnValidThread());
440
441 ClearCache();
442 }
443
444 /////////////////////////////////////////////////////////////////////
445
SingleRequestCertVerifier(CertVerifier * cert_verifier)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
~SingleRequestCertVerifier()456 SingleRequestCertVerifier::~SingleRequestCertVerifier() {
457 if (cur_request_) {
458 cert_verifier_->CancelRequest(cur_request_);
459 cur_request_ = NULL;
460 }
461 }
462
Verify(X509Certificate * cert,const std::string & hostname,int flags,CertVerifyResult * verify_result,CompletionCallback * callback)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
OnVerifyCompletion(int result)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