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 // Unretained is safe here as `verifier_` is owned by `this`. If `this` is
55 // deleted, `verifier_' will also be deleted and guarantees that any
56 // outstanding callbacks won't be called. (See CertVerifier::Verify comments.)
57 CompletionOnceCallback caching_callback = base::BindOnce(
58 &CachingCertVerifier::OnRequestFinished, base::Unretained(this),
59 config_id_, params, start_time, std::move(callback), verify_result);
60 int result = verifier_->Verify(params, verify_result,
61 std::move(caching_callback), out_req, net_log);
62 if (result != ERR_IO_PENDING) {
63 // Synchronous completion; add directly to cache.
64 AddResultToCache(config_id_, params, start_time, *verify_result, result);
65 }
66
67 return result;
68 }
69
SetConfig(const CertVerifier::Config & config)70 void CachingCertVerifier::SetConfig(const CertVerifier::Config& config) {
71 verifier_->SetConfig(config);
72 config_id_++;
73 ClearCache();
74 }
75
AddObserver(CertVerifier::Observer * observer)76 void CachingCertVerifier::AddObserver(CertVerifier::Observer* observer) {
77 verifier_->AddObserver(observer);
78 }
79
RemoveObserver(CertVerifier::Observer * observer)80 void CachingCertVerifier::RemoveObserver(CertVerifier::Observer* observer) {
81 verifier_->RemoveObserver(observer);
82 }
83
84 CachingCertVerifier::CachedResult::CachedResult() = default;
85
86 CachingCertVerifier::CachedResult::~CachedResult() = default;
87
CacheValidityPeriod(base::Time now)88 CachingCertVerifier::CacheValidityPeriod::CacheValidityPeriod(base::Time now)
89 : verification_time(now), expiration_time(now) {}
90
CacheValidityPeriod(base::Time now,base::Time expiration)91 CachingCertVerifier::CacheValidityPeriod::CacheValidityPeriod(
92 base::Time now,
93 base::Time expiration)
94 : verification_time(now), expiration_time(expiration) {}
95
operator ()(const CacheValidityPeriod & now,const CacheValidityPeriod & expiration) const96 bool CachingCertVerifier::CacheExpirationFunctor::operator()(
97 const CacheValidityPeriod& now,
98 const CacheValidityPeriod& expiration) const {
99 // Ensure this functor is being used for expiration only, and not strict
100 // weak ordering/sorting. |now| should only ever contain a single
101 // base::Time.
102 // Note: DCHECK_EQ is not used due to operator<< overloading requirements.
103 DCHECK(now.verification_time == now.expiration_time);
104
105 // |now| contains only a single time (verification_time), while |expiration|
106 // contains the validity range - both when the certificate was verified and
107 // when the verification result should expire.
108 //
109 // If the user receives a "not yet valid" message, and adjusts their clock
110 // foward to the correct time, this will (typically) cause
111 // now.verification_time to advance past expiration.expiration_time, thus
112 // treating the cached result as an expired entry and re-verifying.
113 // If the user receives a "expired" message, and adjusts their clock
114 // backwards to the correct time, this will cause now.verification_time to
115 // be less than expiration_verification_time, thus treating the cached
116 // result as an expired entry and re-verifying.
117 // If the user receives either of those messages, and does not adjust their
118 // clock, then the result will be (typically) be cached until the expiration
119 // TTL.
120 //
121 // This algorithm is only problematic if the user consistently keeps
122 // adjusting their clock backwards in increments smaller than the expiration
123 // TTL, in which case, cached elements continue to be added. However,
124 // because the cache has a fixed upper bound, if no entries are expired, a
125 // 'random' entry will be, thus keeping the memory constraints bounded over
126 // time.
127 return now.verification_time >= expiration.verification_time &&
128 now.verification_time < expiration.expiration_time;
129 }
130
OnRequestFinished(uint32_t config_id,const RequestParams & params,base::Time start_time,CompletionOnceCallback callback,CertVerifyResult * verify_result,int error)131 void CachingCertVerifier::OnRequestFinished(uint32_t config_id,
132 const RequestParams& params,
133 base::Time start_time,
134 CompletionOnceCallback callback,
135 CertVerifyResult* verify_result,
136 int error) {
137 AddResultToCache(config_id, params, start_time, *verify_result, error);
138
139 // Now chain to the user's callback, which may delete |this|.
140 std::move(callback).Run(error);
141 }
142
AddResultToCache(uint32_t config_id,const RequestParams & params,base::Time start_time,const CertVerifyResult & verify_result,int error)143 void CachingCertVerifier::AddResultToCache(
144 uint32_t config_id,
145 const RequestParams& params,
146 base::Time start_time,
147 const CertVerifyResult& verify_result,
148 int error) {
149 // If the configuration has changed since this verification was started,
150 // don't add it to the cache.
151 if (config_id != config_id_)
152 return;
153
154 // When caching, this uses the time that validation started as the
155 // beginning of the validity, rather than the time that it ended (aka
156 // base::Time::Now()), to account for the fact that during validation,
157 // the clock may have changed.
158 //
159 // If the clock has changed significantly, then this result will ideally
160 // be evicted and the next time the certificate is encountered, it will
161 // be revalidated.
162 //
163 // Because of this, it's possible for situations to arise where the
164 // clock was correct at the start of validation, changed to an
165 // incorrect time during validation (such as too far in the past or
166 // future), and then was reset to the correct time. If this happens,
167 // it's likely that the result will not be a valid/correct result,
168 // but will still be used from the cache because the clock was reset
169 // to the correct time after the (bad) validation result completed.
170 //
171 // However, this solution optimizes for the case where the clock is
172 // bad at the start of validation, and subsequently is corrected. In
173 // that situation, the result is also incorrect, but because the clock
174 // was corrected after validation, if the cache validity period was
175 // computed at the end of validation, it would continue to serve an
176 // invalid result for kTTLSecs.
177 CachedResult cached_result;
178 cached_result.error = error;
179 cached_result.result = verify_result;
180 cache_.Put(
181 params, cached_result, CacheValidityPeriod(start_time),
182 CacheValidityPeriod(start_time, start_time + base::Seconds(kTTLSecs)));
183 }
184
OnCertVerifierChanged()185 void CachingCertVerifier::OnCertVerifierChanged() {
186 config_id_++;
187 ClearCache();
188 }
189
OnTrustStoreChanged()190 void CachingCertVerifier::OnTrustStoreChanged() {
191 config_id_++;
192 ClearCache();
193 }
194
ClearCache()195 void CachingCertVerifier::ClearCache() {
196 cache_.Clear();
197 }
198
GetCacheSize() const199 size_t CachingCertVerifier::GetCacheSize() const {
200 return cache_.size();
201 }
202
203 } // namespace net
204