diff options
Diffstat (limited to 'net/base')
-rw-r--r-- | net/base/x509_certificate.cc | 92 | ||||
-rw-r--r-- | net/base/x509_certificate.h | 38 | ||||
-rw-r--r-- | net/base/x509_certificate_mac.cc | 14 | ||||
-rw-r--r-- | net/base/x509_certificate_nss.cc | 9 | ||||
-rw-r--r-- | net/base/x509_certificate_unittest.cc | 70 | ||||
-rw-r--r-- | net/base/x509_certificate_win.cc | 13 |
6 files changed, 194 insertions, 42 deletions
diff --git a/net/base/x509_certificate.cc b/net/base/x509_certificate.cc index 24388d8..700b254 100644 --- a/net/base/x509_certificate.cc +++ b/net/base/x509_certificate.cc @@ -4,6 +4,10 @@ #include "net/base/x509_certificate.h" +#if defined(USE_NSS) +#include <cert.h> +#endif + #include "base/histogram.h" #include "base/logging.h" #include "base/time.h" @@ -24,6 +28,33 @@ bool IsNullFingerprint(const X509Certificate::Fingerprint& fingerprint) { } // namespace +// static +bool X509Certificate::IsSameOSCert(X509Certificate::OSCertHandle a, + X509Certificate::OSCertHandle b) { + DCHECK(a && b); + if (a == b) + return true; +#if defined(OS_WIN) + return a->cbCertEncoded == b->cbCertEncoded && + memcmp(a->pbCertEncoded, b->pbCertEncoded, a->cbCertEncoded) == 0; +#elif defined(OS_MACOSX) + if (CFEqual(a, b)) + return true; + CSSM_DATA a_data, b_data; + return SecCertificateGetData(a, &a_data) == noErr && + SecCertificateGetData(b, &b_data) == noErr && + a_data.Length == b_data.Length && + memcmp(a_data.Data, b_data.Data, a_data.Length) == 0; +#elif defined(USE_NSS) + return a->derCert.len == b->derCert.len && + memcmp(a->derCert.data, b->derCert.data, a->derCert.len) == 0; +#else + // TODO(snej): not implemented + UNREACHED(); + return false; +#endif +} + bool X509Certificate::FingerprintLessThan::operator()( const Fingerprint& lhs, const Fingerprint& rhs) const { @@ -57,14 +88,13 @@ X509Certificate::Cache* X509Certificate::Cache::GetInstance() { return Singleton<X509Certificate::Cache>::get(); } -// Insert |cert| into the cache. The cache does NOT AddRef |cert|. The cache -// must not already contain a certificate with the same fingerprint. +// Insert |cert| into the cache. The cache does NOT AddRef |cert|. +// Any existing certificate with the same fingerprint will be replaced. void X509Certificate::Cache::Insert(X509Certificate* cert) { AutoLock lock(lock_); DCHECK(!IsNullFingerprint(cert->fingerprint())) << "Only insert certs with real fingerprints."; - DCHECK(cache_.find(cert->fingerprint()) == cache_.end()); cache_[cert->fingerprint()] = cert; }; @@ -133,8 +163,10 @@ bool X509Certificate::Policy::HasDeniedCert() const { } // static -X509Certificate* X509Certificate::CreateFromHandle(OSCertHandle cert_handle, - Source source) { +X509Certificate* X509Certificate::CreateFromHandle( + OSCertHandle cert_handle, + Source source, + const OSCertHandles& intermediates) { DCHECK(cert_handle); DCHECK(source != SOURCE_UNUSED); @@ -144,18 +176,20 @@ X509Certificate* X509Certificate::CreateFromHandle(OSCertHandle cert_handle, cache->Find(CalculateFingerprint(cert_handle)); if (cached_cert) { DCHECK(cached_cert->source_ != SOURCE_UNUSED); - if (cached_cert->source_ >= source) { - // We've found a certificate with the same fingerprint in our cache. We - // own the |cert_handle|, which makes it our job to free it. + if (cached_cert->source_ > source || + (cached_cert->source_ == source && + cached_cert->HasIntermediateCertificates(intermediates))) { + // Return the certificate with the same fingerprint from our cache. + // But we own the input OSCertHandle, which makes it our job to free it. FreeOSCertHandle(cert_handle); DHISTOGRAM_COUNTS("X509CertificateReuseCount", 1); return cached_cert; } - // Kick out the old certificate from our cache. The new one is better. - cache->Remove(cached_cert); + // Else the new cert is better and will replace the old one in the cache. } + // Otherwise, allocate a new object. - return new X509Certificate(cert_handle, source); + return new X509Certificate(cert_handle, source, intermediates); } // static @@ -165,13 +199,25 @@ X509Certificate* X509Certificate::CreateFromBytes(const char* data, if (!cert_handle) return NULL; - return CreateFromHandle(cert_handle, SOURCE_LONE_CERT_IMPORT); + return CreateFromHandle(cert_handle, + SOURCE_LONE_CERT_IMPORT, + OSCertHandles()); } -X509Certificate::X509Certificate(OSCertHandle cert_handle, Source source) +X509Certificate::X509Certificate(OSCertHandle cert_handle, + Source source, + const OSCertHandles& intermediates) : cert_handle_(cert_handle), source_(source) { +#if defined(OS_MACOSX) || defined(OS_WIN) + // Copy/retain the intermediate cert handles. + for (size_t i = 0; i < intermediates.size(); ++i) + intermediate_ca_certs_.push_back(DupOSCertHandle(intermediates[i])); +#endif + // Platform-specific initialization. Initialize(); + // Store the certificate in the cache in case we need it later. + X509Certificate::Cache::GetInstance()->Insert(this); } X509Certificate::X509Certificate(const std::string& subject, @@ -202,4 +248,24 @@ bool X509Certificate::HasExpired() const { return base::Time::Now() > valid_expiry(); } +bool X509Certificate::HasIntermediateCertificate(OSCertHandle cert) { +#if defined(OS_MACOSX) || defined(OS_WIN) + for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) { + if (IsSameOSCert(cert, intermediate_ca_certs_[i])) + return true; + } + return false; +#else + return true; +#endif +} + +bool X509Certificate::HasIntermediateCertificates(const OSCertHandles& certs) { + for (size_t i = 0; i < certs.size(); ++i) { + if (!HasIntermediateCertificate(certs[i])) + return false; + } + return true; +} + } // namespace net diff --git a/net/base/x509_certificate.h b/net/base/x509_certificate.h index f3f3fea..ec287ce 100644 --- a/net/base/x509_certificate.h +++ b/net/base/x509_certificate.h @@ -72,6 +72,8 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { typedef void* OSCertHandle; #endif + typedef std::vector<OSCertHandle> OSCertHandles; + // Principal represent an X.509 principal. struct Principal { Principal() { } @@ -152,10 +154,11 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { // prefers the handle from the network because our HTTP cache isn't // caching the corresponding intermediate CA certificates yet // (http://crbug.com/7065). - // + // The list of intermediate certificates is ignored under NSS (i.e. Linux.) // The returned pointer must be stored in a scoped_refptr<X509Certificate>. static X509Certificate* CreateFromHandle(OSCertHandle cert_handle, - Source source); + Source source, + const OSCertHandles& intermediates); // Create an X509Certificate from the BER-encoded representation. // Returns NULL on failure. @@ -210,20 +213,20 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { bool HasExpired() const; #if defined(OS_MACOSX) || defined(OS_WIN) - // Adds an untrusted intermediate certificate that may be needed for - // chain building. - void AddIntermediateCertificate(OSCertHandle cert) { - intermediate_ca_certs_.push_back(cert); - } - // Returns intermediate certificates added via AddIntermediateCertificate(). // Ownership follows the "get" rule: it is the caller's responsibility to // retain the elements of the result. - const std::vector<OSCertHandle>& GetIntermediateCertificates() const { + const OSCertHandles& GetIntermediateCertificates() const { return intermediate_ca_certs_; } #endif + // Returns true if I already contain the given intermediate cert. + bool HasIntermediateCertificate(OSCertHandle cert); + + // Returns true if I already contain all the given intermediate certs. + bool HasIntermediateCertificates(const OSCertHandles& certs); + #if defined(OS_MACOSX) // Does this certificate's usage allow SSL client authentication? bool SupportsSSLClientAuth() const; @@ -262,9 +265,14 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { OSCertHandle os_cert_handle() const { return cert_handle_; } + // Returns true if two OSCertHandles refer to identical certificates. + static bool IsSameOSCert(OSCertHandle a, OSCertHandle b); + + private: friend class base::RefCountedThreadSafe<X509Certificate>; FRIEND_TEST(X509CertificateTest, Cache); + FRIEND_TEST(X509CertificateTest, IntermediateCertificates); // A cache of X509Certificate objects. class Cache { @@ -294,7 +302,8 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { // Construct an X509Certificate from a handle to the certificate object // in the underlying crypto library. - X509Certificate(OSCertHandle cert_handle, Source source); + X509Certificate(OSCertHandle cert_handle, Source source, + const OSCertHandles& intermediates); ~X509Certificate(); @@ -308,7 +317,10 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { static OSCertHandle CreateOSCertHandleFromBytes(const char* data, int length); - // Frees an OS certificate handle. + // Duplicates (or adds a reference to) an OS certificate handle. + static OSCertHandle DupOSCertHandle(OSCertHandle cert_handle); + + // Frees (or releases a reference to) an OS certificate handle. static void FreeOSCertHandle(OSCertHandle cert_handle); // Calculates the SHA-1 fingerprint of the certificate. Returns an empty @@ -335,8 +347,8 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { #if defined(OS_MACOSX) || defined(OS_WIN) // Untrusted intermediate certificates associated with this certificate - // that may be needed for chain building. - std::vector<OSCertHandle> intermediate_ca_certs_; + // that may be needed for chain building. (NSS impl does not need these.) + OSCertHandles intermediate_ca_certs_; #endif // Where the certificate comes from. diff --git a/net/base/x509_certificate_mac.cc b/net/base/x509_certificate_mac.cc index 36fc65a..fa1c17e0 100644 --- a/net/base/x509_certificate_mac.cc +++ b/net/base/x509_certificate_mac.cc @@ -405,9 +405,6 @@ void X509Certificate::Initialize() { &valid_expiry_); fingerprint_ = CalculateFingerprint(cert_handle_); - - // Store the certificate in the cache in case we need it later. - X509Certificate::Cache::GetInstance()->Insert(this); } // static @@ -689,6 +686,14 @@ X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( } // static +X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle( + OSCertHandle handle) { + if (!handle) + return NULL; + return reinterpret_cast<OSCertHandle>(const_cast<void*>(CFRetain(handle))); +} + +// static void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) { CFRelease(cert_handle); } @@ -785,7 +790,8 @@ bool X509Certificate::GetSSLClientCertificates ( continue; scoped_refptr<X509Certificate> cert( - CreateFromHandle(cert_handle, SOURCE_LONE_CERT_IMPORT)); + CreateFromHandle(cert_handle, SOURCE_LONE_CERT_IMPORT, + OSCertHandles())); // cert_handle is adoped by cert, so I don't need to release it myself. if (cert->HasExpired() || !cert->SupportsSSLClientAuth()) continue; diff --git a/net/base/x509_certificate_nss.cc b/net/base/x509_certificate_nss.cc index b25688e..705690a 100644 --- a/net/base/x509_certificate_nss.cc +++ b/net/base/x509_certificate_nss.cc @@ -472,9 +472,6 @@ void X509Certificate::Initialize() { ParseDate(&cert_handle_->validity.notAfter, &valid_expiry_); fingerprint_ = CalculateFingerprint(cert_handle_); - - // Store the certificate in the cache in case we need it later. - X509Certificate::Cache::GetInstance()->Insert(this); } // static @@ -630,6 +627,12 @@ X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( } // static +X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle( + OSCertHandle cert_handle) { + return CERT_DupCertificate(cert_handle); +} + +// static void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) { CERT_DestroyCertificate(cert_handle); } diff --git a/net/base/x509_certificate_unittest.cc b/net/base/x509_certificate_unittest.cc index 7904cf0..7081f1ad 100644 --- a/net/base/x509_certificate_unittest.cc +++ b/net/base/x509_certificate_unittest.cc @@ -309,14 +309,16 @@ TEST(X509CertificateTest, Cache) { google_cert_handle = X509Certificate::CreateOSCertHandleFromBytes( reinterpret_cast<const char*>(google_der), sizeof(google_der)); scoped_refptr<X509Certificate> cert1 = X509Certificate::CreateFromHandle( - google_cert_handle, X509Certificate::SOURCE_LONE_CERT_IMPORT); + google_cert_handle, X509Certificate::SOURCE_LONE_CERT_IMPORT, + X509Certificate::OSCertHandles()); // Add a certificate from the same source (SOURCE_LONE_CERT_IMPORT). This // should return the cached certificate (cert1). google_cert_handle = X509Certificate::CreateOSCertHandleFromBytes( reinterpret_cast<const char*>(google_der), sizeof(google_der)); scoped_refptr<X509Certificate> cert2 = X509Certificate::CreateFromHandle( - google_cert_handle, X509Certificate::SOURCE_LONE_CERT_IMPORT); + google_cert_handle, X509Certificate::SOURCE_LONE_CERT_IMPORT, + X509Certificate::OSCertHandles()); EXPECT_EQ(cert1, cert2); @@ -325,7 +327,8 @@ TEST(X509CertificateTest, Cache) { google_cert_handle = X509Certificate::CreateOSCertHandleFromBytes( reinterpret_cast<const char*>(google_der), sizeof(google_der)); scoped_refptr<X509Certificate> cert3 = X509Certificate::CreateFromHandle( - google_cert_handle, X509Certificate::SOURCE_FROM_NETWORK); + google_cert_handle, X509Certificate::SOURCE_FROM_NETWORK, + X509Certificate::OSCertHandles()); EXPECT_NE(cert1, cert3); @@ -334,14 +337,16 @@ TEST(X509CertificateTest, Cache) { google_cert_handle = X509Certificate::CreateOSCertHandleFromBytes( reinterpret_cast<const char*>(google_der), sizeof(google_der)); scoped_refptr<X509Certificate> cert4 = X509Certificate::CreateFromHandle( - google_cert_handle, X509Certificate::SOURCE_FROM_NETWORK); + google_cert_handle, X509Certificate::SOURCE_FROM_NETWORK, + X509Certificate::OSCertHandles()); EXPECT_EQ(cert3, cert4); google_cert_handle = X509Certificate::CreateOSCertHandleFromBytes( reinterpret_cast<const char*>(google_der), sizeof(google_der)); scoped_refptr<X509Certificate> cert5 = X509Certificate::CreateFromHandle( - google_cert_handle, X509Certificate::SOURCE_FROM_NETWORK); + google_cert_handle, X509Certificate::SOURCE_FROM_NETWORK, + X509Certificate::OSCertHandles()); EXPECT_EQ(cert3, cert5); } @@ -396,4 +401,59 @@ TEST(X509CertificateTest, Policy) { EXPECT_TRUE(policy.HasDeniedCert()); } +#if defined(OS_MACOSX) || defined(OS_WIN) +TEST(X509CertificateTest, IntermediateCertificates) { + X509Certificate::OSCertHandle handle1, handle2, handle3, handle4; + + // Create object with no intermediates: + handle1 = X509Certificate::CreateOSCertHandleFromBytes( + reinterpret_cast<const char*>(google_der), sizeof(google_der)); + X509Certificate::OSCertHandles intermediates1; + scoped_refptr<X509Certificate> cert1; + cert1 = X509Certificate::CreateFromHandle(handle1, + X509Certificate::SOURCE_FROM_NETWORK, + intermediates1); + EXPECT_TRUE(cert1->HasIntermediateCertificates(intermediates1)); + handle2 = X509Certificate::CreateOSCertHandleFromBytes( + reinterpret_cast<const char*>(webkit_der), sizeof(webkit_der)); + EXPECT_FALSE(cert1->HasIntermediateCertificate(handle2)); + + // Create object with 2 intermediates: + handle1 = X509Certificate::CreateOSCertHandleFromBytes( + reinterpret_cast<const char*>(google_der), sizeof(google_der)); + X509Certificate::OSCertHandles intermediates2; + handle3 = X509Certificate::CreateOSCertHandleFromBytes( + reinterpret_cast<const char*>(thawte_der), sizeof(thawte_der)); + intermediates2.push_back(handle2); + intermediates2.push_back(handle3); + scoped_refptr<X509Certificate> cert2; + cert2 = X509Certificate::CreateFromHandle(handle1, + X509Certificate::SOURCE_FROM_NETWORK, + intermediates2); + + // The cache should have stored cert2 'cause it has more intermediates: + EXPECT_NE(cert1, cert2); + + // Verify it has all the intermediates: + EXPECT_TRUE(cert2->HasIntermediateCertificate(handle2)); + EXPECT_TRUE(cert2->HasIntermediateCertificate(handle3)); + handle4 = X509Certificate::CreateOSCertHandleFromBytes( + reinterpret_cast<const char*>(paypal_null_der), sizeof(paypal_null_der)); + EXPECT_FALSE(cert2->HasIntermediateCertificate(handle4)); + + // Create object with 1 intermediate: + handle3 = X509Certificate::CreateOSCertHandleFromBytes( + reinterpret_cast<const char*>(thawte_der), sizeof(thawte_der)); + X509Certificate::OSCertHandles intermediates3; + intermediates2.push_back(handle3); + scoped_refptr<X509Certificate> cert3; + cert3 = X509Certificate::CreateFromHandle(handle1, + X509Certificate::SOURCE_FROM_NETWORK, + intermediates3); + + // The cache should have returned cert2 'cause it has more intermediates: + EXPECT_EQ(cert3, cert2); +} +#endif + } // namespace net diff --git a/net/base/x509_certificate_win.cc b/net/base/x509_certificate_win.cc index df43814..7f3f09e 100644 --- a/net/base/x509_certificate_win.cc +++ b/net/base/x509_certificate_win.cc @@ -463,9 +463,6 @@ void X509Certificate::Initialize() { valid_expiry_ = Time::FromFileTime(cert_handle_->pCertInfo->NotAfter); fingerprint_ = CalculateFingerprint(cert_handle_); - - // Store the certificate in the cache in case we need it later. - X509Certificate::Cache::GetInstance()->Insert(this); } // static @@ -484,7 +481,8 @@ X509Certificate* X509Certificate::CreateFromPickle(const Pickle& pickle, NULL, reinterpret_cast<const void **>(&cert_handle))) return NULL; - return CreateFromHandle(cert_handle, SOURCE_LONE_CERT_IMPORT); + return CreateFromHandle(cert_handle, SOURCE_LONE_CERT_IMPORT, + OSCertHandles()); } void X509Certificate::Persist(Pickle* pickle) { @@ -746,6 +744,13 @@ X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( return cert_handle; } + +// static +X509Certificate::OSCertHandle X509Certificate::DupOSCertHandle( + OSCertHandle cert_handle) { + return CertDuplicateCertificateContext(cert_handle); +} + // static void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) { CertFreeCertificateContext(cert_handle); |