1 // Copyright (c) 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "net/http/disk_based_cert_cache.h"
10 #include "base/callback_helpers.h"
11 #include "base/memory/ref_counted.h"
12 #include "base/metrics/histogram.h"
13 #include "base/profiler/scoped_tracker.h"
14 #include "base/stl_util.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "net/base/io_buffer.h"
17 #include "net/base/net_errors.h"
18 #include "net/disk_cache/disk_cache.h"
24 // TODO(brandonsalmon): change this number to improve performance.
25 const size_t kMemoryCacheMaxSize
= 30;
27 // Used to obtain a unique cache key for a certificate in the form of
29 std::string
GetCacheKeyForCert(
30 const X509Certificate::OSCertHandle cert_handle
) {
31 SHA1HashValue fingerprint
=
32 X509Certificate::CalculateFingerprint(cert_handle
);
35 base::HexEncode(fingerprint
.data
, arraysize(fingerprint
.data
));
41 DISK_CACHE_ENTRY_CORRUPT
,
46 void RecordCacheResult(CacheResult result
) {
47 UMA_HISTOGRAM_ENUMERATION(
48 "DiskBasedCertCache.CertIoCacheResult", result
, CACHE_RESULT_MAX
);
53 // WriteWorkers represent pending SetCertificate jobs in the DiskBasedCertCache.
54 // Each certificate requested to be stored is assigned a WriteWorker.
55 // The same certificate should not have multiple WriteWorkers at the same
56 // time; instead, add a user callback to the existing WriteWorker.
57 class DiskBasedCertCache::WriteWorker
{
59 // |backend| is the backend to store |certificate| in, using
60 // |key| as the key for the disk_cache::Entry.
61 // |cleanup_callback| is called to clean up this ReadWorker,
62 // regardless of success or failure.
63 WriteWorker(disk_cache::Backend
* backend
,
64 const std::string
& key
,
65 X509Certificate::OSCertHandle cert_handle
,
66 const base::Closure
& cleanup_callback
);
70 // Writes the given certificate to the cache. On completion, will invoke all
74 // Adds a callback to the set of callbacks to be run when this
75 // WriteWorker finishes processing.
76 void AddCallback(const SetCallback
& user_callback
);
78 // Signals the WriteWorker to abort early. The WriteWorker will be destroyed
79 // upon the completion of any pending callbacks. User callbacks will be
80 // invoked with an empty string.
88 STATE_CREATE_COMPLETE
,
94 void OnIOComplete(int rv
);
98 int DoOpenComplete(int rv
);
100 int DoCreateComplete(int rv
);
102 int DoWriteComplete(int rv
);
106 // Invokes all of the |user_callbacks_|
107 void RunCallbacks(int rv
);
109 disk_cache::Backend
* backend_
;
110 const X509Certificate::OSCertHandle cert_handle_
;
114 disk_cache::Entry
* entry_
;
116 scoped_refptr
<IOBuffer
> buffer_
;
119 base::Closure cleanup_callback_
;
120 std::vector
<SetCallback
> user_callbacks_
;
121 CompletionCallback io_callback_
;
124 DiskBasedCertCache::WriteWorker::WriteWorker(
125 disk_cache::Backend
* backend
,
126 const std::string
& key
,
127 X509Certificate::OSCertHandle cert_handle
,
128 const base::Closure
& cleanup_callback
)
130 cert_handle_(X509Certificate::DupOSCertHandle(cert_handle
)),
134 next_state_(STATE_NONE
),
136 cleanup_callback_(cleanup_callback
),
138 base::Bind(&WriteWorker::OnIOComplete
, base::Unretained(this))) {
141 DiskBasedCertCache::WriteWorker::~WriteWorker() {
143 X509Certificate::FreeOSCertHandle(cert_handle_
);
148 void DiskBasedCertCache::WriteWorker::Start() {
149 DCHECK_EQ(STATE_NONE
, next_state_
);
151 next_state_
= STATE_OPEN
;
154 if (rv
== ERR_IO_PENDING
)
160 void DiskBasedCertCache::WriteWorker::AddCallback(
161 const SetCallback
& user_callback
) {
162 user_callbacks_
.push_back(user_callback
);
165 void DiskBasedCertCache::WriteWorker::Cancel() {
169 void DiskBasedCertCache::WriteWorker::OnIOComplete(int rv
) {
177 if (rv
== ERR_IO_PENDING
)
183 int DiskBasedCertCache::WriteWorker::DoLoop(int rv
) {
185 State state
= next_state_
;
186 next_state_
= STATE_NONE
;
191 case STATE_OPEN_COMPLETE
:
192 rv
= DoOpenComplete(rv
);
197 case STATE_CREATE_COMPLETE
:
198 rv
= DoCreateComplete(rv
);
203 case STATE_WRITE_COMPLETE
:
204 rv
= DoWriteComplete(rv
);
210 } while (rv
!= ERR_IO_PENDING
&& next_state_
!= STATE_NONE
);
215 int DiskBasedCertCache::WriteWorker::DoOpen() {
216 next_state_
= STATE_OPEN_COMPLETE
;
217 return backend_
->OpenEntry(key_
, &entry_
, io_callback_
);
220 int DiskBasedCertCache::WriteWorker::DoOpenComplete(int rv
) {
221 // The entry doesn't exist yet, so we should create it.
223 next_state_
= STATE_CREATE
;
227 next_state_
= STATE_WRITE
;
231 int DiskBasedCertCache::WriteWorker::DoCreate() {
232 next_state_
= STATE_CREATE_COMPLETE
;
233 return backend_
->CreateEntry(key_
, &entry_
, io_callback_
);
236 int DiskBasedCertCache::WriteWorker::DoCreateComplete(int rv
) {
240 next_state_
= STATE_WRITE
;
244 int DiskBasedCertCache::WriteWorker::DoWrite() {
245 std::string write_data
;
246 bool encoded
= X509Certificate::GetDEREncoded(cert_handle_
, &write_data
);
251 buffer_
= new IOBuffer(write_data
.size());
252 io_buf_len_
= write_data
.size();
253 memcpy(buffer_
->data(), write_data
.data(), io_buf_len_
);
255 next_state_
= STATE_WRITE_COMPLETE
;
257 return entry_
->WriteData(0 /* index */,
262 true /* truncate */);
265 int DiskBasedCertCache::WriteWorker::DoWriteComplete(int rv
) {
266 if (rv
< io_buf_len_
)
272 void DiskBasedCertCache::WriteWorker::Finish(int rv
) {
273 cleanup_callback_
.Run();
274 cleanup_callback_
.Reset();
279 void DiskBasedCertCache::WriteWorker::RunCallbacks(int rv
) {
284 for (std::vector
<SetCallback
>::const_iterator it
= user_callbacks_
.begin();
285 it
!= user_callbacks_
.end();
289 user_callbacks_
.clear();
292 // ReadWorkers represent pending GetCertificate jobs in the DiskBasedCertCache.
293 // Each certificate requested to be retrieved from the cache is assigned a
294 // ReadWorker. The same |key| should not have multiple ReadWorkers at the
295 // same time; instead, call AddCallback to add a user callback to the
296 // existing ReadWorker.
297 class DiskBasedCertCache::ReadWorker
{
299 // |backend| is the backend to read |certificate| from, using
300 // |key| as the key for the disk_cache::Entry.
301 // |cleanup_callback| is called to clean up this ReadWorker,
302 // regardless of success or failure.
303 ReadWorker(disk_cache::Backend
* backend
,
304 const std::string
& key
,
305 const GetCallback
& cleanup_callback
);
309 // Reads the given certificate from the cache. On completion, will invoke all
313 // Adds a callback to the set of callbacks to be run when this
314 // ReadWorker finishes processing.
315 void AddCallback(const GetCallback
& user_callback
);
317 // Signals the ReadWorker to abort early. The ReadWorker will be destroyed
318 // upon the completion of any pending callbacks. User callbacks will be
319 // invoked with a NULL cert handle.
331 void OnIOComplete(int rv
);
334 int DoOpenComplete(int rv
);
336 int DoReadComplete(int rv
);
339 // Invokes all of |user_callbacks_|
342 disk_cache::Backend
* backend_
;
343 X509Certificate::OSCertHandle cert_handle_
;
347 disk_cache::Entry
* entry_
;
350 scoped_refptr
<IOBuffer
> buffer_
;
353 GetCallback cleanup_callback_
;
354 std::vector
<GetCallback
> user_callbacks_
;
355 CompletionCallback io_callback_
;
358 DiskBasedCertCache::ReadWorker::ReadWorker(disk_cache::Backend
* backend
,
359 const std::string
& key
,
360 const GetCallback
& cleanup_callback
)
366 next_state_(STATE_NONE
),
368 cleanup_callback_(cleanup_callback
),
370 base::Bind(&ReadWorker::OnIOComplete
, base::Unretained(this))) {
373 DiskBasedCertCache::ReadWorker::~ReadWorker() {
377 X509Certificate::FreeOSCertHandle(cert_handle_
);
380 void DiskBasedCertCache::ReadWorker::Start() {
381 DCHECK_EQ(STATE_NONE
, next_state_
);
382 next_state_
= STATE_OPEN
;
385 if (rv
== ERR_IO_PENDING
)
391 void DiskBasedCertCache::ReadWorker::AddCallback(
392 const GetCallback
& user_callback
) {
393 user_callbacks_
.push_back(user_callback
);
396 void DiskBasedCertCache::ReadWorker::Cancel() {
400 void DiskBasedCertCache::ReadWorker::OnIOComplete(int rv
) {
401 // TODO(vadimt): Remove ScopedTracker below once crbug.com/422516 is fixed.
402 tracked_objects::ScopedTracker
tracking_profile(
403 FROM_HERE_WITH_EXPLICIT_FUNCTION(
404 "422516 DiskBasedCertCache::ReadWorker::OnIOComplete"));
413 if (rv
== ERR_IO_PENDING
)
419 int DiskBasedCertCache::ReadWorker::DoLoop(int rv
) {
421 State state
= next_state_
;
422 next_state_
= STATE_NONE
;
427 case STATE_OPEN_COMPLETE
:
428 rv
= DoOpenComplete(rv
);
433 case STATE_READ_COMPLETE
:
434 rv
= DoReadComplete(rv
);
440 } while (rv
!= ERR_IO_PENDING
&& next_state_
!= STATE_NONE
);
445 int DiskBasedCertCache::ReadWorker::DoOpen() {
446 next_state_
= STATE_OPEN_COMPLETE
;
447 return backend_
->OpenEntry(key_
, &entry_
, io_callback_
);
450 int DiskBasedCertCache::ReadWorker::DoOpenComplete(int rv
) {
452 RecordCacheResult(DISK_CACHE_ERROR
);
456 next_state_
= STATE_READ
;
460 int DiskBasedCertCache::ReadWorker::DoRead() {
461 next_state_
= STATE_READ_COMPLETE
;
462 io_buf_len_
= entry_
->GetDataSize(0 /* index */);
463 buffer_
= new IOBuffer(io_buf_len_
);
464 return entry_
->ReadData(
465 0 /* index */, 0 /* offset */, buffer_
.get(), io_buf_len_
, io_callback_
);
468 int DiskBasedCertCache::ReadWorker::DoReadComplete(int rv
) {
469 // The cache should return the entire buffer length. If it does not,
470 // it is probably indicative of an issue other than corruption.
471 if (rv
< io_buf_len_
) {
472 RecordCacheResult(DISK_CACHE_ERROR
);
475 cert_handle_
= X509Certificate::CreateOSCertHandleFromBytes(buffer_
->data(),
478 RecordCacheResult(DISK_CACHE_ENTRY_CORRUPT
);
482 RecordCacheResult(DISK_CACHE_HIT
);
486 void DiskBasedCertCache::ReadWorker::Finish(int rv
) {
487 cleanup_callback_
.Run(cert_handle_
);
488 cleanup_callback_
.Reset();
493 void DiskBasedCertCache::ReadWorker::RunCallbacks() {
494 for (std::vector
<GetCallback
>::const_iterator it
= user_callbacks_
.begin();
495 it
!= user_callbacks_
.end();
497 it
->Run(cert_handle_
);
499 user_callbacks_
.clear();
502 void DiskBasedCertCache::CertFree::operator()(
503 X509Certificate::OSCertHandle cert_handle
) {
504 X509Certificate::FreeOSCertHandle(cert_handle
);
507 DiskBasedCertCache::DiskBasedCertCache(disk_cache::Backend
* backend
)
509 mru_cert_cache_(kMemoryCacheMaxSize
),
511 mem_cache_misses_(0),
512 weak_factory_(this) {
516 DiskBasedCertCache::~DiskBasedCertCache() {
517 for (WriteWorkerMap::iterator it
= write_worker_map_
.begin();
518 it
!= write_worker_map_
.end();
520 it
->second
->Cancel();
522 for (ReadWorkerMap::iterator it
= read_worker_map_
.begin();
523 it
!= read_worker_map_
.end();
525 it
->second
->Cancel();
529 void DiskBasedCertCache::GetCertificate(const std::string
& key
,
530 const GetCallback
& cb
) {
531 DCHECK(!key
.empty());
533 // If the handle is already in the MRU cache, just return that (via callback).
534 // Note, this will also bring the cert_handle to the front of the recency
535 // list in the MRU cache.
536 MRUCertCache::iterator mru_it
= mru_cert_cache_
.Get(key
);
537 if (mru_it
!= mru_cert_cache_
.end()) {
538 RecordCacheResult(MEMORY_CACHE_HIT
);
540 cb
.Run(mru_it
->second
);
545 ReadWorkerMap::iterator it
= read_worker_map_
.find(key
);
547 if (it
== read_worker_map_
.end()) {
549 new ReadWorker(backend_
,
551 base::Bind(&DiskBasedCertCache::FinishedReadOperation
,
552 weak_factory_
.GetWeakPtr(),
554 read_worker_map_
[key
] = worker
;
555 worker
->AddCallback(cb
);
558 it
->second
->AddCallback(cb
);
562 void DiskBasedCertCache::SetCertificate(
563 const X509Certificate::OSCertHandle cert_handle
,
564 const SetCallback
& cb
) {
565 DCHECK(!cb
.is_null());
567 std::string key
= GetCacheKeyForCert(cert_handle
);
569 WriteWorkerMap::iterator it
= write_worker_map_
.find(key
);
571 if (it
== write_worker_map_
.end()) {
572 WriteWorker
* worker
=
573 new WriteWorker(backend_
,
576 base::Bind(&DiskBasedCertCache::FinishedWriteOperation
,
577 weak_factory_
.GetWeakPtr(),
580 write_worker_map_
[key
] = worker
;
581 worker
->AddCallback(cb
);
584 it
->second
->AddCallback(cb
);
588 void DiskBasedCertCache::FinishedReadOperation(
589 const std::string
& key
,
590 X509Certificate::OSCertHandle cert_handle
) {
592 mru_cert_cache_
.Put(key
, X509Certificate::DupOSCertHandle(cert_handle
));
593 read_worker_map_
.erase(key
);
596 void DiskBasedCertCache::FinishedWriteOperation(
597 const std::string
& key
,
598 X509Certificate::OSCertHandle cert_handle
) {
599 write_worker_map_
.erase(key
);
601 mru_cert_cache_
.Put(key
, X509Certificate::DupOSCertHandle(cert_handle
));