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 } |
438 break; | 453 break; |
439 } | 454 } |
440 default: | 455 default: |
441 NOTREACHED(); | 456 NOTREACHED(); |
442 return ERR_INVALID_ARGUMENT; | 457 return ERR_INVALID_ARGUMENT; |
443 } | 458 } |
444 | 459 |
445 // TODO(rkn): Perhaps ExportPrivateKey should be changed to output a | 460 // TODO(rkn): Perhaps ExportPrivateKey should be changed to output a |
446 // std::string* to prevent this copying. | 461 // std::string* to prevent this copying. |
447 std::string key_out(private_key_info.begin(), private_key_info.end()); | 462 std::string key_out(private_key_info.begin(), private_key_info.end()); |
448 | 463 |
449 private_key->swap(key_out); | 464 private_key->swap(key_out); |
450 cert->swap(der_cert); | 465 cert->swap(der_cert); |
wtc
2011/12/20 19:46:55
Nit: it would be better to set *expiration_time on
mattm
2011/12/20 20:38:55
Done.
| |
451 return OK; | 466 return OK; |
452 } | 467 } |
453 | 468 |
454 void OriginBoundCertService::CancelRequest(RequestHandle req) { | 469 void OriginBoundCertService::CancelRequest(RequestHandle req) { |
455 DCHECK(CalledOnValidThread()); | 470 DCHECK(CalledOnValidThread()); |
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 |