diff options
-rw-r--r-- | base/openssl_util.h | 4 | ||||
-rw-r--r-- | net/base/cert_test_util.cc | 156 | ||||
-rw-r--r-- | net/base/cert_test_util.h | 19 | ||||
-rw-r--r-- | net/base/test_root_certs.cc | 59 | ||||
-rw-r--r-- | net/base/test_root_certs.h | 103 | ||||
-rw-r--r-- | net/base/test_root_certs_mac.cc | 135 | ||||
-rw-r--r-- | net/base/test_root_certs_nss.cc | 119 | ||||
-rw-r--r-- | net/base/test_root_certs_openssl.cc | 51 | ||||
-rw-r--r-- | net/base/test_root_certs_win.cc | 206 | ||||
-rw-r--r-- | net/base/x509_certificate.h | 7 | ||||
-rw-r--r-- | net/base/x509_certificate_mac.cc | 70 | ||||
-rw-r--r-- | net/base/x509_certificate_openssl.cc | 23 | ||||
-rw-r--r-- | net/base/x509_certificate_unittest.cc | 39 | ||||
-rw-r--r-- | net/base/x509_certificate_win.cc | 37 | ||||
-rw-r--r-- | net/net.gyp | 11 | ||||
-rw-r--r-- | net/test/test_server.cc | 36 | ||||
-rw-r--r-- | net/test/test_server.h | 12 | ||||
-rw-r--r-- | net/test/test_server_posix.cc | 4 | ||||
-rw-r--r-- | net/test/test_server_win.cc | 28 | ||||
-rw-r--r-- | tools/valgrind/memcheck/suppressions.txt | 7 |
20 files changed, 794 insertions, 332 deletions
diff --git a/base/openssl_util.h b/base/openssl_util.h index 60cb0b7..9ce7f81 100644 --- a/base/openssl_util.h +++ b/base/openssl_util.h @@ -18,7 +18,9 @@ class ScopedOpenSSL { public: ScopedOpenSSL() : ptr_(NULL) { } explicit ScopedOpenSSL(T* ptr) : ptr_(ptr) { } - ~ScopedOpenSSL() { if (ptr_) (*destructor)(ptr_); } + ~ScopedOpenSSL() { + reset(NULL); + } T* get() const { return ptr_; } void reset(T* ptr) { diff --git a/net/base/cert_test_util.cc b/net/base/cert_test_util.cc index df00b9d..fb0c0f8 100644 --- a/net/base/cert_test_util.cc +++ b/net/base/cert_test_util.cc @@ -4,153 +4,37 @@ #include "net/base/cert_test_util.h" -#include "build/build_config.h" - -#if defined(USE_OPENSSL) -#include <openssl/err.h> -#include <openssl/ssl.h> -#include <openssl/x509v3.h> -#include "base/openssl_util.h" -#elif defined(USE_NSS) -#include <cert.h> -#include "base/nss_util.h" -#elif defined(OS_MACOSX) -#include <Security/Security.h> -#include "base/mac/scoped_cftyperef.h" -#endif - +#include "base/file_path.h" #include "base/file_util.h" -#include "base/logging.h" #include "base/path_service.h" #include "net/base/x509_certificate.h" namespace net { -#if defined(USE_OPENSSL) -X509Certificate* AddTemporaryRootCertToStore(X509* x509_cert) { - if (!X509_STORE_add_cert(X509Certificate::cert_store(), x509_cert)) { - unsigned long error_code = ERR_get_error(); - if (ERR_GET_LIB(error_code) != ERR_LIB_X509 || - ERR_GET_REASON(error_code) != X509_R_CERT_ALREADY_IN_HASH_TABLE) { - base::ClearOpenSSLERRStack(FROM_HERE); - return NULL; - } - } - return X509Certificate::CreateFromHandle( - x509_cert, X509Certificate::SOURCE_LONE_CERT_IMPORT, - X509Certificate::OSCertHandles()); -} - -X509Certificate* LoadTemporaryRootCert(const FilePath& filename) { - base::EnsureOpenSSLInit(); - - std::string rawcert; - if (!file_util::ReadFileToString(filename, &rawcert)) { - LOG(ERROR) << "Can't load certificate " << filename.value(); - return NULL; - } - - base::ScopedOpenSSL<BIO, BIO_free_all> cert_bio( - BIO_new_mem_buf(const_cast<char*>(rawcert.c_str()), - rawcert.length())); - if (!cert_bio.get()) { - LOG(ERROR) << "Can't create read-only BIO " << filename.value(); - return NULL; - } - - base::ScopedOpenSSL<X509, X509_free> pem_cert(PEM_read_bio_X509( - cert_bio.get(), NULL, NULL, NULL)); - if (pem_cert.get()) - return AddTemporaryRootCertToStore(pem_cert.get()); - - // File does not contain PEM data, let's try DER. - const unsigned char* der_data = - reinterpret_cast<const unsigned char*>(rawcert.c_str()); - int der_length = rawcert.length(); - base::ScopedOpenSSL<X509, X509_free> der_cert(d2i_X509( - NULL, &der_data, der_length)); - if (der_cert.get()) - return AddTemporaryRootCertToStore(der_cert.get()); - - LOG(ERROR) << "Can't parse certificate " << filename.value(); - return NULL; +FilePath GetTestCertsDirectory() { + FilePath certs_dir; + PathService::Get(base::DIR_SOURCE_ROOT, &certs_dir); + certs_dir = certs_dir.AppendASCII("net"); + certs_dir = certs_dir.AppendASCII("data"); + certs_dir = certs_dir.AppendASCII("ssl"); + certs_dir = certs_dir.AppendASCII("certificates"); + return certs_dir; } -#elif defined(USE_NSS) -X509Certificate* LoadTemporaryRootCert(const FilePath& filename) { - base::EnsureNSSInit(); - std::string rawcert; - if (!file_util::ReadFileToString(filename, &rawcert)) { - LOG(ERROR) << "Can't load certificate " << filename.value(); +scoped_refptr<X509Certificate> ImportCertFromFile( + const FilePath& certs_dir, + const std::string& cert_file) { + FilePath cert_path = certs_dir.AppendASCII(cert_file); + std::string cert_data; + if (!file_util::ReadFileToString(cert_path, &cert_data)) return NULL; - } - CERTCertificate *cert; - cert = CERT_DecodeCertFromPackage(const_cast<char *>(rawcert.c_str()), - rawcert.length()); - if (!cert) { - LOG(ERROR) << "Can't convert certificate " << filename.value(); + CertificateList certs_in_file = + X509Certificate::CreateCertificateListFromBytes( + cert_data.data(), cert_data.size(), X509Certificate::FORMAT_AUTO); + if (certs_in_file.empty()) return NULL; - } - - // TODO(port): remove this const_cast after NSS 3.12.3 is released - CERTCertTrust trust; - int rv = CERT_DecodeTrustString(&trust, const_cast<char *>("TCu,Cu,Tu")); - if (rv != SECSuccess) { - LOG(ERROR) << "Can't decode trust string"; - CERT_DestroyCertificate(cert); - return NULL; - } - - rv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), cert, &trust); - if (rv != SECSuccess) { - LOG(ERROR) << "Can't change trust for certificate " << filename.value(); - CERT_DestroyCertificate(cert); - return NULL; - } - - X509Certificate* result = X509Certificate::CreateFromHandle( - cert, - X509Certificate::SOURCE_LONE_CERT_IMPORT, - X509Certificate::OSCertHandles()); - CERT_DestroyCertificate(cert); - return result; -} -#endif - -#if defined(OS_MACOSX) -X509Certificate* LoadTemporaryRootCert(const FilePath& filename) { - std::string rawcert; - if (!file_util::ReadFileToString(filename, &rawcert)) { - LOG(ERROR) << "Can't load certificate " << filename.value(); - return NULL; - } - - CFDataRef pem = CFDataCreate(kCFAllocatorDefault, - reinterpret_cast<const UInt8*>(rawcert.data()), - static_cast<CFIndex>(rawcert.size())); - if (!pem) - return NULL; - base::mac::ScopedCFTypeRef<CFDataRef> scoped_pem(pem); - - SecExternalFormat input_format = kSecFormatUnknown; - SecExternalItemType item_type = kSecItemTypeUnknown; - CFArrayRef cert_array = NULL; - if (SecKeychainItemImport(pem, NULL, &input_format, &item_type, 0, NULL, NULL, - &cert_array)) - return NULL; - base::mac::ScopedCFTypeRef<CFArrayRef> scoped_cert_array(cert_array); - - if (!CFArrayGetCount(cert_array)) - return NULL; - - SecCertificateRef cert_ref = static_cast<SecCertificateRef>( - const_cast<void*>(CFArrayGetValueAtIndex(cert_array, 0))); - - return X509Certificate::CreateFromHandle(cert_ref, - X509Certificate::SOURCE_LONE_CERT_IMPORT, - X509Certificate::OSCertHandles()); + return certs_in_file[0]; } -#endif } // namespace net diff --git a/net/base/cert_test_util.h b/net/base/cert_test_util.h index 8709156..3452ac5 100644 --- a/net/base/cert_test_util.h +++ b/net/base/cert_test_util.h @@ -6,7 +6,9 @@ #define NET_BASE_CERT_TEST_UTIL_H_ #pragma once -#include "build/build_config.h" +#include <string> + +#include "base/ref_counted.h" class FilePath; @@ -14,11 +16,16 @@ namespace net { class X509Certificate; -#if defined(USE_NSS) || defined(OS_MACOSX) || defined(USE_OPENSSL) -// Loads and trusts a root CA certificate (stored in a file) temporarily. -// TODO(wtc): Implement this function on Windows (http://crbug.com/8470). -X509Certificate* LoadTemporaryRootCert(const FilePath& filename); -#endif +// Returns a FilePath object representing the src/net/data/ssl/certificates +// directory in the source tree. +FilePath GetTestCertsDirectory(); + +// Imports a certificate file in the src/net/data/ssl/certificates directory. +// certs_dir represents the test certificates directory. cert_file is the +// name of the certificate file. If cert_file contains multiple certificates, +// the first certificate found will be returned. +scoped_refptr<X509Certificate> ImportCertFromFile(const FilePath& certs_dir, + const std::string& cert_file); } // namespace net diff --git a/net/base/test_root_certs.cc b/net/base/test_root_certs.cc new file mode 100644 index 0000000..6d4bc18 --- /dev/null +++ b/net/base/test_root_certs.cc @@ -0,0 +1,59 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/base/test_root_certs.h" + +#include <string> + +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/logging.h" +#include "net/base/x509_certificate.h" + +namespace net { + +namespace { + +bool g_has_instance = false; + +base::LazyInstance<TestRootCerts, + base::LeakyLazyInstanceTraits<TestRootCerts> > + g_test_root_certs(base::LINKER_INITIALIZED); + +CertificateList LoadCertificates(const FilePath& filename) { + std::string raw_cert; + if (!file_util::ReadFileToString(filename, &raw_cert)) { + LOG(ERROR) << "Can't load certificate " << filename.value(); + return CertificateList(); + } + + return X509Certificate::CreateCertificateListFromBytes( + raw_cert.data(), raw_cert.length(), X509Certificate::FORMAT_AUTO); +} + +} // namespace + +// static +TestRootCerts* TestRootCerts::GetInstance() { + return g_test_root_certs.Pointer(); +} + +bool TestRootCerts::HasInstance() { + return g_has_instance; +} + +bool TestRootCerts::AddFromFile(const FilePath& file) { + CertificateList root_certs = LoadCertificates(file); + if (root_certs.empty() || root_certs.size() > 1) + return false; + + return Add(root_certs.front()); +} + +TestRootCerts::TestRootCerts() { + Init(); + g_has_instance = true; +} + +} // namespace net diff --git a/net/base/test_root_certs.h b/net/base/test_root_certs.h new file mode 100644 index 0000000..3fa8fcfd --- /dev/null +++ b/net/base/test_root_certs.h @@ -0,0 +1,103 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef NET_BASE_TEST_ROOT_CERTS_H_ +#define NET_BASE_TEST_ROOT_CERTS_H_ +#pragma once + +#include "base/lazy_instance.h" +#include "build/build_config.h" + +#if defined(OS_WIN) +#include <windows.h> +#include <wincrypt.h> +#elif defined(OS_MACOSX) +#include <CoreFoundation/CFArray.h> +#include <Security/SecTrust.h> +#include "base/mac/scoped_cftyperef.h" +#elif defined(USE_NSS) +#include <list> +#endif + +class FilePath; + +namespace net { + +class X509Certificate; + +// TestRootCerts is a helper class for unit tests that is used to +// artificially mark a certificate as trusted, independent of the local +// machine configuration. +class TestRootCerts { + public: + // Obtains the Singleton instance to the trusted certificates. + static TestRootCerts* GetInstance(); + + // Returns true if an instance exists, without forcing an initialization. + static bool HasInstance(); + + // Marks |certificate| as trusted for X509Certificate::Verify(). Returns + // false if the certificate could not be marked trusted. + bool Add(X509Certificate* certificate); + + // Reads a single certificate from |file| and marks it as trusted. Returns + // false if an error is encountered, such as being unable to read |file| + // or more than one certificate existing in |file|. + bool AddFromFile(const FilePath& file); + + // Clears the trusted status of any certificates that were previously + // marked trusted via Add(). + void Clear(); + + // Returns true if there are no certificates that have been marked trusted. + bool IsEmpty() const; + +#if defined(OS_MACOSX) + CFArrayRef temporary_roots() const { return temporary_roots_; } + + // Modifies the root certificates of |trust_ref| to include the + // certificates stored in |temporary_roots_|. If IsEmpty() is true, this + // does not modify |trust_ref|. + OSStatus FixupSecTrustRef(SecTrustRef trust_ref) const; +#elif defined(OS_WIN) + HCERTSTORE temporary_roots() const { return temporary_roots_; } + + // Returns an HCERTCHAINENGINE suitable to be used for certificate + // validation routines, or NULL to indicate that the default system chain + // engine is appropriate. The caller is responsible for freeing the + // returned HCERTCHAINENGINE. + HCERTCHAINENGINE GetChainEngine() const; +#endif + + private: + friend struct base::DefaultLazyInstanceTraits<TestRootCerts>; + + TestRootCerts(); + ~TestRootCerts(); + + // Performs platform-dependent initialization. + void Init(); + +#if defined(OS_MACOSX) + base::mac::ScopedCFTypeRef<CFMutableArrayRef> temporary_roots_; +#elif defined(OS_WIN) + HCERTSTORE temporary_roots_; +#elif defined(USE_NSS) + // It is necessary to maintain a cache of the original certificate trust + // settings, in order to restore them when Clear() is called. + class TrustEntry; + std::list<TrustEntry*> trust_cache_; +#endif + +#if defined(OS_WIN) || defined(USE_OPENSSL) + // True if there are no temporarily trusted root certificates. + bool empty_; +#endif + + DISALLOW_COPY_AND_ASSIGN(TestRootCerts); +}; + +} // namespace net + +#endif // NET_BASE_TEST_ROOT_CERTS_H_ diff --git a/net/base/test_root_certs_mac.cc b/net/base/test_root_certs_mac.cc new file mode 100644 index 0000000..6a8611a --- /dev/null +++ b/net/base/test_root_certs_mac.cc @@ -0,0 +1,135 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/base/test_root_certs.h" + +#include <Security/Security.h> + +#include "base/logging.h" +#include "base/mac/scoped_cftyperef.h" +#include "net/base/x509_certificate.h" + +namespace net { + +namespace { + +#if !defined(MAC_OS_X_VERSION_10_6) || \ + MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6 +// Declared in <Security/SecBase.h> of the 10.6 SDK. +enum { + errSecUnimplemented = -4, +}; +#endif + +typedef OSStatus (*SecTrustSetAnchorCertificatesOnlyFuncPtr)(SecTrustRef, + Boolean); + +Boolean OurSecCertificateEqual(const void* value1, const void* value2) { + if (CFGetTypeID(value1) != SecCertificateGetTypeID() || + CFGetTypeID(value2) != SecCertificateGetTypeID()) + return CFEqual(value1, value2); + return X509Certificate::IsSameOSCert( + reinterpret_cast<SecCertificateRef>(const_cast<void*>(value1)), + reinterpret_cast<SecCertificateRef>(const_cast<void*>(value2))); +} + +const void* RetainWrapper(CFAllocatorRef unused, const void* value) { + return CFRetain(value); +} + +void ReleaseWrapper(CFAllocatorRef unused, const void* value) { + CFRelease(value); +} + +// CFEqual prior to 10.6 only performed pointer checks on SecCertificateRefs, +// rather than checking if they were the same (logical) certificate, so a +// custom structure is used for the array callbacks. +const CFArrayCallBacks kCertArrayCallbacks = { + 0, // version + RetainWrapper, + ReleaseWrapper, + CFCopyDescription, + OurSecCertificateEqual, +}; + +} // namespace + +bool TestRootCerts::Add(X509Certificate* certificate) { + if (CFArrayContainsValue(temporary_roots_, + CFRangeMake(0, CFArrayGetCount(temporary_roots_)), + certificate->os_cert_handle())) + return true; + CFArrayAppendValue(temporary_roots_, certificate->os_cert_handle()); + return true; +} + +void TestRootCerts::Clear() { + CFArrayRemoveAllValues(temporary_roots_); +} + +bool TestRootCerts::IsEmpty() const { + return CFArrayGetCount(temporary_roots_) == 0; +} + +OSStatus TestRootCerts::FixupSecTrustRef(SecTrustRef trust_ref) const { + if (IsEmpty()) + return noErr; + + CFBundleRef bundle = + CFBundleGetBundleWithIdentifier(CFSTR("com.apple.security")); + SecTrustSetAnchorCertificatesOnlyFuncPtr set_anchor_certificates_only = NULL; + if (bundle) { + set_anchor_certificates_only = + reinterpret_cast<SecTrustSetAnchorCertificatesOnlyFuncPtr>( + CFBundleGetFunctionPointerForName(bundle, + CFSTR("SecTrustSetAnchorCertificatesOnly"))); + } + + OSStatus status = noErr; + if (set_anchor_certificates_only) { + // OS X 10.6 includes a function where the system trusts can be + // preserved while appending application trusts. This is preferable, + // because it preserves any user trust settings (explicit distrust), + // which the naive copy in 10.5 does not. Unfortunately, though the + // function pointer may be available, it is not always implemented. If it + // returns errSecUnimplemented, fall through to the 10.5 behaviour. + status = SecTrustSetAnchorCertificates(trust_ref, temporary_roots_); + if (status) + return status; + status = set_anchor_certificates_only(trust_ref, false); + if (status != errSecUnimplemented) + return status; + + // Restore the original settings before falling back. + status = SecTrustSetAnchorCertificates(trust_ref, NULL); + if (status) + return status; + } + + // On 10.5, the system certificates have to be copied and merged into + // the application trusts, and may override any user trust settings. + CFArrayRef system_roots = NULL; + status = SecTrustCopyAnchorCertificates(&system_roots); + if (status) + return status; + + base::mac::ScopedCFTypeRef<CFArrayRef> scoped_system_roots(system_roots); + base::mac::ScopedCFTypeRef<CFMutableArrayRef> scoped_roots( + CFArrayCreateMutableCopy(kCFAllocatorDefault, 0, + scoped_system_roots)); + DCHECK(scoped_roots.get()); + + CFArrayAppendArray(scoped_roots, temporary_roots_, + CFRangeMake(0, CFArrayGetCount(temporary_roots_))); + return SecTrustSetAnchorCertificates(trust_ref, scoped_roots); +} + +TestRootCerts::~TestRootCerts() {} + +void TestRootCerts::Init() { + temporary_roots_.reset(CFArrayCreateMutable(kCFAllocatorDefault, 0, + &kCertArrayCallbacks)); +} + +} // namespace net diff --git a/net/base/test_root_certs_nss.cc b/net/base/test_root_certs_nss.cc new file mode 100644 index 0000000..ae5ff5c --- /dev/null +++ b/net/base/test_root_certs_nss.cc @@ -0,0 +1,119 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/base/test_root_certs.h" + +#include <cert.h> + +#include "base/logging.h" +#include "base/nss_util.h" +#include "base/stl_util-inl.h" +#include "net/base/x509_certificate.h" + +namespace net { + +// TrustEntry is used to store the original CERTCertificate and CERTCertTrust +// for a certificate whose trust status has been changed by the +// TestRootCerts. +class TestRootCerts::TrustEntry { + public: + // Creates a new TrustEntry by incrementing the reference to |certificate| + // and copying |trust|. + TrustEntry(CERTCertificate* certificate, CERTCertTrust trust); + ~TrustEntry(); + + CERTCertificate* certificate() const { return certificate_; } + CERTCertTrust trust() const { return trust_; } + + private: + // The temporary root certificate. + CERTCertificate* certificate_; + + // The original trust settings, before |certificate_| was manipulated to + // be a temporarily trusted root. + CERTCertTrust trust_; + + DISALLOW_COPY_AND_ASSIGN(TrustEntry); +}; + +TestRootCerts::TrustEntry::TrustEntry(CERTCertificate* certificate, + CERTCertTrust trust) + : certificate_(CERT_DupCertificate(certificate)), + trust_(trust) { +} + +TestRootCerts::TrustEntry::~TrustEntry() { + CERT_DestroyCertificate(certificate_); +} + +bool TestRootCerts::Add(X509Certificate* certificate) { + // Preserve the original trust bits so that they can be restored when + // the certificate is removed. + CERTCertTrust original_trust; + SECStatus rv = CERT_GetCertTrust(certificate->os_cert_handle(), + &original_trust); + if (rv != SECSuccess) { + // CERT_GetCertTrust will fail if the certificate does not have any + // particular trust settings associated with it, and attempts to use + // |original_trust| later to restore the original trust settings will not + // cause the trust settings to be revoked. If the certificate has no + // particular trust settings associated with it, mark the certificate as + // a valid CA certificate with no specific trust. + rv = CERT_DecodeTrustString(&original_trust, "c,c,c"); + } + + // Change the trust bits to unconditionally trust this certificate. + CERTCertTrust new_trust; + rv = CERT_DecodeTrustString(&new_trust, "TCu,Cu,Tu"); + if (rv != SECSuccess) { + LOG(ERROR) << "Cannot decode certificate trust string."; + return false; + } + + rv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), + certificate->os_cert_handle(), + &new_trust); + if (rv != SECSuccess) { + LOG(ERROR) << "Cannot change certificate trust."; + return false; + } + + trust_cache_.push_back(new TrustEntry(certificate->os_cert_handle(), + original_trust)); + return true; +} + +void TestRootCerts::Clear() { + // Restore the certificate trusts to what they were originally, before + // Add() was called. Work from the rear first, since if a certificate was + // added twice, the second entry's original trust status will be that of + // the first entry, while the first entry contains the desired resultant + // status. + for (std::list<TrustEntry*>::reverse_iterator it = trust_cache_.rbegin(); + it != trust_cache_.rend(); ++it) { + CERTCertTrust original_trust = (*it)->trust(); + SECStatus rv = CERT_ChangeCertTrust(CERT_GetDefaultCertDB(), + (*it)->certificate(), + &original_trust); + // DCHECK(), rather than LOG(), as a failure to restore the original + // trust can cause flake or hard-to-trace errors in any unit tests that + // occur after Clear() has been called. + DCHECK_EQ(SECSuccess, rv) << "Cannot restore certificate trust."; + } + STLDeleteElements(&trust_cache_); +} + +bool TestRootCerts::IsEmpty() const { + return trust_cache_.empty(); +} + +TestRootCerts::~TestRootCerts() { + Clear(); +} + +void TestRootCerts::Init() { + base::EnsureNSSInit(); +} + +} // namespace net diff --git a/net/base/test_root_certs_openssl.cc b/net/base/test_root_certs_openssl.cc new file mode 100644 index 0000000..8307703 --- /dev/null +++ b/net/base/test_root_certs_openssl.cc @@ -0,0 +1,51 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/base/test_root_certs.h" + +#include <openssl/err.h> +#include <openssl/x509v3.h> + +#include "base/logging.h" +#include "base/openssl_util.h" +#include "base/tracked.h" +#include "net/base/x509_certificate.h" + +namespace net { + +bool TestRootCerts::Add(X509Certificate* certificate) { + if (!X509_STORE_add_cert(X509Certificate::cert_store(), + certificate->os_cert_handle())) { + unsigned long error_code = ERR_peek_error(); + if (ERR_GET_LIB(error_code) != ERR_LIB_X509 || + ERR_GET_REASON(error_code) != X509_R_CERT_ALREADY_IN_HASH_TABLE) { + base::ClearOpenSSLERRStack(FROM_HERE); + return false; + } + ERR_clear_error(); + } + + empty_ = false; + return true; +} + +void TestRootCerts::Clear() { + if (empty_) + return; + + X509Certificate::ResetCertStore(); + empty_ = true; +} + +bool TestRootCerts::IsEmpty() const { + return empty_; +} + +TestRootCerts::~TestRootCerts() {} + +void TestRootCerts::Init() { + empty_ = true; +} + +} // namespace net diff --git a/net/base/test_root_certs_win.cc b/net/base/test_root_certs_win.cc new file mode 100644 index 0000000..7862e40 --- /dev/null +++ b/net/base/test_root_certs_win.cc @@ -0,0 +1,206 @@ +// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "net/base/test_root_certs.h" + +#include <windows.h> +#include <wincrypt.h> + +#include "base/basictypes.h" +#include "base/lazy_instance.h" +#include "base/logging.h" +#include "net/base/x509_certificate.h" + +namespace net { + +namespace { + +// Provides a CertDllOpenStoreProv callback provider function, to be called +// by CertOpenStore when the CERT_STORE_PROV_SYSTEM_W store is opened. See +// http://msdn.microsoft.com/en-us/library/aa376043(VS.85).aspx. +BOOL WINAPI InterceptedOpenStoreW(LPCSTR store_provider, + DWORD encoding, + HCRYPTPROV crypt_provider, + DWORD flags, + const void* extra, + HCERTSTORE memory_store, + PCERT_STORE_PROV_INFO store_info); + +// CryptoAPIInjector is used to inject a store provider function for system +// certificate stores before the one provided internally by Crypt32.dll. +// Once injected, there is no way to remove, so every call to open a system +// store will be redirected to the injected function. +struct CryptoAPIInjector { + // The previous default function for opening system stores. For most + // configurations, this should point to Crypt32's internal + // I_CertDllOpenSystemStoreProvW function. + PFN_CERT_DLL_OPEN_STORE_PROV_FUNC original_function; + + // The handle that CryptoAPI uses to ensure the DLL implementing + // |original_function| remains loaded in memory. + HCRYPTOIDFUNCADDR original_handle; + + private: + friend struct base::DefaultLazyInstanceTraits<CryptoAPIInjector>; + + CryptoAPIInjector() + : original_function(NULL), + original_handle(NULL) { + HCRYPTOIDFUNCSET registered_functions = + CryptInitOIDFunctionSet(CRYPT_OID_OPEN_STORE_PROV_FUNC, 0); + + // Preserve the original handler function in |original_function|. If other + // functions are overridden, they will also need to be preserved. + BOOL ok = CryptGetOIDFunctionAddress( + registered_functions, 0, CERT_STORE_PROV_SYSTEM_W, 0, + reinterpret_cast<void**>(&original_function), &original_handle); + DCHECK(ok); + + // For now, intercept only the numeric form of the system store + // function, CERT_STORE_PROV_SYSTEM_W (0x0A), which is what Crypt32 + // functionality uses exclusively. Depending on the machine that tests + // are being run on, it may prove necessary to also intercept + // sz_CERT_STORE_PROV_SYSTEM_[A/W] and CERT_STORE_PROV_SYSTEM_A, based + // on whether or not any third-party CryptoAPI modules have been + // installed. + const CRYPT_OID_FUNC_ENTRY kFunctionToIntercept = + { CERT_STORE_PROV_SYSTEM_W, &InterceptedOpenStoreW }; + + // Inject kFunctionToIntercept at the front of the linked list that + // crypt32 uses when CertOpenStore is called, replacing the existing + // registered function. + ok = CryptInstallOIDFunctionAddress(NULL, 0, + CRYPT_OID_OPEN_STORE_PROV_FUNC, 1, + &kFunctionToIntercept, + CRYPT_INSTALL_OID_FUNC_BEFORE_FLAG); + DCHECK(ok); + } + + // This is never called, because this object is intentionally leaked. + // Certificate verification happens on a non-joinable worker thread, which + // may still be running when ~AtExitManager is called, so the LazyInstance + // must be leaky. + ~CryptoAPIInjector() { + original_function = NULL; + CryptFreeOIDFunctionAddress(original_handle, NULL); + } +}; + +base::LazyInstance<CryptoAPIInjector, + base::LeakyLazyInstanceTraits<CryptoAPIInjector> > + g_capi_injector(base::LINKER_INITIALIZED); + +BOOL WINAPI InterceptedOpenStoreW(LPCSTR store_provider, + DWORD encoding, + HCRYPTPROV crypt_provider, + DWORD flags, + const void* store_name, + HCERTSTORE memory_store, + PCERT_STORE_PROV_INFO store_info) { + // If the high word is all zeroes, then |store_provider| is a numeric ID. + // Otherwise, it's a pointer to a null-terminated ASCII string. See the + // documentation for CryptGetOIDFunctionAddress for more information. + uint32 store_as_uint = reinterpret_cast<uint32>(store_provider); + if (store_as_uint > 0xFFFF || store_provider != CERT_STORE_PROV_SYSTEM_W || + !g_capi_injector.Get().original_function) + return FALSE; + + BOOL ok = g_capi_injector.Get().original_function(store_provider, encoding, + crypt_provider, flags, + store_name, memory_store, + store_info); + // Only the Root store should have certificates injected. If + // CERT_SYSTEM_STORE_RELOCATE_FLAG is set, then |store_name| points to a + // CERT_SYSTEM_STORE_RELOCATE_PARA structure, rather than a + // NULL-terminated wide string, so check before making a string + // comparison. + if (!ok || TestRootCerts::GetInstance()->IsEmpty() || + (flags & CERT_SYSTEM_STORE_RELOCATE_FLAG) || + lstrcmpiW(reinterpret_cast<LPCWSTR>(store_name), L"root")) + return ok; + + // The result of CertOpenStore with CERT_STORE_PROV_SYSTEM_W is documented + // to be a collection store, and that appears to hold for |memory_store|. + // Attempting to add an individual certificate to |memory_store| causes + // the request to be forwarded to the first physical store in the + // collection that accepts modifications, which will cause a secure + // confirmation dialog to be displayed, confirming the user wishes to + // trust the certificate. However, appending a store to the collection + // will merely modify the temporary collection store, and will not persist + // any changes to the underlying physical store. When the |memory_store| is + // searched to see if a certificate is in the Root store, all the + // underlying stores in the collection will be searched, and any certificate + // in temporary_roots() will be found and seen as trusted. + return CertAddStoreToCollection( + memory_store, TestRootCerts::GetInstance()->temporary_roots(), 0, 0); +} + +} // namespace + +bool TestRootCerts::Add(X509Certificate* certificate) { + // Ensure that the default CryptoAPI functionality has been intercepted. + // If a test certificate is never added, then no interception should + // happen. + g_capi_injector.Get(); + + BOOL ok = CertAddCertificateContextToStore( + temporary_roots_, certificate->os_cert_handle(), + CERT_STORE_ADD_NEW, NULL); + if (!ok) { + // If the certificate is already added, return successfully. + return GetLastError() == CRYPT_E_EXISTS; + } + + empty_ = false; + return true; +} + +void TestRootCerts::Clear() { + empty_ = true; + + PCCERT_CONTEXT prev_cert = NULL; + while (prev_cert = CertEnumCertificatesInStore(temporary_roots_, NULL)) + CertDeleteCertificateFromStore(prev_cert); +} + +bool TestRootCerts::IsEmpty() const { + return empty_; +} + +HCERTCHAINENGINE TestRootCerts::GetChainEngine() const { + if (IsEmpty()) + return NULL; // Default chain engine will suffice. + + // Each HCERTCHAINENGINE caches both the configured system stores and + // information about each chain that has been built. In order to ensure + // that changes to |temporary_roots_| are properly propagated and that the + // various caches are flushed, when at least one certificate is added, + // return a new chain engine for every call. Each chain engine creation + // should re-open the root store, ensuring the most recent changes are + // visible. + CERT_CHAIN_ENGINE_CONFIG engine_config = { + sizeof(engine_config) + }; + engine_config.dwFlags = + CERT_CHAIN_ENABLE_CACHE_AUTO_UPDATE | + CERT_CHAIN_ENABLE_SHARE_STORE; + HCERTCHAINENGINE chain_engine = NULL; + BOOL ok = CertCreateCertificateChainEngine(&engine_config, &chain_engine); + DCHECK(ok); + return chain_engine; +} + +TestRootCerts::~TestRootCerts() { + CertCloseStore(temporary_roots_, 0); +} + +void TestRootCerts::Init() { + empty_ = true; + temporary_roots_ = CertOpenStore( + CERT_STORE_PROV_MEMORY, 0, NULL, + CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG, NULL); + DCHECK(temporary_roots_); +} + +} // namespace net diff --git a/net/base/x509_certificate.h b/net/base/x509_certificate.h index c2a378a..89bf476 100644 --- a/net/base/x509_certificate.h +++ b/net/base/x509_certificate.h @@ -283,6 +283,7 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { private: friend class base::RefCountedThreadSafe<X509Certificate>; + friend class TestRootCerts; // For unit tests FRIEND_TEST_ALL_PREFIXES(X509CertificateTest, Cache); FRIEND_TEST_ALL_PREFIXES(X509CertificateTest, IntermediateCertificates); @@ -302,6 +303,12 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { #endif bool VerifyEV() const; +#if defined(USE_OPENSSL) + // Resets the store returned by cert_store() to default state. Used by + // TestRootCerts to undo modifications. + static void ResetCertStore(); +#endif + // Calculates the SHA-1 fingerprint of the certificate. Returns an empty // (all zero) fingerprint on failure. static SHA1Fingerprint CalculateFingerprint(OSCertHandle cert_handle); diff --git a/net/base/x509_certificate_mac.cc b/net/base/x509_certificate_mac.cc index d4858e6..2187cd3 100644 --- a/net/base/x509_certificate_mac.cc +++ b/net/base/x509_certificate_mac.cc @@ -17,6 +17,7 @@ #include "net/base/cert_status_flags.h" #include "net/base/cert_verify_result.h" #include "net/base/net_errors.h" +#include "net/base/test_root_certs.h" using base::mac::ScopedCFTypeRef; using base::Time; @@ -25,62 +26,6 @@ namespace net { namespace { -class MacTrustedCertificates { - public: - // Sets the trusted root certificate used by tests. Call with |cert| set - // to NULL to clear the test certificate. - void SetTestCertificate(X509Certificate* cert) { - AutoLock lock(lock_); - test_certificate_ = cert; - } - - // Returns an array containing the trusted certificates for use with - // SecTrustSetAnchorCertificates(). Returns NULL if the system-supplied - // list of trust anchors is acceptable (that is, there is not test - // certificate available). Ownership follows the Create Rule (caller - // is responsible for calling CFRelease on the non-NULL result). - CFArrayRef CopyTrustedCertificateArray() { - AutoLock lock(lock_); - - if (!test_certificate_) - return NULL; - - // Failure to copy the anchor certificates or add the test certificate - // is non-fatal; SecTrustEvaluate() will use the system anchors instead. - CFArrayRef anchor_array; - OSStatus status = SecTrustCopyAnchorCertificates(&anchor_array); - if (status) - return NULL; - ScopedCFTypeRef<CFArrayRef> scoped_anchor_array(anchor_array); - CFMutableArrayRef merged_array = CFArrayCreateMutableCopy( - kCFAllocatorDefault, 0, anchor_array); - if (!merged_array) - return NULL; - CFArrayAppendValue(merged_array, test_certificate_->os_cert_handle()); - - return merged_array; - } - private: - friend struct base::DefaultLazyInstanceTraits<MacTrustedCertificates>; - - // Obtain an instance of MacTrustedCertificates via the singleton - // interface. - MacTrustedCertificates() : test_certificate_(NULL) { } - - // An X509Certificate object that may be appended to the list of - // system trusted anchors. - scoped_refptr<X509Certificate> test_certificate_; - - // The trusted cache may be accessed from multiple threads. - mutable Lock lock_; - - DISALLOW_COPY_AND_ASSIGN(MacTrustedCertificates); -}; - -base::LazyInstance<MacTrustedCertificates, - base::LeakyLazyInstanceTraits<MacTrustedCertificates> > - g_mac_trusted_certificates(base::LINKER_INITIALIZED); - typedef OSStatus (*SecTrustCopyExtendedResultFuncPtr)(SecTrustRef, CFDictionaryRef*); @@ -426,10 +371,6 @@ void AddCertificatesFromBytes(const char* data, size_t length, } // namespace -void SetMacTestCertificate(X509Certificate* cert) { - g_mac_trusted_certificates.Get().SetTestCertificate(cert); -} - void X509Certificate::Initialize() { const CSSM_X509_NAME* name; OSStatus status = SecCertificateGetSubject(cert_handle_, &name); @@ -528,13 +469,8 @@ int X509Certificate::Verify(const std::string& hostname, int flags, return NetErrorFromOSStatus(status); ScopedCFTypeRef<SecTrustRef> scoped_trust_ref(trust_ref); - // Set the trusted anchor certificates for the SecTrustRef by merging the - // system trust anchors and the test root certificate. - CFArrayRef anchor_array = - g_mac_trusted_certificates.Get().CopyTrustedCertificateArray(); - ScopedCFTypeRef<CFArrayRef> scoped_anchor_array(anchor_array); - if (anchor_array) { - status = SecTrustSetAnchorCertificates(trust_ref, anchor_array); + if (TestRootCerts::HasInstance()) { + status = TestRootCerts::GetInstance()->FixupSecTrustRef(trust_ref); if (status) return NetErrorFromOSStatus(status); } diff --git a/net/base/x509_certificate_openssl.cc b/net/base/x509_certificate_openssl.cc index cee803f..5fd3a67 100644 --- a/net/base/x509_certificate_openssl.cc +++ b/net/base/x509_certificate_openssl.cc @@ -216,16 +216,20 @@ class X509InitSingleton { int der_cache_ex_index() const { return der_cache_ex_index_; } X509_STORE* store() const { return store_.get(); } + void ResetCertStore() { + store_.reset(X509_STORE_new()); + DCHECK(store_.get()); + X509_STORE_set_default_paths(store_.get()); + // TODO(joth): Enable CRL (see X509_STORE_set_flags(X509_V_FLAG_CRL_CHECK)). + } + private: friend struct DefaultSingletonTraits<X509InitSingleton>; - X509InitSingleton() - : der_cache_ex_index_((base::EnsureOpenSSLInit(), - X509_get_ex_new_index(0, 0, 0, 0, - DERCache_free))), - store_(X509_STORE_new()) { + X509InitSingleton() { + base::EnsureOpenSSLInit(); + der_cache_ex_index_ = X509_get_ex_new_index(0, 0, 0, 0, DERCache_free); DCHECK_NE(der_cache_ex_index_, -1); - X509_STORE_set_default_paths(store_.get()); - // TODO(joth): Enable CRL (see X509_STORE_set_flags(X509_V_FLAG_CRL_CHECK)). + ResetCertStore(); } int der_cache_ex_index_; @@ -312,6 +316,11 @@ void X509Certificate::Initialize() { nxou::ParseDate(X509_get_notAfter(cert_handle_), &valid_expiry_); } +// static +void X509Certificate::ResetCertStore() { + X509InitSingleton::Get()->ResetCertStore(); +} + SHA1Fingerprint X509Certificate::CalculateFingerprint(OSCertHandle cert) { SHA1Fingerprint sha1; unsigned int sha1_size = static_cast<unsigned int>(sizeof(sha1.data)); diff --git a/net/base/x509_certificate_unittest.cc b/net/base/x509_certificate_unittest.cc index 31173e4..b316b12c6 100644 --- a/net/base/x509_certificate_unittest.cc +++ b/net/base/x509_certificate_unittest.cc @@ -1,4 +1,4 @@ -// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved. +// Copyright (c) 2010 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. @@ -11,6 +11,7 @@ #include "net/base/cert_verify_result.h" #include "net/base/net_errors.h" #include "net/base/test_certificate_data.h" +#include "net/base/test_root_certs.h" #include "net/base/x509_certificate.h" #include "testing/gtest/include/gtest/gtest.h" @@ -161,30 +162,6 @@ const CertificateFormatTestData FormatTestData[] = { NULL, } }, }; -// Returns a FilePath object representing the src/net/data/ssl/certificates -// directory in the source tree. -FilePath GetTestCertsDirectory() { - FilePath certs_dir; - PathService::Get(base::DIR_SOURCE_ROOT, &certs_dir); - certs_dir = certs_dir.AppendASCII("net"); - certs_dir = certs_dir.AppendASCII("data"); - certs_dir = certs_dir.AppendASCII("ssl"); - certs_dir = certs_dir.AppendASCII("certificates"); - return certs_dir; -} - -// Imports a certificate file in the src/net/data/ssl/certificates directory. -// certs_dir represents the test certificates directory. cert_file is the -// name of the certificate file. -X509Certificate* ImportCertFromFile(const FilePath& certs_dir, - const std::string& cert_file) { - FilePath cert_path = certs_dir.AppendASCII(cert_file); - std::string cert_data; - if (!file_util::ReadFileToString(cert_path, &cert_data)) - return NULL; - return X509Certificate::CreateFromBytes(cert_data.data(), cert_data.size()); -} - CertificateList CreateCertificateListFromFile( const FilePath& certs_dir, const std::string& cert_file, @@ -427,13 +404,8 @@ TEST(X509CertificateTest, UnoSoftCertParsing) { EXPECT_NE(0, verify_result.cert_status & CERT_STATUS_AUTHORITY_INVALID); } -#if defined(USE_NSS) || defined(USE_OPENSSL) // A regression test for http://crbug.com/31497. // This certificate will expire on 2012-04-08. -// TODO(wtc): we can't run this test on Mac because MacTrustedCertificates -// can hold only one additional trusted root certificate for unit tests. -// TODO(wtc): we can't run this test on Windows because LoadTemporaryRootCert -// isn't implemented (http//crbug.com/8470). TEST(X509CertificateTest, IntermediateCARequireExplicitPolicy) { FilePath certs_dir = GetTestCertsDirectory(); @@ -448,9 +420,8 @@ TEST(X509CertificateTest, IntermediateCARequireExplicitPolicy) { ASSERT_NE(static_cast<X509Certificate*>(NULL), intermediate_cert); FilePath root_cert_path = certs_dir.AppendASCII("dod_root_ca_2_cert.der"); - scoped_refptr<X509Certificate> root_cert = - LoadTemporaryRootCert(root_cert_path); - ASSERT_NE(static_cast<X509Certificate*>(NULL), root_cert); + TestRootCerts* root_certs = TestRootCerts::GetInstance(); + ASSERT_TRUE(root_certs->AddFromFile(root_cert_path)); X509Certificate::OSCertHandles intermediates; intermediates.push_back(intermediate_cert->os_cert_handle()); @@ -464,8 +435,8 @@ TEST(X509CertificateTest, IntermediateCARequireExplicitPolicy) { int error = cert_chain->Verify("www.us.army.mil", flags, &verify_result); EXPECT_EQ(OK, error); EXPECT_EQ(0, verify_result.cert_status); + root_certs->Clear(); } -#endif // Tests X509Certificate::Cache via X509Certificate::CreateFromHandle. We // call X509Certificate::CreateFromHandle several times and observe whether diff --git a/net/base/x509_certificate_win.cc b/net/base/x509_certificate_win.cc index 71aa545..509c6c9 100644 --- a/net/base/x509_certificate_win.cc +++ b/net/base/x509_certificate_win.cc @@ -4,6 +4,7 @@ #include "net/base/x509_certificate.h" +#include "base/crypto/scoped_capi_types.h" #include "base/lazy_instance.h" #include "base/logging.h" #include "base/pickle.h" @@ -15,6 +16,7 @@ #include "net/base/ev_root_ca_metadata.h" #include "net/base/net_errors.h" #include "net/base/scoped_cert_chain_context.h" +#include "net/base/test_root_certs.h" #pragma comment(lib, "crypt32.lib") @@ -24,6 +26,21 @@ namespace net { namespace { +typedef base::ScopedCAPIHandle< + HCERTSTORE, + base::CAPIDestroyerWithFlags<HCERTSTORE, + CertCloseStore, 0> > ScopedHCERTSTORE; + +struct FreeChainEngineFunctor { + void operator()(HCERTCHAINENGINE engine) const { + if (engine) + CertFreeCertificateChainEngine(engine); + } +}; + +typedef base::ScopedCAPIHandle<HCERTCHAINENGINE, FreeChainEngineFunctor> + ScopedHCERTCHAINENGINE; + //----------------------------------------------------------------------------- // TODO(wtc): This is a copy of the MapSecurityError function in @@ -609,15 +626,26 @@ int X509Certificate::Verify(const std::string& hostname, } } + // For non-test scenarios, use the default HCERTCHAINENGINE, NULL, which + // corresponds to HCCE_CURRENT_USER and is is initialized as needed by + // crypt32. However, when testing, it is necessary to create a new + // HCERTCHAINENGINE and use that instead. This is because each + // HCERTCHAINENGINE maintains a cache of information about certificates + // encountered, and each test run may modify the trust status of a + // certificate. + ScopedHCERTCHAINENGINE chain_engine(NULL); + if (TestRootCerts::HasInstance()) + chain_engine.reset(TestRootCerts::GetInstance()->GetChainEngine()); + PCCERT_CHAIN_CONTEXT chain_context; // IE passes a non-NULL pTime argument that specifies the current system // time. IE passes CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT as the // chain_flags argument. if (!CertGetCertificateChain( - NULL, // default chain engine, HCCE_CURRENT_USER + chain_engine, cert_handle_, NULL, // current system time - cert_handle_->hCertStore, // search this store + cert_handle_->hCertStore, &chain_para, chain_flags, NULL, // reserved @@ -631,10 +659,10 @@ int X509Certificate::Verify(const std::string& hostname, chain_para.RequestedIssuancePolicy.Usage.rgpszUsageIdentifier = NULL; CertFreeCertificateChain(chain_context); if (!CertGetCertificateChain( - NULL, // default chain engine, HCCE_CURRENT_USER + chain_engine, cert_handle_, NULL, // current system time - cert_handle_->hCertStore, // search this store + cert_handle_->hCertStore, &chain_para, chain_flags, NULL, // reserved @@ -645,7 +673,6 @@ int X509Certificate::Verify(const std::string& hostname, ScopedCertChainContext scoped_chain_context(chain_context); GetCertChainInfo(chain_context, verify_result); - verify_result->cert_status |= MapCertChainErrorStatusToCertStatus( chain_context->TrustStatus.dwErrorStatus); diff --git a/net/net.gyp b/net/net.gyp index 89abccff..4460b41 100644 --- a/net/net.gyp +++ b/net/net.gyp @@ -177,6 +177,12 @@ 'base/ssl_info.h', 'base/static_cookie_policy.cc', 'base/static_cookie_policy.h', + 'base/test_root_certs.cc', + 'base/test_root_certs.h', + 'base/test_root_certs_mac.cc', + 'base/test_root_certs_nss.cc', + 'base/test_root_certs_openssl.cc', + 'base/test_root_certs_win.cc', 'base/transport_security_state.cc', 'base/transport_security_state.h', 'base/sys_addrinfo.h', @@ -252,6 +258,7 @@ 'sources!': [ 'base/cert_database_nss.cc', 'base/keygen_handler_nss.cc', + 'base/test_root_certs_nss.cc', 'base/x509_certificate_nss.cc', 'third_party/mozilla_security_manager/nsKeygenHandler.cpp', 'third_party/mozilla_security_manager/nsKeygenHandler.h', @@ -272,6 +279,7 @@ 'base/keygen_handler_nss.cc', 'base/nss_memio.c', 'base/nss_memio.h', + 'base/test_root_certs_nss.cc', 'base/x509_certificate_nss.cc', 'third_party/mozilla_security_manager/nsKeygenHandler.cpp', 'third_party/mozilla_security_manager/nsKeygenHandler.h', @@ -289,6 +297,7 @@ 'base/keygen_handler_openssl.cc', 'base/openssl_memory_private_key_store.cc', 'base/openssl_private_key_store.h', + 'base/test_root_certs_openssl.cc', 'base/x509_certificate_openssl.cc', 'base/x509_openssl_util.cc', 'base/x509_openssl_util.h', @@ -1158,7 +1167,7 @@ ['use_openssl==1', { 'dependencies': [ '../third_party/openssl/openssl.gyp:openssl', - ] + ], }, { 'dependencies': [ '../build/linux/system.gyp:nss', diff --git a/net/test/test_server.cc b/net/test/test_server.cc index a6e5a82..9722dc1 100644 --- a/net/test/test_server.cc +++ b/net/test/test_server.cc @@ -26,10 +26,10 @@ #include "base/utf_string_conversions.h" #include "base/values.h" #include "googleurl/src/gurl.h" -#include "net/base/cert_test_util.h" #include "net/base/host_port_pair.h" #include "net/base/host_resolver.h" #include "net/base/test_completion_callback.h" +#include "net/base/test_root_certs.h" #include "net/socket/tcp_client_socket.h" #include "net/test/python_utils.h" #include "testing/platform_test.h" @@ -58,10 +58,6 @@ std::string GetHostname(TestServer::Type type, } // namespace -#if defined(OS_MACOSX) -void SetMacTestCertificate(X509Certificate* cert); -#endif - TestServer::HTTPSOptions::HTTPSOptions() : server_certificate(CERT_OK), request_client_certificate(false), @@ -103,9 +99,8 @@ TestServer::TestServer(const HTTPSOptions& https_options, } TestServer::~TestServer() { -#if defined(OS_MACOSX) - SetMacTestCertificate(NULL); -#endif + TestRootCerts* root_certs = TestRootCerts::GetInstance(); + root_certs->Clear(); Stop(); } @@ -132,8 +127,6 @@ bool TestServer::Start() { if (type_ == TYPE_HTTPS) { if (!LoadTestRootCert()) return false; - if (!CheckCATrusted()) - return false; } // Get path to python server script @@ -318,27 +311,8 @@ FilePath TestServer::GetRootCertificatePath() { } bool TestServer::LoadTestRootCert() { -#if defined(USE_OPENSSL) || defined(USE_NSS) - if (cert_) - return true; - - // TODO(dkegel): figure out how to get this to only happen once? - - // This currently leaks a little memory. - // TODO(dkegel): fix the leak and remove the entry in - // tools/valgrind/memcheck/suppressions.txt - ANNOTATE_SCOPED_MEMORY_LEAK; // Tell heap checker about the leak. - cert_ = LoadTemporaryRootCert(GetRootCertificatePath()); - return (cert_ != NULL); -#elif defined(OS_MACOSX) - X509Certificate* cert = LoadTemporaryRootCert(GetRootCertificatePath()); - if (!cert) - return false; - SetMacTestCertificate(cert); - return true; -#else - return true; -#endif + TestRootCerts* root_certs = TestRootCerts::GetInstance(); + return root_certs->AddFromFile(GetRootCertificatePath()); } bool TestServer::AddCommandLineArguments(CommandLine* command_line) const { diff --git a/net/test/test_server.h b/net/test/test_server.h index 4154302..9686aef 100644 --- a/net/test/test_server.h +++ b/net/test/test_server.h @@ -23,11 +23,6 @@ #include "base/scoped_handle_win.h" #endif -#if defined(USE_OPENSSL) || defined(USE_NSS) -#include "base/ref_counted.h" -#include "net/base/x509_certificate.h" -#endif - class CommandLine; class DictionaryValue; class GURL; @@ -156,9 +151,6 @@ class TestServer { // Returns path to the root certificate. FilePath GetRootCertificatePath(); - // Returns false if our test root certificate is not trusted. - bool CheckCATrusted() WARN_UNUSED_RESULT; - // Load the test root cert, if it hasn't been loaded yet. bool LoadTestRootCert() WARN_UNUSED_RESULT; @@ -203,10 +195,6 @@ class TestServer { // If |type_| is TYPE_HTTPS, the TLS settings to use for the test server. HTTPSOptions https_options_; -#if defined(USE_OPENSSL) || defined(USE_NSS) - scoped_refptr<X509Certificate> cert_; -#endif - Type type_; // Has the server been started? diff --git a/net/test/test_server_posix.cc b/net/test/test_server_posix.cc index 43bdb10..d14561e 100644 --- a/net/test/test_server_posix.cc +++ b/net/test/test_server_posix.cc @@ -163,8 +163,4 @@ bool TestServer::WaitToStart() { return true; } -bool TestServer::CheckCATrusted() { - return true; -} - } // namespace net diff --git a/net/test/test_server_win.cc b/net/test/test_server_win.cc index e1c54e9..e38d0bc 100644 --- a/net/test/test_server_win.cc +++ b/net/test/test_server_win.cc @@ -216,32 +216,4 @@ bool TestServer::WaitToStart() { return true; } -bool TestServer::CheckCATrusted() { - HCERTSTORE cert_store = CertOpenSystemStore(NULL, L"ROOT"); - if (!cert_store) { - LOG(ERROR) << " could not open trusted root CA store"; - return false; - } - PCCERT_CONTEXT cert = - CertFindCertificateInStore(cert_store, - X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, - 0, - CERT_FIND_ISSUER_STR, - L"Test CA", - NULL); - if (cert) - CertFreeCertificateContext(cert); - CertCloseStore(cert_store, 0); - - if (!cert) { - LOG(ERROR) << " TEST CONFIGURATION ERROR: you need to import the test ca " - "certificate to your trusted roots for this test to work. " - "For more info visit:\n" - "http://dev.chromium.org/developers/testing\n"; - return false; - } - - return true; -} - } // namespace net diff --git a/tools/valgrind/memcheck/suppressions.txt b/tools/valgrind/memcheck/suppressions.txt index 65778dd..f7f9ccc 100644 --- a/tools/valgrind/memcheck/suppressions.txt +++ b/tools/valgrind/memcheck/suppressions.txt @@ -597,6 +597,13 @@ fun:_ZN23AutocompleteEditViewGtk6UpdateEPK11TabContents fun:_ZN15LocationBarView6UpdateEPK11TabContents } +{ + bug_66941 + Memcheck:Leak + ... + fun:STAN_ChangeCertTrust + fun:CERT_ChangeCertTrust +} #----------------------------------------------------------------------- # 2. intentional unit test errors, or stuff that is somehow a false positive |