| 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 |