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); |
| 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 |