• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
RecordGetDomainBoundCertResult(GetCertResult result)72 void RecordGetDomainBoundCertResult(GetCertResult result) {
73   UMA_HISTOGRAM_ENUMERATION("DomainBoundCerts.GetDomainBoundCertResult", result,
74                             GET_CERT_RESULT_MAX);
75 }
76 
RecordGetCertTime(base::TimeDelta request_time)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
GenerateCert(const std::string & server_identifier,uint32 serial_number,int * error)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:
ServerBoundCertServiceRequest(base::TimeTicks request_start,const CompletionCallback & callback,std::string * private_key,std::string * cert)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.
Cancel()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.
Post(int error,const std::string & private_key,const std::string & cert)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 
canceled() const203   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 
ServerBoundCertServiceWorker(const std::string & server_identifier,const WorkerDoneCallback & callback)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.
Start(const scoped_refptr<base::TaskRunner> & task_runner)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:
Run()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:
ServerBoundCertServiceJob(bool create_if_missing)279   ServerBoundCertServiceJob(bool create_if_missing)
280       : create_if_missing_(create_if_missing) {
281   }
282 
~ServerBoundCertServiceJob()283   ~ServerBoundCertServiceJob() {
284     if (!requests_.empty())
285       DeleteAllCanceled();
286   }
287 
AddRequest(ServerBoundCertServiceRequest * request,bool create_if_missing=false)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 
HandleResult(int error,const std::string & private_key,const std::string & cert)294   void HandleResult(int error,
295                     const std::string& private_key,
296                     const std::string& cert) {
297     PostAll(error, private_key, cert);
298   }
299 
CreateIfMissing() const300   bool CreateIfMissing() const { return create_if_missing_; }
301 
302  private:
PostAll(int error,const std::string & private_key,const std::string & cert)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 
DeleteAllCanceled()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 
RequestHandle()334 ServerBoundCertService::RequestHandle::RequestHandle()
335     : service_(NULL),
336       request_(NULL) {}
337 
~RequestHandle()338 ServerBoundCertService::RequestHandle::~RequestHandle() {
339   Cancel();
340 }
341 
Cancel()342 void ServerBoundCertService::RequestHandle::Cancel() {
343   if (request_) {
344     service_->CancelRequest(request_);
345     request_ = NULL;
346     callback_.Reset();
347   }
348 }
349 
RequestStarted(ServerBoundCertService * service,ServerBoundCertServiceRequest * request,const CompletionCallback & callback)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 
OnRequestComplete(int result)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 
ServerBoundCertService(ServerBoundCertStore * server_bound_cert_store,const scoped_refptr<base::TaskRunner> & task_runner)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 
~ServerBoundCertService()383 ServerBoundCertService::~ServerBoundCertService() {
384   STLDeleteValues(&inflight_);
385 }
386 
387 //static
GetDomainForHost(const std::string & host)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 
GetOrCreateDomainBoundCert(const std::string & host,std::string * private_key,std::string * cert,const CompletionCallback & callback,RequestHandle * out_req)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 
GetDomainBoundCert(const std::string & host,std::string * private_key,std::string * cert,const CompletionCallback & callback,RequestHandle * out_req)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 
GotServerBoundCert(int err,const std::string & server_identifier,base::Time expiration_time,const std::string & key,const std::string & cert)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 
GetCertStore()541 ServerBoundCertStore* ServerBoundCertService::GetCertStore() {
542   return server_bound_cert_store_.get();
543 }
544 
CancelRequest(ServerBoundCertServiceRequest * req)545 void ServerBoundCertService::CancelRequest(ServerBoundCertServiceRequest* req) {
546   DCHECK(CalledOnValidThread());
547   req->Cancel();
548 }
549 
GeneratedServerBoundCert(const std::string & server_identifier,int error,scoped_ptr<ServerBoundCertStore::ServerBoundCert> cert)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 
HandleResult(int error,const std::string & server_identifier,const std::string & private_key,const std::string & cert)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 
JoinToInFlightRequest(const base::TimeTicks & request_start,const std::string & domain,std::string * private_key,std::string * cert,bool create_if_missing,const CompletionCallback & callback,RequestHandle * out_req)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 
LookupDomainBoundCert(const base::TimeTicks & request_start,const std::string & domain,std::string * private_key,std::string * cert,bool create_if_missing,const CompletionCallback & callback,RequestHandle * out_req)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 
cert_count()672 int ServerBoundCertService::cert_count() {
673   return server_bound_cert_store_->GetCertCount();
674 }
675 
676 }  // namespace net
677