Home | History | Annotate | Download | only in ssl
      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/ssl/server_bound_cert_service.h"
      6 
      7 #include <algorithm>
      8 #include <limits>
      9 
     10 #include "base/bind.h"
     11 #include "base/bind_helpers.h"
     12 #include "base/callback_helpers.h"
     13 #include "base/compiler_specific.h"
     14 #include "base/location.h"
     15 #include "base/logging.h"
     16 #include "base/memory/ref_counted.h"
     17 #include "base/memory/scoped_ptr.h"
     18 #include "base/message_loop/message_loop_proxy.h"
     19 #include "base/metrics/histogram.h"
     20 #include "base/rand_util.h"
     21 #include "base/stl_util.h"
     22 #include "base/task_runner.h"
     23 #include "crypto/ec_private_key.h"
     24 #include "net/base/net_errors.h"
     25 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
     26 #include "net/cert/x509_certificate.h"
     27 #include "net/cert/x509_util.h"
     28 #include "url/gurl.h"
     29 
     30 #if defined(USE_NSS)
     31 #include <private/pprthred.h>  // PR_DetachThread
     32 #endif
     33 
     34 namespace net {
     35 
     36 namespace {
     37 
     38 const int kValidityPeriodInDays = 365;
     39 // When we check the system time, we add this many days to the end of the check
     40 // so the result will still hold even after chrome has been running for a
     41 // while.
     42 const int kSystemTimeValidityBufferInDays = 90;
     43 
     44 // Used by the GetDomainBoundCertResult histogram to record the final
     45 // outcome of each GetDomainBoundCert or GetOrCreateDomainBoundCert call.
     46 // Do not re-use values.
     47 enum GetCertResult {
     48   // Synchronously found and returned an existing domain bound cert.
     49   SYNC_SUCCESS = 0,
     50   // Retrieved or generated and returned a domain bound cert asynchronously.
     51   ASYNC_SUCCESS = 1,
     52   // Retrieval/generation request was cancelled before the cert generation
     53   // completed.
     54   ASYNC_CANCELLED = 2,
     55   // Cert generation failed.
     56   ASYNC_FAILURE_KEYGEN = 3,
     57   ASYNC_FAILURE_CREATE_CERT = 4,
     58   ASYNC_FAILURE_EXPORT_KEY = 5,
     59   ASYNC_FAILURE_UNKNOWN = 6,
     60   // GetDomainBoundCert or GetOrCreateDomainBoundCert was called with
     61   // invalid arguments.
     62   INVALID_ARGUMENT = 7,
     63   // We don't support any of the cert types the server requested.
     64   UNSUPPORTED_TYPE = 8,
     65   // Server asked for a different type of certs while we were generating one.
     66   TYPE_MISMATCH = 9,
     67   // Couldn't start a worker to generate a cert.
     68   WORKER_FAILURE = 10,
     69   GET_CERT_RESULT_MAX
     70 };
     71 
     72 void RecordGetDomainBoundCertResult(GetCertResult result) {
     73   UMA_HISTOGRAM_ENUMERATION("DomainBoundCerts.GetDomainBoundCertResult", result,
     74                             GET_CERT_RESULT_MAX);
     75 }
     76 
     77 void RecordGetCertTime(base::TimeDelta request_time) {
     78   UMA_HISTOGRAM_CUSTOM_TIMES("DomainBoundCerts.GetCertTime",
     79                              request_time,
     80                              base::TimeDelta::FromMilliseconds(1),
     81                              base::TimeDelta::FromMinutes(5),
     82                              50);
     83 }
     84 
     85 // On success, returns a ServerBoundCert object and sets |*error| to OK.
     86 // Otherwise, returns NULL, and |*error| will be set to a net error code.
     87 // |serial_number| is passed in because base::RandInt cannot be called from an
     88 // unjoined thread, due to relying on a non-leaked LazyInstance
     89 scoped_ptr<ServerBoundCertStore::ServerBoundCert> GenerateCert(
     90     const std::string& server_identifier,
     91     uint32 serial_number,
     92     int* error) {
     93   scoped_ptr<ServerBoundCertStore::ServerBoundCert> result;
     94 
     95   base::TimeTicks start = base::TimeTicks::Now();
     96   base::Time not_valid_before = base::Time::Now();
     97   base::Time not_valid_after =
     98       not_valid_before + base::TimeDelta::FromDays(kValidityPeriodInDays);
     99   std::string der_cert;
    100   std::vector<uint8> private_key_info;
    101   scoped_ptr<crypto::ECPrivateKey> key;
    102   if (!x509_util::CreateKeyAndDomainBoundCertEC(server_identifier,
    103                                                 serial_number,
    104                                                 not_valid_before,
    105                                                 not_valid_after,
    106                                                 &key,
    107                                                 &der_cert)) {
    108     DLOG(ERROR) << "Unable to create x509 cert for client";
    109     *error = ERR_ORIGIN_BOUND_CERT_GENERATION_FAILED;
    110     return result.Pass();
    111   }
    112 
    113   if (!key->ExportEncryptedPrivateKey(ServerBoundCertService::kEPKIPassword,
    114                                       1, &private_key_info)) {
    115     DLOG(ERROR) << "Unable to export private key";
    116     *error = ERR_PRIVATE_KEY_EXPORT_FAILED;
    117     return result.Pass();
    118   }
    119 
    120   // TODO(rkn): Perhaps ExportPrivateKey should be changed to output a
    121   // std::string* to prevent this copying.
    122   std::string key_out(private_key_info.begin(), private_key_info.end());
    123 
    124   result.reset(new ServerBoundCertStore::ServerBoundCert(
    125       server_identifier,
    126       not_valid_before,
    127       not_valid_after,
    128       key_out,
    129       der_cert));
    130   UMA_HISTOGRAM_CUSTOM_TIMES("DomainBoundCerts.GenerateCertTime",
    131                              base::TimeTicks::Now() - start,
    132                              base::TimeDelta::FromMilliseconds(1),
    133                              base::TimeDelta::FromMinutes(5),
    134                              50);
    135   *error = OK;
    136   return result.Pass();
    137 }
    138 
    139 }  // namespace
    140 
    141 // Represents the output and result callback of a request.
    142 class ServerBoundCertServiceRequest {
    143  public:
    144   ServerBoundCertServiceRequest(base::TimeTicks request_start,
    145                                 const CompletionCallback& callback,
    146                                 std::string* private_key,
    147                                 std::string* cert)
    148       : request_start_(request_start),
    149         callback_(callback),
    150         private_key_(private_key),
    151         cert_(cert) {
    152   }
    153 
    154   // Ensures that the result callback will never be made.
    155   void Cancel() {
    156     RecordGetDomainBoundCertResult(ASYNC_CANCELLED);
    157     callback_.Reset();
    158     private_key_ = NULL;
    159     cert_ = NULL;
    160   }
    161 
    162   // Copies the contents of |private_key| and |cert| to the caller's output
    163   // arguments and calls the callback.
    164   void Post(int error,
    165             const std::string& private_key,
    166             const std::string& cert) {
    167     switch (error) {
    168       case OK: {
    169         base::TimeDelta request_time = base::TimeTicks::Now() - request_start_;
    170         UMA_HISTOGRAM_CUSTOM_TIMES("DomainBoundCerts.GetCertTimeAsync",
    171                                    request_time,
    172                                    base::TimeDelta::FromMilliseconds(1),
    173                                    base::TimeDelta::FromMinutes(5),
    174                                    50);
    175         RecordGetCertTime(request_time);
    176         RecordGetDomainBoundCertResult(ASYNC_SUCCESS);
    177         break;
    178       }
    179       case ERR_KEY_GENERATION_FAILED:
    180         RecordGetDomainBoundCertResult(ASYNC_FAILURE_KEYGEN);
    181         break;
    182       case ERR_ORIGIN_BOUND_CERT_GENERATION_FAILED:
    183         RecordGetDomainBoundCertResult(ASYNC_FAILURE_CREATE_CERT);
    184         break;
    185       case ERR_PRIVATE_KEY_EXPORT_FAILED:
    186         RecordGetDomainBoundCertResult(ASYNC_FAILURE_EXPORT_KEY);
    187         break;
    188       case ERR_INSUFFICIENT_RESOURCES:
    189         RecordGetDomainBoundCertResult(WORKER_FAILURE);
    190         break;
    191       default:
    192         RecordGetDomainBoundCertResult(ASYNC_FAILURE_UNKNOWN);
    193         break;
    194     }
    195     if (!callback_.is_null()) {
    196       *private_key_ = private_key;
    197       *cert_ = cert;
    198       callback_.Run(error);
    199     }
    200     delete this;
    201   }
    202 
    203   bool canceled() const { return callback_.is_null(); }
    204 
    205  private:
    206   base::TimeTicks request_start_;
    207   CompletionCallback callback_;
    208   std::string* private_key_;
    209   std::string* cert_;
    210 };
    211 
    212 // ServerBoundCertServiceWorker runs on a worker thread and takes care of the
    213 // blocking process of performing key generation. Will take care of deleting
    214 // itself once Start() is called.
    215 class ServerBoundCertServiceWorker {
    216  public:
    217   typedef base::Callback<void(
    218       const std::string&,
    219       int,
    220       scoped_ptr<ServerBoundCertStore::ServerBoundCert>)> WorkerDoneCallback;
    221 
    222   ServerBoundCertServiceWorker(
    223       const std::string& server_identifier,
    224       const WorkerDoneCallback& callback)
    225       : server_identifier_(server_identifier),
    226         serial_number_(base::RandInt(0, std::numeric_limits<int>::max())),
    227         origin_loop_(base::MessageLoopProxy::current()),
    228         callback_(callback) {
    229   }
    230 
    231   // Starts the worker on |task_runner|. If the worker fails to start, such as
    232   // if the task runner is shutting down, then it will take care of deleting
    233   // itself.
    234   bool Start(const scoped_refptr<base::TaskRunner>& task_runner) {
    235     DCHECK(origin_loop_->RunsTasksOnCurrentThread());
    236 
    237     return task_runner->PostTask(
    238         FROM_HERE,
    239         base::Bind(&ServerBoundCertServiceWorker::Run, base::Owned(this)));
    240   }
    241 
    242  private:
    243   void Run() {
    244     // Runs on a worker thread.
    245     int error = ERR_FAILED;
    246     scoped_ptr<ServerBoundCertStore::ServerBoundCert> cert =
    247         GenerateCert(server_identifier_, serial_number_, &error);
    248     DVLOG(1) << "GenerateCert " << server_identifier_ << " returned " << error;
    249 #if defined(USE_NSS)
    250     // Detach the thread from NSPR.
    251     // Calling NSS functions attaches the thread to NSPR, which stores
    252     // the NSPR thread ID in thread-specific data.
    253     // The threads in our thread pool terminate after we have called
    254     // PR_Cleanup. Unless we detach them from NSPR, net_unittests gets
    255     // segfaults on shutdown when the threads' thread-specific data
    256     // destructors run.
    257     PR_DetachThread();
    258 #endif
    259     origin_loop_->PostTask(FROM_HERE,
    260                            base::Bind(callback_, server_identifier_, error,
    261                                       base::Passed(&cert)));
    262   }
    263 
    264   const std::string server_identifier_;
    265   // Note that serial_number_ must be initialized on a non-worker thread
    266   // (see documentation for GenerateCert).
    267   uint32 serial_number_;
    268   scoped_refptr<base::SequencedTaskRunner> origin_loop_;
    269   WorkerDoneCallback callback_;
    270 
    271   DISALLOW_COPY_AND_ASSIGN(ServerBoundCertServiceWorker);
    272 };
    273 
    274 // A ServerBoundCertServiceJob is a one-to-one counterpart of an
    275 // ServerBoundCertServiceWorker. It lives only on the ServerBoundCertService's
    276 // origin message loop.
    277 class ServerBoundCertServiceJob {
    278  public:
    279   ServerBoundCertServiceJob(bool create_if_missing)
    280       : create_if_missing_(create_if_missing) {
    281   }
    282 
    283   ~ServerBoundCertServiceJob() {
    284     if (!requests_.empty())
    285       DeleteAllCanceled();
    286   }
    287 
    288   void AddRequest(ServerBoundCertServiceRequest* request,
    289                   bool create_if_missing = false) {
    290     create_if_missing_ |= create_if_missing;
    291     requests_.push_back(request);
    292   }
    293 
    294   void HandleResult(int error,
    295                     const std::string& private_key,
    296                     const std::string& cert) {
    297     PostAll(error, private_key, cert);
    298   }
    299 
    300   bool CreateIfMissing() const { return create_if_missing_; }
    301 
    302  private:
    303   void PostAll(int error,
    304                const std::string& private_key,
    305                const std::string& cert) {
    306     std::vector<ServerBoundCertServiceRequest*> requests;
    307     requests_.swap(requests);
    308 
    309     for (std::vector<ServerBoundCertServiceRequest*>::iterator
    310          i = requests.begin(); i != requests.end(); i++) {
    311       (*i)->Post(error, private_key, cert);
    312       // Post() causes the ServerBoundCertServiceRequest to delete itself.
    313     }
    314   }
    315 
    316   void DeleteAllCanceled() {
    317     for (std::vector<ServerBoundCertServiceRequest*>::iterator
    318          i = requests_.begin(); i != requests_.end(); i++) {
    319       if ((*i)->canceled()) {
    320         delete *i;
    321       } else {
    322         LOG(DFATAL) << "ServerBoundCertServiceRequest leaked!";
    323       }
    324     }
    325   }
    326 
    327   std::vector<ServerBoundCertServiceRequest*> requests_;
    328   bool create_if_missing_;
    329 };
    330 
    331 // static
    332 const char ServerBoundCertService::kEPKIPassword[] = "";
    333 
    334 ServerBoundCertService::RequestHandle::RequestHandle()
    335     : service_(NULL),
    336       request_(NULL) {}
    337 
    338 ServerBoundCertService::RequestHandle::~RequestHandle() {
    339   Cancel();
    340 }
    341 
    342 void ServerBoundCertService::RequestHandle::Cancel() {
    343   if (request_) {
    344     service_->CancelRequest(request_);
    345     request_ = NULL;
    346     callback_.Reset();
    347   }
    348 }
    349 
    350 void ServerBoundCertService::RequestHandle::RequestStarted(
    351     ServerBoundCertService* service,
    352     ServerBoundCertServiceRequest* request,
    353     const CompletionCallback& callback) {
    354   DCHECK(request_ == NULL);
    355   service_ = service;
    356   request_ = request;
    357   callback_ = callback;
    358 }
    359 
    360 void ServerBoundCertService::RequestHandle::OnRequestComplete(int result) {
    361   request_ = NULL;
    362   // Running the callback might delete |this|, so we can't touch any of our
    363   // members afterwards. Reset callback_ first.
    364   base::ResetAndReturn(&callback_).Run(result);
    365 }
    366 
    367 ServerBoundCertService::ServerBoundCertService(
    368     ServerBoundCertStore* server_bound_cert_store,
    369     const scoped_refptr<base::TaskRunner>& task_runner)
    370     : server_bound_cert_store_(server_bound_cert_store),
    371       task_runner_(task_runner),
    372       requests_(0),
    373       cert_store_hits_(0),
    374       inflight_joins_(0),
    375       workers_created_(0),
    376       weak_ptr_factory_(this) {
    377   base::Time start = base::Time::Now();
    378   base::Time end = start + base::TimeDelta::FromDays(
    379       kValidityPeriodInDays + kSystemTimeValidityBufferInDays);
    380   is_system_time_valid_ = x509_util::IsSupportedValidityRange(start, end);
    381 }
    382 
    383 ServerBoundCertService::~ServerBoundCertService() {
    384   STLDeleteValues(&inflight_);
    385 }
    386 
    387 //static
    388 std::string ServerBoundCertService::GetDomainForHost(const std::string& host) {
    389   std::string domain =
    390       registry_controlled_domains::GetDomainAndRegistry(
    391           host, registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
    392   if (domain.empty())
    393     return host;
    394   return domain;
    395 }
    396 
    397 int ServerBoundCertService::GetOrCreateDomainBoundCert(
    398     const std::string& host,
    399     std::string* private_key,
    400     std::string* cert,
    401     const CompletionCallback& callback,
    402     RequestHandle* out_req) {
    403   DVLOG(1) << __FUNCTION__ << " " << host;
    404   DCHECK(CalledOnValidThread());
    405   base::TimeTicks request_start = base::TimeTicks::Now();
    406 
    407   if (callback.is_null() || !private_key || !cert || host.empty()) {
    408     RecordGetDomainBoundCertResult(INVALID_ARGUMENT);
    409     return ERR_INVALID_ARGUMENT;
    410   }
    411 
    412   std::string domain = GetDomainForHost(host);
    413   if (domain.empty()) {
    414     RecordGetDomainBoundCertResult(INVALID_ARGUMENT);
    415     return ERR_INVALID_ARGUMENT;
    416   }
    417 
    418   requests_++;
    419 
    420   // See if a request for the same domain is currently in flight.
    421   bool create_if_missing = true;
    422   if (JoinToInFlightRequest(request_start, domain, private_key, cert,
    423                             create_if_missing, callback, out_req)) {
    424     return ERR_IO_PENDING;
    425   }
    426 
    427   int err = LookupDomainBoundCert(request_start, domain, private_key, cert,
    428                                   create_if_missing, callback, out_req);
    429   if (err == ERR_FILE_NOT_FOUND) {
    430     // Sync lookup did not find a valid cert.  Start generating a new one.
    431     workers_created_++;
    432     ServerBoundCertServiceWorker* worker = new ServerBoundCertServiceWorker(
    433         domain,
    434         base::Bind(&ServerBoundCertService::GeneratedServerBoundCert,
    435                    weak_ptr_factory_.GetWeakPtr()));
    436     if (!worker->Start(task_runner_)) {
    437       // TODO(rkn): Log to the NetLog.
    438       LOG(ERROR) << "ServerBoundCertServiceWorker couldn't be started.";
    439       RecordGetDomainBoundCertResult(WORKER_FAILURE);
    440       return ERR_INSUFFICIENT_RESOURCES;
    441     }
    442     // We are waiting for cert generation.  Create a job & request to track it.
    443     ServerBoundCertServiceJob* job =
    444         new ServerBoundCertServiceJob(create_if_missing);
    445     inflight_[domain] = job;
    446 
    447     ServerBoundCertServiceRequest* request = new ServerBoundCertServiceRequest(
    448         request_start,
    449         base::Bind(&RequestHandle::OnRequestComplete,
    450                    base::Unretained(out_req)),
    451         private_key,
    452         cert);
    453     job->AddRequest(request);
    454     out_req->RequestStarted(this, request, callback);
    455     return ERR_IO_PENDING;
    456   }
    457 
    458   return err;
    459 }
    460 
    461 int ServerBoundCertService::GetDomainBoundCert(
    462     const std::string& host,
    463     std::string* private_key,
    464     std::string* cert,
    465     const CompletionCallback& callback,
    466     RequestHandle* out_req) {
    467   DVLOG(1) << __FUNCTION__ << " " << host;
    468   DCHECK(CalledOnValidThread());
    469   base::TimeTicks request_start = base::TimeTicks::Now();
    470 
    471   if (callback.is_null() || !private_key || !cert || host.empty()) {
    472     RecordGetDomainBoundCertResult(INVALID_ARGUMENT);
    473     return ERR_INVALID_ARGUMENT;
    474   }
    475 
    476   std::string domain = GetDomainForHost(host);
    477   if (domain.empty()) {
    478     RecordGetDomainBoundCertResult(INVALID_ARGUMENT);
    479     return ERR_INVALID_ARGUMENT;
    480   }
    481 
    482   requests_++;
    483 
    484   // See if a request for the same domain currently in flight.
    485   bool create_if_missing = false;
    486   if (JoinToInFlightRequest(request_start, domain, private_key, cert,
    487                             create_if_missing, callback, out_req)) {
    488     return ERR_IO_PENDING;
    489   }
    490 
    491   int err = LookupDomainBoundCert(request_start, domain, private_key, cert,
    492                                   create_if_missing, callback, out_req);
    493   return err;
    494 }
    495 
    496 void ServerBoundCertService::GotServerBoundCert(
    497     int err,
    498     const std::string& server_identifier,
    499     base::Time expiration_time,
    500     const std::string& key,
    501     const std::string& cert) {
    502   DCHECK(CalledOnValidThread());
    503 
    504   std::map<std::string, ServerBoundCertServiceJob*>::iterator j;
    505   j = inflight_.find(server_identifier);
    506   if (j == inflight_.end()) {
    507     NOTREACHED();
    508     return;
    509   }
    510 
    511   if (err == OK) {
    512     // Async DB lookup found a valid cert.
    513     DVLOG(1) << "Cert store had valid cert for " << server_identifier;
    514     cert_store_hits_++;
    515     // ServerBoundCertServiceRequest::Post will do the histograms and stuff.
    516     HandleResult(OK, server_identifier, key, cert);
    517     return;
    518   }
    519   // Async lookup did not find a valid cert. If no request asked to create one,
    520   // return the error directly.
    521   if (!j->second->CreateIfMissing()) {
    522     HandleResult(err, server_identifier, key, cert);
    523     return;
    524   }
    525   // At least one request asked to create a cert => start generating a new one.
    526   workers_created_++;
    527   ServerBoundCertServiceWorker* worker = new ServerBoundCertServiceWorker(
    528       server_identifier,
    529       base::Bind(&ServerBoundCertService::GeneratedServerBoundCert,
    530                  weak_ptr_factory_.GetWeakPtr()));
    531   if (!worker->Start(task_runner_)) {
    532     // TODO(rkn): Log to the NetLog.
    533     LOG(ERROR) << "ServerBoundCertServiceWorker couldn't be started.";
    534     HandleResult(ERR_INSUFFICIENT_RESOURCES,
    535                  server_identifier,
    536                  std::string(),
    537                  std::string());
    538   }
    539 }
    540 
    541 ServerBoundCertStore* ServerBoundCertService::GetCertStore() {
    542   return server_bound_cert_store_.get();
    543 }
    544 
    545 void ServerBoundCertService::CancelRequest(ServerBoundCertServiceRequest* req) {
    546   DCHECK(CalledOnValidThread());
    547   req->Cancel();
    548 }
    549 
    550 void ServerBoundCertService::GeneratedServerBoundCert(
    551     const std::string& server_identifier,
    552     int error,
    553     scoped_ptr<ServerBoundCertStore::ServerBoundCert> cert) {
    554   DCHECK(CalledOnValidThread());
    555 
    556   if (error == OK) {
    557     // TODO(mattm): we should just Pass() the cert object to
    558     // SetServerBoundCert().
    559     server_bound_cert_store_->SetServerBoundCert(
    560         cert->server_identifier(),
    561         cert->creation_time(),
    562         cert->expiration_time(),
    563         cert->private_key(),
    564         cert->cert());
    565 
    566     HandleResult(error, server_identifier, cert->private_key(), cert->cert());
    567   } else {
    568     HandleResult(error, server_identifier, std::string(), std::string());
    569   }
    570 }
    571 
    572 void ServerBoundCertService::HandleResult(
    573     int error,
    574     const std::string& server_identifier,
    575     const std::string& private_key,
    576     const std::string& cert) {
    577   DCHECK(CalledOnValidThread());
    578 
    579   std::map<std::string, ServerBoundCertServiceJob*>::iterator j;
    580   j = inflight_.find(server_identifier);
    581   if (j == inflight_.end()) {
    582     NOTREACHED();
    583     return;
    584   }
    585   ServerBoundCertServiceJob* job = j->second;
    586   inflight_.erase(j);
    587 
    588   job->HandleResult(error, private_key, cert);
    589   delete job;
    590 }
    591 
    592 bool ServerBoundCertService::JoinToInFlightRequest(
    593     const base::TimeTicks& request_start,
    594     const std::string& domain,
    595     std::string* private_key,
    596     std::string* cert,
    597     bool create_if_missing,
    598     const CompletionCallback& callback,
    599     RequestHandle* out_req) {
    600   ServerBoundCertServiceJob* job = NULL;
    601   std::map<std::string, ServerBoundCertServiceJob*>::const_iterator j =
    602       inflight_.find(domain);
    603   if (j != inflight_.end()) {
    604     // A request for the same domain is in flight already. We'll attach our
    605     // callback, but we'll also mark it as requiring a cert if one's mising.
    606     job = j->second;
    607     inflight_joins_++;
    608 
    609     ServerBoundCertServiceRequest* request = new ServerBoundCertServiceRequest(
    610         request_start,
    611         base::Bind(&RequestHandle::OnRequestComplete,
    612                    base::Unretained(out_req)),
    613         private_key,
    614         cert);
    615     job->AddRequest(request, create_if_missing);
    616     out_req->RequestStarted(this, request, callback);
    617     return true;
    618   }
    619   return false;
    620 }
    621 
    622 int ServerBoundCertService::LookupDomainBoundCert(
    623     const base::TimeTicks& request_start,
    624     const std::string& domain,
    625     std::string* private_key,
    626     std::string* cert,
    627     bool create_if_missing,
    628     const CompletionCallback& callback,
    629     RequestHandle* out_req) {
    630   // Check if a domain bound cert already exists for this domain. Note that
    631   // |expiration_time| is ignored, and expired certs are considered valid.
    632   base::Time expiration_time;
    633   int err = server_bound_cert_store_->GetServerBoundCert(
    634       domain,
    635       &expiration_time  /* ignored */,
    636       private_key,
    637       cert,
    638       base::Bind(&ServerBoundCertService::GotServerBoundCert,
    639                  weak_ptr_factory_.GetWeakPtr()));
    640 
    641   if (err == OK) {
    642     // Sync lookup found a valid cert.
    643     DVLOG(1) << "Cert store had valid cert for " << domain;
    644     cert_store_hits_++;
    645     RecordGetDomainBoundCertResult(SYNC_SUCCESS);
    646     base::TimeDelta request_time = base::TimeTicks::Now() - request_start;
    647     UMA_HISTOGRAM_TIMES("DomainBoundCerts.GetCertTimeSync", request_time);
    648     RecordGetCertTime(request_time);
    649     return OK;
    650   }
    651 
    652   if (err == ERR_IO_PENDING) {
    653     // We are waiting for async DB lookup.  Create a job & request to track it.
    654     ServerBoundCertServiceJob* job =
    655         new ServerBoundCertServiceJob(create_if_missing);
    656     inflight_[domain] = job;
    657 
    658     ServerBoundCertServiceRequest* request = new ServerBoundCertServiceRequest(
    659         request_start,
    660         base::Bind(&RequestHandle::OnRequestComplete,
    661                    base::Unretained(out_req)),
    662         private_key,
    663         cert);
    664     job->AddRequest(request);
    665     out_req->RequestStarted(this, request, callback);
    666     return ERR_IO_PENDING;
    667   }
    668 
    669   return err;
    670 }
    671 
    672 int ServerBoundCertService::cert_count() {
    673   return server_bound_cert_store_->GetCertCount();
    674 }
    675 
    676 }  // namespace net
    677