disk_based_cert_cache.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
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. 4 5#include "net/http/disk_based_cert_cache.h" 6 7#include <vector> 8 9#include "base/bind.h" 10#include "base/callback_helpers.h" 11#include "base/memory/ref_counted.h" 12#include "base/metrics/histogram.h" 13#include "base/stl_util.h" 14#include "base/strings/string_number_conversions.h" 15#include "net/base/io_buffer.h" 16#include "net/base/net_errors.h" 17#include "net/disk_cache/disk_cache.h" 18 19namespace net { 20 21namespace { 22 23// TODO(brandonsalmon): change this number to improve performance. 24const size_t kMemoryCacheMaxSize = 30; 25 26// Used to obtain a unique cache key for a certificate in the form of 27// "cert:<hash>". 28std::string GetCacheKeyForCert( 29 const X509Certificate::OSCertHandle cert_handle) { 30 SHA1HashValue fingerprint = 31 X509Certificate::CalculateFingerprint(cert_handle); 32 33 return "cert:" + 34 base::HexEncode(fingerprint.data, arraysize(fingerprint.data)); 35} 36 37enum CacheResult { 38 MEMORY_CACHE_HIT = 0, 39 DISK_CACHE_HIT, 40 DISK_CACHE_ENTRY_CORRUPT, 41 DISK_CACHE_ERROR, 42 CACHE_RESULT_MAX 43}; 44 45void RecordCacheResult(CacheResult result) { 46 UMA_HISTOGRAM_ENUMERATION( 47 "DiskBasedCertCache.CertIoCacheResult", result, CACHE_RESULT_MAX); 48} 49 50} // namespace 51 52// WriteWorkers represent pending SetCertificate jobs in the DiskBasedCertCache. 53// Each certificate requested to be stored is assigned a WriteWorker. 54// The same certificate should not have multiple WriteWorkers at the same 55// time; instead, add a user callback to the existing WriteWorker. 56class DiskBasedCertCache::WriteWorker { 57 public: 58 // |backend| is the backend to store |certificate| in, using 59 // |key| as the key for the disk_cache::Entry. 60 // |cleanup_callback| is called to clean up this ReadWorker, 61 // regardless of success or failure. 62 WriteWorker(disk_cache::Backend* backend, 63 const std::string& key, 64 X509Certificate::OSCertHandle cert_handle, 65 const base::Closure& cleanup_callback); 66 67 ~WriteWorker(); 68 69 // Writes the given certificate to the cache. On completion, will invoke all 70 // user callbacks. 71 void Start(); 72 73 // Adds a callback to the set of callbacks to be run when this 74 // WriteWorker finishes processing. 75 void AddCallback(const SetCallback& user_callback); 76 77 // Signals the WriteWorker to abort early. The WriteWorker will be destroyed 78 // upon the completion of any pending callbacks. User callbacks will be 79 // invoked with an empty string. 80 void Cancel(); 81 82 private: 83 enum State { 84 STATE_CREATE, 85 STATE_CREATE_COMPLETE, 86 STATE_OPEN, 87 STATE_OPEN_COMPLETE, 88 STATE_WRITE, 89 STATE_WRITE_COMPLETE, 90 STATE_NONE 91 }; 92 93 void OnIOComplete(int rv); 94 int DoLoop(int rv); 95 96 int DoCreate(); 97 int DoCreateComplete(int rv); 98 int DoOpen(); 99 int DoOpenComplete(int rv); 100 int DoWrite(); 101 int DoWriteComplete(int rv); 102 103 void Finish(int rv); 104 105 // Invokes all of the |user_callbacks_| 106 void RunCallbacks(int rv); 107 108 disk_cache::Backend* backend_; 109 const X509Certificate::OSCertHandle cert_handle_; 110 std::string key_; 111 bool canceled_; 112 113 disk_cache::Entry* entry_; 114 State next_state_; 115 scoped_refptr<IOBuffer> buffer_; 116 int io_buf_len_; 117 118 base::Closure cleanup_callback_; 119 std::vector<SetCallback> user_callbacks_; 120 CompletionCallback io_callback_; 121}; 122 123DiskBasedCertCache::WriteWorker::WriteWorker( 124 disk_cache::Backend* backend, 125 const std::string& key, 126 X509Certificate::OSCertHandle cert_handle, 127 const base::Closure& cleanup_callback) 128 : backend_(backend), 129 cert_handle_(X509Certificate::DupOSCertHandle(cert_handle)), 130 key_(key), 131 canceled_(false), 132 entry_(NULL), 133 next_state_(STATE_NONE), 134 io_buf_len_(0), 135 cleanup_callback_(cleanup_callback), 136 io_callback_( 137 base::Bind(&WriteWorker::OnIOComplete, base::Unretained(this))) { 138} 139 140DiskBasedCertCache::WriteWorker::~WriteWorker() { 141 if (cert_handle_) 142 X509Certificate::FreeOSCertHandle(cert_handle_); 143 if (entry_) 144 entry_->Close(); 145} 146 147void DiskBasedCertCache::WriteWorker::Start() { 148 DCHECK_EQ(STATE_NONE, next_state_); 149 next_state_ = STATE_CREATE; 150 int rv = DoLoop(OK); 151 152 if (rv == ERR_IO_PENDING) 153 return; 154 155 Finish(rv); 156} 157 158void DiskBasedCertCache::WriteWorker::AddCallback( 159 const SetCallback& user_callback) { 160 user_callbacks_.push_back(user_callback); 161} 162 163void DiskBasedCertCache::WriteWorker::Cancel() { 164 canceled_ = true; 165} 166 167void DiskBasedCertCache::WriteWorker::OnIOComplete(int rv) { 168 if (canceled_) { 169 Finish(ERR_FAILED); 170 return; 171 } 172 173 rv = DoLoop(rv); 174 175 if (rv == ERR_IO_PENDING) 176 return; 177 178 Finish(rv); 179} 180 181int DiskBasedCertCache::WriteWorker::DoLoop(int rv) { 182 do { 183 State state = next_state_; 184 next_state_ = STATE_NONE; 185 switch (state) { 186 case STATE_CREATE: 187 rv = DoCreate(); 188 break; 189 case STATE_CREATE_COMPLETE: 190 rv = DoCreateComplete(rv); 191 break; 192 case STATE_OPEN: 193 rv = DoOpen(); 194 break; 195 case STATE_OPEN_COMPLETE: 196 rv = DoOpenComplete(rv); 197 break; 198 case STATE_WRITE: 199 rv = DoWrite(); 200 break; 201 case STATE_WRITE_COMPLETE: 202 rv = DoWriteComplete(rv); 203 break; 204 case STATE_NONE: 205 NOTREACHED(); 206 break; 207 } 208 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); 209 210 return rv; 211} 212 213int DiskBasedCertCache::WriteWorker::DoCreate() { 214 next_state_ = STATE_CREATE_COMPLETE; 215 216 return backend_->CreateEntry(key_, &entry_, io_callback_); 217} 218 219int DiskBasedCertCache::WriteWorker::DoCreateComplete(int rv) { 220 // An error here usually signifies that the entry already exists. 221 // If this occurs, it is necessary to instead open the previously 222 // existing entry. 223 if (rv < 0) { 224 next_state_ = STATE_OPEN; 225 return OK; 226 } 227 228 next_state_ = STATE_WRITE; 229 return OK; 230} 231 232int DiskBasedCertCache::WriteWorker::DoOpen() { 233 next_state_ = STATE_OPEN_COMPLETE; 234 return backend_->OpenEntry(key_, &entry_, io_callback_); 235} 236 237int DiskBasedCertCache::WriteWorker::DoOpenComplete(int rv) { 238 if (rv < 0) 239 return rv; 240 241 next_state_ = STATE_WRITE; 242 return OK; 243} 244 245int DiskBasedCertCache::WriteWorker::DoWrite() { 246 std::string write_data; 247 bool encoded = X509Certificate::GetDEREncoded(cert_handle_, &write_data); 248 249 if (!encoded) 250 return ERR_FAILED; 251 252 buffer_ = new IOBuffer(write_data.size()); 253 io_buf_len_ = write_data.size(); 254 memcpy(buffer_->data(), write_data.data(), io_buf_len_); 255 256 next_state_ = STATE_WRITE_COMPLETE; 257 258 return entry_->WriteData(0 /* index */, 259 0 /* offset */, 260 buffer_, 261 write_data.size(), 262 io_callback_, 263 true /* truncate */); 264} 265 266int DiskBasedCertCache::WriteWorker::DoWriteComplete(int rv) { 267 if (rv < io_buf_len_) 268 return ERR_FAILED; 269 270 return OK; 271} 272 273void DiskBasedCertCache::WriteWorker::Finish(int rv) { 274 cleanup_callback_.Run(); 275 cleanup_callback_.Reset(); 276 RunCallbacks(rv); 277 delete this; 278} 279 280void DiskBasedCertCache::WriteWorker::RunCallbacks(int rv) { 281 std::string key; 282 if (rv >= 0) 283 key = key_; 284 285 for (std::vector<SetCallback>::const_iterator it = user_callbacks_.begin(); 286 it != user_callbacks_.end(); 287 ++it) { 288 it->Run(key); 289 } 290 user_callbacks_.clear(); 291} 292 293// ReadWorkers represent pending GetCertificate jobs in the DiskBasedCertCache. 294// Each certificate requested to be retrieved from the cache is assigned a 295// ReadWorker. The same |key| should not have multiple ReadWorkers at the 296// same time; instead, call AddCallback to add a user callback to the 297// existing ReadWorker. 298class DiskBasedCertCache::ReadWorker { 299 public: 300 // |backend| is the backend to read |certificate| from, using 301 // |key| as the key for the disk_cache::Entry. 302 // |cleanup_callback| is called to clean up this ReadWorker, 303 // regardless of success or failure. 304 ReadWorker(disk_cache::Backend* backend, 305 const std::string& key, 306 const GetCallback& cleanup_callback); 307 308 ~ReadWorker(); 309 310 // Reads the given certificate from the cache. On completion, will invoke all 311 // user callbacks. 312 void Start(); 313 314 // Adds a callback to the set of callbacks to be run when this 315 // ReadWorker finishes processing. 316 void AddCallback(const GetCallback& user_callback); 317 318 // Signals the ReadWorker to abort early. The ReadWorker will be destroyed 319 // upon the completion of any pending callbacks. User callbacks will be 320 // invoked with a NULL cert handle. 321 void Cancel(); 322 323 private: 324 enum State { 325 STATE_OPEN, 326 STATE_OPEN_COMPLETE, 327 STATE_READ, 328 STATE_READ_COMPLETE, 329 STATE_NONE 330 }; 331 332 void OnIOComplete(int rv); 333 int DoLoop(int rv); 334 int DoOpen(); 335 int DoOpenComplete(int rv); 336 int DoRead(); 337 int DoReadComplete(int rv); 338 void Finish(int rv); 339 340 // Invokes all of |user_callbacks_| 341 void RunCallbacks(); 342 343 disk_cache::Backend* backend_; 344 X509Certificate::OSCertHandle cert_handle_; 345 std::string key_; 346 bool canceled_; 347 348 disk_cache::Entry* entry_; 349 350 State next_state_; 351 scoped_refptr<IOBuffer> buffer_; 352 int io_buf_len_; 353 354 GetCallback cleanup_callback_; 355 std::vector<GetCallback> user_callbacks_; 356 CompletionCallback io_callback_; 357}; 358 359DiskBasedCertCache::ReadWorker::ReadWorker(disk_cache::Backend* backend, 360 const std::string& key, 361 const GetCallback& cleanup_callback) 362 : backend_(backend), 363 cert_handle_(NULL), 364 key_(key), 365 canceled_(false), 366 entry_(NULL), 367 next_state_(STATE_NONE), 368 io_buf_len_(0), 369 cleanup_callback_(cleanup_callback), 370 io_callback_( 371 base::Bind(&ReadWorker::OnIOComplete, base::Unretained(this))) { 372} 373 374DiskBasedCertCache::ReadWorker::~ReadWorker() { 375 if (entry_) 376 entry_->Close(); 377 if (cert_handle_) 378 X509Certificate::FreeOSCertHandle(cert_handle_); 379} 380 381void DiskBasedCertCache::ReadWorker::Start() { 382 DCHECK_EQ(STATE_NONE, next_state_); 383 next_state_ = STATE_OPEN; 384 int rv = DoLoop(OK); 385 386 if (rv == ERR_IO_PENDING) 387 return; 388 389 Finish(rv); 390} 391 392void DiskBasedCertCache::ReadWorker::AddCallback( 393 const GetCallback& user_callback) { 394 user_callbacks_.push_back(user_callback); 395} 396 397void DiskBasedCertCache::ReadWorker::Cancel() { 398 canceled_ = true; 399} 400 401void DiskBasedCertCache::ReadWorker::OnIOComplete(int rv) { 402 if (canceled_) { 403 Finish(ERR_FAILED); 404 return; 405 } 406 407 rv = DoLoop(rv); 408 409 if (rv == ERR_IO_PENDING) 410 return; 411 412 Finish(rv); 413} 414 415int DiskBasedCertCache::ReadWorker::DoLoop(int rv) { 416 do { 417 State state = next_state_; 418 next_state_ = STATE_NONE; 419 switch (state) { 420 case STATE_OPEN: 421 rv = DoOpen(); 422 break; 423 case STATE_OPEN_COMPLETE: 424 rv = DoOpenComplete(rv); 425 break; 426 case STATE_READ: 427 rv = DoRead(); 428 break; 429 case STATE_READ_COMPLETE: 430 rv = DoReadComplete(rv); 431 break; 432 case STATE_NONE: 433 NOTREACHED(); 434 break; 435 } 436 } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE); 437 438 return rv; 439} 440 441int DiskBasedCertCache::ReadWorker::DoOpen() { 442 next_state_ = STATE_OPEN_COMPLETE; 443 return backend_->OpenEntry(key_, &entry_, io_callback_); 444} 445 446int DiskBasedCertCache::ReadWorker::DoOpenComplete(int rv) { 447 if (rv < 0) { 448 RecordCacheResult(DISK_CACHE_ERROR); 449 return rv; 450 } 451 452 next_state_ = STATE_READ; 453 return OK; 454} 455 456int DiskBasedCertCache::ReadWorker::DoRead() { 457 next_state_ = STATE_READ_COMPLETE; 458 io_buf_len_ = entry_->GetDataSize(0 /* index */); 459 buffer_ = new IOBuffer(io_buf_len_); 460 return entry_->ReadData( 461 0 /* index */, 0 /* offset */, buffer_, io_buf_len_, io_callback_); 462} 463 464int DiskBasedCertCache::ReadWorker::DoReadComplete(int rv) { 465 // The cache should return the entire buffer length. If it does not, 466 // it is probably indicative of an issue other than corruption. 467 if (rv < io_buf_len_) { 468 RecordCacheResult(DISK_CACHE_ERROR); 469 return ERR_FAILED; 470 } 471 cert_handle_ = X509Certificate::CreateOSCertHandleFromBytes(buffer_->data(), 472 io_buf_len_); 473 if (!cert_handle_) { 474 RecordCacheResult(DISK_CACHE_ENTRY_CORRUPT); 475 return ERR_FAILED; 476 } 477 478 RecordCacheResult(DISK_CACHE_HIT); 479 return OK; 480} 481 482void DiskBasedCertCache::ReadWorker::Finish(int rv) { 483 cleanup_callback_.Run(cert_handle_); 484 cleanup_callback_.Reset(); 485 RunCallbacks(); 486 delete this; 487} 488 489void DiskBasedCertCache::ReadWorker::RunCallbacks() { 490 for (std::vector<GetCallback>::const_iterator it = user_callbacks_.begin(); 491 it != user_callbacks_.end(); 492 ++it) { 493 it->Run(cert_handle_); 494 } 495 user_callbacks_.clear(); 496} 497 498void DiskBasedCertCache::CertFree::operator()( 499 X509Certificate::OSCertHandle cert_handle) { 500 X509Certificate::FreeOSCertHandle(cert_handle); 501} 502 503DiskBasedCertCache::DiskBasedCertCache(disk_cache::Backend* backend) 504 : backend_(backend), 505 mru_cert_cache_(kMemoryCacheMaxSize), 506 mem_cache_hits_(0), 507 mem_cache_misses_(0), 508 weak_factory_(this) { 509 DCHECK(backend_); 510} 511 512DiskBasedCertCache::~DiskBasedCertCache() { 513 for (WriteWorkerMap::iterator it = write_worker_map_.begin(); 514 it != write_worker_map_.end(); 515 ++it) { 516 it->second->Cancel(); 517 } 518 for (ReadWorkerMap::iterator it = read_worker_map_.begin(); 519 it != read_worker_map_.end(); 520 ++it) { 521 it->second->Cancel(); 522 } 523} 524 525void DiskBasedCertCache::GetCertificate(const std::string& key, 526 const GetCallback& cb) { 527 DCHECK(!key.empty()); 528 529 // If the handle is already in the MRU cache, just return that (via callback). 530 // Note, this will also bring the cert_handle to the front of the recency 531 // list in the MRU cache. 532 MRUCertCache::iterator mru_it = mru_cert_cache_.Get(key); 533 if (mru_it != mru_cert_cache_.end()) { 534 RecordCacheResult(MEMORY_CACHE_HIT); 535 ++mem_cache_hits_; 536 cb.Run(mru_it->second); 537 return; 538 } 539 ++mem_cache_misses_; 540 541 ReadWorkerMap::iterator it = read_worker_map_.find(key); 542 543 if (it == read_worker_map_.end()) { 544 ReadWorker* worker = 545 new ReadWorker(backend_, 546 key, 547 base::Bind(&DiskBasedCertCache::FinishedReadOperation, 548 weak_factory_.GetWeakPtr(), 549 key)); 550 read_worker_map_[key] = worker; 551 worker->AddCallback(cb); 552 worker->Start(); 553 } else { 554 it->second->AddCallback(cb); 555 } 556} 557 558void DiskBasedCertCache::SetCertificate( 559 const X509Certificate::OSCertHandle cert_handle, 560 const SetCallback& cb) { 561 DCHECK(!cb.is_null()); 562 DCHECK(cert_handle); 563 std::string key = GetCacheKeyForCert(cert_handle); 564 565 WriteWorkerMap::iterator it = write_worker_map_.find(key); 566 567 if (it == write_worker_map_.end()) { 568 WriteWorker* worker = 569 new WriteWorker(backend_, 570 key, 571 cert_handle, 572 base::Bind(&DiskBasedCertCache::FinishedWriteOperation, 573 weak_factory_.GetWeakPtr(), 574 key, 575 cert_handle)); 576 write_worker_map_[key] = worker; 577 worker->AddCallback(cb); 578 worker->Start(); 579 } else { 580 it->second->AddCallback(cb); 581 } 582} 583 584void DiskBasedCertCache::FinishedReadOperation( 585 const std::string& key, 586 X509Certificate::OSCertHandle cert_handle) { 587 if (cert_handle) 588 mru_cert_cache_.Put(key, X509Certificate::DupOSCertHandle(cert_handle)); 589 read_worker_map_.erase(key); 590} 591 592void DiskBasedCertCache::FinishedWriteOperation( 593 const std::string& key, 594 X509Certificate::OSCertHandle cert_handle) { 595 write_worker_map_.erase(key); 596 if (!key.empty()) 597 mru_cert_cache_.Put(key, X509Certificate::DupOSCertHandle(cert_handle)); 598} 599 600} // namespace net 601