summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--base/openssl_util.h4
-rw-r--r--net/base/cert_test_util.cc156
-rw-r--r--net/base/cert_test_util.h19
-rw-r--r--net/base/test_root_certs.cc59
-rw-r--r--net/base/test_root_certs.h103
-rw-r--r--net/base/test_root_certs_mac.cc135
-rw-r--r--net/base/test_root_certs_nss.cc119
-rw-r--r--net/base/test_root_certs_openssl.cc51
-rw-r--r--net/base/test_root_certs_win.cc206
-rw-r--r--net/base/x509_certificate.h7
-rw-r--r--net/base/x509_certificate_mac.cc70
-rw-r--r--net/base/x509_certificate_openssl.cc23
-rw-r--r--net/base/x509_certificate_unittest.cc39
-rw-r--r--net/base/x509_certificate_win.cc37
-rw-r--r--net/net.gyp11
-rw-r--r--net/test/test_server.cc36
-rw-r--r--net/test/test_server.h12
-rw-r--r--net/test/test_server_posix.cc4
-rw-r--r--net/test/test_server_win.cc28
-rw-r--r--tools/valgrind/memcheck/suppressions.txt7
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