Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(851)

Side by Side Diff: net/base/x509_certificate.cc

Issue 2944008: Refactor X509Certificate caching to cache the OS handle, rather than the X509Certificate (Closed)
Patch Set: Rebase to trunk after splitting out 4645001 Created 9 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. 1 // Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "net/base/x509_certificate.h" 5 #include "net/base/x509_certificate.h"
6 6
7 #include <map> 7 #include <map>
8 8
9 #include "base/lazy_instance.h" 9 #include "base/lazy_instance.h"
10 #include "base/logging.h" 10 #include "base/logging.h"
(...skipping 23 matching lines...) Expand all
34 const X509Certificate::Format kFormatDecodePriority[] = { 34 const X509Certificate::Format kFormatDecodePriority[] = {
35 X509Certificate::FORMAT_SINGLE_CERTIFICATE, 35 X509Certificate::FORMAT_SINGLE_CERTIFICATE,
36 X509Certificate::FORMAT_PKCS7 36 X509Certificate::FORMAT_PKCS7
37 }; 37 };
38 38
39 // The PEM block header used for DER certificates 39 // The PEM block header used for DER certificates
40 const char kCertificateHeader[] = "CERTIFICATE"; 40 const char kCertificateHeader[] = "CERTIFICATE";
41 // The PEM block header used for PKCS#7 data 41 // The PEM block header used for PKCS#7 data
42 const char kPKCS7Header[] = "PKCS7"; 42 const char kPKCS7Header[] = "PKCS7";
43 43
44 // A thread-safe cache for X509Certificate objects. 44 // A thread-safe cache for OS certificate handles.
45 // 45 //
46 // The cache does not hold a reference to the certificate objects. The objects 46 // Within each of the supported underlying crypto libraries, a certificate
47 // must |Remove| themselves from the cache upon destruction (or else the cache 47 // handle is represented as a ref-counted object that contains the parsed
48 // will be holding dead pointers to the objects). 48 // data for the certificate. In addition, the underlying OS handle may also
49 // TODO(rsleevi): There exists a chance of a use-after-free, due to a race 49 // contain a copy of the original ASN.1 DER used to constructed the handle.
50 // between Find() and Remove(). See http://crbug.com/49377 50 //
51 // In order to reduce the memory usage when multiple SSL connections exist,
52 // with each connection storing the server's identity certificate plus any
53 // intermediates supplied, the certificate handles are cached. Any two
54 // X509Certificates that were created from the same ASN.1 DER data,
55 // regardless of where that data came from, will share the same underlying
56 // OS certificate handle.
51 class X509CertificateCache { 57 class X509CertificateCache {
52 public: 58 public:
53 void Insert(X509Certificate* cert); 59 // Performs a compare-and-swap like operation. If an OS certificate handle
54 void Remove(X509Certificate* cert); 60 // for the same certificate data as |*cert_handle| already exists in the
55 X509Certificate* Find(const SHA1Fingerprint& fingerprint); 61 // cache, the original |*cert_handle| will be freed, and |cert_handle|
62 // will be updated to point to the existing cached certificate, after
63 // adding to the reference count. If an equivalent OS certificate handle
64 // is not found, |*cert_handle| will be added to the cache and its
65 // reference count increased, while otherwise remaining unmodified and
66 // pointing to the caller's original certificate.
67 void InsertOrUpdate(X509Certificate::OSCertHandle* cert_handle);
68
69 // Decrements the reference count for |cert_handle|, and if it is the last
70 // reference held in the cache, frees the underlying OS certificate
71 // handle. InsertOrUpdate() must have been called prior to Remove() for
72 // the |cert_handle|.
73 void Remove(X509Certificate::OSCertHandle cert_handle);
56 74
57 private: 75 private:
58 typedef std::map<SHA1Fingerprint, X509Certificate*, SHA1FingerprintLessThan> 76 // A single entry in the cache. Certificates will be keyed by their SHA1
59 CertMap; 77 // fingerprints, but will not be considered equivalent unless the entire
78 // certificate data matches.
79 struct Entry {
80 Entry() : cert_handle(NULL), ref_count(0) {}
81 X509Certificate::OSCertHandle cert_handle;
82
83 // Increased by each call to InsertOrUpdate(), and balanced by each call
84 // to Remove(). When it equals 0, all references have been released, so
85 // the cache entry will be removed and the OS certificate released.
86 size_t ref_count;
87 };
88 typedef std::map<SHA1Fingerprint, Entry, SHA1FingerprintLessThan> CertMap;
60 89
61 // Obtain an instance of X509CertificateCache via a LazyInstance. 90 // Obtain an instance of X509CertificateCache via a LazyInstance.
62 X509CertificateCache() {} 91 X509CertificateCache() {}
63 ~X509CertificateCache() {} 92 ~X509CertificateCache() {}
64 friend struct base::DefaultLazyInstanceTraits<X509CertificateCache>; 93 friend struct base::DefaultLazyInstanceTraits<X509CertificateCache>;
65 94
66 // You must acquire this lock before using any private data of this object. 95 // You must acquire this lock before using any private data of this
67 // You must not block while holding this lock. 96 // object. You must not block while holding this lock.
68 Lock lock_; 97 Lock lock_;
69 98
70 // The certificate cache. You must acquire |lock_| before using |cache_|. 99 // The certificate cache. You must acquire |lock_| before using |cache_|.
71 CertMap cache_; 100 CertMap cache_;
72 101
73 DISALLOW_COPY_AND_ASSIGN(X509CertificateCache); 102 DISALLOW_COPY_AND_ASSIGN(X509CertificateCache);
74 }; 103 };
75 104
76 base::LazyInstance<X509CertificateCache, 105 base::LazyInstance<X509CertificateCache,
77 base::LeakyLazyInstanceTraits<X509CertificateCache> > 106 base::LeakyLazyInstanceTraits<X509CertificateCache> >
78 g_x509_certificate_cache(base::LINKER_INITIALIZED); 107 g_x509_certificate_cache(base::LINKER_INITIALIZED);
79 108
80 // Insert |cert| into the cache. The cache does NOT AddRef |cert|. 109 void X509CertificateCache::InsertOrUpdate(
81 // Any existing certificate with the same fingerprint will be replaced. 110 X509Certificate::OSCertHandle* cert_handle) {
82 void X509CertificateCache::Insert(X509Certificate* cert) { 111 DCHECK(cert_handle);
112 SHA1Fingerprint fingerprint =
113 X509Certificate::CalculateFingerprint(*cert_handle);
114 DCHECK(!IsNullFingerprint(fingerprint)) <<
115 "Only insert certs with real fingerprints.";
116
117 AutoLock lock(lock_);
118 CertMap::iterator pos(cache_.find(fingerprint));
119 if (pos == cache_.end()) {
120 // Cached entry not found. Initialize a new entry. |*cert_handle| is
121 // guaranteed to have at least one OS reference (the caller's), which
122 // will be incremented, along with cache_entry.ref_count, below.
123 Entry cache_entry;
124 cache_entry.cert_handle = *cert_handle;
125 cache_entry.ref_count = 0;
126 CertMap::value_type cache_value(fingerprint, cache_entry);
127 pos = cache_.insert(cache_value).first;
128 } else {
129 bool is_same_cert =
130 X509Certificate::IsSameOSCert(*cert_handle, pos->second.cert_handle);
131 if (!is_same_cert) {
132 // Two certificates don't match, likely due to a SHA1 hash collision.
133 // Given the low probability, the simplest solution is to not cache
134 // the certificate, which should not affect performance too
135 // negatively.
136 return;
137 }
138 // Release the caller's reference to |*cert_handle|, as it will be
139 // replaced by the cached handle just found.
140 X509Certificate::FreeOSCertHandle(*cert_handle);
141 DHISTOGRAM_COUNTS("X509CertificateReuseCount", 1);
142 }
143
144 ++pos->second.ref_count;
145 *cert_handle = X509Certificate::DupOSCertHandle(pos->second.cert_handle);
146 }
147
148 void X509CertificateCache::Remove(X509Certificate::OSCertHandle cert_handle) {
149 SHA1Fingerprint fingerprint =
150 X509Certificate::CalculateFingerprint(cert_handle);
83 AutoLock lock(lock_); 151 AutoLock lock(lock_);
84 152
85 DCHECK(!IsNullFingerprint(cert->fingerprint())) << 153 CertMap::iterator pos(cache_.find(fingerprint));
86 "Only insert certs with real fingerprints."; 154 if (pos == cache_.end())
87 cache_[cert->fingerprint()] = cert; 155 return; // A cache collision where the winning cert was already freed.
88 };
89 156
90 // Remove |cert| from the cache. The cache does not assume that |cert| is 157 bool is_same_cert = X509Certificate::IsSameOSCert(cert_handle,
91 // already in the cache. 158 pos->second.cert_handle);
92 void X509CertificateCache::Remove(X509Certificate* cert) { 159 DCHECK(is_same_cert) << "Hash collision between different certificates";
93 AutoLock lock(lock_); 160 if (!is_same_cert)
161 return; // A cache collision where the winning cert is still around.
94 162
95 CertMap::iterator pos(cache_.find(cert->fingerprint())); 163 if (--pos->second.ref_count == 0) {
96 if (pos == cache_.end()) 164 // The last reference to |cert_handle| has been removed, so release the
97 return; // It is not an error to remove a cert that is not in the cache. 165 // underlying OS handle and remove from the cache. The caller still
98 cache_.erase(pos); 166 // holds a reference to the underlying OS handle and still bears
99 }; 167 // responsibility for freeing it.
100 168 X509Certificate::FreeOSCertHandle(pos->second.cert_handle);
101 // Find a certificate in the cache with the given fingerprint. If one does 169 cache_.erase(pos);
102 // not exist, this method returns NULL. 170 }
103 X509Certificate* X509CertificateCache::Find( 171 }
104 const SHA1Fingerprint& fingerprint) {
105 AutoLock lock(lock_);
106
107 CertMap::iterator pos(cache_.find(fingerprint));
108 if (pos == cache_.end())
109 return NULL;
110
111 return pos->second;
112 };
113 172
114 } // namespace 173 } // namespace
115 174
116 bool X509Certificate::LessThan::operator()(X509Certificate* lhs, 175 bool X509Certificate::LessThan::operator()(X509Certificate* lhs,
117 X509Certificate* rhs) const { 176 X509Certificate* rhs) const {
118 if (lhs == rhs) 177 if (lhs == rhs)
119 return false; 178 return false;
120 179
121 SHA1FingerprintLessThan fingerprint_functor; 180 SHA1FingerprintLessThan fingerprint_functor;
122 return fingerprint_functor(lhs->fingerprint_, rhs->fingerprint_); 181 return fingerprint_functor(lhs->fingerprint_, rhs->fingerprint_);
123 } 182 }
124 183
125 // static 184 // static
126 X509Certificate* X509Certificate::CreateFromHandle( 185 X509Certificate* X509Certificate::CreateFromHandle(
127 OSCertHandle cert_handle, 186 OSCertHandle cert_handle,
128 Source source,
129 const OSCertHandles& intermediates) { 187 const OSCertHandles& intermediates) {
130 DCHECK(cert_handle); 188 DCHECK(cert_handle);
131 DCHECK(source != SOURCE_UNUSED); 189 X509Certificate* cert = new X509Certificate(cert_handle, intermediates);
132
133 // Check if we already have this certificate in memory.
134 X509CertificateCache* cache = g_x509_certificate_cache.Pointer();
135 X509Certificate* cached_cert =
136 cache->Find(CalculateFingerprint(cert_handle));
137 if (cached_cert) {
138 DCHECK(cached_cert->source_ != SOURCE_UNUSED);
139 if (cached_cert->source_ > source ||
140 (cached_cert->source_ == source &&
141 cached_cert->HasIntermediateCertificates(intermediates))) {
142 // Return the certificate with the same fingerprint from our cache.
143 DHISTOGRAM_COUNTS("X509CertificateReuseCount", 1);
144 return cached_cert;
145 }
146 // Else the new cert is better and will replace the old one in the cache.
147 }
148
149 // Otherwise, allocate and cache a new object.
150 X509Certificate* cert = new X509Certificate(cert_handle, source,
151 intermediates);
152 cache->Insert(cert);
153 return cert; 190 return cert;
154 } 191 }
155 192
156 #if defined(OS_WIN) 193 #if defined(OS_WIN)
157 static X509Certificate::OSCertHandle CreateOSCert(base::StringPiece der_cert) { 194 static X509Certificate::OSCertHandle CreateOSCert(base::StringPiece der_cert) {
158 X509Certificate::OSCertHandle cert_handle = NULL; 195 X509Certificate::OSCertHandle cert_handle = NULL;
159 BOOL ok = CertAddEncodedCertificateToStore( 196 BOOL ok = CertAddEncodedCertificateToStore(
160 X509Certificate::cert_store(), X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, 197 X509Certificate::cert_store(), X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
161 reinterpret_cast<const BYTE*>(der_cert.data()), der_cert.size(), 198 reinterpret_cast<const BYTE*>(der_cert.data()), der_cert.size(),
162 CERT_STORE_ADD_USE_EXISTING, &cert_handle); 199 CERT_STORE_ADD_USE_EXISTING, &cert_handle);
(...skipping 14 matching lines...) Expand all
177 214
178 X509Certificate::OSCertHandles intermediate_ca_certs; 215 X509Certificate::OSCertHandles intermediate_ca_certs;
179 for (size_t i = 1; i < der_certs.size(); i++) { 216 for (size_t i = 1; i < der_certs.size(); i++) {
180 OSCertHandle handle = CreateOSCert(der_certs[i]); 217 OSCertHandle handle = CreateOSCert(der_certs[i]);
181 DCHECK(handle); 218 DCHECK(handle);
182 intermediate_ca_certs.push_back(handle); 219 intermediate_ca_certs.push_back(handle);
183 } 220 }
184 221
185 OSCertHandle handle = CreateOSCert(der_certs[0]); 222 OSCertHandle handle = CreateOSCert(der_certs[0]);
186 DCHECK(handle); 223 DCHECK(handle);
187 X509Certificate* cert = 224 X509Certificate* cert = CreateFromHandle(handle, intermediate_ca_certs);
188 CreateFromHandle(handle, SOURCE_FROM_NETWORK, intermediate_ca_certs);
189 FreeOSCertHandle(handle); 225 FreeOSCertHandle(handle);
190 for (size_t i = 0; i < intermediate_ca_certs.size(); i++) 226 for (size_t i = 0; i < intermediate_ca_certs.size(); i++)
191 FreeOSCertHandle(intermediate_ca_certs[i]); 227 FreeOSCertHandle(intermediate_ca_certs[i]);
192 228
193 return cert; 229 return cert;
194 } 230 }
195 231
196 // static 232 // static
197 X509Certificate* X509Certificate::CreateFromBytes(const char* data, 233 X509Certificate* X509Certificate::CreateFromBytes(const char* data,
198 int length) { 234 int length) {
199 OSCertHandle cert_handle = CreateOSCertHandleFromBytes(data, length); 235 OSCertHandle cert_handle = CreateOSCertHandleFromBytes(data, length);
200 if (!cert_handle) 236 if (!cert_handle)
201 return NULL; 237 return NULL;
202 238
203 X509Certificate* cert = CreateFromHandle(cert_handle, 239 X509Certificate* cert = CreateFromHandle(cert_handle, OSCertHandles());
204 SOURCE_LONE_CERT_IMPORT,
205 OSCertHandles());
206 FreeOSCertHandle(cert_handle); 240 FreeOSCertHandle(cert_handle);
207 return cert; 241 return cert;
208 } 242 }
209 243
210 // static 244 // static
211 X509Certificate* X509Certificate::CreateFromPickle(const Pickle& pickle, 245 X509Certificate* X509Certificate::CreateFromPickle(const Pickle& pickle,
212 void** pickle_iter, 246 void** pickle_iter,
213 PickleType type) { 247 PickleType type) {
214 OSCertHandle cert_handle = ReadCertHandleFromPickle(pickle, pickle_iter); 248 OSCertHandle cert_handle = ReadCertHandleFromPickle(pickle, pickle_iter);
215 OSCertHandles intermediates; 249 OSCertHandles intermediates;
(...skipping 24 matching lines...) Expand all
240 if (!ok) { 274 if (!ok) {
241 FreeOSCertHandle(cert_handle); 275 FreeOSCertHandle(cert_handle);
242 for (size_t i = 0; i < intermediates.size(); ++i) 276 for (size_t i = 0; i < intermediates.size(); ++i)
243 FreeOSCertHandle(intermediates[i]); 277 FreeOSCertHandle(intermediates[i]);
244 return NULL; 278 return NULL;
245 } 279 }
246 } 280 }
247 281
248 if (!cert_handle) 282 if (!cert_handle)
249 return NULL; 283 return NULL;
250 X509Certificate* cert = CreateFromHandle(cert_handle, SOURCE_FROM_CACHE, 284 X509Certificate* cert = CreateFromHandle(cert_handle, intermediates);
251 intermediates);
252 FreeOSCertHandle(cert_handle); 285 FreeOSCertHandle(cert_handle);
253 for (size_t i = 0; i < intermediates.size(); ++i) 286 for (size_t i = 0; i < intermediates.size(); ++i)
254 FreeOSCertHandle(intermediates[i]); 287 FreeOSCertHandle(intermediates[i]);
255 288
256 return cert; 289 return cert;
257 } 290 }
258 291
259 // static 292 // static
260 CertificateList X509Certificate::CreateCertificateListFromBytes( 293 CertificateList X509Certificate::CreateCertificateListFromBytes(
261 const char* data, int length, int format) { 294 const char* data, int length, int format) {
(...skipping 56 matching lines...) Expand 10 before | Expand all | Expand 10 after
318 kFormatDecodePriority[i]); 351 kFormatDecodePriority[i]);
319 } 352 }
320 353
321 CertificateList results; 354 CertificateList results;
322 // No certificates parsed. 355 // No certificates parsed.
323 if (certificates.empty()) 356 if (certificates.empty())
324 return results; 357 return results;
325 358
326 for (OSCertHandles::iterator it = certificates.begin(); 359 for (OSCertHandles::iterator it = certificates.begin();
327 it != certificates.end(); ++it) { 360 it != certificates.end(); ++it) {
328 X509Certificate* result = CreateFromHandle(*it, SOURCE_LONE_CERT_IMPORT, 361 X509Certificate* result = CreateFromHandle(*it, OSCertHandles());
329 OSCertHandles());
330 results.push_back(scoped_refptr<X509Certificate>(result)); 362 results.push_back(scoped_refptr<X509Certificate>(result));
331 FreeOSCertHandle(*it); 363 FreeOSCertHandle(*it);
332 } 364 }
333 365
334 return results; 366 return results;
335 } 367 }
336 368
337 X509Certificate::X509Certificate(OSCertHandle cert_handle, 369 X509Certificate::X509Certificate(OSCertHandle cert_handle,
338 Source source,
339 const OSCertHandles& intermediates) 370 const OSCertHandles& intermediates)
340 : cert_handle_(DupOSCertHandle(cert_handle)), 371 : cert_handle_(DupOSCertHandle(cert_handle)) {
341 source_(source) { 372 X509CertificateCache* cache = g_x509_certificate_cache.Pointer();
342 // Copy/retain the intermediate cert handles. 373 cache->InsertOrUpdate(&cert_handle_);
343 for (size_t i = 0; i < intermediates.size(); ++i) 374 for (size_t i = 0; i < intermediates.size(); ++i) {
344 intermediate_ca_certs_.push_back(DupOSCertHandle(intermediates[i])); 375 // Duplicate the incoming certificate, as the caller retains ownership
376 // of |intermediates|.
377 OSCertHandle intermediate = DupOSCertHandle(intermediates[i]);
378 // Update the cache, which will assume ownership of the duplicated
379 // handle and return a suitable equivalent, potentially from the cache.
380 cache->InsertOrUpdate(&intermediate);
381 intermediate_ca_certs_.push_back(intermediate);
382 }
345 // Platform-specific initialization. 383 // Platform-specific initialization.
346 Initialize(); 384 Initialize();
347 } 385 }
348 386
349 X509Certificate::X509Certificate(const std::string& subject, 387 X509Certificate::X509Certificate(const std::string& subject,
350 const std::string& issuer, 388 const std::string& issuer,
351 base::Time start_date, 389 base::Time start_date,
352 base::Time expiration_date) 390 base::Time expiration_date)
353 : subject_(subject), 391 : subject_(subject),
354 issuer_(issuer), 392 issuer_(issuer),
355 valid_start_(start_date), 393 valid_start_(start_date),
356 valid_expiry_(expiration_date), 394 valid_expiry_(expiration_date),
357 cert_handle_(NULL), 395 cert_handle_(NULL) {
358 source_(SOURCE_UNUSED) {
359 memset(fingerprint_.data, 0, sizeof(fingerprint_.data)); 396 memset(fingerprint_.data, 0, sizeof(fingerprint_.data));
360 } 397 }
361 398
362 X509Certificate::~X509Certificate() { 399 X509Certificate::~X509Certificate() {
363 // We might not be in the cache, but it is safe to remove ourselves anyway. 400 X509CertificateCache* cache = g_x509_certificate_cache.Pointer();
364 g_x509_certificate_cache.Get().Remove(this); 401 if (cert_handle_) {
365 if (cert_handle_) 402 cache->Remove(cert_handle_);
366 FreeOSCertHandle(cert_handle_); 403 FreeOSCertHandle(cert_handle_);
367 for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) 404 }
405 for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) {
406 cache->Remove(intermediate_ca_certs_[i]);
368 FreeOSCertHandle(intermediate_ca_certs_[i]); 407 FreeOSCertHandle(intermediate_ca_certs_[i]);
408 }
369 } 409 }
370 410
371 void X509Certificate::Persist(Pickle* pickle) { 411 void X509Certificate::Persist(Pickle* pickle) {
372 DCHECK(cert_handle_); 412 DCHECK(cert_handle_);
373 if (!WriteCertHandleToPickle(cert_handle_, pickle)) { 413 if (!WriteCertHandleToPickle(cert_handle_, pickle)) {
374 NOTREACHED(); 414 NOTREACHED();
375 return; 415 return;
376 } 416 }
377 417
378 if (!pickle->WriteSize(intermediate_ca_certs_.size())) { 418 if (!pickle->WriteSize(intermediate_ca_certs_.size())) {
(...skipping 27 matching lines...) Expand all
406 446
407 bool X509Certificate::HasIntermediateCertificates(const OSCertHandles& certs) { 447 bool X509Certificate::HasIntermediateCertificates(const OSCertHandles& certs) {
408 for (size_t i = 0; i < certs.size(); ++i) { 448 for (size_t i = 0; i < certs.size(); ++i) {
409 if (!HasIntermediateCertificate(certs[i])) 449 if (!HasIntermediateCertificate(certs[i]))
410 return false; 450 return false;
411 } 451 }
412 return true; 452 return true;
413 } 453 }
414 454
415 } // namespace net 455 } // namespace net
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698