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"
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"
29 #include <private/pprthred.h> // PR_DetachThread
36 const int kKeySizeInBits
= 1024;
37 const int kValidityPeriodInDays
= 365;
39 bool IsSupportedCertType(uint8 type
) {
41 case CLIENT_CERT_RSA_SIGN
:
42 case CLIENT_CERT_ECDSA_SIGN
:
51 // Represents the output and result callback of a request.
52 class OriginBoundCertServiceRequest
{
54 OriginBoundCertServiceRequest(const CompletionCallback
& callback
,
55 SSLClientCertType
* type
,
56 std::string
* private_key
,
58 : callback_(callback
),
60 private_key_(private_key
),
64 // Ensures that the result callback will never be made.
72 // Copies the contents of |private_key| and |cert| to the caller's output
73 // arguments and calls the callback.
75 SSLClientCertType type
,
76 const std::string
& private_key
,
77 const std::string
& cert
) {
78 if (!callback_
.is_null()) {
80 *private_key_
= private_key
;
87 bool canceled() const { return callback_
.is_null(); }
90 CompletionCallback callback_
;
91 SSLClientCertType
* type_
;
92 std::string
* private_key_
;
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
{
101 OriginBoundCertServiceWorker(
102 const std::string
& origin
,
103 SSLClientCertType type
,
104 OriginBoundCertService
* origin_bound_cert_service
)
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
),
115 DCHECK_EQ(MessageLoop::current(), origin_loop_
);
117 return base::WorkerPool::PostTask(
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
126 DCHECK_EQ(MessageLoop::current(), origin_loop_
);
127 base::AutoLock
locked(lock_
);
133 // Runs on a worker thread.
134 error_
= OriginBoundCertService::GenerateCert(origin_
,
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
153 // DoReply runs on the origin thread.
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_
);
163 origin_bound_cert_service_
->HandleResult(
164 origin_
, error_
, type_
, expiration_time_
, private_key_
, cert_
);
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.
183 base::AutoLock
locked(lock_
);
184 canceled
= canceled_
;
186 origin_loop_
->PostTask(
187 FROM_HERE
, base::Bind(&OriginBoundCertServiceWorker::DoReply
,
188 base::Unretained(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_.
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.
212 base::Time expiration_time_
;
213 std::string private_key_
;
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
{
224 OriginBoundCertServiceJob(OriginBoundCertServiceWorker
* worker
,
225 SSLClientCertType type
)
226 : worker_(worker
), type_(type
) {
229 ~OriginBoundCertServiceJob() {
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
) {
247 PostAll(error
, type
, private_key
, cert
);
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()) {
271 LOG(DFATAL
) << "OriginBoundCertServiceRequest leaked!";
276 std::vector
<OriginBoundCertServiceRequest
*> requests_
;
277 OriginBoundCertServiceWorker
* worker_
;
278 SSLClientCertType type_
;
282 const char OriginBoundCertService::kEPKIPassword
[] = "";
284 OriginBoundCertService::OriginBoundCertService(
285 OriginBoundCertStore
* origin_bound_cert_store
)
286 : origin_bound_cert_store_(origin_bound_cert_store
),
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
,
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()) {
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
]);
318 if (preferred_type
== CLIENT_CERT_INVALID_TYPE
) {
319 // None of the requested types are supported.
321 return ERR_CLIENT_AUTH_CERT_TYPE_UNSUPPORTED
;
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
,
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 "
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
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
;
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
369 return ERR_ORIGIN_BOUND_CERT_GENERATION_TYPE_MISMATCH
;
373 // Need to make a new request.
374 OriginBoundCertServiceWorker
* worker
= new OriginBoundCertServiceWorker(
378 job
= new OriginBoundCertServiceJob(worker
, preferred_type
);
379 if (!worker
->Start()) {
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
);
394 return ERR_IO_PENDING
;
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
,
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
;
410 case CLIENT_CERT_RSA_SIGN
: {
411 scoped_ptr
<crypto::RSAPrivateKey
> key(
412 crypto::RSAPrivateKey::Create(kKeySizeInBits
));
414 DLOG(ERROR
) << "Unable to create key pair for client";
415 return ERR_KEY_GENERATION_FAILED
;
417 if (!x509_util::CreateOriginBoundCertRSA(
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
;
434 case CLIENT_CERT_ECDSA_SIGN
: {
435 scoped_ptr
<crypto::ECPrivateKey
> key(crypto::ECPrivateKey::Create());
437 DLOG(ERROR
) << "Unable to create key pair for client";
438 return ERR_KEY_GENERATION_FAILED
;
440 if (!x509_util::CreateOriginBoundCertEC(
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
;
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
;
473 void OriginBoundCertService::CancelRequest(RequestHandle req
) {
474 DCHECK(CalledOnValidThread());
475 OriginBoundCertServiceRequest
* request
=
476 reinterpret_cast<OriginBoundCertServiceRequest
*>(req
);
480 // HandleResult is called by OriginBoundCertServiceWorker on the origin message
481 // loop. It deletes OriginBoundCertServiceJob.
482 void OriginBoundCertService::HandleResult(const std::string
& origin
,
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()) {
499 OriginBoundCertServiceJob
* job
= j
->second
;
502 job
->HandleResult(error
, type
, private_key
, cert
);
506 int OriginBoundCertService::cert_count() {
507 return origin_bound_cert_store_
->GetCertCount();