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

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 serial_number_(base::RandInt(0, std::numeric_limits<int>::max())),
84 origin_loop_(MessageLoop::current()),
85 origin_bound_cert_service_(origin_bound_cert_service),
86 canceled_(false),
87 error_(ERR_FAILED) {
88 }
89
90 bool Start() {
91 DCHECK_EQ(MessageLoop::current(), origin_loop_);
92
93 return base::WorkerPool::PostTask(
94 FROM_HERE,
95 NewRunnableMethod(this, &OriginBoundCertServiceWorker::Run),
96 true /* task is slow */);
97 }
98
99 // Cancel is called from the origin loop when the OriginBoundCertService is
100 // getting deleted.
101 void Cancel() {
102 DCHECK_EQ(MessageLoop::current(), origin_loop_);
103 base::AutoLock locked(lock_);
104 canceled_ = true;
105 }
106
107 private:
108 void Run() {
109 // Runs on a worker thread.
110 error_ = OriginBoundCertService::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 uint32 serial_number_;
171 MessageLoop* const origin_loop_;
172 OriginBoundCertService* const origin_bound_cert_service_;
173
174 // lock_ protects canceled_.
175 base::Lock lock_;
176
177 // If canceled_ is true,
178 // * origin_loop_ cannot be accessed by the worker thread,
179 // * origin_bound_cert_service_ cannot be accessed by any thread.
180 bool canceled_;
181
182 int error_;
183 std::string private_key_;
184 std::string cert_;
185
186 DISALLOW_COPY_AND_ASSIGN(OriginBoundCertServiceWorker);
187 };
188
189 // An OriginBoundCertServiceJob is a one-to-one counterpart of an
190 // OriginBoundCertServiceWorker. It lives only on the OriginBoundCertService's
191 // origin message loop.
192 class OriginBoundCertServiceJob {
193 public:
194 explicit OriginBoundCertServiceJob(OriginBoundCertServiceWorker* worker)
195 : worker_(worker) {
196 }
197
198 ~OriginBoundCertServiceJob() {
199 if (worker_) {
200 worker_->Cancel();
201 DeleteAllCanceled();
202 }
203 }
204
205 void AddRequest(OriginBoundCertServiceRequest* request) {
206 requests_.push_back(request);
207 }
208
209 void HandleResult(int error,
210 const std::string& private_key,
211 const std::string& cert) {
212 worker_ = NULL;
213 PostAll(error, private_key, cert);
214 }
215
216 private:
217 void PostAll(int error,
218 const std::string& private_key,
219 const std::string& cert) {
220 std::vector<OriginBoundCertServiceRequest*> requests;
221 requests_.swap(requests);
222
223 for (std::vector<OriginBoundCertServiceRequest*>::iterator
224 i = requests.begin(); i != requests.end(); i++) {
225 (*i)->Post(error, private_key, cert);
226 // Post() causes the OriginBoundCertServiceRequest to delete itself.
227 }
228 }
229
230 void DeleteAllCanceled() {
231 for (std::vector<OriginBoundCertServiceRequest*>::iterator
232 i = requests_.begin(); i != requests_.end(); i++) {
233 if ((*i)->canceled()) {
234 delete *i;
235 } else {
236 LOG(DFATAL) << "OriginBoundCertServiceRequest leaked!";
237 }
238 }
239 }
240
241 std::vector<OriginBoundCertServiceRequest*> requests_;
242 OriginBoundCertServiceWorker* worker_;
243 };
244
26 OriginBoundCertService::OriginBoundCertService( 245 OriginBoundCertService::OriginBoundCertService(
27 OriginBoundCertStore* origin_bound_cert_store) 246 OriginBoundCertStore* origin_bound_cert_store)
28 : origin_bound_cert_store_(origin_bound_cert_store) {} 247 : origin_bound_cert_store_(origin_bound_cert_store),
29 248 requests_(0),
30 OriginBoundCertService::~OriginBoundCertService() {} 249 cache_hits_(0),
31 250 inflight_joins_(0) {}
32 bool OriginBoundCertService::GetOriginBoundCert(const std::string& origin, 251
33 std::string* private_key_result, 252 OriginBoundCertService::~OriginBoundCertService() {
34 std::string* cert_result) { 253 STLDeleteValues(&inflight_);
35 // Check if origin bound cert already exists for this origin. 254 }
255
256 int OriginBoundCertService::GetOriginBoundCert(const std::string& origin,
257 std::string* private_key,
258 std::string* cert,
259 CompletionCallback* callback,
260 RequestHandle* out_req) {
261
262 DCHECK(CalledOnValidThread());
263
264 if (!callback || !private_key || !cert || origin.empty()) {
265 *out_req = NULL;
266 return ERR_INVALID_ARGUMENT;
267 }
268
269 requests_++;
270
271 // Check if an origin bound cert already exists for this origin.
36 if (origin_bound_cert_store_->GetOriginBoundCert(origin, 272 if (origin_bound_cert_store_->GetOriginBoundCert(origin,
37 private_key_result, 273 private_key,
38 cert_result)) 274 cert)) {
39 return true; 275 cache_hits_++;
40 276 *out_req = NULL;
41 // No origin bound cert exists, we have to create one. 277 return OK;
278 }
279
280 // No cache hit. See if an identical request is currently in flight.
wtc 2011/08/09 00:43:34 "No cache hit" should be updated because we aren't
281 OriginBoundCertServiceJob* job;
282 std::map<std::string, OriginBoundCertServiceJob*>::const_iterator j;
283 j = inflight_.find(origin);
284 if (j != inflight_.end()) {
285 // An identical request is in flight already. We'll just attach our
286 // callback.
287 inflight_joins_++;
288 job = j->second;
289 } else {
290 // Need to make a new request.
291 OriginBoundCertServiceWorker* worker =
292 new OriginBoundCertServiceWorker(origin, this);
293 job = new OriginBoundCertServiceJob(worker);
294 if (!worker->Start()) {
295 delete job;
296 delete worker;
297 *out_req = NULL;
298 // TODO(rkn): Log to the NetLog.
299 LOG(ERROR) << "OriginBoundCertServiceWorker couldn't be started.";
300 return ERR_INSUFFICIENT_RESOURCES; // Just a guess.
301 }
302 inflight_[origin] = job;
303 }
304
305 OriginBoundCertServiceRequest* request =
306 new OriginBoundCertServiceRequest(callback, private_key, cert);
307 job->AddRequest(request);
308 *out_req = request;
309 return ERR_IO_PENDING;
310 }
311
312 // static
313 int OriginBoundCertService::GenerateCert(const std::string& origin,
314 uint32 serial_number,
315 std::string* private_key,
316 std::string* cert) {
42 std::string subject = "CN=OBC"; 317 std::string subject = "CN=OBC";
43 scoped_ptr<crypto::RSAPrivateKey> key( 318 scoped_ptr<crypto::RSAPrivateKey> key(
44 crypto::RSAPrivateKey::Create(kKeySizeInBits)); 319 crypto::RSAPrivateKey::Create(kKeySizeInBits));
45 if (!key.get()) { 320 if (!key.get()) {
46 LOG(WARNING) << "Unable to create key pair for client"; 321 LOG(WARNING) << "Unable to create key pair for client";
47 return false; 322 return ERR_KEY_PAIR_GENERATION_FAILED;
48 } 323 }
324
49 scoped_refptr<X509Certificate> x509_cert = X509Certificate::CreateSelfSigned( 325 scoped_refptr<X509Certificate> x509_cert = X509Certificate::CreateSelfSigned(
50 key.get(), 326 key.get(),
51 subject, 327 subject,
52 base::RandInt(0, std::numeric_limits<int>::max()), 328 serial_number,
53 base::TimeDelta::FromDays(kValidityPeriodInDays)); 329 base::TimeDelta::FromDays(kValidityPeriodInDays));
54 if (!x509_cert) { 330 if (!x509_cert) {
55 LOG(WARNING) << "Unable to create x509 cert for client"; 331 LOG(WARNING) << "Unable to create x509 cert for client";
56 return false; 332 return ERR_ORIGIN_BOUND_CERT_GENERATION_FAILED;
57 } 333 }
58 334
59 std::vector<uint8> private_key_info; 335 std::vector<uint8> private_key_info;
60 if (!key->ExportPrivateKey(&private_key_info)) { 336 if (!key->ExportPrivateKey(&private_key_info)) {
61 LOG(WARNING) << "Unable to export private key"; 337 LOG(WARNING) << "Unable to export private key";
62 return false; 338 return ERR_PRIVATE_KEY_EXPORT_FAILED;
63 } 339 }
64 // TODO(rkn): Perhaps ExportPrivateKey should be changed to output a 340 // TODO(rkn): Perhaps ExportPrivateKey should be changed to output a
65 // std::string* to prevent this copying. 341 // std::string* to prevent this copying.
66 std::string key_out(private_key_info.begin(), private_key_info.end()); 342 std::string key_out(private_key_info.begin(), private_key_info.end());
67 343
68 std::string der_cert; 344 std::string der_cert;
69 if (!x509_cert->GetDEREncoded(&der_cert)) { 345 if (!x509_cert->GetDEREncoded(&der_cert)) {
70 LOG(WARNING) << "Unable to get DER-enconded cert"; 346 LOG(WARNING) << "Unable to get DER-encoded cert";
71 return false; 347 return ERR_GET_DER_CERT_FAILED;
72 } 348 }
73 349
74 if (!origin_bound_cert_store_->SetOriginBoundCert(origin, 350 private_key->swap(key_out);
75 key_out, 351 cert->swap(der_cert);
76 der_cert)) { 352 return OK;
77 LOG(WARNING) << "Unable to set origin bound certificate";
78 return false;
79 }
80
81 private_key_result->swap(key_out);
82 cert_result->swap(der_cert);
83 return true;
84 } 353 }
85 354
86 int OriginBoundCertService::GetCertCount() { 355 void OriginBoundCertService::CancelRequest(RequestHandle req) {
356 DCHECK(CalledOnValidThread());
357 OriginBoundCertServiceRequest* request =
358 reinterpret_cast<OriginBoundCertServiceRequest*>(req);
359 request->Cancel();
360 }
361
362 // HandleResult is called by OriginBoundCertServiceWorker on the origin message
363 // loop. It deletes OriginBoundCertServiceJob.
364 void OriginBoundCertService::HandleResult(const std::string& origin,
365 int error,
366 const std::string& private_key,
367 const std::string& cert) {
368 DCHECK(CalledOnValidThread());
369
370 origin_bound_cert_store_->SetOriginBoundCert(origin, private_key, cert);
371
372 std::map<std::string, OriginBoundCertServiceJob*>::iterator j;
373 j = inflight_.find(origin);
374 if (j == inflight_.end()) {
375 NOTREACHED();
376 return;
377 }
378 OriginBoundCertServiceJob* job = j->second;
379 inflight_.erase(j);
380
381 job->HandleResult(error, private_key, cert);
382 delete job;
383 }
384
385 int OriginBoundCertService::get_cert_count() {
87 return origin_bound_cert_store_->GetCertCount(); 386 return origin_bound_cert_store_->GetCertCount();
88 } 387 }
89 388
90 } // namespace net 389 } // namespace net
390
391 DISABLE_RUNNABLE_METHOD_REFCOUNT(net::OriginBoundCertServiceWorker);
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698