Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / net / http / disk_based_cert_cache.cc
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