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 |