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