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

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

Powered by Google App Engine
This is Rietveld 408576698