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