summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authorrsleevi@chromium.org <rsleevi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-16 00:01:37 +0000
committerrsleevi@chromium.org <rsleevi@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-12-16 00:01:37 +0000
commit32765f80889421c6161a7b9e73bc1ee722db6892 (patch)
treebba0f974c84f9859da5b62bc233d00955e63c032 /net
parent235478be87f59f3962eda9d8f3fba04e8a5096e4 (diff)
downloadchromium_src-32765f80889421c6161a7b9e73bc1ee722db6892.zip
chromium_src-32765f80889421c6161a7b9e73bc1ee722db6892.tar.gz
chromium_src-32765f80889421c6161a7b9e73bc1ee722db6892.tar.bz2
Add support for temporarily trusting a certificate for the duration of unit tests on Windows, rather than requiring the machine to be pre-configured out-of-band.
Given the lack of a Microsoft-provided high-level API to supply application-level trusts to the verification routines, this implements a workaround that intercepts attempts to open the trusted system root store and injects the test certificates directly. This allows the unit tests to work without requiring that the Test CA be added to the machine's Trusted Certificates store. While doing so, clean up the interface to adding/removing trusted test certificates, so as to support more than one trusted certificate if necessary. BUG=8470 TEST=To follow Review URL: http://codereview.chromium.org/4646001 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@69351 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-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
18 files changed, 784 insertions, 331 deletions
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