OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "net/base/origin_bound_cert_service.h" | 5 #include "net/base/origin_bound_cert_service.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <limits> | 8 #include <limits> |
9 | 9 |
10 #include "base/bind.h" | 10 #include "base/bind.h" |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
127 base::AutoLock locked(lock_); | 127 base::AutoLock locked(lock_); |
128 canceled_ = true; | 128 canceled_ = true; |
129 } | 129 } |
130 | 130 |
131 private: | 131 private: |
132 void Run() { | 132 void Run() { |
133 // Runs on a worker thread. | 133 // Runs on a worker thread. |
134 error_ = OriginBoundCertService::GenerateCert(origin_, | 134 error_ = OriginBoundCertService::GenerateCert(origin_, |
135 type_, | 135 type_, |
136 serial_number_, | 136 serial_number_, |
| 137 &expiration_time_, |
137 &private_key_, | 138 &private_key_, |
138 &cert_); | 139 &cert_); |
139 #if defined(USE_NSS) | 140 #if defined(USE_NSS) |
140 // Detach the thread from NSPR. | 141 // Detach the thread from NSPR. |
141 // Calling NSS functions attaches the thread to NSPR, which stores | 142 // Calling NSS functions attaches the thread to NSPR, which stores |
142 // the NSPR thread ID in thread-specific data. | 143 // the NSPR thread ID in thread-specific data. |
143 // The threads in our thread pool terminate after we have called | 144 // The threads in our thread pool terminate after we have called |
144 // PR_Cleanup. Unless we detach them from NSPR, net_unittests gets | 145 // PR_Cleanup. Unless we detach them from NSPR, net_unittests gets |
145 // segfaults on shutdown when the threads' thread-specific data | 146 // segfaults on shutdown when the threads' thread-specific data |
146 // destructors run. | 147 // destructors run. |
147 PR_DetachThread(); | 148 PR_DetachThread(); |
148 #endif | 149 #endif |
149 Finish(); | 150 Finish(); |
150 } | 151 } |
151 | 152 |
152 // DoReply runs on the origin thread. | 153 // DoReply runs on the origin thread. |
153 void DoReply() { | 154 void DoReply() { |
154 DCHECK_EQ(MessageLoop::current(), origin_loop_); | 155 DCHECK_EQ(MessageLoop::current(), origin_loop_); |
155 { | 156 { |
156 // We lock here because the worker thread could still be in Finished, | 157 // We lock here because the worker thread could still be in Finished, |
157 // after the PostTask, but before unlocking |lock_|. If we do not lock in | 158 // after the PostTask, but before unlocking |lock_|. If we do not lock in |
158 // this case, we will end up deleting a locked Lock, which can lead to | 159 // this case, we will end up deleting a locked Lock, which can lead to |
159 // memory leaks or worse errors. | 160 // memory leaks or worse errors. |
160 base::AutoLock locked(lock_); | 161 base::AutoLock locked(lock_); |
161 if (!canceled_) { | 162 if (!canceled_) { |
162 origin_bound_cert_service_->HandleResult(origin_, error_, type_, | 163 origin_bound_cert_service_->HandleResult( |
163 private_key_, cert_); | 164 origin_, error_, type_, expiration_time_, private_key_, cert_); |
164 } | 165 } |
165 } | 166 } |
166 delete this; | 167 delete this; |
167 } | 168 } |
168 | 169 |
169 void Finish() { | 170 void Finish() { |
170 // Runs on the worker thread. | 171 // Runs on the worker thread. |
171 // We assume that the origin loop outlives the OriginBoundCertService. If | 172 // We assume that the origin loop outlives the OriginBoundCertService. If |
172 // the OriginBoundCertService is deleted, it will call Cancel on us. If it | 173 // the OriginBoundCertService is deleted, it will call Cancel on us. If it |
173 // does so before the Acquire, we'll delete ourselves and return. If it's | 174 // does so before the Acquire, we'll delete ourselves and return. If it's |
(...skipping 27 matching lines...) Expand all Loading... |
201 | 202 |
202 // lock_ protects canceled_. | 203 // lock_ protects canceled_. |
203 base::Lock lock_; | 204 base::Lock lock_; |
204 | 205 |
205 // If canceled_ is true, | 206 // If canceled_ is true, |
206 // * origin_loop_ cannot be accessed by the worker thread, | 207 // * origin_loop_ cannot be accessed by the worker thread, |
207 // * origin_bound_cert_service_ cannot be accessed by any thread. | 208 // * origin_bound_cert_service_ cannot be accessed by any thread. |
208 bool canceled_; | 209 bool canceled_; |
209 | 210 |
210 int error_; | 211 int error_; |
| 212 base::Time expiration_time_; |
211 std::string private_key_; | 213 std::string private_key_; |
212 std::string cert_; | 214 std::string cert_; |
213 | 215 |
214 DISALLOW_COPY_AND_ASSIGN(OriginBoundCertServiceWorker); | 216 DISALLOW_COPY_AND_ASSIGN(OriginBoundCertServiceWorker); |
215 }; | 217 }; |
216 | 218 |
217 // An OriginBoundCertServiceJob is a one-to-one counterpart of an | 219 // An OriginBoundCertServiceJob is a one-to-one counterpart of an |
218 // OriginBoundCertServiceWorker. It lives only on the OriginBoundCertService's | 220 // OriginBoundCertServiceWorker. It lives only on the OriginBoundCertService's |
219 // origin message loop. | 221 // origin message loop. |
220 class OriginBoundCertServiceJob { | 222 class OriginBoundCertServiceJob { |
(...skipping 94 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
315 } | 317 } |
316 if (preferred_type == CLIENT_CERT_INVALID_TYPE) { | 318 if (preferred_type == CLIENT_CERT_INVALID_TYPE) { |
317 // None of the requested types are supported. | 319 // None of the requested types are supported. |
318 *out_req = NULL; | 320 *out_req = NULL; |
319 return ERR_CLIENT_AUTH_CERT_TYPE_UNSUPPORTED; | 321 return ERR_CLIENT_AUTH_CERT_TYPE_UNSUPPORTED; |
320 } | 322 } |
321 | 323 |
322 requests_++; | 324 requests_++; |
323 | 325 |
324 // Check if an origin bound cert of an acceptable type already exists for this | 326 // Check if an origin bound cert of an acceptable type already exists for this |
325 // origin. | 327 // origin, and that it has not expired. |
| 328 base::Time now = base::Time::Now(); |
| 329 base::Time expiration_time; |
326 if (origin_bound_cert_store_->GetOriginBoundCert(origin, | 330 if (origin_bound_cert_store_->GetOriginBoundCert(origin, |
327 type, | 331 type, |
| 332 &expiration_time, |
328 private_key, | 333 private_key, |
329 cert)) { | 334 cert)) { |
330 if (IsSupportedCertType(*type) && | 335 if (expiration_time < now) { |
331 std::find(requested_types.begin(), requested_types.end(), *type) != | 336 DVLOG(1) << "Cert store had expired cert for " << origin; |
332 requested_types.end()) { | 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 { |
333 cert_store_hits_++; | 343 cert_store_hits_++; |
334 *out_req = NULL; | 344 *out_req = NULL; |
335 return OK; | 345 return OK; |
336 } | 346 } |
337 DVLOG(1) << "Cert store had cert of wrong type " << *type << " for " | |
338 << origin; | |
339 } | 347 } |
340 | 348 |
341 // |origin_bound_cert_store_| has no cert for this origin. See if an | 349 // |origin_bound_cert_store_| has no cert for this origin. See if an |
342 // identical request is currently in flight. | 350 // identical request is currently in flight. |
343 OriginBoundCertServiceJob* job = NULL; | 351 OriginBoundCertServiceJob* job = NULL; |
344 std::map<std::string, OriginBoundCertServiceJob*>::const_iterator j; | 352 std::map<std::string, OriginBoundCertServiceJob*>::const_iterator j; |
345 j = inflight_.find(origin); | 353 j = inflight_.find(origin); |
346 if (j != inflight_.end()) { | 354 if (j != inflight_.end()) { |
347 // An identical request is in flight already. We'll just attach our | 355 // An identical request is in flight already. We'll just attach our |
348 // callback. | 356 // callback. |
349 job = j->second; | 357 job = j->second; |
350 // Check that the job is for an acceptable type of cert. | 358 // Check that the job is for an acceptable type of cert. |
351 if (std::find(requested_types.begin(), requested_types.end(), job->type()) | 359 if (std::find(requested_types.begin(), requested_types.end(), job->type()) |
352 == requested_types.end()) { | 360 == requested_types.end()) { |
353 DVLOG(1) << "Found inflight job of wrong type " << job->type() | 361 DVLOG(1) << "Found inflight job of wrong type " << job->type() |
354 << " for " << origin; | 362 << " for " << origin; |
355 *out_req = NULL; | 363 *out_req = NULL; |
356 // If we get here, the server is asking for different types of certs in | 364 // If we get here, the server is asking for different types of certs in |
357 // short succession. This probably means the server is broken or | 365 // short succession. This probably means the server is broken or |
358 // misconfigured. Since we only store one type of cert per origin, we | 366 // misconfigured. Since we only store one type of cert per origin, we |
359 // are unable to handle this well. Just return an error and let the first | 367 // are unable to handle this well. Just return an error and let the first |
360 // job finish. | 368 // job finish. |
361 return ERR_ORIGIN_BOUND_CERT_GENERATION_TYPE_MISMATCH; | 369 return ERR_ORIGIN_BOUND_CERT_GENERATION_TYPE_MISMATCH; |
362 } | 370 } |
363 inflight_joins_++; | 371 inflight_joins_++; |
364 } else { | 372 } else { |
365 // Need to make a new request. | 373 // Need to make a new request. |
366 OriginBoundCertServiceWorker* worker = | 374 OriginBoundCertServiceWorker* worker = new OriginBoundCertServiceWorker( |
367 new OriginBoundCertServiceWorker(origin, preferred_type, this); | 375 origin, |
| 376 preferred_type, |
| 377 this); |
368 job = new OriginBoundCertServiceJob(worker, preferred_type); | 378 job = new OriginBoundCertServiceJob(worker, preferred_type); |
369 if (!worker->Start()) { | 379 if (!worker->Start()) { |
370 delete job; | 380 delete job; |
371 delete worker; | 381 delete worker; |
372 *out_req = NULL; | 382 *out_req = NULL; |
373 // TODO(rkn): Log to the NetLog. | 383 // TODO(rkn): Log to the NetLog. |
374 LOG(ERROR) << "OriginBoundCertServiceWorker couldn't be started."; | 384 LOG(ERROR) << "OriginBoundCertServiceWorker couldn't be started."; |
375 return ERR_INSUFFICIENT_RESOURCES; // Just a guess. | 385 return ERR_INSUFFICIENT_RESOURCES; // Just a guess. |
376 } | 386 } |
377 inflight_[origin] = job; | 387 inflight_[origin] = job; |
378 } | 388 } |
379 | 389 |
380 OriginBoundCertServiceRequest* request = | 390 OriginBoundCertServiceRequest* request = |
381 new OriginBoundCertServiceRequest(callback, type, private_key, cert); | 391 new OriginBoundCertServiceRequest(callback, type, private_key, cert); |
382 job->AddRequest(request); | 392 job->AddRequest(request); |
383 *out_req = request; | 393 *out_req = request; |
384 return ERR_IO_PENDING; | 394 return ERR_IO_PENDING; |
385 } | 395 } |
386 | 396 |
387 // static | 397 // static |
388 int OriginBoundCertService::GenerateCert(const std::string& origin, | 398 int OriginBoundCertService::GenerateCert(const std::string& origin, |
389 SSLClientCertType type, | 399 SSLClientCertType type, |
390 uint32 serial_number, | 400 uint32 serial_number, |
| 401 base::Time* expiration_time, |
391 std::string* private_key, | 402 std::string* private_key, |
392 std::string* cert) { | 403 std::string* cert) { |
| 404 base::Time now = base::Time::Now(); |
| 405 base::Time not_valid_after = |
| 406 now + base::TimeDelta::FromDays(kValidityPeriodInDays); |
393 std::string der_cert; | 407 std::string der_cert; |
394 std::vector<uint8> private_key_info; | 408 std::vector<uint8> private_key_info; |
395 switch (type) { | 409 switch (type) { |
396 case CLIENT_CERT_RSA_SIGN: { | 410 case CLIENT_CERT_RSA_SIGN: { |
397 scoped_ptr<crypto::RSAPrivateKey> key( | 411 scoped_ptr<crypto::RSAPrivateKey> key( |
398 crypto::RSAPrivateKey::Create(kKeySizeInBits)); | 412 crypto::RSAPrivateKey::Create(kKeySizeInBits)); |
399 if (!key.get()) { | 413 if (!key.get()) { |
400 DLOG(ERROR) << "Unable to create key pair for client"; | 414 DLOG(ERROR) << "Unable to create key pair for client"; |
401 return ERR_KEY_GENERATION_FAILED; | 415 return ERR_KEY_GENERATION_FAILED; |
402 } | 416 } |
403 if (!x509_util::CreateOriginBoundCertRSA( | 417 if (!x509_util::CreateOriginBoundCertRSA( |
404 key.get(), | 418 key.get(), |
405 origin, | 419 origin, |
406 serial_number, | 420 serial_number, |
407 base::TimeDelta::FromDays(kValidityPeriodInDays), | 421 now, |
| 422 not_valid_after, |
408 &der_cert)) { | 423 &der_cert)) { |
409 DLOG(ERROR) << "Unable to create x509 cert for client"; | 424 DLOG(ERROR) << "Unable to create x509 cert for client"; |
410 return ERR_ORIGIN_BOUND_CERT_GENERATION_FAILED; | 425 return ERR_ORIGIN_BOUND_CERT_GENERATION_FAILED; |
411 } | 426 } |
412 | 427 |
413 if (!key->ExportPrivateKey(&private_key_info)) { | 428 if (!key->ExportPrivateKey(&private_key_info)) { |
414 DLOG(ERROR) << "Unable to export private key"; | 429 DLOG(ERROR) << "Unable to export private key"; |
415 return ERR_PRIVATE_KEY_EXPORT_FAILED; | 430 return ERR_PRIVATE_KEY_EXPORT_FAILED; |
416 } | 431 } |
417 break; | 432 break; |
418 } | 433 } |
419 case CLIENT_CERT_ECDSA_SIGN: { | 434 case CLIENT_CERT_ECDSA_SIGN: { |
420 scoped_ptr<crypto::ECPrivateKey> key(crypto::ECPrivateKey::Create()); | 435 scoped_ptr<crypto::ECPrivateKey> key(crypto::ECPrivateKey::Create()); |
421 if (!key.get()) { | 436 if (!key.get()) { |
422 DLOG(ERROR) << "Unable to create key pair for client"; | 437 DLOG(ERROR) << "Unable to create key pair for client"; |
423 return ERR_KEY_GENERATION_FAILED; | 438 return ERR_KEY_GENERATION_FAILED; |
424 } | 439 } |
425 if (!x509_util::CreateOriginBoundCertEC( | 440 if (!x509_util::CreateOriginBoundCertEC( |
426 key.get(), | 441 key.get(), |
427 origin, | 442 origin, |
428 serial_number, | 443 serial_number, |
429 base::TimeDelta::FromDays(kValidityPeriodInDays), | 444 now, |
| 445 not_valid_after, |
430 &der_cert)) { | 446 &der_cert)) { |
431 DLOG(ERROR) << "Unable to create x509 cert for client"; | 447 DLOG(ERROR) << "Unable to create x509 cert for client"; |
432 return ERR_ORIGIN_BOUND_CERT_GENERATION_FAILED; | 448 return ERR_ORIGIN_BOUND_CERT_GENERATION_FAILED; |
433 } | 449 } |
434 | 450 |
435 if (!key->ExportEncryptedPrivateKey( | 451 if (!key->ExportEncryptedPrivateKey( |
436 kEPKIPassword, 1, &private_key_info)) { | 452 kEPKIPassword, 1, &private_key_info)) { |
437 DLOG(ERROR) << "Unable to export private key"; | 453 DLOG(ERROR) << "Unable to export private key"; |
438 return ERR_PRIVATE_KEY_EXPORT_FAILED; | 454 return ERR_PRIVATE_KEY_EXPORT_FAILED; |
439 } | 455 } |
440 break; | 456 break; |
441 } | 457 } |
442 default: | 458 default: |
443 NOTREACHED(); | 459 NOTREACHED(); |
444 return ERR_INVALID_ARGUMENT; | 460 return ERR_INVALID_ARGUMENT; |
445 } | 461 } |
446 | 462 |
447 // TODO(rkn): Perhaps ExportPrivateKey should be changed to output a | 463 // TODO(rkn): Perhaps ExportPrivateKey should be changed to output a |
448 // std::string* to prevent this copying. | 464 // std::string* to prevent this copying. |
449 std::string key_out(private_key_info.begin(), private_key_info.end()); | 465 std::string key_out(private_key_info.begin(), private_key_info.end()); |
450 | 466 |
451 private_key->swap(key_out); | 467 private_key->swap(key_out); |
452 cert->swap(der_cert); | 468 cert->swap(der_cert); |
| 469 *expiration_time = not_valid_after; |
453 return OK; | 470 return OK; |
454 } | 471 } |
455 | 472 |
456 void OriginBoundCertService::CancelRequest(RequestHandle req) { | 473 void OriginBoundCertService::CancelRequest(RequestHandle req) { |
457 DCHECK(CalledOnValidThread()); | 474 DCHECK(CalledOnValidThread()); |
458 OriginBoundCertServiceRequest* request = | 475 OriginBoundCertServiceRequest* request = |
459 reinterpret_cast<OriginBoundCertServiceRequest*>(req); | 476 reinterpret_cast<OriginBoundCertServiceRequest*>(req); |
460 request->Cancel(); | 477 request->Cancel(); |
461 } | 478 } |
462 | 479 |
463 // HandleResult is called by OriginBoundCertServiceWorker on the origin message | 480 // HandleResult is called by OriginBoundCertServiceWorker on the origin message |
464 // loop. It deletes OriginBoundCertServiceJob. | 481 // loop. It deletes OriginBoundCertServiceJob. |
465 void OriginBoundCertService::HandleResult(const std::string& origin, | 482 void OriginBoundCertService::HandleResult(const std::string& origin, |
466 int error, | 483 int error, |
467 SSLClientCertType type, | 484 SSLClientCertType type, |
| 485 base::Time expiration_time, |
468 const std::string& private_key, | 486 const std::string& private_key, |
469 const std::string& cert) { | 487 const std::string& cert) { |
470 DCHECK(CalledOnValidThread()); | 488 DCHECK(CalledOnValidThread()); |
471 | 489 |
472 origin_bound_cert_store_->SetOriginBoundCert(origin, type, private_key, cert); | 490 origin_bound_cert_store_->SetOriginBoundCert( |
| 491 origin, type, expiration_time, private_key, cert); |
473 | 492 |
474 std::map<std::string, OriginBoundCertServiceJob*>::iterator j; | 493 std::map<std::string, OriginBoundCertServiceJob*>::iterator j; |
475 j = inflight_.find(origin); | 494 j = inflight_.find(origin); |
476 if (j == inflight_.end()) { | 495 if (j == inflight_.end()) { |
477 NOTREACHED(); | 496 NOTREACHED(); |
478 return; | 497 return; |
479 } | 498 } |
480 OriginBoundCertServiceJob* job = j->second; | 499 OriginBoundCertServiceJob* job = j->second; |
481 inflight_.erase(j); | 500 inflight_.erase(j); |
482 | 501 |
483 job->HandleResult(error, type, private_key, cert); | 502 job->HandleResult(error, type, private_key, cert); |
484 delete job; | 503 delete job; |
485 } | 504 } |
486 | 505 |
487 int OriginBoundCertService::cert_count() { | 506 int OriginBoundCertService::cert_count() { |
488 return origin_bound_cert_store_->GetCertCount(); | 507 return origin_bound_cert_store_->GetCertCount(); |
489 } | 508 } |
490 | 509 |
491 } // namespace net | 510 } // namespace net |
OLD | NEW |