• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2016 The Chromium Authors
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/cert/caching_cert_verifier.h"
6 
7 #include <utility>
8 
9 #include "base/functional/bind.h"
10 #include "base/time/time.h"
11 #include "net/base/net_errors.h"
12 
13 namespace net {
14 
15 namespace {
16 
17 // The maximum number of cache entries to use for the ExpiringCache.
18 const unsigned kMaxCacheEntries = 256;
19 
20 // The number of seconds to cache entries.
21 const unsigned kTTLSecs = 1800;  // 30 minutes.
22 
23 }  // namespace
24 
CachingCertVerifier(std::unique_ptr<CertVerifier> verifier)25 CachingCertVerifier::CachingCertVerifier(std::unique_ptr<CertVerifier> verifier)
26     : verifier_(std::move(verifier)), cache_(kMaxCacheEntries) {
27   verifier_->AddObserver(this);
28   CertDatabase::GetInstance()->AddObserver(this);
29 }
30 
~CachingCertVerifier()31 CachingCertVerifier::~CachingCertVerifier() {
32   CertDatabase::GetInstance()->RemoveObserver(this);
33   verifier_->RemoveObserver(this);
34 }
35 
Verify(const CertVerifier::RequestParams & params,CertVerifyResult * verify_result,CompletionOnceCallback callback,std::unique_ptr<Request> * out_req,const NetLogWithSource & net_log)36 int CachingCertVerifier::Verify(const CertVerifier::RequestParams& params,
37                                 CertVerifyResult* verify_result,
38                                 CompletionOnceCallback callback,
39                                 std::unique_ptr<Request>* out_req,
40                                 const NetLogWithSource& net_log) {
41   out_req->reset();
42 
43   requests_++;
44 
45   const CertVerificationCache::value_type* cached_entry =
46       cache_.Get(params, CacheValidityPeriod(base::Time::Now()));
47   if (cached_entry) {
48     ++cache_hits_;
49     *verify_result = cached_entry->result;
50     return cached_entry->error;
51   }
52 
53   base::Time start_time = base::Time::Now();
54   CompletionOnceCallback caching_callback = base::BindOnce(
55       &CachingCertVerifier::OnRequestFinished, base::Unretained(this),
56       config_id_, params, start_time, std::move(callback), verify_result);
57   int result = verifier_->Verify(params, verify_result,
58                                  std::move(caching_callback), out_req, net_log);
59   if (result != ERR_IO_PENDING) {
60     // Synchronous completion; add directly to cache.
61     AddResultToCache(config_id_, params, start_time, *verify_result, result);
62   }
63 
64   return result;
65 }
66 
SetConfig(const CertVerifier::Config & config)67 void CachingCertVerifier::SetConfig(const CertVerifier::Config& config) {
68   verifier_->SetConfig(config);
69   config_id_++;
70   ClearCache();
71 }
72 
AddObserver(CertVerifier::Observer * observer)73 void CachingCertVerifier::AddObserver(CertVerifier::Observer* observer) {
74   verifier_->AddObserver(observer);
75 }
76 
RemoveObserver(CertVerifier::Observer * observer)77 void CachingCertVerifier::RemoveObserver(CertVerifier::Observer* observer) {
78   verifier_->RemoveObserver(observer);
79 }
80 
81 CachingCertVerifier::CachedResult::CachedResult() = default;
82 
83 CachingCertVerifier::CachedResult::~CachedResult() = default;
84 
CacheValidityPeriod(base::Time now)85 CachingCertVerifier::CacheValidityPeriod::CacheValidityPeriod(base::Time now)
86     : verification_time(now), expiration_time(now) {}
87 
CacheValidityPeriod(base::Time now,base::Time expiration)88 CachingCertVerifier::CacheValidityPeriod::CacheValidityPeriod(
89     base::Time now,
90     base::Time expiration)
91     : verification_time(now), expiration_time(expiration) {}
92 
operator ()(const CacheValidityPeriod & now,const CacheValidityPeriod & expiration) const93 bool CachingCertVerifier::CacheExpirationFunctor::operator()(
94     const CacheValidityPeriod& now,
95     const CacheValidityPeriod& expiration) const {
96   // Ensure this functor is being used for expiration only, and not strict
97   // weak ordering/sorting. |now| should only ever contain a single
98   // base::Time.
99   // Note: DCHECK_EQ is not used due to operator<< overloading requirements.
100   DCHECK(now.verification_time == now.expiration_time);
101 
102   // |now| contains only a single time (verification_time), while |expiration|
103   // contains the validity range - both when the certificate was verified and
104   // when the verification result should expire.
105   //
106   // If the user receives a "not yet valid" message, and adjusts their clock
107   // foward to the correct time, this will (typically) cause
108   // now.verification_time to advance past expiration.expiration_time, thus
109   // treating the cached result as an expired entry and re-verifying.
110   // If the user receives a "expired" message, and adjusts their clock
111   // backwards to the correct time, this will cause now.verification_time to
112   // be less than expiration_verification_time, thus treating the cached
113   // result as an expired entry and re-verifying.
114   // If the user receives either of those messages, and does not adjust their
115   // clock, then the result will be (typically) be cached until the expiration
116   // TTL.
117   //
118   // This algorithm is only problematic if the user consistently keeps
119   // adjusting their clock backwards in increments smaller than the expiration
120   // TTL, in which case, cached elements continue to be added. However,
121   // because the cache has a fixed upper bound, if no entries are expired, a
122   // 'random' entry will be, thus keeping the memory constraints bounded over
123   // time.
124   return now.verification_time >= expiration.verification_time &&
125          now.verification_time < expiration.expiration_time;
126 }
127 
OnRequestFinished(uint32_t config_id,const RequestParams & params,base::Time start_time,CompletionOnceCallback callback,CertVerifyResult * verify_result,int error)128 void CachingCertVerifier::OnRequestFinished(uint32_t config_id,
129                                             const RequestParams& params,
130                                             base::Time start_time,
131                                             CompletionOnceCallback callback,
132                                             CertVerifyResult* verify_result,
133                                             int error) {
134   AddResultToCache(config_id, params, start_time, *verify_result, error);
135 
136   // Now chain to the user's callback, which may delete |this|.
137   std::move(callback).Run(error);
138 }
139 
AddResultToCache(uint32_t config_id,const RequestParams & params,base::Time start_time,const CertVerifyResult & verify_result,int error)140 void CachingCertVerifier::AddResultToCache(
141     uint32_t config_id,
142     const RequestParams& params,
143     base::Time start_time,
144     const CertVerifyResult& verify_result,
145     int error) {
146   // If the configuration has changed since this verification was started,
147   // don't add it to the cache.
148   if (config_id != config_id_)
149     return;
150 
151   // When caching, this uses the time that validation started as the
152   // beginning of the validity, rather than the time that it ended (aka
153   // base::Time::Now()), to account for the fact that during validation,
154   // the clock may have changed.
155   //
156   // If the clock has changed significantly, then this result will ideally
157   // be evicted and the next time the certificate is encountered, it will
158   // be revalidated.
159   //
160   // Because of this, it's possible for situations to arise where the
161   // clock was correct at the start of validation, changed to an
162   // incorrect time during validation (such as too far in the past or
163   // future), and then was reset to the correct time. If this happens,
164   // it's likely that the result will not be a valid/correct result,
165   // but will still be used from the cache because the clock was reset
166   // to the correct time after the (bad) validation result completed.
167   //
168   // However, this solution optimizes for the case where the clock is
169   // bad at the start of validation, and subsequently is corrected. In
170   // that situation, the result is also incorrect, but because the clock
171   // was corrected after validation, if the cache validity period was
172   // computed at the end of validation, it would continue to serve an
173   // invalid result for kTTLSecs.
174   CachedResult cached_result;
175   cached_result.error = error;
176   cached_result.result = verify_result;
177   cache_.Put(
178       params, cached_result, CacheValidityPeriod(start_time),
179       CacheValidityPeriod(start_time, start_time + base::Seconds(kTTLSecs)));
180 }
181 
OnCertVerifierChanged()182 void CachingCertVerifier::OnCertVerifierChanged() {
183   config_id_++;
184   ClearCache();
185 }
186 
OnCertDBChanged()187 void CachingCertVerifier::OnCertDBChanged() {
188   config_id_++;
189   ClearCache();
190 }
191 
ClearCache()192 void CachingCertVerifier::ClearCache() {
193   cache_.Clear();
194 }
195 
GetCacheSize() const196 size_t CachingCertVerifier::GetCacheSize() const {
197   return cache_.size();
198 }
199 
200 }  // namespace net
201