Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(250)

Side by Side Diff: net/base/origin_bound_cert_service.cc

Issue 8890073: Handle Origin Bound Certificate expiration. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 9 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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
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
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 &not_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
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);
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698