Updating trunk VERSION from 1014.0 to 1015.0
[chromium-blink-merge.git] / net / base / origin_bound_cert_service.cc
blob246383eeea66f67abb3d2a74ecba702ba80569fd
1 // Copyright (c) 2011 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.
5 #include "net/base/origin_bound_cert_service.h"
7 #include <algorithm>
8 #include <limits>
10 #include "base/bind.h"
11 #include "base/bind_helpers.h"
12 #include "base/compiler_specific.h"
13 #include "base/location.h"
14 #include "base/logging.h"
15 #include "base/memory/ref_counted.h"
16 #include "base/memory/scoped_ptr.h"
17 #include "base/message_loop.h"
18 #include "base/rand_util.h"
19 #include "base/stl_util.h"
20 #include "base/threading/worker_pool.h"
21 #include "crypto/ec_private_key.h"
22 #include "crypto/rsa_private_key.h"
23 #include "net/base/net_errors.h"
24 #include "net/base/origin_bound_cert_store.h"
25 #include "net/base/x509_certificate.h"
26 #include "net/base/x509_util.h"
28 #if defined(USE_NSS)
29 #include <private/pprthred.h> // PR_DetachThread
30 #endif
32 namespace net {
34 namespace {
36 const int kKeySizeInBits = 1024;
37 const int kValidityPeriodInDays = 365;
39 bool IsSupportedCertType(uint8 type) {
40 switch(type) {
41 case CLIENT_CERT_RSA_SIGN:
42 case CLIENT_CERT_ECDSA_SIGN:
43 return true;
44 default:
45 return false;
49 } // namespace
51 // Represents the output and result callback of a request.
52 class OriginBoundCertServiceRequest {
53 public:
54 OriginBoundCertServiceRequest(const CompletionCallback& callback,
55 SSLClientCertType* type,
56 std::string* private_key,
57 std::string* cert)
58 : callback_(callback),
59 type_(type),
60 private_key_(private_key),
61 cert_(cert) {
64 // Ensures that the result callback will never be made.
65 void Cancel() {
66 callback_.Reset();
67 type_ = NULL;
68 private_key_ = NULL;
69 cert_ = NULL;
72 // Copies the contents of |private_key| and |cert| to the caller's output
73 // arguments and calls the callback.
74 void Post(int error,
75 SSLClientCertType type,
76 const std::string& private_key,
77 const std::string& cert) {
78 if (!callback_.is_null()) {
79 *type_ = type;
80 *private_key_ = private_key;
81 *cert_ = cert;
82 callback_.Run(error);
84 delete this;
87 bool canceled() const { return callback_.is_null(); }
89 private:
90 CompletionCallback callback_;
91 SSLClientCertType* type_;
92 std::string* private_key_;
93 std::string* cert_;
96 // OriginBoundCertServiceWorker runs on a worker thread and takes care of the
97 // blocking process of performing key generation. Deletes itself eventually
98 // if Start() succeeds.
99 class OriginBoundCertServiceWorker {
100 public:
101 OriginBoundCertServiceWorker(
102 const std::string& origin,
103 SSLClientCertType type,
104 OriginBoundCertService* origin_bound_cert_service)
105 : origin_(origin),
106 type_(type),
107 serial_number_(base::RandInt(0, std::numeric_limits<int>::max())),
108 origin_loop_(MessageLoop::current()),
109 origin_bound_cert_service_(origin_bound_cert_service),
110 canceled_(false),
111 error_(ERR_FAILED) {
114 bool Start() {
115 DCHECK_EQ(MessageLoop::current(), origin_loop_);
117 return base::WorkerPool::PostTask(
118 FROM_HERE,
119 base::Bind(&OriginBoundCertServiceWorker::Run, base::Unretained(this)),
120 true /* task is slow */);
123 // Cancel is called from the origin loop when the OriginBoundCertService is
124 // getting deleted.
125 void Cancel() {
126 DCHECK_EQ(MessageLoop::current(), origin_loop_);
127 base::AutoLock locked(lock_);
128 canceled_ = true;
131 private:
132 void Run() {
133 // Runs on a worker thread.
134 error_ = OriginBoundCertService::GenerateCert(origin_,
135 type_,
136 serial_number_,
137 &expiration_time_,
138 &private_key_,
139 &cert_);
140 #if defined(USE_NSS)
141 // Detach the thread from NSPR.
142 // Calling NSS functions attaches the thread to NSPR, which stores
143 // the NSPR thread ID in thread-specific data.
144 // The threads in our thread pool terminate after we have called
145 // PR_Cleanup. Unless we detach them from NSPR, net_unittests gets
146 // segfaults on shutdown when the threads' thread-specific data
147 // destructors run.
148 PR_DetachThread();
149 #endif
150 Finish();
153 // DoReply runs on the origin thread.
154 void DoReply() {
155 DCHECK_EQ(MessageLoop::current(), origin_loop_);
157 // We lock here because the worker thread could still be in Finished,
158 // after the PostTask, but before unlocking |lock_|. If we do not lock in
159 // this case, we will end up deleting a locked Lock, which can lead to
160 // memory leaks or worse errors.
161 base::AutoLock locked(lock_);
162 if (!canceled_) {
163 origin_bound_cert_service_->HandleResult(
164 origin_, error_, type_, expiration_time_, private_key_, cert_);
167 delete this;
170 void Finish() {
171 // Runs on the worker thread.
172 // We assume that the origin loop outlives the OriginBoundCertService. If
173 // the OriginBoundCertService is deleted, it will call Cancel on us. If it
174 // does so before the Acquire, we'll delete ourselves and return. If it's
175 // trying to do so concurrently, then it'll block on the lock and we'll
176 // call PostTask while the OriginBoundCertService (and therefore the
177 // MessageLoop) is still alive. If it does so after this function, we
178 // assume that the MessageLoop will process pending tasks. In which case
179 // we'll notice the |canceled_| flag in DoReply.
181 bool canceled;
183 base::AutoLock locked(lock_);
184 canceled = canceled_;
185 if (!canceled) {
186 origin_loop_->PostTask(
187 FROM_HERE, base::Bind(&OriginBoundCertServiceWorker::DoReply,
188 base::Unretained(this)));
191 if (canceled)
192 delete this;
195 const std::string origin_;
196 const SSLClientCertType type_;
197 // Note that serial_number_ must be initialized on a non-worker thread
198 // (see documentation for OriginBoundCertService::GenerateCert).
199 uint32 serial_number_;
200 MessageLoop* const origin_loop_;
201 OriginBoundCertService* const origin_bound_cert_service_;
203 // lock_ protects canceled_.
204 base::Lock lock_;
206 // If canceled_ is true,
207 // * origin_loop_ cannot be accessed by the worker thread,
208 // * origin_bound_cert_service_ cannot be accessed by any thread.
209 bool canceled_;
211 int error_;
212 base::Time expiration_time_;
213 std::string private_key_;
214 std::string cert_;
216 DISALLOW_COPY_AND_ASSIGN(OriginBoundCertServiceWorker);
219 // An OriginBoundCertServiceJob is a one-to-one counterpart of an
220 // OriginBoundCertServiceWorker. It lives only on the OriginBoundCertService's
221 // origin message loop.
222 class OriginBoundCertServiceJob {
223 public:
224 OriginBoundCertServiceJob(OriginBoundCertServiceWorker* worker,
225 SSLClientCertType type)
226 : worker_(worker), type_(type) {
229 ~OriginBoundCertServiceJob() {
230 if (worker_) {
231 worker_->Cancel();
232 DeleteAllCanceled();
236 SSLClientCertType type() const { return type_; }
238 void AddRequest(OriginBoundCertServiceRequest* request) {
239 requests_.push_back(request);
242 void HandleResult(int error,
243 SSLClientCertType type,
244 const std::string& private_key,
245 const std::string& cert) {
246 worker_ = NULL;
247 PostAll(error, type, private_key, cert);
250 private:
251 void PostAll(int error,
252 SSLClientCertType type,
253 const std::string& private_key,
254 const std::string& cert) {
255 std::vector<OriginBoundCertServiceRequest*> requests;
256 requests_.swap(requests);
258 for (std::vector<OriginBoundCertServiceRequest*>::iterator
259 i = requests.begin(); i != requests.end(); i++) {
260 (*i)->Post(error, type, private_key, cert);
261 // Post() causes the OriginBoundCertServiceRequest to delete itself.
265 void DeleteAllCanceled() {
266 for (std::vector<OriginBoundCertServiceRequest*>::iterator
267 i = requests_.begin(); i != requests_.end(); i++) {
268 if ((*i)->canceled()) {
269 delete *i;
270 } else {
271 LOG(DFATAL) << "OriginBoundCertServiceRequest leaked!";
276 std::vector<OriginBoundCertServiceRequest*> requests_;
277 OriginBoundCertServiceWorker* worker_;
278 SSLClientCertType type_;
281 // static
282 const char OriginBoundCertService::kEPKIPassword[] = "";
284 OriginBoundCertService::OriginBoundCertService(
285 OriginBoundCertStore* origin_bound_cert_store)
286 : origin_bound_cert_store_(origin_bound_cert_store),
287 requests_(0),
288 cert_store_hits_(0),
289 inflight_joins_(0) {}
291 OriginBoundCertService::~OriginBoundCertService() {
292 STLDeleteValues(&inflight_);
295 int OriginBoundCertService::GetOriginBoundCert(
296 const std::string& origin,
297 const std::vector<uint8>& requested_types,
298 SSLClientCertType* type,
299 std::string* private_key,
300 std::string* cert,
301 const CompletionCallback& callback,
302 RequestHandle* out_req) {
303 DCHECK(CalledOnValidThread());
305 if (callback.is_null() || !private_key || !cert || origin.empty() ||
306 requested_types.empty()) {
307 *out_req = NULL;
308 return ERR_INVALID_ARGUMENT;
311 SSLClientCertType preferred_type = CLIENT_CERT_INVALID_TYPE;
312 for (size_t i = 0; i < requested_types.size(); ++i) {
313 if (IsSupportedCertType(requested_types[i])) {
314 preferred_type = static_cast<SSLClientCertType>(requested_types[i]);
315 break;
318 if (preferred_type == CLIENT_CERT_INVALID_TYPE) {
319 // None of the requested types are supported.
320 *out_req = NULL;
321 return ERR_CLIENT_AUTH_CERT_TYPE_UNSUPPORTED;
324 requests_++;
326 // Check if an origin bound cert of an acceptable type already exists for this
327 // origin, and that it has not expired.
328 base::Time now = base::Time::Now();
329 base::Time expiration_time;
330 if (origin_bound_cert_store_->GetOriginBoundCert(origin,
331 type,
332 &expiration_time,
333 private_key,
334 cert)) {
335 if (expiration_time < now) {
336 DVLOG(1) << "Cert store had expired cert for " << origin;
337 } else if (!IsSupportedCertType(*type) ||
338 std::find(requested_types.begin(), requested_types.end(),
339 *type) == requested_types.end()) {
340 DVLOG(1) << "Cert store had cert of wrong type " << *type << " for "
341 << origin;
342 } else {
343 cert_store_hits_++;
344 *out_req = NULL;
345 return OK;
349 // |origin_bound_cert_store_| has no cert for this origin. See if an
350 // identical request is currently in flight.
351 OriginBoundCertServiceJob* job = NULL;
352 std::map<std::string, OriginBoundCertServiceJob*>::const_iterator j;
353 j = inflight_.find(origin);
354 if (j != inflight_.end()) {
355 // An identical request is in flight already. We'll just attach our
356 // callback.
357 job = j->second;
358 // Check that the job is for an acceptable type of cert.
359 if (std::find(requested_types.begin(), requested_types.end(), job->type())
360 == requested_types.end()) {
361 DVLOG(1) << "Found inflight job of wrong type " << job->type()
362 << " for " << origin;
363 *out_req = NULL;
364 // If we get here, the server is asking for different types of certs in
365 // short succession. This probably means the server is broken or
366 // misconfigured. Since we only store one type of cert per origin, we
367 // are unable to handle this well. Just return an error and let the first
368 // job finish.
369 return ERR_ORIGIN_BOUND_CERT_GENERATION_TYPE_MISMATCH;
371 inflight_joins_++;
372 } else {
373 // Need to make a new request.
374 OriginBoundCertServiceWorker* worker = new OriginBoundCertServiceWorker(
375 origin,
376 preferred_type,
377 this);
378 job = new OriginBoundCertServiceJob(worker, preferred_type);
379 if (!worker->Start()) {
380 delete job;
381 delete worker;
382 *out_req = NULL;
383 // TODO(rkn): Log to the NetLog.
384 LOG(ERROR) << "OriginBoundCertServiceWorker couldn't be started.";
385 return ERR_INSUFFICIENT_RESOURCES; // Just a guess.
387 inflight_[origin] = job;
390 OriginBoundCertServiceRequest* request =
391 new OriginBoundCertServiceRequest(callback, type, private_key, cert);
392 job->AddRequest(request);
393 *out_req = request;
394 return ERR_IO_PENDING;
397 // static
398 int OriginBoundCertService::GenerateCert(const std::string& origin,
399 SSLClientCertType type,
400 uint32 serial_number,
401 base::Time* expiration_time,
402 std::string* private_key,
403 std::string* cert) {
404 base::Time now = base::Time::Now();
405 base::Time not_valid_after =
406 now + base::TimeDelta::FromDays(kValidityPeriodInDays);
407 std::string der_cert;
408 std::vector<uint8> private_key_info;
409 switch (type) {
410 case CLIENT_CERT_RSA_SIGN: {
411 scoped_ptr<crypto::RSAPrivateKey> key(
412 crypto::RSAPrivateKey::Create(kKeySizeInBits));
413 if (!key.get()) {
414 DLOG(ERROR) << "Unable to create key pair for client";
415 return ERR_KEY_GENERATION_FAILED;
417 if (!x509_util::CreateOriginBoundCertRSA(
418 key.get(),
419 origin,
420 serial_number,
421 now,
422 not_valid_after,
423 &der_cert)) {
424 DLOG(ERROR) << "Unable to create x509 cert for client";
425 return ERR_ORIGIN_BOUND_CERT_GENERATION_FAILED;
428 if (!key->ExportPrivateKey(&private_key_info)) {
429 DLOG(ERROR) << "Unable to export private key";
430 return ERR_PRIVATE_KEY_EXPORT_FAILED;
432 break;
434 case CLIENT_CERT_ECDSA_SIGN: {
435 scoped_ptr<crypto::ECPrivateKey> key(crypto::ECPrivateKey::Create());
436 if (!key.get()) {
437 DLOG(ERROR) << "Unable to create key pair for client";
438 return ERR_KEY_GENERATION_FAILED;
440 if (!x509_util::CreateOriginBoundCertEC(
441 key.get(),
442 origin,
443 serial_number,
444 now,
445 not_valid_after,
446 &der_cert)) {
447 DLOG(ERROR) << "Unable to create x509 cert for client";
448 return ERR_ORIGIN_BOUND_CERT_GENERATION_FAILED;
451 if (!key->ExportEncryptedPrivateKey(
452 kEPKIPassword, 1, &private_key_info)) {
453 DLOG(ERROR) << "Unable to export private key";
454 return ERR_PRIVATE_KEY_EXPORT_FAILED;
456 break;
458 default:
459 NOTREACHED();
460 return ERR_INVALID_ARGUMENT;
463 // TODO(rkn): Perhaps ExportPrivateKey should be changed to output a
464 // std::string* to prevent this copying.
465 std::string key_out(private_key_info.begin(), private_key_info.end());
467 private_key->swap(key_out);
468 cert->swap(der_cert);
469 *expiration_time = not_valid_after;
470 return OK;
473 void OriginBoundCertService::CancelRequest(RequestHandle req) {
474 DCHECK(CalledOnValidThread());
475 OriginBoundCertServiceRequest* request =
476 reinterpret_cast<OriginBoundCertServiceRequest*>(req);
477 request->Cancel();
480 // HandleResult is called by OriginBoundCertServiceWorker on the origin message
481 // loop. It deletes OriginBoundCertServiceJob.
482 void OriginBoundCertService::HandleResult(const std::string& origin,
483 int error,
484 SSLClientCertType type,
485 base::Time expiration_time,
486 const std::string& private_key,
487 const std::string& cert) {
488 DCHECK(CalledOnValidThread());
490 origin_bound_cert_store_->SetOriginBoundCert(
491 origin, type, expiration_time, private_key, cert);
493 std::map<std::string, OriginBoundCertServiceJob*>::iterator j;
494 j = inflight_.find(origin);
495 if (j == inflight_.end()) {
496 NOTREACHED();
497 return;
499 OriginBoundCertServiceJob* job = j->second;
500 inflight_.erase(j);
502 job->HandleResult(error, type, private_key, cert);
503 delete job;
506 int OriginBoundCertService::cert_count() {
507 return origin_bound_cert_store_->GetCertCount();
510 } // namespace net