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