| 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/cert/multi_threaded_cert_verifier.h" | |
| 6 | |
| 7 #include <algorithm> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/bind_helpers.h" | |
| 11 #include "base/compiler_specific.h" | |
| 12 #include "base/message_loop/message_loop.h" | |
| 13 #include "base/metrics/histogram.h" | |
| 14 #include "base/stl_util.h" | |
| 15 #include "base/synchronization/lock.h" | |
| 16 #include "base/threading/worker_pool.h" | |
| 17 #include "base/time/time.h" | |
| 18 #include "base/values.h" | |
| 19 #include "net/base/hash_value.h" | |
| 20 #include "net/base/net_errors.h" | |
| 21 #include "net/base/net_log.h" | |
| 22 #include "net/cert/cert_trust_anchor_provider.h" | |
| 23 #include "net/cert/cert_verify_proc.h" | |
| 24 #include "net/cert/crl_set.h" | |
| 25 #include "net/cert/x509_certificate.h" | |
| 26 #include "net/cert/x509_certificate_net_log_param.h" | |
| 27 | |
| 28 #if defined(USE_NSS) || defined(OS_IOS) | |
| 29 #include <private/pprthred.h> // PR_DetachThread | |
| 30 #endif | |
| 31 | |
| 32 namespace net { | |
| 33 | |
| 34 //////////////////////////////////////////////////////////////////////////// | |
| 35 | |
| 36 // Life of a request: | |
| 37 // | |
| 38 // MultiThreadedCertVerifier CertVerifierJob CertVerifierWorker Request | |
| 39 // | (origin loop) (worker loop) | |
| 40 // | | |
| 41 // Verify() | |
| 42 // |---->-------------------------------------<creates> | |
| 43 // | | |
| 44 // |---->-------------------<creates> | |
| 45 // | | |
| 46 // |---->-------------------------------------------------------<creates> | |
| 47 // | | |
| 48 // |---->---------------------------------------Start | |
| 49 // | | | |
| 50 // | PostTask | |
| 51 // | | |
| 52 // | <starts verifying> | |
| 53 // |---->-------------------AddRequest | | |
| 54 // | | |
| 55 // | | |
| 56 // | | |
| 57 // Finish | |
| 58 // | | |
| 59 // PostTask | |
| 60 // | |
| 61 // | | |
| 62 // DoReply | |
| 63 // |----<-----------------------------------------| | |
| 64 // HandleResult | |
| 65 // | | |
| 66 // |---->------------------HandleResult | |
| 67 // | | |
| 68 // |------>---------------------------Post | |
| 69 // | |
| 70 // | |
| 71 // | |
| 72 // On a cache hit, MultiThreadedCertVerifier::Verify() returns synchronously | |
| 73 // without posting a task to a worker thread. | |
| 74 | |
| 75 namespace { | |
| 76 | |
| 77 // The default value of max_cache_entries_. | |
| 78 const unsigned kMaxCacheEntries = 256; | |
| 79 | |
| 80 // The number of seconds for which we'll cache a cache entry. | |
| 81 const unsigned kTTLSecs = 1800; // 30 minutes. | |
| 82 | |
| 83 base::Value* CertVerifyResultCallback(const CertVerifyResult& verify_result, | |
| 84 NetLog::LogLevel log_level) { | |
| 85 base::DictionaryValue* results = new base::DictionaryValue(); | |
| 86 results->SetBoolean("has_md5", verify_result.has_md5); | |
| 87 results->SetBoolean("has_md2", verify_result.has_md2); | |
| 88 results->SetBoolean("has_md4", verify_result.has_md4); | |
| 89 results->SetBoolean("is_issued_by_known_root", | |
| 90 verify_result.is_issued_by_known_root); | |
| 91 results->SetBoolean("is_issued_by_additional_trust_anchor", | |
| 92 verify_result.is_issued_by_additional_trust_anchor); | |
| 93 results->SetBoolean("common_name_fallback_used", | |
| 94 verify_result.common_name_fallback_used); | |
| 95 results->SetInteger("cert_status", verify_result.cert_status); | |
| 96 results->Set("verified_cert", | |
| 97 NetLogX509CertificateCallback(verify_result.verified_cert.get(), | |
| 98 log_level)); | |
| 99 | |
| 100 base::ListValue* hashes = new base::ListValue(); | |
| 101 for (std::vector<HashValue>::const_iterator it = | |
| 102 verify_result.public_key_hashes.begin(); | |
| 103 it != verify_result.public_key_hashes.end(); | |
| 104 ++it) { | |
| 105 hashes->AppendString(it->ToString()); | |
| 106 } | |
| 107 results->Set("public_key_hashes", hashes); | |
| 108 | |
| 109 return results; | |
| 110 } | |
| 111 | |
| 112 } // namespace | |
| 113 | |
| 114 MultiThreadedCertVerifier::CachedResult::CachedResult() : error(ERR_FAILED) {} | |
| 115 | |
| 116 MultiThreadedCertVerifier::CachedResult::~CachedResult() {} | |
| 117 | |
| 118 MultiThreadedCertVerifier::CacheValidityPeriod::CacheValidityPeriod( | |
| 119 const base::Time& now) | |
| 120 : verification_time(now), | |
| 121 expiration_time(now) { | |
| 122 } | |
| 123 | |
| 124 MultiThreadedCertVerifier::CacheValidityPeriod::CacheValidityPeriod( | |
| 125 const base::Time& now, | |
| 126 const base::Time& expiration) | |
| 127 : verification_time(now), | |
| 128 expiration_time(expiration) { | |
| 129 } | |
| 130 | |
| 131 bool MultiThreadedCertVerifier::CacheExpirationFunctor::operator()( | |
| 132 const CacheValidityPeriod& now, | |
| 133 const CacheValidityPeriod& expiration) const { | |
| 134 // Ensure this functor is being used for expiration only, and not strict | |
| 135 // weak ordering/sorting. |now| should only ever contain a single | |
| 136 // base::Time. | |
| 137 // Note: DCHECK_EQ is not used due to operator<< overloading requirements. | |
| 138 DCHECK(now.verification_time == now.expiration_time); | |
| 139 | |
| 140 // |now| contains only a single time (verification_time), while |expiration| | |
| 141 // contains the validity range - both when the certificate was verified and | |
| 142 // when the verification result should expire. | |
| 143 // | |
| 144 // If the user receives a "not yet valid" message, and adjusts their clock | |
| 145 // foward to the correct time, this will (typically) cause | |
| 146 // now.verification_time to advance past expiration.expiration_time, thus | |
| 147 // treating the cached result as an expired entry and re-verifying. | |
| 148 // If the user receives a "expired" message, and adjusts their clock | |
| 149 // backwards to the correct time, this will cause now.verification_time to | |
| 150 // be less than expiration_verification_time, thus treating the cached | |
| 151 // result as an expired entry and re-verifying. | |
| 152 // If the user receives either of those messages, and does not adjust their | |
| 153 // clock, then the result will be (typically) be cached until the expiration | |
| 154 // TTL. | |
| 155 // | |
| 156 // This algorithm is only problematic if the user consistently keeps | |
| 157 // adjusting their clock backwards in increments smaller than the expiration | |
| 158 // TTL, in which case, cached elements continue to be added. However, | |
| 159 // because the cache has a fixed upper bound, if no entries are expired, a | |
| 160 // 'random' entry will be, thus keeping the memory constraints bounded over | |
| 161 // time. | |
| 162 return now.verification_time >= expiration.verification_time && | |
| 163 now.verification_time < expiration.expiration_time; | |
| 164 }; | |
| 165 | |
| 166 | |
| 167 // Represents the output and result callback of a request. | |
| 168 class CertVerifierRequest { | |
| 169 public: | |
| 170 CertVerifierRequest(const CompletionCallback& callback, | |
| 171 CertVerifyResult* verify_result, | |
| 172 const BoundNetLog& net_log) | |
| 173 : callback_(callback), | |
| 174 verify_result_(verify_result), | |
| 175 net_log_(net_log) { | |
| 176 net_log_.BeginEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST); | |
| 177 } | |
| 178 | |
| 179 ~CertVerifierRequest() { | |
| 180 } | |
| 181 | |
| 182 // Ensures that the result callback will never be made. | |
| 183 void Cancel() { | |
| 184 callback_.Reset(); | |
| 185 verify_result_ = NULL; | |
| 186 net_log_.AddEvent(NetLog::TYPE_CANCELLED); | |
| 187 net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST); | |
| 188 } | |
| 189 | |
| 190 // Copies the contents of |verify_result| to the caller's | |
| 191 // CertVerifyResult and calls the callback. | |
| 192 void Post(const MultiThreadedCertVerifier::CachedResult& verify_result) { | |
| 193 if (!callback_.is_null()) { | |
| 194 net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST); | |
| 195 *verify_result_ = verify_result.result; | |
| 196 callback_.Run(verify_result.error); | |
| 197 } | |
| 198 delete this; | |
| 199 } | |
| 200 | |
| 201 bool canceled() const { return callback_.is_null(); } | |
| 202 | |
| 203 const BoundNetLog& net_log() const { return net_log_; } | |
| 204 | |
| 205 private: | |
| 206 CompletionCallback callback_; | |
| 207 CertVerifyResult* verify_result_; | |
| 208 const BoundNetLog net_log_; | |
| 209 }; | |
| 210 | |
| 211 | |
| 212 // CertVerifierWorker runs on a worker thread and takes care of the blocking | |
| 213 // process of performing the certificate verification. Deletes itself | |
| 214 // eventually if Start() succeeds. | |
| 215 class CertVerifierWorker { | |
| 216 public: | |
| 217 CertVerifierWorker(CertVerifyProc* verify_proc, | |
| 218 X509Certificate* cert, | |
| 219 const std::string& hostname, | |
| 220 int flags, | |
| 221 CRLSet* crl_set, | |
| 222 const CertificateList& additional_trust_anchors, | |
| 223 MultiThreadedCertVerifier* cert_verifier) | |
| 224 : verify_proc_(verify_proc), | |
| 225 cert_(cert), | |
| 226 hostname_(hostname), | |
| 227 flags_(flags), | |
| 228 crl_set_(crl_set), | |
| 229 additional_trust_anchors_(additional_trust_anchors), | |
| 230 origin_loop_(base::MessageLoop::current()), | |
| 231 cert_verifier_(cert_verifier), | |
| 232 canceled_(false), | |
| 233 error_(ERR_FAILED) { | |
| 234 } | |
| 235 | |
| 236 // Returns the certificate being verified. May only be called /before/ | |
| 237 // Start() is called. | |
| 238 X509Certificate* certificate() const { return cert_.get(); } | |
| 239 | |
| 240 bool Start() { | |
| 241 DCHECK_EQ(base::MessageLoop::current(), origin_loop_); | |
| 242 | |
| 243 return base::WorkerPool::PostTask( | |
| 244 FROM_HERE, base::Bind(&CertVerifierWorker::Run, base::Unretained(this)), | |
| 245 true /* task is slow */); | |
| 246 } | |
| 247 | |
| 248 // Cancel is called from the origin loop when the MultiThreadedCertVerifier is | |
| 249 // getting deleted. | |
| 250 void Cancel() { | |
| 251 DCHECK_EQ(base::MessageLoop::current(), origin_loop_); | |
| 252 base::AutoLock locked(lock_); | |
| 253 canceled_ = true; | |
| 254 } | |
| 255 | |
| 256 private: | |
| 257 void Run() { | |
| 258 // Runs on a worker thread. | |
| 259 error_ = verify_proc_->Verify(cert_.get(), | |
| 260 hostname_, | |
| 261 flags_, | |
| 262 crl_set_.get(), | |
| 263 additional_trust_anchors_, | |
| 264 &verify_result_); | |
| 265 #if defined(USE_NSS) || defined(OS_IOS) | |
| 266 // Detach the thread from NSPR. | |
| 267 // Calling NSS functions attaches the thread to NSPR, which stores | |
| 268 // the NSPR thread ID in thread-specific data. | |
| 269 // The threads in our thread pool terminate after we have called | |
| 270 // PR_Cleanup. Unless we detach them from NSPR, net_unittests gets | |
| 271 // segfaults on shutdown when the threads' thread-specific data | |
| 272 // destructors run. | |
| 273 PR_DetachThread(); | |
| 274 #endif | |
| 275 Finish(); | |
| 276 } | |
| 277 | |
| 278 // DoReply runs on the origin thread. | |
| 279 void DoReply() { | |
| 280 DCHECK_EQ(base::MessageLoop::current(), origin_loop_); | |
| 281 { | |
| 282 // We lock here because the worker thread could still be in Finished, | |
| 283 // after the PostTask, but before unlocking |lock_|. If we do not lock in | |
| 284 // this case, we will end up deleting a locked Lock, which can lead to | |
| 285 // memory leaks or worse errors. | |
| 286 base::AutoLock locked(lock_); | |
| 287 if (!canceled_) { | |
| 288 cert_verifier_->HandleResult(cert_.get(), | |
| 289 hostname_, | |
| 290 flags_, | |
| 291 additional_trust_anchors_, | |
| 292 error_, | |
| 293 verify_result_); | |
| 294 } | |
| 295 } | |
| 296 delete this; | |
| 297 } | |
| 298 | |
| 299 void Finish() { | |
| 300 // Runs on the worker thread. | |
| 301 // We assume that the origin loop outlives the MultiThreadedCertVerifier. If | |
| 302 // the MultiThreadedCertVerifier is deleted, it will call Cancel on us. If | |
| 303 // it does so before the Acquire, we'll delete ourselves and return. If it's | |
| 304 // trying to do so concurrently, then it'll block on the lock and we'll call | |
| 305 // PostTask while the MultiThreadedCertVerifier (and therefore the | |
| 306 // MessageLoop) is still alive. | |
| 307 // If it does so after this function, we assume that the MessageLoop will | |
| 308 // process pending tasks. In which case we'll notice the |canceled_| flag | |
| 309 // in DoReply. | |
| 310 | |
| 311 bool canceled; | |
| 312 { | |
| 313 base::AutoLock locked(lock_); | |
| 314 canceled = canceled_; | |
| 315 if (!canceled) { | |
| 316 origin_loop_->PostTask( | |
| 317 FROM_HERE, base::Bind( | |
| 318 &CertVerifierWorker::DoReply, base::Unretained(this))); | |
| 319 } | |
| 320 } | |
| 321 | |
| 322 if (canceled) | |
| 323 delete this; | |
| 324 } | |
| 325 | |
| 326 scoped_refptr<CertVerifyProc> verify_proc_; | |
| 327 scoped_refptr<X509Certificate> cert_; | |
| 328 const std::string hostname_; | |
| 329 const int flags_; | |
| 330 scoped_refptr<CRLSet> crl_set_; | |
| 331 const CertificateList additional_trust_anchors_; | |
| 332 base::MessageLoop* const origin_loop_; | |
| 333 MultiThreadedCertVerifier* const cert_verifier_; | |
| 334 | |
| 335 // lock_ protects canceled_. | |
| 336 base::Lock lock_; | |
| 337 | |
| 338 // If canceled_ is true, | |
| 339 // * origin_loop_ cannot be accessed by the worker thread, | |
| 340 // * cert_verifier_ cannot be accessed by any thread. | |
| 341 bool canceled_; | |
| 342 | |
| 343 int error_; | |
| 344 CertVerifyResult verify_result_; | |
| 345 | |
| 346 DISALLOW_COPY_AND_ASSIGN(CertVerifierWorker); | |
| 347 }; | |
| 348 | |
| 349 // A CertVerifierJob is a one-to-one counterpart of a CertVerifierWorker. It | |
| 350 // lives only on the CertVerifier's origin message loop. | |
| 351 class CertVerifierJob { | |
| 352 public: | |
| 353 CertVerifierJob(CertVerifierWorker* worker, | |
| 354 const BoundNetLog& net_log) | |
| 355 : start_time_(base::TimeTicks::Now()), | |
| 356 worker_(worker), | |
| 357 net_log_(net_log) { | |
| 358 net_log_.BeginEvent( | |
| 359 NetLog::TYPE_CERT_VERIFIER_JOB, | |
| 360 base::Bind(&NetLogX509CertificateCallback, | |
| 361 base::Unretained(worker_->certificate()))); | |
| 362 } | |
| 363 | |
| 364 ~CertVerifierJob() { | |
| 365 if (worker_) { | |
| 366 net_log_.AddEvent(NetLog::TYPE_CANCELLED); | |
| 367 net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_JOB); | |
| 368 worker_->Cancel(); | |
| 369 DeleteAllCanceled(); | |
| 370 } | |
| 371 } | |
| 372 | |
| 373 void AddRequest(CertVerifierRequest* request) { | |
| 374 request->net_log().AddEvent( | |
| 375 NetLog::TYPE_CERT_VERIFIER_REQUEST_BOUND_TO_JOB, | |
| 376 net_log_.source().ToEventParametersCallback()); | |
| 377 | |
| 378 requests_.push_back(request); | |
| 379 } | |
| 380 | |
| 381 void HandleResult( | |
| 382 const MultiThreadedCertVerifier::CachedResult& verify_result, | |
| 383 bool is_first_job) { | |
| 384 worker_ = NULL; | |
| 385 net_log_.EndEvent( | |
| 386 NetLog::TYPE_CERT_VERIFIER_JOB, | |
| 387 base::Bind(&CertVerifyResultCallback, verify_result.result)); | |
| 388 base::TimeDelta latency = base::TimeTicks::Now() - start_time_; | |
| 389 UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_Job_Latency", | |
| 390 latency, | |
| 391 base::TimeDelta::FromMilliseconds(1), | |
| 392 base::TimeDelta::FromMinutes(10), | |
| 393 100); | |
| 394 if (is_first_job) { | |
| 395 UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_First_Job_Latency", | |
| 396 latency, | |
| 397 base::TimeDelta::FromMilliseconds(1), | |
| 398 base::TimeDelta::FromMinutes(10), | |
| 399 100); | |
| 400 } | |
| 401 PostAll(verify_result); | |
| 402 } | |
| 403 | |
| 404 private: | |
| 405 void PostAll(const MultiThreadedCertVerifier::CachedResult& verify_result) { | |
| 406 std::vector<CertVerifierRequest*> requests; | |
| 407 requests_.swap(requests); | |
| 408 | |
| 409 for (std::vector<CertVerifierRequest*>::iterator | |
| 410 i = requests.begin(); i != requests.end(); i++) { | |
| 411 (*i)->Post(verify_result); | |
| 412 // Post() causes the CertVerifierRequest to delete itself. | |
| 413 } | |
| 414 } | |
| 415 | |
| 416 void DeleteAllCanceled() { | |
| 417 for (std::vector<CertVerifierRequest*>::iterator | |
| 418 i = requests_.begin(); i != requests_.end(); i++) { | |
| 419 if ((*i)->canceled()) { | |
| 420 delete *i; | |
| 421 } else { | |
| 422 LOG(DFATAL) << "CertVerifierRequest leaked!"; | |
| 423 } | |
| 424 } | |
| 425 } | |
| 426 | |
| 427 const base::TimeTicks start_time_; | |
| 428 std::vector<CertVerifierRequest*> requests_; | |
| 429 CertVerifierWorker* worker_; | |
| 430 const BoundNetLog net_log_; | |
| 431 }; | |
| 432 | |
| 433 MultiThreadedCertVerifier::MultiThreadedCertVerifier( | |
| 434 CertVerifyProc* verify_proc) | |
| 435 : cache_(kMaxCacheEntries), | |
| 436 first_job_(NULL), | |
| 437 requests_(0), | |
| 438 cache_hits_(0), | |
| 439 inflight_joins_(0), | |
| 440 verify_proc_(verify_proc), | |
| 441 trust_anchor_provider_(NULL) { | |
| 442 CertDatabase::GetInstance()->AddObserver(this); | |
| 443 } | |
| 444 | |
| 445 MultiThreadedCertVerifier::~MultiThreadedCertVerifier() { | |
| 446 STLDeleteValues(&inflight_); | |
| 447 CertDatabase::GetInstance()->RemoveObserver(this); | |
| 448 } | |
| 449 | |
| 450 void MultiThreadedCertVerifier::SetCertTrustAnchorProvider( | |
| 451 CertTrustAnchorProvider* trust_anchor_provider) { | |
| 452 DCHECK(CalledOnValidThread()); | |
| 453 trust_anchor_provider_ = trust_anchor_provider; | |
| 454 } | |
| 455 | |
| 456 int MultiThreadedCertVerifier::Verify(X509Certificate* cert, | |
| 457 const std::string& hostname, | |
| 458 int flags, | |
| 459 CRLSet* crl_set, | |
| 460 CertVerifyResult* verify_result, | |
| 461 const CompletionCallback& callback, | |
| 462 RequestHandle* out_req, | |
| 463 const BoundNetLog& net_log) { | |
| 464 DCHECK(CalledOnValidThread()); | |
| 465 | |
| 466 if (callback.is_null() || !verify_result || hostname.empty()) { | |
| 467 *out_req = NULL; | |
| 468 return ERR_INVALID_ARGUMENT; | |
| 469 } | |
| 470 | |
| 471 requests_++; | |
| 472 | |
| 473 const CertificateList empty_cert_list; | |
| 474 const CertificateList& additional_trust_anchors = | |
| 475 trust_anchor_provider_ ? | |
| 476 trust_anchor_provider_->GetAdditionalTrustAnchors() : empty_cert_list; | |
| 477 | |
| 478 const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(), | |
| 479 hostname, flags, additional_trust_anchors); | |
| 480 const CertVerifierCache::value_type* cached_entry = | |
| 481 cache_.Get(key, CacheValidityPeriod(base::Time::Now())); | |
| 482 if (cached_entry) { | |
| 483 ++cache_hits_; | |
| 484 *out_req = NULL; | |
| 485 *verify_result = cached_entry->result; | |
| 486 return cached_entry->error; | |
| 487 } | |
| 488 | |
| 489 // No cache hit. See if an identical request is currently in flight. | |
| 490 CertVerifierJob* job; | |
| 491 std::map<RequestParams, CertVerifierJob*>::const_iterator j; | |
| 492 j = inflight_.find(key); | |
| 493 if (j != inflight_.end()) { | |
| 494 // An identical request is in flight already. We'll just attach our | |
| 495 // callback. | |
| 496 inflight_joins_++; | |
| 497 job = j->second; | |
| 498 } else { | |
| 499 // Need to make a new request. | |
| 500 CertVerifierWorker* worker = | |
| 501 new CertVerifierWorker(verify_proc_.get(), | |
| 502 cert, | |
| 503 hostname, | |
| 504 flags, | |
| 505 crl_set, | |
| 506 additional_trust_anchors, | |
| 507 this); | |
| 508 job = new CertVerifierJob( | |
| 509 worker, | |
| 510 BoundNetLog::Make(net_log.net_log(), NetLog::SOURCE_CERT_VERIFIER_JOB)); | |
| 511 if (!worker->Start()) { | |
| 512 delete job; | |
| 513 delete worker; | |
| 514 *out_req = NULL; | |
| 515 // TODO(wtc): log to the NetLog. | |
| 516 LOG(ERROR) << "CertVerifierWorker couldn't be started."; | |
| 517 return ERR_INSUFFICIENT_RESOURCES; // Just a guess. | |
| 518 } | |
| 519 inflight_.insert(std::make_pair(key, job)); | |
| 520 if (requests_ == 1) { | |
| 521 // Cleared in HandleResult. | |
| 522 first_job_ = job; | |
| 523 } | |
| 524 } | |
| 525 | |
| 526 CertVerifierRequest* request = | |
| 527 new CertVerifierRequest(callback, verify_result, net_log); | |
| 528 job->AddRequest(request); | |
| 529 *out_req = request; | |
| 530 return ERR_IO_PENDING; | |
| 531 } | |
| 532 | |
| 533 void MultiThreadedCertVerifier::CancelRequest(RequestHandle req) { | |
| 534 DCHECK(CalledOnValidThread()); | |
| 535 CertVerifierRequest* request = reinterpret_cast<CertVerifierRequest*>(req); | |
| 536 request->Cancel(); | |
| 537 } | |
| 538 | |
| 539 MultiThreadedCertVerifier::RequestParams::RequestParams( | |
| 540 const SHA1HashValue& cert_fingerprint_arg, | |
| 541 const SHA1HashValue& ca_fingerprint_arg, | |
| 542 const std::string& hostname_arg, | |
| 543 int flags_arg, | |
| 544 const CertificateList& additional_trust_anchors) | |
| 545 : hostname(hostname_arg), | |
| 546 flags(flags_arg) { | |
| 547 hash_values.reserve(2 + additional_trust_anchors.size()); | |
| 548 hash_values.push_back(cert_fingerprint_arg); | |
| 549 hash_values.push_back(ca_fingerprint_arg); | |
| 550 for (size_t i = 0; i < additional_trust_anchors.size(); ++i) | |
| 551 hash_values.push_back(additional_trust_anchors[i]->fingerprint()); | |
| 552 } | |
| 553 | |
| 554 MultiThreadedCertVerifier::RequestParams::~RequestParams() {} | |
| 555 | |
| 556 bool MultiThreadedCertVerifier::RequestParams::operator<( | |
| 557 const RequestParams& other) const { | |
| 558 // |flags| is compared before |cert_fingerprint|, |ca_fingerprint|, and | |
| 559 // |hostname| under assumption that integer comparisons are faster than | |
| 560 // memory and string comparisons. | |
| 561 if (flags != other.flags) | |
| 562 return flags < other.flags; | |
| 563 if (hostname != other.hostname) | |
| 564 return hostname < other.hostname; | |
| 565 return std::lexicographical_compare( | |
| 566 hash_values.begin(), hash_values.end(), | |
| 567 other.hash_values.begin(), other.hash_values.end(), | |
| 568 net::SHA1HashValueLessThan()); | |
| 569 } | |
| 570 | |
| 571 // HandleResult is called by CertVerifierWorker on the origin message loop. | |
| 572 // It deletes CertVerifierJob. | |
| 573 void MultiThreadedCertVerifier::HandleResult( | |
| 574 X509Certificate* cert, | |
| 575 const std::string& hostname, | |
| 576 int flags, | |
| 577 const CertificateList& additional_trust_anchors, | |
| 578 int error, | |
| 579 const CertVerifyResult& verify_result) { | |
| 580 DCHECK(CalledOnValidThread()); | |
| 581 | |
| 582 const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(), | |
| 583 hostname, flags, additional_trust_anchors); | |
| 584 | |
| 585 CachedResult cached_result; | |
| 586 cached_result.error = error; | |
| 587 cached_result.result = verify_result; | |
| 588 base::Time now = base::Time::Now(); | |
| 589 cache_.Put( | |
| 590 key, cached_result, CacheValidityPeriod(now), | |
| 591 CacheValidityPeriod(now, now + base::TimeDelta::FromSeconds(kTTLSecs))); | |
| 592 | |
| 593 std::map<RequestParams, CertVerifierJob*>::iterator j; | |
| 594 j = inflight_.find(key); | |
| 595 if (j == inflight_.end()) { | |
| 596 NOTREACHED(); | |
| 597 return; | |
| 598 } | |
| 599 CertVerifierJob* job = j->second; | |
| 600 inflight_.erase(j); | |
| 601 bool is_first_job = false; | |
| 602 if (first_job_ == job) { | |
| 603 is_first_job = true; | |
| 604 first_job_ = NULL; | |
| 605 } | |
| 606 | |
| 607 job->HandleResult(cached_result, is_first_job); | |
| 608 delete job; | |
| 609 } | |
| 610 | |
| 611 void MultiThreadedCertVerifier::OnCACertChanged( | |
| 612 const X509Certificate* cert) { | |
| 613 DCHECK(CalledOnValidThread()); | |
| 614 | |
| 615 ClearCache(); | |
| 616 } | |
| 617 | |
| 618 } // namespace net | |
| 619 | |
| OLD | NEW |