This source file includes following definitions.
- expiration_time
- expiration_time
- net_log_
- Cancel
- Post
- canceled
- net_log
- error_
- certificate
- Start
- Cancel
- Run
- DoReply
- Finish
- net_log_
- AddRequest
- HandleResult
- PostAll
- DeleteAllCanceled
- trust_anchor_provider_
- SetCertTrustAnchorProvider
- Verify
- CancelRequest
- flags
- HandleResult
- OnCACertChanged
#include "net/cert/multi_threaded_cert_verifier.h"
#include <algorithm>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/compiler_specific.h"
#include "base/message_loop/message_loop.h"
#include "base/metrics/histogram.h"
#include "base/stl_util.h"
#include "base/synchronization/lock.h"
#include "base/threading/worker_pool.h"
#include "base/time/time.h"
#include "net/base/net_errors.h"
#include "net/base/net_log.h"
#include "net/cert/cert_trust_anchor_provider.h"
#include "net/cert/cert_verify_proc.h"
#include "net/cert/crl_set.h"
#include "net/cert/x509_certificate.h"
#include "net/cert/x509_certificate_net_log_param.h"
#if defined(USE_NSS) || defined(OS_IOS)
#include <private/pprthred.h>
#endif
namespace net {
namespace {
const unsigned kMaxCacheEntries = 256;
const unsigned kTTLSecs = 1800;
}
MultiThreadedCertVerifier::CachedResult::CachedResult() : error(ERR_FAILED) {}
MultiThreadedCertVerifier::CachedResult::~CachedResult() {}
MultiThreadedCertVerifier::CacheValidityPeriod::CacheValidityPeriod(
const base::Time& now)
: verification_time(now),
expiration_time(now) {
}
MultiThreadedCertVerifier::CacheValidityPeriod::CacheValidityPeriod(
const base::Time& now,
const base::Time& expiration)
: verification_time(now),
expiration_time(expiration) {
}
bool MultiThreadedCertVerifier::CacheExpirationFunctor::operator()(
const CacheValidityPeriod& now,
const CacheValidityPeriod& expiration) const {
DCHECK(now.verification_time == now.expiration_time);
return now.verification_time >= expiration.verification_time &&
now.verification_time < expiration.expiration_time;
};
class CertVerifierRequest {
public:
CertVerifierRequest(const CompletionCallback& callback,
CertVerifyResult* verify_result,
const BoundNetLog& net_log)
: callback_(callback),
verify_result_(verify_result),
net_log_(net_log) {
net_log_.BeginEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST);
}
~CertVerifierRequest() {
}
void Cancel() {
callback_.Reset();
verify_result_ = NULL;
net_log_.AddEvent(NetLog::TYPE_CANCELLED);
net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST);
}
void Post(const MultiThreadedCertVerifier::CachedResult& verify_result) {
if (!callback_.is_null()) {
net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST);
*verify_result_ = verify_result.result;
callback_.Run(verify_result.error);
}
delete this;
}
bool canceled() const { return callback_.is_null(); }
const BoundNetLog& net_log() const { return net_log_; }
private:
CompletionCallback callback_;
CertVerifyResult* verify_result_;
const BoundNetLog net_log_;
};
class CertVerifierWorker {
public:
CertVerifierWorker(CertVerifyProc* verify_proc,
X509Certificate* cert,
const std::string& hostname,
int flags,
CRLSet* crl_set,
const CertificateList& additional_trust_anchors,
MultiThreadedCertVerifier* cert_verifier)
: verify_proc_(verify_proc),
cert_(cert),
hostname_(hostname),
flags_(flags),
crl_set_(crl_set),
additional_trust_anchors_(additional_trust_anchors),
origin_loop_(base::MessageLoop::current()),
cert_verifier_(cert_verifier),
canceled_(false),
error_(ERR_FAILED) {
}
X509Certificate* certificate() const { return cert_.get(); }
bool Start() {
DCHECK_EQ(base::MessageLoop::current(), origin_loop_);
return base::WorkerPool::PostTask(
FROM_HERE, base::Bind(&CertVerifierWorker::Run, base::Unretained(this)),
true );
}
void Cancel() {
DCHECK_EQ(base::MessageLoop::current(), origin_loop_);
base::AutoLock locked(lock_);
canceled_ = true;
}
private:
void Run() {
error_ = verify_proc_->Verify(cert_.get(),
hostname_,
flags_,
crl_set_.get(),
additional_trust_anchors_,
&verify_result_);
#if defined(USE_NSS) || defined(OS_IOS)
PR_DetachThread();
#endif
Finish();
}
void DoReply() {
DCHECK_EQ(base::MessageLoop::current(), origin_loop_);
{
base::AutoLock locked(lock_);
if (!canceled_) {
cert_verifier_->HandleResult(cert_.get(),
hostname_,
flags_,
additional_trust_anchors_,
error_,
verify_result_);
}
}
delete this;
}
void Finish() {
bool canceled;
{
base::AutoLock locked(lock_);
canceled = canceled_;
if (!canceled) {
origin_loop_->PostTask(
FROM_HERE, base::Bind(
&CertVerifierWorker::DoReply, base::Unretained(this)));
}
}
if (canceled)
delete this;
}
scoped_refptr<CertVerifyProc> verify_proc_;
scoped_refptr<X509Certificate> cert_;
const std::string hostname_;
const int flags_;
scoped_refptr<CRLSet> crl_set_;
const CertificateList additional_trust_anchors_;
base::MessageLoop* const origin_loop_;
MultiThreadedCertVerifier* const cert_verifier_;
base::Lock lock_;
bool canceled_;
int error_;
CertVerifyResult verify_result_;
DISALLOW_COPY_AND_ASSIGN(CertVerifierWorker);
};
class CertVerifierJob {
public:
CertVerifierJob(CertVerifierWorker* worker,
const BoundNetLog& net_log)
: start_time_(base::TimeTicks::Now()),
worker_(worker),
net_log_(net_log) {
net_log_.BeginEvent(
NetLog::TYPE_CERT_VERIFIER_JOB,
base::Bind(&NetLogX509CertificateCallback,
base::Unretained(worker_->certificate())));
}
~CertVerifierJob() {
if (worker_) {
net_log_.AddEvent(NetLog::TYPE_CANCELLED);
net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_JOB);
worker_->Cancel();
DeleteAllCanceled();
}
}
void AddRequest(CertVerifierRequest* request) {
request->net_log().AddEvent(
NetLog::TYPE_CERT_VERIFIER_REQUEST_BOUND_TO_JOB,
net_log_.source().ToEventParametersCallback());
requests_.push_back(request);
}
void HandleResult(
const MultiThreadedCertVerifier::CachedResult& verify_result) {
worker_ = NULL;
net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_JOB);
UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_Job_Latency",
base::TimeTicks::Now() - start_time_,
base::TimeDelta::FromMilliseconds(1),
base::TimeDelta::FromMinutes(10),
100);
PostAll(verify_result);
}
private:
void PostAll(const MultiThreadedCertVerifier::CachedResult& verify_result) {
std::vector<CertVerifierRequest*> requests;
requests_.swap(requests);
for (std::vector<CertVerifierRequest*>::iterator
i = requests.begin(); i != requests.end(); i++) {
(*i)->Post(verify_result);
}
}
void DeleteAllCanceled() {
for (std::vector<CertVerifierRequest*>::iterator
i = requests_.begin(); i != requests_.end(); i++) {
if ((*i)->canceled()) {
delete *i;
} else {
LOG(DFATAL) << "CertVerifierRequest leaked!";
}
}
}
const base::TimeTicks start_time_;
std::vector<CertVerifierRequest*> requests_;
CertVerifierWorker* worker_;
const BoundNetLog net_log_;
};
MultiThreadedCertVerifier::MultiThreadedCertVerifier(
CertVerifyProc* verify_proc)
: cache_(kMaxCacheEntries),
requests_(0),
cache_hits_(0),
inflight_joins_(0),
verify_proc_(verify_proc),
trust_anchor_provider_(NULL) {
CertDatabase::GetInstance()->AddObserver(this);
}
MultiThreadedCertVerifier::~MultiThreadedCertVerifier() {
STLDeleteValues(&inflight_);
CertDatabase::GetInstance()->RemoveObserver(this);
}
void MultiThreadedCertVerifier::SetCertTrustAnchorProvider(
CertTrustAnchorProvider* trust_anchor_provider) {
DCHECK(CalledOnValidThread());
trust_anchor_provider_ = trust_anchor_provider;
}
int MultiThreadedCertVerifier::Verify(X509Certificate* cert,
const std::string& hostname,
int flags,
CRLSet* crl_set,
CertVerifyResult* verify_result,
const CompletionCallback& callback,
RequestHandle* out_req,
const BoundNetLog& net_log) {
DCHECK(CalledOnValidThread());
if (callback.is_null() || !verify_result || hostname.empty()) {
*out_req = NULL;
return ERR_INVALID_ARGUMENT;
}
requests_++;
const CertificateList empty_cert_list;
const CertificateList& additional_trust_anchors =
trust_anchor_provider_ ?
trust_anchor_provider_->GetAdditionalTrustAnchors() : empty_cert_list;
const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(),
hostname, flags, additional_trust_anchors);
const CertVerifierCache::value_type* cached_entry =
cache_.Get(key, CacheValidityPeriod(base::Time::Now()));
if (cached_entry) {
++cache_hits_;
*out_req = NULL;
*verify_result = cached_entry->result;
return cached_entry->error;
}
CertVerifierJob* job;
std::map<RequestParams, CertVerifierJob*>::const_iterator j;
j = inflight_.find(key);
if (j != inflight_.end()) {
inflight_joins_++;
job = j->second;
} else {
CertVerifierWorker* worker =
new CertVerifierWorker(verify_proc_.get(),
cert,
hostname,
flags,
crl_set,
additional_trust_anchors,
this);
job = new CertVerifierJob(
worker,
BoundNetLog::Make(net_log.net_log(), NetLog::SOURCE_CERT_VERIFIER_JOB));
if (!worker->Start()) {
delete job;
delete worker;
*out_req = NULL;
LOG(ERROR) << "CertVerifierWorker couldn't be started.";
return ERR_INSUFFICIENT_RESOURCES;
}
inflight_.insert(std::make_pair(key, job));
}
CertVerifierRequest* request =
new CertVerifierRequest(callback, verify_result, net_log);
job->AddRequest(request);
*out_req = request;
return ERR_IO_PENDING;
}
void MultiThreadedCertVerifier::CancelRequest(RequestHandle req) {
DCHECK(CalledOnValidThread());
CertVerifierRequest* request = reinterpret_cast<CertVerifierRequest*>(req);
request->Cancel();
}
MultiThreadedCertVerifier::RequestParams::RequestParams(
const SHA1HashValue& cert_fingerprint_arg,
const SHA1HashValue& ca_fingerprint_arg,
const std::string& hostname_arg,
int flags_arg,
const CertificateList& additional_trust_anchors)
: hostname(hostname_arg),
flags(flags_arg) {
hash_values.reserve(2 + additional_trust_anchors.size());
hash_values.push_back(cert_fingerprint_arg);
hash_values.push_back(ca_fingerprint_arg);
for (size_t i = 0; i < additional_trust_anchors.size(); ++i)
hash_values.push_back(additional_trust_anchors[i]->fingerprint());
}
MultiThreadedCertVerifier::RequestParams::~RequestParams() {}
bool MultiThreadedCertVerifier::RequestParams::operator<(
const RequestParams& other) const {
if (flags != other.flags)
return flags < other.flags;
if (hostname != other.hostname)
return hostname < other.hostname;
return std::lexicographical_compare(
hash_values.begin(), hash_values.end(),
other.hash_values.begin(), other.hash_values.end(),
net::SHA1HashValueLessThan());
}
void MultiThreadedCertVerifier::HandleResult(
X509Certificate* cert,
const std::string& hostname,
int flags,
const CertificateList& additional_trust_anchors,
int error,
const CertVerifyResult& verify_result) {
DCHECK(CalledOnValidThread());
const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(),
hostname, flags, additional_trust_anchors);
CachedResult cached_result;
cached_result.error = error;
cached_result.result = verify_result;
base::Time now = base::Time::Now();
cache_.Put(
key, cached_result, CacheValidityPeriod(now),
CacheValidityPeriod(now, now + base::TimeDelta::FromSeconds(kTTLSecs)));
std::map<RequestParams, CertVerifierJob*>::iterator j;
j = inflight_.find(key);
if (j == inflight_.end()) {
NOTREACHED();
return;
}
CertVerifierJob* job = j->second;
inflight_.erase(j);
job->HandleResult(cached_result);
delete job;
}
void MultiThreadedCertVerifier::OnCACertChanged(
const X509Certificate* cert) {
DCHECK(CalledOnValidThread());
ClearCache();
}
}