blob: 4bc82ed5d9ba1e6e3663dd92da85488e8a2e88eb [file] [log] [blame]
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +00001// 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"
Torne (Richard Coles)868fa2f2013-06-11 10:57:03 +010018#include "base/message_loop/message_loop_proxy.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000019#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"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000024#include "net/base/net_errors.h"
25#include "net/base/registry_controlled_domains/registry_controlled_domain.h"
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +010026#include "net/cert/x509_certificate.h"
27#include "net/cert/x509_util.h"
Ben Murdoch7dbb3d52013-07-17 14:55:54 +010028#include "url/gurl.h"
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000029
30#if defined(USE_NSS)
31#include <private/pprthred.h> // PR_DetachThread
32#endif
33
34namespace net {
35
36namespace {
37
38const int kKeySizeInBits = 1024;
39const int kValidityPeriodInDays = 365;
40// When we check the system time, we add this many days to the end of the check
41// so the result will still hold even after chrome has been running for a
42// while.
43const int kSystemTimeValidityBufferInDays = 90;
44
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000045// Used by the GetDomainBoundCertResult histogram to record the final
46// outcome of each GetDomainBoundCert call. Do not re-use values.
47enum 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 was called with invalid arguments.
61 INVALID_ARGUMENT = 7,
62 // We don't support any of the cert types the server requested.
63 UNSUPPORTED_TYPE = 8,
64 // Server asked for a different type of certs while we were generating one.
65 TYPE_MISMATCH = 9,
66 // Couldn't start a worker to generate a cert.
67 WORKER_FAILURE = 10,
68 GET_CERT_RESULT_MAX
69};
70
71void RecordGetDomainBoundCertResult(GetCertResult result) {
72 UMA_HISTOGRAM_ENUMERATION("DomainBoundCerts.GetDomainBoundCertResult", result,
73 GET_CERT_RESULT_MAX);
74}
75
76void RecordGetCertTime(base::TimeDelta request_time) {
77 UMA_HISTOGRAM_CUSTOM_TIMES("DomainBoundCerts.GetCertTime",
78 request_time,
79 base::TimeDelta::FromMilliseconds(1),
80 base::TimeDelta::FromMinutes(5),
81 50);
82}
83
84// On success, returns a ServerBoundCert object and sets |*error| to OK.
85// Otherwise, returns NULL, and |*error| will be set to a net error code.
86// |serial_number| is passed in because base::RandInt cannot be called from an
87// unjoined thread, due to relying on a non-leaked LazyInstance
88scoped_ptr<ServerBoundCertStore::ServerBoundCert> GenerateCert(
89 const std::string& server_identifier,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +000090 uint32 serial_number,
91 int* error) {
92 scoped_ptr<ServerBoundCertStore::ServerBoundCert> result;
93
94 base::TimeTicks start = base::TimeTicks::Now();
95 base::Time not_valid_before = base::Time::Now();
96 base::Time not_valid_after =
97 not_valid_before + base::TimeDelta::FromDays(kValidityPeriodInDays);
98 std::string der_cert;
99 std::vector<uint8> private_key_info;
Ben Murdochbb1529c2013-08-08 10:24:53 +0100100 scoped_ptr<crypto::ECPrivateKey> key(crypto::ECPrivateKey::Create());
101 if (!key.get()) {
102 DLOG(ERROR) << "Unable to create key pair for client";
103 *error = ERR_KEY_GENERATION_FAILED;
104 return result.Pass();
105 }
106 if (!x509_util::CreateDomainBoundCertEC(key.get(), server_identifier,
107 serial_number, not_valid_before,
108 not_valid_after, &der_cert)) {
109 DLOG(ERROR) << "Unable to create x509 cert for client";
110 *error = ERR_ORIGIN_BOUND_CERT_GENERATION_FAILED;
111 return result.Pass();
112 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000113
Ben Murdochbb1529c2013-08-08 10:24:53 +0100114 if (!key->ExportEncryptedPrivateKey(ServerBoundCertService::kEPKIPassword,
115 1, &private_key_info)) {
116 DLOG(ERROR) << "Unable to export private key";
117 *error = ERR_PRIVATE_KEY_EXPORT_FAILED;
118 return result.Pass();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000119 }
120
121 // TODO(rkn): Perhaps ExportPrivateKey should be changed to output a
122 // std::string* to prevent this copying.
123 std::string key_out(private_key_info.begin(), private_key_info.end());
124
125 result.reset(new ServerBoundCertStore::ServerBoundCert(
Ben Murdochbb1529c2013-08-08 10:24:53 +0100126 server_identifier,
127 not_valid_before,
128 not_valid_after,
129 key_out,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000130 der_cert));
131 UMA_HISTOGRAM_CUSTOM_TIMES("DomainBoundCerts.GenerateCertTime",
132 base::TimeTicks::Now() - start,
133 base::TimeDelta::FromMilliseconds(1),
134 base::TimeDelta::FromMinutes(5),
135 50);
136 *error = OK;
137 return result.Pass();
138}
139
140} // namespace
141
142// Represents the output and result callback of a request.
143class ServerBoundCertServiceRequest {
144 public:
145 ServerBoundCertServiceRequest(base::TimeTicks request_start,
146 const CompletionCallback& callback,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000147 std::string* private_key,
148 std::string* cert)
149 : request_start_(request_start),
150 callback_(callback),
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000151 private_key_(private_key),
152 cert_(cert) {
153 }
154
155 // Ensures that the result callback will never be made.
156 void Cancel() {
157 RecordGetDomainBoundCertResult(ASYNC_CANCELLED);
158 callback_.Reset();
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000159 private_key_ = NULL;
160 cert_ = NULL;
161 }
162
163 // Copies the contents of |private_key| and |cert| to the caller's output
164 // arguments and calls the callback.
165 void Post(int error,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000166 const std::string& private_key,
167 const std::string& cert) {
168 switch (error) {
169 case OK: {
170 base::TimeDelta request_time = base::TimeTicks::Now() - request_start_;
171 UMA_HISTOGRAM_CUSTOM_TIMES("DomainBoundCerts.GetCertTimeAsync",
172 request_time,
173 base::TimeDelta::FromMilliseconds(1),
174 base::TimeDelta::FromMinutes(5),
175 50);
176 RecordGetCertTime(request_time);
177 RecordGetDomainBoundCertResult(ASYNC_SUCCESS);
178 break;
179 }
180 case ERR_KEY_GENERATION_FAILED:
181 RecordGetDomainBoundCertResult(ASYNC_FAILURE_KEYGEN);
182 break;
183 case ERR_ORIGIN_BOUND_CERT_GENERATION_FAILED:
184 RecordGetDomainBoundCertResult(ASYNC_FAILURE_CREATE_CERT);
185 break;
186 case ERR_PRIVATE_KEY_EXPORT_FAILED:
187 RecordGetDomainBoundCertResult(ASYNC_FAILURE_EXPORT_KEY);
188 break;
189 case ERR_INSUFFICIENT_RESOURCES:
190 RecordGetDomainBoundCertResult(WORKER_FAILURE);
191 break;
192 default:
193 RecordGetDomainBoundCertResult(ASYNC_FAILURE_UNKNOWN);
194 break;
195 }
196 if (!callback_.is_null()) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000197 *private_key_ = private_key;
198 *cert_ = cert;
199 callback_.Run(error);
200 }
201 delete this;
202 }
203
204 bool canceled() const { return callback_.is_null(); }
205
206 private:
207 base::TimeTicks request_start_;
208 CompletionCallback callback_;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000209 std::string* private_key_;
210 std::string* cert_;
211};
212
213// ServerBoundCertServiceWorker runs on a worker thread and takes care of the
214// blocking process of performing key generation. Will take care of deleting
215// itself once Start() is called.
216class ServerBoundCertServiceWorker {
217 public:
218 typedef base::Callback<void(
219 const std::string&,
220 int,
221 scoped_ptr<ServerBoundCertStore::ServerBoundCert>)> WorkerDoneCallback;
222
223 ServerBoundCertServiceWorker(
224 const std::string& server_identifier,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000225 const WorkerDoneCallback& callback)
226 : server_identifier_(server_identifier),
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000227 serial_number_(base::RandInt(0, std::numeric_limits<int>::max())),
228 origin_loop_(base::MessageLoopProxy::current()),
229 callback_(callback) {
230 }
231
Torne (Richard Coles)b2df76e2013-05-13 16:52:09 +0100232 // Starts the worker on |task_runner|. If the worker fails to start, such as
233 // if the task runner is shutting down, then it will take care of deleting
234 // itself.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000235 bool Start(const scoped_refptr<base::TaskRunner>& task_runner) {
236 DCHECK(origin_loop_->RunsTasksOnCurrentThread());
237
238 return task_runner->PostTask(
239 FROM_HERE,
240 base::Bind(&ServerBoundCertServiceWorker::Run, base::Owned(this)));
241 }
242
243 private:
244 void Run() {
245 // Runs on a worker thread.
246 int error = ERR_FAILED;
247 scoped_ptr<ServerBoundCertStore::ServerBoundCert> cert =
Ben Murdochbb1529c2013-08-08 10:24:53 +0100248 GenerateCert(server_identifier_, serial_number_, &error);
249 DVLOG(1) << "GenerateCert " << server_identifier_ << " returned " << error;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000250#if defined(USE_NSS)
251 // Detach the thread from NSPR.
252 // Calling NSS functions attaches the thread to NSPR, which stores
253 // the NSPR thread ID in thread-specific data.
254 // The threads in our thread pool terminate after we have called
255 // PR_Cleanup. Unless we detach them from NSPR, net_unittests gets
256 // segfaults on shutdown when the threads' thread-specific data
257 // destructors run.
258 PR_DetachThread();
259#endif
260 origin_loop_->PostTask(FROM_HERE,
261 base::Bind(callback_, server_identifier_, error,
262 base::Passed(&cert)));
263 }
264
265 const std::string server_identifier_;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000266 // Note that serial_number_ must be initialized on a non-worker thread
267 // (see documentation for GenerateCert).
268 uint32 serial_number_;
269 scoped_refptr<base::SequencedTaskRunner> origin_loop_;
270 WorkerDoneCallback callback_;
271
272 DISALLOW_COPY_AND_ASSIGN(ServerBoundCertServiceWorker);
273};
274
275// A ServerBoundCertServiceJob is a one-to-one counterpart of an
276// ServerBoundCertServiceWorker. It lives only on the ServerBoundCertService's
277// origin message loop.
278class ServerBoundCertServiceJob {
279 public:
Ben Murdochbb1529c2013-08-08 10:24:53 +0100280 ServerBoundCertServiceJob() { }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000281
282 ~ServerBoundCertServiceJob() {
283 if (!requests_.empty())
284 DeleteAllCanceled();
285 }
286
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000287 void AddRequest(ServerBoundCertServiceRequest* request) {
288 requests_.push_back(request);
289 }
290
291 void HandleResult(int error,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000292 const std::string& private_key,
293 const std::string& cert) {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100294 PostAll(error, private_key, cert);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000295 }
296
297 private:
298 void PostAll(int error,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000299 const std::string& private_key,
300 const std::string& cert) {
301 std::vector<ServerBoundCertServiceRequest*> requests;
302 requests_.swap(requests);
303
304 for (std::vector<ServerBoundCertServiceRequest*>::iterator
305 i = requests.begin(); i != requests.end(); i++) {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100306 (*i)->Post(error, private_key, cert);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000307 // Post() causes the ServerBoundCertServiceRequest to delete itself.
308 }
309 }
310
311 void DeleteAllCanceled() {
312 for (std::vector<ServerBoundCertServiceRequest*>::iterator
313 i = requests_.begin(); i != requests_.end(); i++) {
314 if ((*i)->canceled()) {
315 delete *i;
316 } else {
317 LOG(DFATAL) << "ServerBoundCertServiceRequest leaked!";
318 }
319 }
320 }
321
322 std::vector<ServerBoundCertServiceRequest*> requests_;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000323};
324
325// static
326const char ServerBoundCertService::kEPKIPassword[] = "";
327
328ServerBoundCertService::RequestHandle::RequestHandle()
329 : service_(NULL),
330 request_(NULL) {}
331
332ServerBoundCertService::RequestHandle::~RequestHandle() {
333 Cancel();
334}
335
336void ServerBoundCertService::RequestHandle::Cancel() {
337 if (request_) {
338 service_->CancelRequest(request_);
339 request_ = NULL;
340 callback_.Reset();
341 }
342}
343
344void ServerBoundCertService::RequestHandle::RequestStarted(
345 ServerBoundCertService* service,
346 ServerBoundCertServiceRequest* request,
347 const CompletionCallback& callback) {
348 DCHECK(request_ == NULL);
349 service_ = service;
350 request_ = request;
351 callback_ = callback;
352}
353
354void ServerBoundCertService::RequestHandle::OnRequestComplete(int result) {
355 request_ = NULL;
356 // Running the callback might delete |this|, so we can't touch any of our
357 // members afterwards. Reset callback_ first.
358 base::ResetAndReturn(&callback_).Run(result);
359}
360
361ServerBoundCertService::ServerBoundCertService(
362 ServerBoundCertStore* server_bound_cert_store,
363 const scoped_refptr<base::TaskRunner>& task_runner)
364 : server_bound_cert_store_(server_bound_cert_store),
365 task_runner_(task_runner),
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100366 weak_ptr_factory_(this),
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000367 requests_(0),
368 cert_store_hits_(0),
Ben Murdochbb1529c2013-08-08 10:24:53 +0100369 inflight_joins_(0),
370 workers_created_(0) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000371 base::Time start = base::Time::Now();
372 base::Time end = start + base::TimeDelta::FromDays(
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100373 kValidityPeriodInDays + kSystemTimeValidityBufferInDays);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000374 is_system_time_valid_ = x509_util::IsSupportedValidityRange(start, end);
375}
376
377ServerBoundCertService::~ServerBoundCertService() {
378 STLDeleteValues(&inflight_);
379}
380
381//static
382std::string ServerBoundCertService::GetDomainForHost(const std::string& host) {
383 std::string domain =
Torne (Richard Coles)a93a17c2013-05-15 11:34:50 +0100384 registry_controlled_domains::GetDomainAndRegistry(
Torne (Richard Coles)90dce4d2013-05-29 14:40:03 +0100385 host, registry_controlled_domains::INCLUDE_PRIVATE_REGISTRIES);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000386 if (domain.empty())
387 return host;
388 return domain;
389}
390
391int ServerBoundCertService::GetDomainBoundCert(
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100392 const std::string& host,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000393 std::string* private_key,
394 std::string* cert,
395 const CompletionCallback& callback,
396 RequestHandle* out_req) {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100397 DVLOG(1) << __FUNCTION__ << " " << host;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000398 DCHECK(CalledOnValidThread());
399 base::TimeTicks request_start = base::TimeTicks::Now();
400
Ben Murdochbb1529c2013-08-08 10:24:53 +0100401 if (callback.is_null() || !private_key || !cert || host.empty()) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000402 RecordGetDomainBoundCertResult(INVALID_ARGUMENT);
403 return ERR_INVALID_ARGUMENT;
404 }
405
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100406 std::string domain = GetDomainForHost(host);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000407 if (domain.empty()) {
408 RecordGetDomainBoundCertResult(INVALID_ARGUMENT);
409 return ERR_INVALID_ARGUMENT;
410 }
411
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000412 requests_++;
413
414 // See if an identical request is currently in flight.
415 ServerBoundCertServiceJob* job = NULL;
416 std::map<std::string, ServerBoundCertServiceJob*>::const_iterator j;
417 j = inflight_.find(domain);
418 if (j != inflight_.end()) {
419 // An identical request is in flight already. We'll just attach our
420 // callback.
421 job = j->second;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000422 inflight_joins_++;
423
424 ServerBoundCertServiceRequest* request = new ServerBoundCertServiceRequest(
425 request_start,
426 base::Bind(&RequestHandle::OnRequestComplete,
427 base::Unretained(out_req)),
Ben Murdochbb1529c2013-08-08 10:24:53 +0100428 private_key, cert);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000429 job->AddRequest(request);
430 out_req->RequestStarted(this, request, callback);
431 return ERR_IO_PENDING;
432 }
433
434 // Check if a domain bound cert of an acceptable type already exists for this
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100435 // domain. Note that |expiration_time| is ignored, and expired certs are
436 // considered valid.
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000437 base::Time expiration_time;
Ben Murdochbb1529c2013-08-08 10:24:53 +0100438 int err = server_bound_cert_store_->GetServerBoundCert(
439 domain,
440 &expiration_time /* ignored */,
441 private_key,
442 cert,
443 base::Bind(&ServerBoundCertService::GotServerBoundCert,
444 weak_ptr_factory_.GetWeakPtr()));
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000445
Ben Murdochbb1529c2013-08-08 10:24:53 +0100446 if (err == OK) {
447 // Sync lookup found a valid cert.
448 DVLOG(1) << "Cert store had valid cert for " << domain;
449 cert_store_hits_++;
450 RecordGetDomainBoundCertResult(SYNC_SUCCESS);
451 base::TimeDelta request_time = base::TimeTicks::Now() - request_start;
452 UMA_HISTOGRAM_TIMES("DomainBoundCerts.GetCertTimeSync", request_time);
453 RecordGetCertTime(request_time);
454 return OK;
455 }
456
457 if (err == ERR_FILE_NOT_FOUND) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100458 // Sync lookup did not find a valid cert. Start generating a new one.
Ben Murdochbb1529c2013-08-08 10:24:53 +0100459 workers_created_++;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000460 ServerBoundCertServiceWorker* worker = new ServerBoundCertServiceWorker(
461 domain,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000462 base::Bind(&ServerBoundCertService::GeneratedServerBoundCert,
463 weak_ptr_factory_.GetWeakPtr()));
464 if (!worker->Start(task_runner_)) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000465 // TODO(rkn): Log to the NetLog.
466 LOG(ERROR) << "ServerBoundCertServiceWorker couldn't be started.";
467 RecordGetDomainBoundCertResult(WORKER_FAILURE);
468 return ERR_INSUFFICIENT_RESOURCES;
469 }
470 }
471
Ben Murdochbb1529c2013-08-08 10:24:53 +0100472 if (err == ERR_IO_PENDING || err == ERR_FILE_NOT_FOUND) {
473 // We are either waiting for async DB lookup, or waiting for cert
474 // generation. Create a job & request to track it.
475 job = new ServerBoundCertServiceJob();
476 inflight_[domain] = job;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000477
Ben Murdochbb1529c2013-08-08 10:24:53 +0100478 ServerBoundCertServiceRequest* request = new ServerBoundCertServiceRequest(
479 request_start,
480 base::Bind(&RequestHandle::OnRequestComplete,
481 base::Unretained(out_req)),
482 private_key, cert);
483 job->AddRequest(request);
484 out_req->RequestStarted(this, request, callback);
485 return ERR_IO_PENDING;
486 }
487
488 return err;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000489}
490
491void ServerBoundCertService::GotServerBoundCert(
Ben Murdochbb1529c2013-08-08 10:24:53 +0100492 int err,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000493 const std::string& server_identifier,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000494 base::Time expiration_time,
495 const std::string& key,
496 const std::string& cert) {
497 DCHECK(CalledOnValidThread());
498
499 std::map<std::string, ServerBoundCertServiceJob*>::iterator j;
500 j = inflight_.find(server_identifier);
501 if (j == inflight_.end()) {
502 NOTREACHED();
503 return;
504 }
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000505
Ben Murdochbb1529c2013-08-08 10:24:53 +0100506 if (err == OK) {
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100507 // Async DB lookup found a valid cert.
Ben Murdochbb1529c2013-08-08 10:24:53 +0100508 DVLOG(1) << "Cert store had valid cert for " << server_identifier;
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100509 cert_store_hits_++;
510 // ServerBoundCertServiceRequest::Post will do the histograms and stuff.
Ben Murdochbb1529c2013-08-08 10:24:53 +0100511 HandleResult(OK, server_identifier, key, cert);
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100512 return;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000513 }
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100514 // Async lookup did not find a valid cert. Start generating a new one.
Ben Murdochbb1529c2013-08-08 10:24:53 +0100515 workers_created_++;
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000516 ServerBoundCertServiceWorker* worker = new ServerBoundCertServiceWorker(
517 server_identifier,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000518 base::Bind(&ServerBoundCertService::GeneratedServerBoundCert,
519 weak_ptr_factory_.GetWeakPtr()));
520 if (!worker->Start(task_runner_)) {
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000521 // TODO(rkn): Log to the NetLog.
522 LOG(ERROR) << "ServerBoundCertServiceWorker couldn't be started.";
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100523 HandleResult(ERR_INSUFFICIENT_RESOURCES,
524 server_identifier,
Torne (Richard Coles)c2e0dbd2013-05-09 18:35:53 +0100525 std::string(),
526 std::string());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000527 return;
528 }
529}
530
531ServerBoundCertStore* ServerBoundCertService::GetCertStore() {
532 return server_bound_cert_store_.get();
533}
534
535void ServerBoundCertService::CancelRequest(ServerBoundCertServiceRequest* req) {
536 DCHECK(CalledOnValidThread());
537 req->Cancel();
538}
539
540void ServerBoundCertService::GeneratedServerBoundCert(
541 const std::string& server_identifier,
542 int error,
543 scoped_ptr<ServerBoundCertStore::ServerBoundCert> cert) {
544 DCHECK(CalledOnValidThread());
545
546 if (error == OK) {
547 // TODO(mattm): we should just Pass() the cert object to
548 // SetServerBoundCert().
549 server_bound_cert_store_->SetServerBoundCert(
Ben Murdochbb1529c2013-08-08 10:24:53 +0100550 cert->server_identifier(),
551 cert->creation_time(),
552 cert->expiration_time(),
553 cert->private_key(),
554 cert->cert());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000555
Ben Murdochbb1529c2013-08-08 10:24:53 +0100556 HandleResult(error, server_identifier, cert->private_key(), cert->cert());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000557 } else {
Ben Murdochbb1529c2013-08-08 10:24:53 +0100558 HandleResult(error, server_identifier, std::string(), std::string());
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000559 }
560}
561
562void ServerBoundCertService::HandleResult(
563 int error,
564 const std::string& server_identifier,
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000565 const std::string& private_key,
566 const std::string& cert) {
567 DCHECK(CalledOnValidThread());
568
569 std::map<std::string, ServerBoundCertServiceJob*>::iterator j;
570 j = inflight_.find(server_identifier);
571 if (j == inflight_.end()) {
572 NOTREACHED();
573 return;
574 }
575 ServerBoundCertServiceJob* job = j->second;
576 inflight_.erase(j);
577
Ben Murdochbb1529c2013-08-08 10:24:53 +0100578 job->HandleResult(error, private_key, cert);
Torne (Richard Coles)2a99a7e2013-03-28 15:31:22 +0000579 delete job;
580}
581
582int ServerBoundCertService::cert_count() {
583 return server_bound_cert_store_->GetCertCount();
584}
585
586} // namespace net