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