Chromium Code Reviews| 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/compiler_specific.h" | 10 #include "base/compiler_specific.h" |
| (...skipping 81 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 92 }; | 92 }; |
| 93 | 93 |
| 94 // OriginBoundCertServiceWorker runs on a worker thread and takes care of the | 94 // OriginBoundCertServiceWorker runs on a worker thread and takes care of the |
| 95 // blocking process of performing key generation. Deletes itself eventually | 95 // blocking process of performing key generation. Deletes itself eventually |
| 96 // if Start() succeeds. | 96 // if Start() succeeds. |
| 97 class OriginBoundCertServiceWorker { | 97 class OriginBoundCertServiceWorker { |
| 98 public: | 98 public: |
| 99 OriginBoundCertServiceWorker( | 99 OriginBoundCertServiceWorker( |
| 100 const std::string& origin, | 100 const std::string& origin, |
| 101 SSLClientCertType type, | 101 SSLClientCertType type, |
| 102 base::Time not_valid_after, | |
| 102 OriginBoundCertService* origin_bound_cert_service) | 103 OriginBoundCertService* origin_bound_cert_service) |
| 103 : origin_(origin), | 104 : origin_(origin), |
| 104 type_(type), | 105 type_(type), |
| 106 not_valid_after_(not_valid_after), | |
| 105 serial_number_(base::RandInt(0, std::numeric_limits<int>::max())), | 107 serial_number_(base::RandInt(0, std::numeric_limits<int>::max())), |
| 106 origin_loop_(MessageLoop::current()), | 108 origin_loop_(MessageLoop::current()), |
| 107 origin_bound_cert_service_(origin_bound_cert_service), | 109 origin_bound_cert_service_(origin_bound_cert_service), |
| 108 canceled_(false), | 110 canceled_(false), |
| 109 error_(ERR_FAILED) { | 111 error_(ERR_FAILED) { |
| 110 } | 112 } |
| 111 | 113 |
| 112 bool Start() { | 114 bool Start() { |
| 113 DCHECK_EQ(MessageLoop::current(), origin_loop_); | 115 DCHECK_EQ(MessageLoop::current(), origin_loop_); |
| 114 | 116 |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 125 base::AutoLock locked(lock_); | 127 base::AutoLock locked(lock_); |
| 126 canceled_ = true; | 128 canceled_ = true; |
| 127 } | 129 } |
| 128 | 130 |
| 129 private: | 131 private: |
| 130 void Run() { | 132 void Run() { |
| 131 // Runs on a worker thread. | 133 // Runs on a worker thread. |
| 132 error_ = OriginBoundCertService::GenerateCert(origin_, | 134 error_ = OriginBoundCertService::GenerateCert(origin_, |
| 133 type_, | 135 type_, |
| 134 serial_number_, | 136 serial_number_, |
| 137 not_valid_after_, | |
| 135 &private_key_, | 138 &private_key_, |
| 136 &cert_); | 139 &cert_); |
| 137 #if defined(USE_NSS) | 140 #if defined(USE_NSS) |
| 138 // Detach the thread from NSPR. | 141 // Detach the thread from NSPR. |
| 139 // Calling NSS functions attaches the thread to NSPR, which stores | 142 // Calling NSS functions attaches the thread to NSPR, which stores |
| 140 // the NSPR thread ID in thread-specific data. | 143 // the NSPR thread ID in thread-specific data. |
| 141 // The threads in our thread pool terminate after we have called | 144 // The threads in our thread pool terminate after we have called |
| 142 // PR_Cleanup. Unless we detach them from NSPR, net_unittests gets | 145 // PR_Cleanup. Unless we detach them from NSPR, net_unittests gets |
| 143 // segfaults on shutdown when the threads' thread-specific data | 146 // segfaults on shutdown when the threads' thread-specific data |
| 144 // destructors run. | 147 // destructors run. |
| 145 PR_DetachThread(); | 148 PR_DetachThread(); |
| 146 #endif | 149 #endif |
| 147 Finish(); | 150 Finish(); |
| 148 } | 151 } |
| 149 | 152 |
| 150 // DoReply runs on the origin thread. | 153 // DoReply runs on the origin thread. |
| 151 void DoReply() { | 154 void DoReply() { |
| 152 DCHECK_EQ(MessageLoop::current(), origin_loop_); | 155 DCHECK_EQ(MessageLoop::current(), origin_loop_); |
| 153 { | 156 { |
| 154 // 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, |
| 155 // 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 |
| 156 // 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 |
| 157 // memory leaks or worse errors. | 160 // memory leaks or worse errors. |
| 158 base::AutoLock locked(lock_); | 161 base::AutoLock locked(lock_); |
| 159 if (!canceled_) { | 162 if (!canceled_) { |
| 160 origin_bound_cert_service_->HandleResult(origin_, error_, type_, | 163 origin_bound_cert_service_->HandleResult( |
| 161 private_key_, cert_); | 164 origin_, error_, type_, not_valid_after_, private_key_, cert_); |
| 162 } | 165 } |
| 163 } | 166 } |
| 164 delete this; | 167 delete this; |
| 165 } | 168 } |
| 166 | 169 |
| 167 void Finish() { | 170 void Finish() { |
| 168 // Runs on the worker thread. | 171 // Runs on the worker thread. |
| 169 // We assume that the origin loop outlives the OriginBoundCertService. If | 172 // We assume that the origin loop outlives the OriginBoundCertService. If |
| 170 // 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 |
| 171 // 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 12 matching lines...) Expand all Loading... | |
| 184 FROM_HERE, | 187 FROM_HERE, |
| 185 NewRunnableMethod(this, &OriginBoundCertServiceWorker::DoReply)); | 188 NewRunnableMethod(this, &OriginBoundCertServiceWorker::DoReply)); |
| 186 } | 189 } |
| 187 } | 190 } |
| 188 if (canceled) | 191 if (canceled) |
| 189 delete this; | 192 delete this; |
| 190 } | 193 } |
| 191 | 194 |
| 192 const std::string origin_; | 195 const std::string origin_; |
| 193 const SSLClientCertType type_; | 196 const SSLClientCertType type_; |
| 197 const base::Time not_valid_after_; | |
| 194 // Note that serial_number_ must be initialized on a non-worker thread | 198 // Note that serial_number_ must be initialized on a non-worker thread |
| 195 // (see documentation for OriginBoundCertService::GenerateCert). | 199 // (see documentation for OriginBoundCertService::GenerateCert). |
| 196 uint32 serial_number_; | 200 uint32 serial_number_; |
| 197 MessageLoop* const origin_loop_; | 201 MessageLoop* const origin_loop_; |
| 198 OriginBoundCertService* const origin_bound_cert_service_; | 202 OriginBoundCertService* const origin_bound_cert_service_; |
| 199 | 203 |
| 200 // lock_ protects canceled_. | 204 // lock_ protects canceled_. |
| 201 base::Lock lock_; | 205 base::Lock lock_; |
| 202 | 206 |
| 203 // If canceled_ is true, | 207 // If canceled_ is true, |
| (...skipping 109 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 313 } | 317 } |
| 314 if (preferred_type == CLIENT_CERT_INVALID_TYPE) { | 318 if (preferred_type == CLIENT_CERT_INVALID_TYPE) { |
| 315 // None of the requested types are supported. | 319 // None of the requested types are supported. |
| 316 *out_req = NULL; | 320 *out_req = NULL; |
| 317 return ERR_CLIENT_AUTH_CERT_TYPE_UNSUPPORTED; | 321 return ERR_CLIENT_AUTH_CERT_TYPE_UNSUPPORTED; |
| 318 } | 322 } |
| 319 | 323 |
| 320 requests_++; | 324 requests_++; |
| 321 | 325 |
| 322 // 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 |
| 323 // origin. | 327 // origin, and that it has not expired. |
| 328 base::Time now = base::Time::Now(); | |
|
wtc
2011/12/14 02:03:39
For unit tests, we may want to provide a way to ov
| |
| 329 base::Time not_valid_after; | |
| 324 if (origin_bound_cert_store_->GetOriginBoundCert(origin, | 330 if (origin_bound_cert_store_->GetOriginBoundCert(origin, |
| 325 type, | 331 type, |
| 332 ¬_valid_after, | |
| 326 private_key, | 333 private_key, |
| 327 cert)) { | 334 cert)) { |
| 328 if (IsSupportedCertType(*type) && | 335 if (not_valid_after < now) { |
| 329 std::find(requested_types.begin(), requested_types.end(), *type) != | 336 DVLOG(1) << "Cert store had expired cert for " << origin; |
| 330 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 { | |
| 331 cert_store_hits_++; | 343 cert_store_hits_++; |
| 332 *out_req = NULL; | 344 *out_req = NULL; |
| 333 return OK; | 345 return OK; |
| 334 } | 346 } |
| 335 DVLOG(1) << "Cert store had cert of wrong type " << *type << " for " | |
| 336 << origin; | |
| 337 } | 347 } |
| 338 | 348 |
| 339 // |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 |
| 340 // identical request is currently in flight. | 350 // identical request is currently in flight. |
| 341 OriginBoundCertServiceJob* job = NULL; | 351 OriginBoundCertServiceJob* job = NULL; |
| 342 std::map<std::string, OriginBoundCertServiceJob*>::const_iterator j; | 352 std::map<std::string, OriginBoundCertServiceJob*>::const_iterator j; |
| 343 j = inflight_.find(origin); | 353 j = inflight_.find(origin); |
| 344 if (j != inflight_.end()) { | 354 if (j != inflight_.end()) { |
| 345 // 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 |
| 346 // callback. | 356 // callback. |
| 347 job = j->second; | 357 job = j->second; |
| 348 // Check that the job is for an acceptable type of cert. | 358 // Check that the job is for an acceptable type of cert. |
| 349 if (std::find(requested_types.begin(), requested_types.end(), job->type()) | 359 if (std::find(requested_types.begin(), requested_types.end(), job->type()) |
| 350 == requested_types.end()) { | 360 == requested_types.end()) { |
| 351 DVLOG(1) << "Found inflight job of wrong type " << job->type() | 361 DVLOG(1) << "Found inflight job of wrong type " << job->type() |
| 352 << " for " << origin; | 362 << " for " << origin; |
| 353 *out_req = NULL; | 363 *out_req = NULL; |
| 354 // 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 |
| 355 // short succession. This probably means the server is broken or | 365 // short succession. This probably means the server is broken or |
| 356 // 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 |
| 357 // 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 |
| 358 // job finish. | 368 // job finish. |
| 359 return ERR_ORIGIN_BOUND_CERT_GENERATION_TYPE_MISMATCH; | 369 return ERR_ORIGIN_BOUND_CERT_GENERATION_TYPE_MISMATCH; |
| 360 } | 370 } |
| 361 inflight_joins_++; | 371 inflight_joins_++; |
| 362 } else { | 372 } else { |
| 363 // Need to make a new request. | 373 // Need to make a new request. |
| 364 OriginBoundCertServiceWorker* worker = | 374 OriginBoundCertServiceWorker* worker = new OriginBoundCertServiceWorker( |
| 365 new OriginBoundCertServiceWorker(origin, preferred_type, this); | 375 origin, |
| 376 preferred_type, | |
| 377 now + base::TimeDelta::FromDays(kValidityPeriodInDays), | |
|
wtc
2011/12/15 03:18:51
It doesn't seem necessary to pass not_valid_after
mattm
2011/12/20 00:28:38
Done.
| |
| 378 this); | |
| 366 job = new OriginBoundCertServiceJob(worker, preferred_type); | 379 job = new OriginBoundCertServiceJob(worker, preferred_type); |
| 367 if (!worker->Start()) { | 380 if (!worker->Start()) { |
| 368 delete job; | 381 delete job; |
| 369 delete worker; | 382 delete worker; |
| 370 *out_req = NULL; | 383 *out_req = NULL; |
| 371 // TODO(rkn): Log to the NetLog. | 384 // TODO(rkn): Log to the NetLog. |
| 372 LOG(ERROR) << "OriginBoundCertServiceWorker couldn't be started."; | 385 LOG(ERROR) << "OriginBoundCertServiceWorker couldn't be started."; |
| 373 return ERR_INSUFFICIENT_RESOURCES; // Just a guess. | 386 return ERR_INSUFFICIENT_RESOURCES; // Just a guess. |
| 374 } | 387 } |
| 375 inflight_[origin] = job; | 388 inflight_[origin] = job; |
| 376 } | 389 } |
| 377 | 390 |
| 378 OriginBoundCertServiceRequest* request = | 391 OriginBoundCertServiceRequest* request = |
| 379 new OriginBoundCertServiceRequest(callback, type, private_key, cert); | 392 new OriginBoundCertServiceRequest(callback, type, private_key, cert); |
| 380 job->AddRequest(request); | 393 job->AddRequest(request); |
| 381 *out_req = request; | 394 *out_req = request; |
| 382 return ERR_IO_PENDING; | 395 return ERR_IO_PENDING; |
| 383 } | 396 } |
| 384 | 397 |
| 385 // static | 398 // static |
| 386 int OriginBoundCertService::GenerateCert(const std::string& origin, | 399 int OriginBoundCertService::GenerateCert(const std::string& origin, |
| 387 SSLClientCertType type, | 400 SSLClientCertType type, |
| 388 uint32 serial_number, | 401 uint32 serial_number, |
| 402 base::Time not_valid_after, | |
| 389 std::string* private_key, | 403 std::string* private_key, |
| 390 std::string* cert) { | 404 std::string* cert) { |
| 391 std::string der_cert; | 405 std::string der_cert; |
| 392 std::vector<uint8> private_key_info; | 406 std::vector<uint8> private_key_info; |
| 393 switch (type) { | 407 switch (type) { |
| 394 case CLIENT_CERT_RSA_SIGN: { | 408 case CLIENT_CERT_RSA_SIGN: { |
| 395 scoped_ptr<crypto::RSAPrivateKey> key( | 409 scoped_ptr<crypto::RSAPrivateKey> key( |
| 396 crypto::RSAPrivateKey::Create(kKeySizeInBits)); | 410 crypto::RSAPrivateKey::Create(kKeySizeInBits)); |
| 397 if (!key.get()) { | 411 if (!key.get()) { |
| 398 DLOG(ERROR) << "Unable to create key pair for client"; | 412 DLOG(ERROR) << "Unable to create key pair for client"; |
| 399 return ERR_KEY_GENERATION_FAILED; | 413 return ERR_KEY_GENERATION_FAILED; |
| 400 } | 414 } |
| 401 if (!x509_util::CreateOriginBoundCertRSA( | 415 if (!x509_util::CreateOriginBoundCertRSA( |
| 402 key.get(), | 416 key.get(), |
| 403 origin, | 417 origin, |
| 404 serial_number, | 418 serial_number, |
| 405 base::TimeDelta::FromDays(kValidityPeriodInDays), | 419 not_valid_after, |
|
wtc
2011/12/15 03:18:51
It seems that the GenerateCert method doesn't need
mattm
2011/12/20 00:28:38
Done.
| |
| 406 &der_cert)) { | 420 &der_cert)) { |
| 407 DLOG(ERROR) << "Unable to create x509 cert for client"; | 421 DLOG(ERROR) << "Unable to create x509 cert for client"; |
| 408 return ERR_ORIGIN_BOUND_CERT_GENERATION_FAILED; | 422 return ERR_ORIGIN_BOUND_CERT_GENERATION_FAILED; |
| 409 } | 423 } |
| 410 | 424 |
| 411 if (!key->ExportPrivateKey(&private_key_info)) { | 425 if (!key->ExportPrivateKey(&private_key_info)) { |
| 412 DLOG(ERROR) << "Unable to export private key"; | 426 DLOG(ERROR) << "Unable to export private key"; |
| 413 return ERR_PRIVATE_KEY_EXPORT_FAILED; | 427 return ERR_PRIVATE_KEY_EXPORT_FAILED; |
| 414 } | 428 } |
| 415 break; | 429 break; |
| 416 } | 430 } |
| 417 case CLIENT_CERT_ECDSA_SIGN: { | 431 case CLIENT_CERT_ECDSA_SIGN: { |
| 418 scoped_ptr<crypto::ECPrivateKey> key(crypto::ECPrivateKey::Create()); | 432 scoped_ptr<crypto::ECPrivateKey> key(crypto::ECPrivateKey::Create()); |
| 419 if (!key.get()) { | 433 if (!key.get()) { |
| 420 DLOG(ERROR) << "Unable to create key pair for client"; | 434 DLOG(ERROR) << "Unable to create key pair for client"; |
| 421 return ERR_KEY_GENERATION_FAILED; | 435 return ERR_KEY_GENERATION_FAILED; |
| 422 } | 436 } |
| 423 if (!x509_util::CreateOriginBoundCertEC( | 437 if (!x509_util::CreateOriginBoundCertEC( |
| 424 key.get(), | 438 key.get(), |
| 425 origin, | 439 origin, |
| 426 serial_number, | 440 serial_number, |
| 427 base::TimeDelta::FromDays(kValidityPeriodInDays), | 441 not_valid_after, |
| 428 &der_cert)) { | 442 &der_cert)) { |
| 429 DLOG(ERROR) << "Unable to create x509 cert for client"; | 443 DLOG(ERROR) << "Unable to create x509 cert for client"; |
| 430 return ERR_ORIGIN_BOUND_CERT_GENERATION_FAILED; | 444 return ERR_ORIGIN_BOUND_CERT_GENERATION_FAILED; |
| 431 } | 445 } |
| 432 | 446 |
| 433 if (!key->ExportEncryptedPrivateKey( | 447 if (!key->ExportEncryptedPrivateKey( |
| 434 kEPKIPassword, 1, &private_key_info)) { | 448 kEPKIPassword, 1, &private_key_info)) { |
| 435 DLOG(ERROR) << "Unable to export private key"; | 449 DLOG(ERROR) << "Unable to export private key"; |
| 436 return ERR_PRIVATE_KEY_EXPORT_FAILED; | 450 return ERR_PRIVATE_KEY_EXPORT_FAILED; |
| 437 } | 451 } |
| (...skipping 18 matching lines...) Expand all Loading... | |
| 456 OriginBoundCertServiceRequest* request = | 470 OriginBoundCertServiceRequest* request = |
| 457 reinterpret_cast<OriginBoundCertServiceRequest*>(req); | 471 reinterpret_cast<OriginBoundCertServiceRequest*>(req); |
| 458 request->Cancel(); | 472 request->Cancel(); |
| 459 } | 473 } |
| 460 | 474 |
| 461 // HandleResult is called by OriginBoundCertServiceWorker on the origin message | 475 // HandleResult is called by OriginBoundCertServiceWorker on the origin message |
| 462 // loop. It deletes OriginBoundCertServiceJob. | 476 // loop. It deletes OriginBoundCertServiceJob. |
| 463 void OriginBoundCertService::HandleResult(const std::string& origin, | 477 void OriginBoundCertService::HandleResult(const std::string& origin, |
| 464 int error, | 478 int error, |
| 465 SSLClientCertType type, | 479 SSLClientCertType type, |
| 480 base::Time not_valid_after, | |
| 466 const std::string& private_key, | 481 const std::string& private_key, |
| 467 const std::string& cert) { | 482 const std::string& cert) { |
| 468 DCHECK(CalledOnValidThread()); | 483 DCHECK(CalledOnValidThread()); |
| 469 | 484 |
| 470 origin_bound_cert_store_->SetOriginBoundCert(origin, type, private_key, cert); | 485 origin_bound_cert_store_->SetOriginBoundCert( |
| 486 origin, type, not_valid_after, private_key, cert); | |
| 471 | 487 |
| 472 std::map<std::string, OriginBoundCertServiceJob*>::iterator j; | 488 std::map<std::string, OriginBoundCertServiceJob*>::iterator j; |
| 473 j = inflight_.find(origin); | 489 j = inflight_.find(origin); |
| 474 if (j == inflight_.end()) { | 490 if (j == inflight_.end()) { |
| 475 NOTREACHED(); | 491 NOTREACHED(); |
| 476 return; | 492 return; |
| 477 } | 493 } |
| 478 OriginBoundCertServiceJob* job = j->second; | 494 OriginBoundCertServiceJob* job = j->second; |
| 479 inflight_.erase(j); | 495 inflight_.erase(j); |
| 480 | 496 |
| 481 job->HandleResult(error, type, private_key, cert); | 497 job->HandleResult(error, type, private_key, cert); |
| 482 delete job; | 498 delete job; |
| 483 } | 499 } |
| 484 | 500 |
| 485 int OriginBoundCertService::cert_count() { | 501 int OriginBoundCertService::cert_count() { |
| 486 return origin_bound_cert_store_->GetCertCount(); | 502 return origin_bound_cert_store_->GetCertCount(); |
| 487 } | 503 } |
| 488 | 504 |
| 489 } // namespace net | 505 } // namespace net |
| 490 | 506 |
| 491 DISABLE_RUNNABLE_METHOD_REFCOUNT(net::OriginBoundCertServiceWorker); | 507 DISABLE_RUNNABLE_METHOD_REFCOUNT(net::OriginBoundCertServiceWorker); |
| OLD | NEW |