Home | History | Annotate | Download | only in http
      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 
     19 namespace net {
     20 
     21 namespace {
     22 
     23 // TODO(brandonsalmon): change this number to improve performance.
     24 const size_t kMemoryCacheMaxSize = 30;
     25 
     26 // Used to obtain a unique cache key for a certificate in the form of
     27 // "cert:<hash>".
     28 std::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 
     37 enum 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 
     45 void 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.
     56 class 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_OPEN,
     85     STATE_OPEN_COMPLETE,
     86     STATE_CREATE,
     87     STATE_CREATE_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 DoOpen();
     97   int DoOpenComplete(int rv);
     98   int DoCreate();
     99   int DoCreateComplete(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 
    123 DiskBasedCertCache::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 
    140 DiskBasedCertCache::WriteWorker::~WriteWorker() {
    141   if (cert_handle_)
    142     X509Certificate::FreeOSCertHandle(cert_handle_);
    143   if (entry_)
    144     entry_->Close();
    145 }
    146 
    147 void DiskBasedCertCache::WriteWorker::Start() {
    148   DCHECK_EQ(STATE_NONE, next_state_);
    149 
    150   next_state_ = STATE_OPEN;
    151   int rv = DoLoop(OK);
    152 
    153   if (rv == ERR_IO_PENDING)
    154     return;
    155 
    156   Finish(rv);
    157 }
    158 
    159 void DiskBasedCertCache::WriteWorker::AddCallback(
    160     const SetCallback& user_callback) {
    161   user_callbacks_.push_back(user_callback);
    162 }
    163 
    164 void DiskBasedCertCache::WriteWorker::Cancel() {
    165   canceled_ = true;
    166 }
    167 
    168 void 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 
    182 int DiskBasedCertCache::WriteWorker::DoLoop(int rv) {
    183   do {
    184     State state = next_state_;
    185     next_state_ = STATE_NONE;
    186     switch (state) {
    187       case STATE_OPEN:
    188         rv = DoOpen();
    189         break;
    190       case STATE_OPEN_COMPLETE:
    191         rv = DoOpenComplete(rv);
    192         break;
    193       case STATE_CREATE:
    194         rv = DoCreate();
    195         break;
    196       case STATE_CREATE_COMPLETE:
    197         rv = DoCreateComplete(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 && next_state_ != STATE_NONE);
    210 
    211   return rv;
    212 }
    213 
    214 int DiskBasedCertCache::WriteWorker::DoOpen() {
    215   next_state_ = STATE_OPEN_COMPLETE;
    216   return backend_->OpenEntry(key_, &entry_, io_callback_);
    217 }
    218 
    219 int DiskBasedCertCache::WriteWorker::DoOpenComplete(int rv) {
    220   // The entry doesn't exist yet, so we should create it.
    221   if (rv < 0) {
    222     next_state_ = STATE_CREATE;
    223     return OK;
    224   }
    225 
    226   next_state_ = STATE_WRITE;
    227   return OK;
    228 }
    229 
    230 int DiskBasedCertCache::WriteWorker::DoCreate() {
    231   next_state_ = STATE_CREATE_COMPLETE;
    232   return backend_->CreateEntry(key_, &entry_, io_callback_);
    233 }
    234 
    235 int DiskBasedCertCache::WriteWorker::DoCreateComplete(int rv) {
    236   if (rv < 0)
    237     return rv;
    238 
    239   next_state_ = STATE_WRITE;
    240   return OK;
    241 }
    242 
    243 int DiskBasedCertCache::WriteWorker::DoWrite() {
    244   std::string write_data;
    245   bool encoded = X509Certificate::GetDEREncoded(cert_handle_, &write_data);
    246 
    247   if (!encoded)
    248     return ERR_FAILED;
    249 
    250   buffer_ = new IOBuffer(write_data.size());
    251   io_buf_len_ = write_data.size();
    252   memcpy(buffer_->data(), write_data.data(), io_buf_len_);
    253 
    254   next_state_ = STATE_WRITE_COMPLETE;
    255 
    256   return entry_->WriteData(0 /* index */,
    257                            0 /* offset */,
    258                            buffer_.get(),
    259                            write_data.size(),
    260                            io_callback_,
    261                            true /* truncate */);
    262 }
    263 
    264 int DiskBasedCertCache::WriteWorker::DoWriteComplete(int rv) {
    265   if (rv < io_buf_len_)
    266     return ERR_FAILED;
    267 
    268   return OK;
    269 }
    270 
    271 void DiskBasedCertCache::WriteWorker::Finish(int rv) {
    272   cleanup_callback_.Run();
    273   cleanup_callback_.Reset();
    274   RunCallbacks(rv);
    275   delete this;
    276 }
    277 
    278 void DiskBasedCertCache::WriteWorker::RunCallbacks(int rv) {
    279   std::string key;
    280   if (rv >= 0)
    281     key = key_;
    282 
    283   for (std::vector<SetCallback>::const_iterator it = user_callbacks_.begin();
    284        it != user_callbacks_.end();
    285        ++it) {
    286     it->Run(key);
    287   }
    288   user_callbacks_.clear();
    289 }
    290 
    291 // ReadWorkers represent pending GetCertificate jobs in the DiskBasedCertCache.
    292 // Each certificate requested to be retrieved from the cache is assigned a
    293 // ReadWorker. The same |key| should not have multiple ReadWorkers at the
    294 // same time; instead, call AddCallback to add a user callback to the
    295 // existing ReadWorker.
    296 class DiskBasedCertCache::ReadWorker {
    297  public:
    298   // |backend| is the backend to read |certificate| from, using
    299   // |key| as the key for the disk_cache::Entry.
    300   // |cleanup_callback| is called to clean up this ReadWorker,
    301   // regardless of success or failure.
    302   ReadWorker(disk_cache::Backend* backend,
    303              const std::string& key,
    304              const GetCallback& cleanup_callback);
    305 
    306   ~ReadWorker();
    307 
    308   // Reads the given certificate from the cache. On completion, will invoke all
    309   // user callbacks.
    310   void Start();
    311 
    312   // Adds a callback to the set of callbacks to be run when this
    313   // ReadWorker finishes processing.
    314   void AddCallback(const GetCallback& user_callback);
    315 
    316   // Signals the ReadWorker to abort early. The ReadWorker will be destroyed
    317   // upon the completion of any pending callbacks. User callbacks will be
    318   // invoked with a NULL cert handle.
    319   void Cancel();
    320 
    321  private:
    322   enum State {
    323     STATE_OPEN,
    324     STATE_OPEN_COMPLETE,
    325     STATE_READ,
    326     STATE_READ_COMPLETE,
    327     STATE_NONE
    328   };
    329 
    330   void OnIOComplete(int rv);
    331   int DoLoop(int rv);
    332   int DoOpen();
    333   int DoOpenComplete(int rv);
    334   int DoRead();
    335   int DoReadComplete(int rv);
    336   void Finish(int rv);
    337 
    338   // Invokes all of |user_callbacks_|
    339   void RunCallbacks();
    340 
    341   disk_cache::Backend* backend_;
    342   X509Certificate::OSCertHandle cert_handle_;
    343   std::string key_;
    344   bool canceled_;
    345 
    346   disk_cache::Entry* entry_;
    347 
    348   State next_state_;
    349   scoped_refptr<IOBuffer> buffer_;
    350   int io_buf_len_;
    351 
    352   GetCallback cleanup_callback_;
    353   std::vector<GetCallback> user_callbacks_;
    354   CompletionCallback io_callback_;
    355 };
    356 
    357 DiskBasedCertCache::ReadWorker::ReadWorker(disk_cache::Backend* backend,
    358                                            const std::string& key,
    359                                            const GetCallback& cleanup_callback)
    360     : backend_(backend),
    361       cert_handle_(NULL),
    362       key_(key),
    363       canceled_(false),
    364       entry_(NULL),
    365       next_state_(STATE_NONE),
    366       io_buf_len_(0),
    367       cleanup_callback_(cleanup_callback),
    368       io_callback_(
    369           base::Bind(&ReadWorker::OnIOComplete, base::Unretained(this))) {
    370 }
    371 
    372 DiskBasedCertCache::ReadWorker::~ReadWorker() {
    373   if (entry_)
    374     entry_->Close();
    375   if (cert_handle_)
    376     X509Certificate::FreeOSCertHandle(cert_handle_);
    377 }
    378 
    379 void DiskBasedCertCache::ReadWorker::Start() {
    380   DCHECK_EQ(STATE_NONE, next_state_);
    381   next_state_ = STATE_OPEN;
    382   int rv = DoLoop(OK);
    383 
    384   if (rv == ERR_IO_PENDING)
    385     return;
    386 
    387   Finish(rv);
    388 }
    389 
    390 void DiskBasedCertCache::ReadWorker::AddCallback(
    391     const GetCallback& user_callback) {
    392   user_callbacks_.push_back(user_callback);
    393 }
    394 
    395 void DiskBasedCertCache::ReadWorker::Cancel() {
    396   canceled_ = true;
    397 }
    398 
    399 void DiskBasedCertCache::ReadWorker::OnIOComplete(int rv) {
    400   if (canceled_) {
    401     Finish(ERR_FAILED);
    402     return;
    403   }
    404 
    405   rv = DoLoop(rv);
    406 
    407   if (rv == ERR_IO_PENDING)
    408     return;
    409 
    410   Finish(rv);
    411 }
    412 
    413 int DiskBasedCertCache::ReadWorker::DoLoop(int rv) {
    414   do {
    415     State state = next_state_;
    416     next_state_ = STATE_NONE;
    417     switch (state) {
    418       case STATE_OPEN:
    419         rv = DoOpen();
    420         break;
    421       case STATE_OPEN_COMPLETE:
    422         rv = DoOpenComplete(rv);
    423         break;
    424       case STATE_READ:
    425         rv = DoRead();
    426         break;
    427       case STATE_READ_COMPLETE:
    428         rv = DoReadComplete(rv);
    429         break;
    430       case STATE_NONE:
    431         NOTREACHED();
    432         break;
    433     }
    434   } while (rv != ERR_IO_PENDING && next_state_ != STATE_NONE);
    435 
    436   return rv;
    437 }
    438 
    439 int DiskBasedCertCache::ReadWorker::DoOpen() {
    440   next_state_ = STATE_OPEN_COMPLETE;
    441   return backend_->OpenEntry(key_, &entry_, io_callback_);
    442 }
    443 
    444 int DiskBasedCertCache::ReadWorker::DoOpenComplete(int rv) {
    445   if (rv < 0) {
    446     RecordCacheResult(DISK_CACHE_ERROR);
    447     return rv;
    448   }
    449 
    450   next_state_ = STATE_READ;
    451   return OK;
    452 }
    453 
    454 int DiskBasedCertCache::ReadWorker::DoRead() {
    455   next_state_ = STATE_READ_COMPLETE;
    456   io_buf_len_ = entry_->GetDataSize(0 /* index */);
    457   buffer_ = new IOBuffer(io_buf_len_);
    458   return entry_->ReadData(
    459       0 /* index */, 0 /* offset */, buffer_.get(), io_buf_len_, io_callback_);
    460 }
    461 
    462 int DiskBasedCertCache::ReadWorker::DoReadComplete(int rv) {
    463   // The cache should return the entire buffer length. If it does not,
    464   // it is probably indicative of an issue other than corruption.
    465   if (rv < io_buf_len_) {
    466     RecordCacheResult(DISK_CACHE_ERROR);
    467     return ERR_FAILED;
    468   }
    469   cert_handle_ = X509Certificate::CreateOSCertHandleFromBytes(buffer_->data(),
    470                                                               io_buf_len_);
    471   if (!cert_handle_) {
    472     RecordCacheResult(DISK_CACHE_ENTRY_CORRUPT);
    473     return ERR_FAILED;
    474   }
    475 
    476   RecordCacheResult(DISK_CACHE_HIT);
    477   return OK;
    478 }
    479 
    480 void DiskBasedCertCache::ReadWorker::Finish(int rv) {
    481   cleanup_callback_.Run(cert_handle_);
    482   cleanup_callback_.Reset();
    483   RunCallbacks();
    484   delete this;
    485 }
    486 
    487 void DiskBasedCertCache::ReadWorker::RunCallbacks() {
    488   for (std::vector<GetCallback>::const_iterator it = user_callbacks_.begin();
    489        it != user_callbacks_.end();
    490        ++it) {
    491     it->Run(cert_handle_);
    492   }
    493   user_callbacks_.clear();
    494 }
    495 
    496 void DiskBasedCertCache::CertFree::operator()(
    497     X509Certificate::OSCertHandle cert_handle) {
    498   X509Certificate::FreeOSCertHandle(cert_handle);
    499 }
    500 
    501 DiskBasedCertCache::DiskBasedCertCache(disk_cache::Backend* backend)
    502     : backend_(backend),
    503       mru_cert_cache_(kMemoryCacheMaxSize),
    504       mem_cache_hits_(0),
    505       mem_cache_misses_(0),
    506       weak_factory_(this) {
    507   DCHECK(backend_);
    508 }
    509 
    510 DiskBasedCertCache::~DiskBasedCertCache() {
    511   for (WriteWorkerMap::iterator it = write_worker_map_.begin();
    512        it != write_worker_map_.end();
    513        ++it) {
    514     it->second->Cancel();
    515   }
    516   for (ReadWorkerMap::iterator it = read_worker_map_.begin();
    517        it != read_worker_map_.end();
    518        ++it) {
    519     it->second->Cancel();
    520   }
    521 }
    522 
    523 void DiskBasedCertCache::GetCertificate(const std::string& key,
    524                                         const GetCallback& cb) {
    525   DCHECK(!key.empty());
    526 
    527   // If the handle is already in the MRU cache, just return that (via callback).
    528   // Note, this will also bring the cert_handle to the front of the recency
    529   // list in the MRU cache.
    530   MRUCertCache::iterator mru_it = mru_cert_cache_.Get(key);
    531   if (mru_it != mru_cert_cache_.end()) {
    532     RecordCacheResult(MEMORY_CACHE_HIT);
    533     ++mem_cache_hits_;
    534     cb.Run(mru_it->second);
    535     return;
    536   }
    537   ++mem_cache_misses_;
    538 
    539   ReadWorkerMap::iterator it = read_worker_map_.find(key);
    540 
    541   if (it == read_worker_map_.end()) {
    542     ReadWorker* worker =
    543         new ReadWorker(backend_,
    544                        key,
    545                        base::Bind(&DiskBasedCertCache::FinishedReadOperation,
    546                                   weak_factory_.GetWeakPtr(),
    547                                   key));
    548     read_worker_map_[key] = worker;
    549     worker->AddCallback(cb);
    550     worker->Start();
    551   } else {
    552     it->second->AddCallback(cb);
    553   }
    554 }
    555 
    556 void DiskBasedCertCache::SetCertificate(
    557     const X509Certificate::OSCertHandle cert_handle,
    558     const SetCallback& cb) {
    559   DCHECK(!cb.is_null());
    560   DCHECK(cert_handle);
    561   std::string key = GetCacheKeyForCert(cert_handle);
    562 
    563   WriteWorkerMap::iterator it = write_worker_map_.find(key);
    564 
    565   if (it == write_worker_map_.end()) {
    566     WriteWorker* worker =
    567         new WriteWorker(backend_,
    568                         key,
    569                         cert_handle,
    570                         base::Bind(&DiskBasedCertCache::FinishedWriteOperation,
    571                                    weak_factory_.GetWeakPtr(),
    572                                    key,
    573                                    cert_handle));
    574     write_worker_map_[key] = worker;
    575     worker->AddCallback(cb);
    576     worker->Start();
    577   } else {
    578     it->second->AddCallback(cb);
    579   }
    580 }
    581 
    582 void DiskBasedCertCache::FinishedReadOperation(
    583     const std::string& key,
    584     X509Certificate::OSCertHandle cert_handle) {
    585   if (cert_handle)
    586     mru_cert_cache_.Put(key, X509Certificate::DupOSCertHandle(cert_handle));
    587   read_worker_map_.erase(key);
    588 }
    589 
    590 void DiskBasedCertCache::FinishedWriteOperation(
    591     const std::string& key,
    592     X509Certificate::OSCertHandle cert_handle) {
    593   write_worker_map_.erase(key);
    594   if (!key.empty())
    595     mru_cert_cache_.Put(key, X509Certificate::DupOSCertHandle(cert_handle));
    596 }
    597 
    598 }  // namespace net
    599