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