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

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

Issue 7565023: Gave the GetOriginBoundCertificate an asynchronous interface because certificate (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 4 months 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 <limits> 7 #include <limits>
8 8
9 #include "base/compiler_specific.h"
9 #include "base/logging.h" 10 #include "base/logging.h"
10 #include "base/memory/ref_counted.h" 11 #include "base/memory/ref_counted.h"
11 #include "base/memory/scoped_ptr.h" 12 #include "base/memory/scoped_ptr.h"
13 #include "base/message_loop.h"
12 #include "base/rand_util.h" 14 #include "base/rand_util.h"
15 #include "base/stl_util.h"
16 #include "base/threading/worker_pool.h"
13 #include "crypto/rsa_private_key.h" 17 #include "crypto/rsa_private_key.h"
18 #include "net/base/net_errors.h"
14 #include "net/base/origin_bound_cert_store.h" 19 #include "net/base/origin_bound_cert_store.h"
15 #include "net/base/x509_certificate.h" 20 #include "net/base/x509_certificate.h"
16 21
22 #if defined(USE_NSS)
23 #include <private/pprthred.h> // PR_DetachThread
24 #endif
25
17 namespace net { 26 namespace net {
18 27
19 namespace { 28 namespace {
20 29
21 const int kKeySizeInBits = 1024; 30 const int kKeySizeInBits = 1024;
22 const int kValidityPeriodInDays = 365; 31 const int kValidityPeriodInDays = 365;
23 32
24 } // namespace 33 } // namespace
25 34
35 // Represents the output and result callback of a request.
36 class OriginBoundCertServiceRequest {
37 public:
38 OriginBoundCertServiceRequest(CompletionCallback* callback,
39 std::string* private_key,
40 std::string* cert)
41 : callback_(callback),
42 private_key_(private_key),
43 cert_(cert) {
44 }
45
46 // Ensures that the result callback will never be made.
47 void Cancel() {
48 callback_ = NULL;
49 private_key_ = NULL;
50 cert_ = NULL;
51 }
52
53 // Copies the contents of |private_key| and |cert| to the caller's fields
54 // and calls the callback.
55 void Post(int error,
56 const std::string& private_key,
57 const std::string& cert) {
58 if (callback_) {
59 *private_key_ = private_key;
60 *cert_ = cert;
61 callback_->Run(error);
62 }
63 delete this;
64 }
65
66 bool canceled() const { return !callback_; }
67
68 private:
69 CompletionCallback* callback_;
70 std::string* private_key_;
71 std::string* cert_;
72 };
73
74 // OriginBoundCertServiceWorker runs on a worker thread and takes care of the
75 // blocking process of performing key generation. Deletes itself eventually
76 // if Start() succeeds.
77 class OriginBoundCertServiceWorker {
78 public:
79 OriginBoundCertServiceWorker(
80 const std::string& origin,
81 OriginBoundCertService* origin_bound_cert_service)
82 : origin_(origin),
83 origin_loop_(MessageLoop::current()),
84 origin_bound_cert_service_(origin_bound_cert_service),
85 canceled_(false),
86 error_(ERR_FAILED) {
87 }
88
89 bool Start() {
90 DCHECK_EQ(MessageLoop::current(), origin_loop_);
91
92 return base::WorkerPool::PostTask(
93 FROM_HERE,
94 NewRunnableMethod(this, &OriginBoundCertServiceWorker::Run),
95 true /* task is slow */);
96 }
97
98 // Cancel is called from the origin loop when the OriginBoundCertService is
99 // getting deleted.
100 void Cancel() {
101 DCHECK_EQ(MessageLoop::current(), origin_loop_);
102 base::AutoLock locked(lock_);
103 canceled_ = true;
104 }
105
106 private:
107 void Run() {
108 uint32 serial_number = base::RandInt(0, std::numeric_limits<int>::max());
109 // Runs on a worker thread.
110 error_ = origin_bound_cert_service_->GenerateCert(origin_,
111 serial_number,
112 &private_key_,
113 &cert_);
114 #if defined(USE_NSS)
115 // Detach the thread from NSPR.
116 // Calling NSS functions attaches the thread to NSPR, which stores
117 // the NSPR thread ID in thread-specific data.
118 // The threads in our thread pool terminate after we have called
119 // PR_Cleanup. Unless we detach them from NSPR, net_unittests gets
120 // segfaults on shutdown when the threads' thread-specific data
121 // destructors run.
122 PR_DetachThread();
123 #endif
124 Finish();
125 }
126
127 // DoReply runs on the origin thread.
128 void DoReply() {
129 DCHECK_EQ(MessageLoop::current(), origin_loop_);
130 {
131 // We lock here because the worker thread could still be in Finished,
132 // after the PostTask, but before unlocking |lock_|. If we do not lock in
133 // this case, we will end up deleting a locked Lock, which can lead to
134 // memory leaks or worse errors.
135 base::AutoLock locked(lock_);
136 if (!canceled_) {
137 origin_bound_cert_service_->HandleResult(
138 origin_, error_, private_key_, cert_);
139 }
140 }
141 delete this;
142 }
143
144 void Finish() {
145 // Runs on the worker thread.
146 // We assume that the origin loop outlives the OriginBoundCertService. If
147 // the OriginBoundCertService is deleted, it will call Cancel on us. If it
148 // does so before the Acquire, we'll delete ourselves and return. If it's
149 // trying to do so concurrently, then it'll block on the lock and we'll
150 // call PostTask while the OriginBoundCertService (and therefore the
151 // MessageLoop) is still alive. If it does so after this function, we
152 // assume that the MessageLoop will process pending tasks. In which case
153 // we'll notice the |canceled_| flag in DoReply.
154
155 bool canceled;
156 {
157 base::AutoLock locked(lock_);
158 canceled = canceled_;
159 if (!canceled) {
160 origin_loop_->PostTask(
161 FROM_HERE,
162 NewRunnableMethod(this, &OriginBoundCertServiceWorker::DoReply));
163 }
164 }
165 if (canceled)
166 delete this;
167 }
168
169 const std::string origin_;
170 MessageLoop* const origin_loop_;
171 OriginBoundCertService* const origin_bound_cert_service_;
172
173 // lock_ protects canceled_.
174 base::Lock lock_;
175
176 // If canceled_ is true,
177 // * origin_loop_ cannot be accessed by the worker thread,
178 // * origin_bound_cert_service_ cannot be accessed by any thread.
179 bool canceled_;
180
181 int error_;
182 std::string private_key_;
183 std::string cert_;
184
185 DISALLOW_COPY_AND_ASSIGN(OriginBoundCertServiceWorker);
186 };
187
188 // An OriginBoundCertServiceJob is a one-to-one counterpart of an
189 // OriginBoundCertServiceWorker. It lives only on the OriginBoundCertService's
190 // origin message loop.
191 class OriginBoundCertServiceJob {
192 public:
193 explicit OriginBoundCertServiceJob(OriginBoundCertServiceWorker* worker)
194 : worker_(worker) {
195 }
196
197 ~OriginBoundCertServiceJob() {
198 if (worker_) {
199 worker_->Cancel();
200 DeleteAllCanceled();
201 }
202 }
203
204 void AddRequest(OriginBoundCertServiceRequest* request) {
205 requests_.push_back(request);
206 }
207
208 void HandleResult(int error,
209 const std::string& private_key,
210 const std::string& cert) {
211 worker_ = NULL;
212 PostAll(error, private_key, cert);
213 }
214
215 private:
216 void PostAll(int error,
217 const std::string& private_key,
218 const std::string& cert) {
219 std::vector<OriginBoundCertServiceRequest*> requests;
220 requests_.swap(requests);
221
222 for (std::vector<OriginBoundCertServiceRequest*>::iterator
223 i = requests.begin(); i != requests.end(); i++) {
224 (*i)->Post(error, private_key, cert);
225 // Post() causes the OriginBoundCertServiceRequest to delete itself.
226 }
227 }
228
229 void DeleteAllCanceled() {
230 for (std::vector<OriginBoundCertServiceRequest*>::iterator
231 i = requests_.begin(); i != requests_.end(); i++) {
232 if ((*i)->canceled()) {
233 delete *i;
234 } else {
235 LOG(DFATAL) << "OriginBoundCertServiceRequest leaked!";
236 }
237 }
238 }
239
240 std::vector<OriginBoundCertServiceRequest*> requests_;
241 OriginBoundCertServiceWorker* worker_;
242 };
243
26 OriginBoundCertService::OriginBoundCertService( 244 OriginBoundCertService::OriginBoundCertService(
27 OriginBoundCertStore* origin_bound_cert_store) 245 OriginBoundCertStore* origin_bound_cert_store)
28 : origin_bound_cert_store_(origin_bound_cert_store) {} 246 : origin_bound_cert_store_(origin_bound_cert_store),
29 247 requests_(0),
30 OriginBoundCertService::~OriginBoundCertService() {} 248 cache_hits_(0),
31 249 inflight_joins_(0) {}
32 bool OriginBoundCertService::GetOriginBoundCert(const std::string& origin, 250
33 std::string* private_key_result, 251 OriginBoundCertService::~OriginBoundCertService() {
34 std::string* cert_result) { 252 STLDeleteValues(&inflight_);
35 // Check if origin bound cert already exists for this origin. 253 }
254
255 int OriginBoundCertService::GetOriginBoundCert(const std::string& origin,
256 std::string* private_key,
257 std::string* cert,
258 CompletionCallback* callback,
259 RequestHandle* out_req) {
260
261 DCHECK(CalledOnValidThread());
262
263 if (!callback || !private_key || !cert || origin.empty()) {
264 *out_req = NULL;
265 return ERR_INVALID_ARGUMENT;
266 }
267
268 requests_++;
269
270 // Check if an origin bound cert already exists for this origin.
36 if (origin_bound_cert_store_->GetOriginBoundCert(origin, 271 if (origin_bound_cert_store_->GetOriginBoundCert(origin,
37 private_key_result, 272 private_key,
38 cert_result)) 273 cert)) {
39 return true; 274 cache_hits_++;
40 275 *out_req = NULL;
41 // No origin bound cert exists, we have to create one. 276 return OK;
277 }
278
279 // No cache hit. See if an identical request is currently in flight.
280 OriginBoundCertServiceJob* job;
281 std::map<std::string, OriginBoundCertServiceJob*>::const_iterator j;
282 j = inflight_.find(origin);
283 if (j != inflight_.end()) {
284 // An identical request is in flight already. We'll just attach our
285 // callback.
286 inflight_joins_++;
287 job = j->second;
288 } else {
289 // Need to make a new request.
290 OriginBoundCertServiceWorker* worker =
291 new OriginBoundCertServiceWorker(origin, this);
292 job = new OriginBoundCertServiceJob(worker);
293 if (!worker->Start()) {
294 delete job;
295 delete worker;
296 *out_req = NULL;
297 // TODO(rkn): Log to the NetLog.
298 LOG(ERROR) << "OriginBoundCertServiceWorker couldn't be started.";
299 return ERR_INSUFFICIENT_RESOURCES; // Just a guess.
300 }
301 inflight_[origin] = job;
302 }
303
304 OriginBoundCertServiceRequest* request =
305 new OriginBoundCertServiceRequest(callback, private_key, cert);
306 job->AddRequest(request);
307 *out_req = request;
308 return ERR_IO_PENDING;
309 }
310
311 int OriginBoundCertService::GenerateCert(const std::string& origin,
312 uint32 serial_number,
313 std::string* private_key,
314 std::string* cert) {
42 std::string subject = "CN=OBC"; 315 std::string subject = "CN=OBC";
43 scoped_ptr<crypto::RSAPrivateKey> key( 316 scoped_ptr<crypto::RSAPrivateKey> key(
44 crypto::RSAPrivateKey::Create(kKeySizeInBits)); 317 crypto::RSAPrivateKey::Create(kKeySizeInBits));
45 if (!key.get()) { 318 if (!key.get()) {
46 LOG(WARNING) << "Unable to create key pair for client"; 319 LOG(WARNING) << "Unable to create key pair for client";
47 return false; 320 return ERR_FAILED;
48 } 321 }
322
49 scoped_refptr<X509Certificate> x509_cert = X509Certificate::CreateSelfSigned( 323 scoped_refptr<X509Certificate> x509_cert = X509Certificate::CreateSelfSigned(
50 key.get(), 324 key.get(),
51 subject, 325 subject,
52 base::RandInt(0, std::numeric_limits<int>::max()), 326 serial_number,
53 base::TimeDelta::FromDays(kValidityPeriodInDays)); 327 base::TimeDelta::FromDays(kValidityPeriodInDays));
54 if (!x509_cert) { 328 if (!x509_cert) {
55 LOG(WARNING) << "Unable to create x509 cert for client"; 329 LOG(WARNING) << "Unable to create x509 cert for client";
56 return false; 330 return ERR_FAILED;
57 } 331 }
58 332
59 std::vector<uint8> private_key_info; 333 std::vector<uint8> private_key_info;
60 if (!key->ExportPrivateKey(&private_key_info)) { 334 if (!key->ExportPrivateKey(&private_key_info)) {
61 LOG(WARNING) << "Unable to export private key"; 335 LOG(WARNING) << "Unable to export private key";
62 return false; 336 return ERR_FAILED;
63 } 337 }
64 // TODO(rkn): Perhaps ExportPrivateKey should be changed to output a 338 // TODO(rkn): Perhaps ExportPrivateKey should be changed to output a
65 // std::string* to prevent this copying. 339 // std::string* to prevent this copying.
66 std::string key_out(private_key_info.begin(), private_key_info.end()); 340 std::string key_out(private_key_info.begin(), private_key_info.end());
67 341
68 std::string der_cert; 342 std::string der_cert;
69 if (!x509_cert->GetDEREncoded(&der_cert)) { 343 if (!x509_cert->GetDEREncoded(&der_cert)) {
70 LOG(WARNING) << "Unable to get DER-enconded cert"; 344 LOG(WARNING) << "Unable to get DER-enconded cert";
71 return false; 345 return ERR_FAILED;
72 } 346 }
73 347
74 if (!origin_bound_cert_store_->SetOriginBoundCert(origin, 348 private_key->swap(key_out);
75 key_out, 349 cert->swap(der_cert);
76 der_cert)) { 350 return OK;
77 LOG(WARNING) << "Unable to set origin bound certificate"; 351 }
78 return false; 352
353 void OriginBoundCertService::CancelRequest(RequestHandle req) {
354 DCHECK(CalledOnValidThread());
355 OriginBoundCertServiceRequest* request =
356 reinterpret_cast<OriginBoundCertServiceRequest*>(req);
357 request->Cancel();
358 }
359
360 // HandleResult is called by OriginBoundCertServiceWorker on the origin message
361 // loop. It deletes OriginBoundCertServiceJob.
362 void OriginBoundCertService::HandleResult(const std::string& origin,
363 int error,
364 const std::string& private_key,
365 const std::string& cert) {
366 DCHECK(CalledOnValidThread());
367
368 origin_bound_cert_store_->SetOriginBoundCert(origin, private_key, cert);
369
370 std::map<std::string, OriginBoundCertServiceJob*>::iterator j;
371 j = inflight_.find(origin);
372 if (j == inflight_.end()) {
373 NOTREACHED();
374 return;
79 } 375 }
376 OriginBoundCertServiceJob* job = j->second;
377 inflight_.erase(j);
80 378
81 private_key_result->swap(key_out); 379 job->HandleResult(error, private_key, cert);
82 cert_result->swap(der_cert); 380 delete job;
83 return true;
84 } 381 }
85 382
86 int OriginBoundCertService::GetCertCount() { 383 int OriginBoundCertService::GetCertCount() {
87 return origin_bound_cert_store_->GetCertCount(); 384 return origin_bound_cert_store_->GetCertCount();
88 } 385 }
89 386
90 } // namespace net 387 } // namespace net
388
389 DISABLE_RUNNABLE_METHOD_REFCOUNT(net::OriginBoundCertServiceWorker);
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698