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

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

Issue 9476035: Make CertVerifier a pure virtual interface. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Win shared fix Created 8 years, 9 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/single_request_cert_verifier.h ('k') | net/base/transport_security_state_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) 2012 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2012 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/cert_verifier.h" 5 #include "net/base/single_request_cert_verifier.h"
6 6
7 #include "base/bind.h" 7 #include "base/bind.h"
8 #include "base/bind_helpers.h" 8 #include "base/bind_helpers.h"
9 #include "base/compiler_specific.h"
10 #include "base/message_loop.h"
11 #include "base/metrics/histogram.h"
12 #include "base/stl_util.h"
13 #include "base/synchronization/lock.h"
14 #include "base/time.h"
15 #include "base/threading/worker_pool.h"
16 #include "net/base/crl_set.h"
17 #include "net/base/net_errors.h" 9 #include "net/base/net_errors.h"
18 #include "net/base/net_log.h"
19 #include "net/base/x509_certificate.h" 10 #include "net/base/x509_certificate.h"
20 #include "net/base/x509_certificate_net_log_param.h"
21
22 #if defined(USE_NSS)
23 #include <private/pprthred.h> // PR_DetachThread
24 #endif
25 11
26 namespace net { 12 namespace net {
27 13
28 ////////////////////////////////////////////////////////////////////////////
29
30 // Life of a request:
31 //
32 // CertVerifier CertVerifierJob CertVerifierWorker Request
33 // | (origin loop) (worker loop)
34 // |
35 // Verify()
36 // |---->-------------------<creates>
37 // |
38 // |---->----<creates>
39 // |
40 // |---->---------------------------------------------------<creates>
41 // |
42 // |---->--------------------Start
43 // | |
44 // | PostTask
45 // |
46 // | <starts verifying>
47 // |---->-----AddRequest |
48 // |
49 // |
50 // |
51 // Finish
52 // |
53 // PostTask
54 //
55 // |
56 // DoReply
57 // |----<-----------------------|
58 // HandleResult
59 // |
60 // |---->-----HandleResult
61 // |
62 // |------>-----------------------------------Post
63 //
64 //
65 //
66 // On a cache hit, CertVerifier::Verify() returns synchronously without
67 // posting a task to a worker thread.
68
69 namespace {
70
71 // The default value of max_cache_entries_.
72 const unsigned kMaxCacheEntries = 256;
73
74 // The number of seconds for which we'll cache a cache entry.
75 const unsigned kTTLSecs = 1800; // 30 minutes.
76
77 } // namespace
78
79 CertVerifier::CachedResult::CachedResult() : error(ERR_FAILED) {}
80
81 CertVerifier::CachedResult::~CachedResult() {}
82
83 // Represents the output and result callback of a request.
84 class CertVerifierRequest {
85 public:
86 CertVerifierRequest(const CompletionCallback& callback,
87 CertVerifyResult* verify_result,
88 const BoundNetLog& net_log)
89 : callback_(callback),
90 verify_result_(verify_result),
91 net_log_(net_log) {
92 net_log_.BeginEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST, NULL);
93 }
94
95 ~CertVerifierRequest() {
96 }
97
98 // Ensures that the result callback will never be made.
99 void Cancel() {
100 callback_.Reset();
101 verify_result_ = NULL;
102 net_log_.AddEvent(NetLog::TYPE_CANCELLED, NULL);
103 net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST, NULL);
104 }
105
106 // Copies the contents of |verify_result| to the caller's
107 // CertVerifyResult and calls the callback.
108 void Post(const CertVerifier::CachedResult& verify_result) {
109 if (!callback_.is_null()) {
110 net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_REQUEST, NULL);
111 *verify_result_ = verify_result.result;
112 callback_.Run(verify_result.error);
113 }
114 delete this;
115 }
116
117 bool canceled() const { return callback_.is_null(); }
118
119 const BoundNetLog& net_log() const { return net_log_; }
120
121 private:
122 CompletionCallback callback_;
123 CertVerifyResult* verify_result_;
124 const BoundNetLog net_log_;
125 };
126
127
128 // CertVerifierWorker runs on a worker thread and takes care of the blocking
129 // process of performing the certificate verification. Deletes itself
130 // eventually if Start() succeeds.
131 class CertVerifierWorker {
132 public:
133 CertVerifierWorker(X509Certificate* cert,
134 const std::string& hostname,
135 int flags,
136 CRLSet* crl_set,
137 CertVerifier* cert_verifier)
138 : cert_(cert),
139 hostname_(hostname),
140 flags_(flags),
141 crl_set_(crl_set),
142 origin_loop_(MessageLoop::current()),
143 cert_verifier_(cert_verifier),
144 canceled_(false),
145 error_(ERR_FAILED) {
146 }
147
148 // Returns the certificate being verified. May only be called /before/
149 // Start() is called.
150 X509Certificate* certificate() const { return cert_; }
151
152 bool Start() {
153 DCHECK_EQ(MessageLoop::current(), origin_loop_);
154
155 return base::WorkerPool::PostTask(
156 FROM_HERE, base::Bind(&CertVerifierWorker::Run, base::Unretained(this)),
157 true /* task is slow */);
158 }
159
160 // Cancel is called from the origin loop when the CertVerifier is getting
161 // deleted.
162 void Cancel() {
163 DCHECK_EQ(MessageLoop::current(), origin_loop_);
164 base::AutoLock locked(lock_);
165 canceled_ = true;
166 }
167
168 private:
169 void Run() {
170 // Runs on a worker thread.
171 error_ = cert_->Verify(hostname_, flags_, crl_set_, &verify_result_);
172 #if defined(USE_NSS)
173 // Detach the thread from NSPR.
174 // Calling NSS functions attaches the thread to NSPR, which stores
175 // the NSPR thread ID in thread-specific data.
176 // The threads in our thread pool terminate after we have called
177 // PR_Cleanup. Unless we detach them from NSPR, net_unittests gets
178 // segfaults on shutdown when the threads' thread-specific data
179 // destructors run.
180 PR_DetachThread();
181 #endif
182 Finish();
183 }
184
185 // DoReply runs on the origin thread.
186 void DoReply() {
187 DCHECK_EQ(MessageLoop::current(), origin_loop_);
188 {
189 // We lock here because the worker thread could still be in Finished,
190 // after the PostTask, but before unlocking |lock_|. If we do not lock in
191 // this case, we will end up deleting a locked Lock, which can lead to
192 // memory leaks or worse errors.
193 base::AutoLock locked(lock_);
194 if (!canceled_) {
195 cert_verifier_->HandleResult(cert_, hostname_, flags_,
196 error_, verify_result_);
197 }
198 }
199 delete this;
200 }
201
202 void Finish() {
203 // Runs on the worker thread.
204 // We assume that the origin loop outlives the CertVerifier. If the
205 // CertVerifier is deleted, it will call Cancel on us. If it does so
206 // before the Acquire, we'll delete ourselves and return. If it's trying to
207 // do so concurrently, then it'll block on the lock and we'll call PostTask
208 // while the CertVerifier (and therefore the MessageLoop) is still alive.
209 // If it does so after this function, we assume that the MessageLoop will
210 // process pending tasks. In which case we'll notice the |canceled_| flag
211 // in DoReply.
212
213 bool canceled;
214 {
215 base::AutoLock locked(lock_);
216 canceled = canceled_;
217 if (!canceled) {
218 origin_loop_->PostTask(
219 FROM_HERE, base::Bind(
220 &CertVerifierWorker::DoReply, base::Unretained(this)));
221 }
222 }
223
224 if (canceled)
225 delete this;
226 }
227
228 scoped_refptr<X509Certificate> cert_;
229 const std::string hostname_;
230 const int flags_;
231 scoped_refptr<CRLSet> crl_set_;
232 MessageLoop* const origin_loop_;
233 CertVerifier* const cert_verifier_;
234
235 // lock_ protects canceled_.
236 base::Lock lock_;
237
238 // If canceled_ is true,
239 // * origin_loop_ cannot be accessed by the worker thread,
240 // * cert_verifier_ cannot be accessed by any thread.
241 bool canceled_;
242
243 int error_;
244 CertVerifyResult verify_result_;
245
246 DISALLOW_COPY_AND_ASSIGN(CertVerifierWorker);
247 };
248
249 // A CertVerifierJob is a one-to-one counterpart of a CertVerifierWorker. It
250 // lives only on the CertVerifier's origin message loop.
251 class CertVerifierJob {
252 public:
253 CertVerifierJob(CertVerifierWorker* worker,
254 const BoundNetLog& net_log)
255 : start_time_(base::TimeTicks::Now()),
256 worker_(worker),
257 net_log_(net_log) {
258 scoped_refptr<NetLog::EventParameters> params(
259 new X509CertificateNetLogParam(worker_->certificate()));
260 net_log_.BeginEvent(NetLog::TYPE_CERT_VERIFIER_JOB, params);
261 }
262
263 ~CertVerifierJob() {
264 if (worker_) {
265 net_log_.AddEvent(NetLog::TYPE_CANCELLED, NULL);
266 net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_JOB, NULL);
267 worker_->Cancel();
268 DeleteAllCanceled();
269 }
270 }
271
272 void AddRequest(CertVerifierRequest* request) {
273 request->net_log().AddEvent(
274 NetLog::TYPE_CERT_VERIFIER_REQUEST_BOUND_TO_JOB,
275 make_scoped_refptr(new NetLogSourceParameter(
276 "source_dependency", net_log_.source())));
277
278 requests_.push_back(request);
279 }
280
281 void HandleResult(const CertVerifier::CachedResult& verify_result) {
282 worker_ = NULL;
283 net_log_.EndEvent(NetLog::TYPE_CERT_VERIFIER_JOB, NULL);
284 UMA_HISTOGRAM_CUSTOM_TIMES("Net.CertVerifier_Job_Latency",
285 base::TimeTicks::Now() - start_time_,
286 base::TimeDelta::FromMilliseconds(1),
287 base::TimeDelta::FromMinutes(10),
288 100);
289 PostAll(verify_result);
290 }
291
292 private:
293 void PostAll(const CertVerifier::CachedResult& verify_result) {
294 std::vector<CertVerifierRequest*> requests;
295 requests_.swap(requests);
296
297 for (std::vector<CertVerifierRequest*>::iterator
298 i = requests.begin(); i != requests.end(); i++) {
299 (*i)->Post(verify_result);
300 // Post() causes the CertVerifierRequest to delete itself.
301 }
302 }
303
304 void DeleteAllCanceled() {
305 for (std::vector<CertVerifierRequest*>::iterator
306 i = requests_.begin(); i != requests_.end(); i++) {
307 if ((*i)->canceled()) {
308 delete *i;
309 } else {
310 LOG(DFATAL) << "CertVerifierRequest leaked!";
311 }
312 }
313 }
314
315 const base::TimeTicks start_time_;
316 std::vector<CertVerifierRequest*> requests_;
317 CertVerifierWorker* worker_;
318 const BoundNetLog net_log_;
319 };
320
321 CertVerifier::CertVerifier()
322 : cache_(kMaxCacheEntries),
323 requests_(0),
324 cache_hits_(0),
325 inflight_joins_(0) {
326 CertDatabase::AddObserver(this);
327 }
328
329 CertVerifier::~CertVerifier() {
330 STLDeleteValues(&inflight_);
331
332 CertDatabase::RemoveObserver(this);
333 }
334
335 int CertVerifier::Verify(X509Certificate* cert,
336 const std::string& hostname,
337 int flags,
338 CRLSet* crl_set,
339 CertVerifyResult* verify_result,
340 const CompletionCallback& callback,
341 RequestHandle* out_req,
342 const BoundNetLog& net_log) {
343 DCHECK(CalledOnValidThread());
344
345 if (callback.is_null() || !verify_result || hostname.empty()) {
346 *out_req = NULL;
347 return ERR_INVALID_ARGUMENT;
348 }
349
350 requests_++;
351
352 const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(),
353 hostname, flags);
354 const CertVerifierCache::value_type* cached_entry =
355 cache_.Get(key, base::TimeTicks::Now());
356 if (cached_entry) {
357 ++cache_hits_;
358 *out_req = NULL;
359 *verify_result = cached_entry->result;
360 return cached_entry->error;
361 }
362
363 // No cache hit. See if an identical request is currently in flight.
364 CertVerifierJob* job;
365 std::map<RequestParams, CertVerifierJob*>::const_iterator j;
366 j = inflight_.find(key);
367 if (j != inflight_.end()) {
368 // An identical request is in flight already. We'll just attach our
369 // callback.
370 inflight_joins_++;
371 job = j->second;
372 } else {
373 // Need to make a new request.
374 CertVerifierWorker* worker = new CertVerifierWorker(cert, hostname, flags,
375 crl_set, this);
376 job = new CertVerifierJob(
377 worker,
378 BoundNetLog::Make(net_log.net_log(), NetLog::SOURCE_CERT_VERIFIER_JOB));
379 if (!worker->Start()) {
380 delete job;
381 delete worker;
382 *out_req = NULL;
383 // TODO(wtc): log to the NetLog.
384 LOG(ERROR) << "CertVerifierWorker couldn't be started.";
385 return ERR_INSUFFICIENT_RESOURCES; // Just a guess.
386 }
387 inflight_.insert(std::make_pair(key, job));
388 }
389
390 CertVerifierRequest* request =
391 new CertVerifierRequest(callback, verify_result, net_log);
392 job->AddRequest(request);
393 *out_req = request;
394 return ERR_IO_PENDING;
395 }
396
397 void CertVerifier::CancelRequest(RequestHandle req) {
398 DCHECK(CalledOnValidThread());
399 CertVerifierRequest* request = reinterpret_cast<CertVerifierRequest*>(req);
400 request->Cancel();
401 }
402
403 // HandleResult is called by CertVerifierWorker on the origin message loop.
404 // It deletes CertVerifierJob.
405 void CertVerifier::HandleResult(X509Certificate* cert,
406 const std::string& hostname,
407 int flags,
408 int error,
409 const CertVerifyResult& verify_result) {
410 DCHECK(CalledOnValidThread());
411
412 const RequestParams key(cert->fingerprint(), cert->ca_fingerprint(),
413 hostname, flags);
414
415 CachedResult cached_result;
416 cached_result.error = error;
417 cached_result.result = verify_result;
418 cache_.Put(key, cached_result, base::TimeTicks::Now(),
419 base::TimeDelta::FromSeconds(kTTLSecs));
420
421 std::map<RequestParams, CertVerifierJob*>::iterator j;
422 j = inflight_.find(key);
423 if (j == inflight_.end()) {
424 NOTREACHED();
425 return;
426 }
427 CertVerifierJob* job = j->second;
428 inflight_.erase(j);
429
430 job->HandleResult(cached_result);
431 delete job;
432 }
433
434 void CertVerifier::OnCertTrustChanged(const X509Certificate* cert) {
435 DCHECK(CalledOnValidThread());
436
437 ClearCache();
438 }
439
440 /////////////////////////////////////////////////////////////////////
441
442 SingleRequestCertVerifier::SingleRequestCertVerifier( 14 SingleRequestCertVerifier::SingleRequestCertVerifier(
443 CertVerifier* cert_verifier) 15 CertVerifier* cert_verifier)
444 : cert_verifier_(cert_verifier), 16 : cert_verifier_(cert_verifier),
445 cur_request_(NULL) { 17 cur_request_(NULL) {
446 DCHECK(cert_verifier_ != NULL); 18 DCHECK(cert_verifier_ != NULL);
447 } 19 }
448 20
449 SingleRequestCertVerifier::~SingleRequestCertVerifier() { 21 SingleRequestCertVerifier::~SingleRequestCertVerifier() {
450 if (cur_request_) { 22 if (cur_request_) {
451 cert_verifier_->CancelRequest(cur_request_); 23 cert_verifier_->CancelRequest(cur_request_);
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after
493 65
494 // Clear the outstanding request information. 66 // Clear the outstanding request information.
495 cur_request_ = NULL; 67 cur_request_ = NULL;
496 cur_request_callback_.Reset(); 68 cur_request_callback_.Reset();
497 69
498 // Call the user's original callback. 70 // Call the user's original callback.
499 callback.Run(result); 71 callback.Run(result);
500 } 72 }
501 73
502 } // namespace net 74 } // namespace net
OLDNEW
« no previous file with comments | « net/base/single_request_cert_verifier.h ('k') | net/base/transport_security_state_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698