diff options
Diffstat (limited to 'net/base')
-rw-r--r-- | net/base/cert_test_util.cc | 22 | ||||
-rw-r--r-- | net/base/cookie_monster.cc | 23 | ||||
-rw-r--r-- | net/base/cookie_monster_unittest.cc | 28 | ||||
-rw-r--r-- | net/base/cookie_options.h | 4 | ||||
-rw-r--r-- | net/base/ev_root_ca_metadata.cc | 2 | ||||
-rw-r--r-- | net/base/net_errors.cc | 5 | ||||
-rw-r--r-- | net/base/net_test_suite.h | 6 | ||||
-rw-r--r-- | net/base/net_util.cc | 4 | ||||
-rw-r--r-- | net/base/openssl_util.cc | 86 | ||||
-rw-r--r-- | net/base/openssl_util.h | 59 | ||||
-rw-r--r-- | net/base/ssl_config_service.cc | 3 | ||||
-rw-r--r-- | net/base/ssl_config_service.h | 19 | ||||
-rw-r--r-- | net/base/ssl_config_service_mac.cc | 2 | ||||
-rw-r--r-- | net/base/ssl_config_service_win.cc | 7 | ||||
-rw-r--r-- | net/base/ssl_false_start_blacklist.txt | 1 | ||||
-rw-r--r-- | net/base/x509_certificate.cc | 4 | ||||
-rw-r--r-- | net/base/x509_certificate.h | 10 | ||||
-rw-r--r-- | net/base/x509_certificate_openssl.cc | 48 | ||||
-rw-r--r-- | net/base/x509_openssl_util.cc | 111 | ||||
-rw-r--r-- | net/base/x509_openssl_util.h | 10 | ||||
-rw-r--r-- | net/base/x509_openssl_util_unittest.cc | 102 |
21 files changed, 370 insertions, 186 deletions
diff --git a/net/base/cert_test_util.cc b/net/base/cert_test_util.cc index 1042d50..d5c678e 100644 --- a/net/base/cert_test_util.cc +++ b/net/base/cert_test_util.cc @@ -8,8 +8,9 @@ #if defined(USE_OPENSSL) #include <openssl/err.h> +#include <openssl/ssl.h> #include <openssl/x509v3.h> -#include "net/base/openssl_util.h" +#include "base/openssl_util.h" #elif defined(USE_NSS) #include <cert.h> #include "base/nss_util.h" @@ -27,15 +28,11 @@ namespace net { #if defined(USE_OPENSSL) X509Certificate* AddTemporaryRootCertToStore(X509* x509_cert) { - OpenSSLInitSingleton* openssl_init = GetOpenSSLInitSingleton(); - - if (!X509_STORE_add_cert(openssl_init->x509_store(), 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) { - do { - LOG(ERROR) << "X509_STORE_add_cert error: " << error_code; - } while ((error_code = ERR_get_error()) != 0); + base::ClearOpenSSLERRStack(); return NULL; } } @@ -45,7 +42,7 @@ X509Certificate* AddTemporaryRootCertToStore(X509* x509_cert) { } X509Certificate* LoadTemporaryRootCert(const FilePath& filename) { - EnsureOpenSSLInit(); + base::EnsureOpenSSLInit(); std::string rawcert; if (!file_util::ReadFileToString(filename, &rawcert)) { @@ -53,7 +50,7 @@ X509Certificate* LoadTemporaryRootCert(const FilePath& filename) { return NULL; } - ScopedSSL<BIO, BIO_free_all> cert_bio( + base::ScopedOpenSSL<BIO, BIO_free_all> cert_bio( BIO_new_mem_buf(const_cast<char*>(rawcert.c_str()), rawcert.length())); if (!cert_bio.get()) { @@ -61,8 +58,8 @@ X509Certificate* LoadTemporaryRootCert(const FilePath& filename) { return NULL; } - ScopedSSL<X509, X509_free> pem_cert(PEM_read_bio_X509(cert_bio.get(), - NULL, NULL, 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()); @@ -70,7 +67,8 @@ X509Certificate* LoadTemporaryRootCert(const FilePath& filename) { const unsigned char* der_data = reinterpret_cast<const unsigned char*>(rawcert.c_str()); int der_length = rawcert.length(); - ScopedSSL<X509, X509_free> der_cert(d2i_X509(NULL, &der_data, der_length)); + base::ScopedOpenSSL<X509, X509_free> der_cert(d2i_X509( + NULL, &der_data, der_length)); if (der_cert.get()) return AddTemporaryRootCertToStore(der_cert.get()); diff --git a/net/base/cookie_monster.cc b/net/base/cookie_monster.cc index b1dba50..c287d2d 100644 --- a/net/base/cookie_monster.cc +++ b/net/base/cookie_monster.cc @@ -701,12 +701,8 @@ static std::string CanonPath(const GURL& url, return CanonPathWithString(url, path_string); } -static Time CanonExpiration(const CookieMonster::ParsedCookie& pc, - const Time& current, - const CookieOptions& options) { - if (options.force_session()) - return Time(); - +static Time CanonExpirationInternal(const CookieMonster::ParsedCookie& pc, + const Time& current) { // First, try the Max-Age attribute. uint64 max_age = 0; if (pc.HasMaxAge() && @@ -727,6 +723,21 @@ static Time CanonExpiration(const CookieMonster::ParsedCookie& pc, return Time(); } +static Time CanonExpiration(const CookieMonster::ParsedCookie& pc, + const Time& current, + const CookieOptions& options) { + Time expiration_time = CanonExpirationInternal(pc, current); + + if (options.force_session()) { + // Only override the expiry adte if it's in the future. If the expiry date + // is before the creation date, the cookie is supposed to be deleted. + if (expiration_time.is_null() || expiration_time > current) + return Time(); + } + + return expiration_time; +} + bool CookieMonster::HasCookieableScheme(const GURL& url) { lock_.AssertAcquired(); diff --git a/net/base/cookie_monster_unittest.cc b/net/base/cookie_monster_unittest.cc index 751b255..6fce2a0 100644 --- a/net/base/cookie_monster_unittest.cc +++ b/net/base/cookie_monster_unittest.cc @@ -2033,4 +2033,32 @@ TEST(CookieMonsterTest, GarbageCollectionTriggers) { } } +// This test checks that setting a cookie forcing it to be a session only +// cookie works as expected. +TEST(CookieMonsterTest, ForceSessionOnly) { + GURL url_google(kUrlGoogle); + scoped_refptr<net::CookieMonster> cm(new net::CookieMonster(NULL, NULL)); + net::CookieOptions options; + + // Set a persistent cookie, but force it to be a session cookie. + options.set_force_session(); + ASSERT_TRUE(cm->SetCookieWithOptions(url_google, + std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-22 22:50:13 GMT", + options)); + + // Get the canonical cookie. + CookieMonster::CookieList cookie_list = cm->GetAllCookies(); + ASSERT_EQ(1U, cookie_list.size()); + ASSERT_FALSE(cookie_list[0].IsPersistent()); + + // Use a past expiry date to delete the cookie, but force it to session only. + ASSERT_TRUE(cm->SetCookieWithOptions(url_google, + std::string(kValidCookieLine) + "; expires=Mon, 18-Apr-1977 22:50:13 GMT", + options)); + + // Check that the cookie was deleted. + cookie_list = cm->GetAllCookies(); + ASSERT_EQ(0U, cookie_list.size()); +} + } // namespace diff --git a/net/base/cookie_options.h b/net/base/cookie_options.h index 8ace523..203adaf 100644 --- a/net/base/cookie_options.h +++ b/net/base/cookie_options.h @@ -24,7 +24,9 @@ class CookieOptions { void set_include_httponly() { exclude_httponly_ = false; } bool exclude_httponly() const { return exclude_httponly_; } - // Forces a cookie to be saved as a session cookie. + // Forces a cookie to be saved as a session cookie. If the expiration time of + // the cookie is in the past, i.e. the cookie would end up being deleted, this + // option is ignored. See CookieMonsterTest.ForceSessionOnly. void set_force_session() { force_session_ = true; } bool force_session() const { return force_session_; } diff --git a/net/base/ev_root_ca_metadata.cc b/net/base/ev_root_ca_metadata.cc index 7de971b..661b652 100644 --- a/net/base/ev_root_ca_metadata.cc +++ b/net/base/ev_root_ca_metadata.cc @@ -358,7 +358,7 @@ EVRootCAMetadata::EVRootCAMetadata() { num_policy_oids_ = arraysize(policy_oids_); // Verify policy_oids_ is in ascending order. for (int i = 0; i < num_policy_oids_ - 1; i++) - CHECK(strcmp(policy_oids_[i], policy_oids_[i + 1]) < 0); + DCHECK(strcmp(policy_oids_[i], policy_oids_[i + 1]) < 0); for (size_t i = 0; i < arraysize(ev_root_ca_metadata); i++) { const EVMetadata& metadata = ev_root_ca_metadata[i]; diff --git a/net/base/net_errors.cc b/net/base/net_errors.cc index 18f6661..6c0e09c 100644 --- a/net/base/net_errors.cc +++ b/net/base/net_errors.cc @@ -5,8 +5,7 @@ #include "net/base/net_errors.h" #include "base/basictypes.h" - -#define STRINGIZE(x) #x +#include "base/stringize_macros.h" namespace net { @@ -19,7 +18,7 @@ const char* ErrorToString(int error) { switch (error) { #define NET_ERROR(label, value) \ case ERR_ ## label: \ - return "net::" STRINGIZE(ERR_ ## label); + return "net::" STRINGIZE_NO_EXPANSION(ERR_ ## label); #include "net/base/net_error_list.h" #undef NET_ERROR default: diff --git a/net/base/net_test_suite.h b/net/base/net_test_suite.h index 00d9844..8ebc4e7 100644 --- a/net/base/net_test_suite.h +++ b/net/base/net_test_suite.h @@ -11,7 +11,9 @@ #include "base/test/test_suite.h" #include "build/build_config.h" #include "net/base/mock_host_resolver.h" +#if defined(USE_NSS) #include "net/ocsp/nss_ocsp.h" +#endif class NetTestSuite : public base::TestSuite { public: @@ -41,9 +43,9 @@ class NetTestSuite : public base::TestSuite { } virtual void Shutdown() { -#if defined(OS_LINUX) +#if defined(USE_NSS) net::ShutdownOCSP(); -#endif // defined(OS_LINUX) +#endif // We want to destroy this here before the TestSuite continues to tear down // the environment. diff --git a/net/base/net_util.cc b/net/base/net_util.cc index 5afba6f..5e02544 100644 --- a/net/base/net_util.cc +++ b/net/base/net_util.cc @@ -2041,12 +2041,12 @@ bool IPNumberMatchesPrefix(const IPAddressNumber& ip_number, uint16* GetPortFieldFromAddrinfo(const struct addrinfo* info) { DCHECK(info); if (info->ai_family == AF_INET) { - DCHECK_EQ(sizeof(sockaddr_in), info->ai_addrlen); + DCHECK_EQ(sizeof(sockaddr_in), static_cast<size_t>(info->ai_addrlen)); struct sockaddr_in* sockaddr = reinterpret_cast<struct sockaddr_in*>(info->ai_addr); return &sockaddr->sin_port; } else if (info->ai_family == AF_INET6) { - DCHECK_EQ(sizeof(sockaddr_in6), info->ai_addrlen); + DCHECK_EQ(sizeof(sockaddr_in6), static_cast<size_t>(info->ai_addrlen)); struct sockaddr_in6* sockaddr = reinterpret_cast<struct sockaddr_in6*>(info->ai_addr); return &sockaddr->sin6_port; diff --git a/net/base/openssl_util.cc b/net/base/openssl_util.cc deleted file mode 100644 index 51797ac..0000000 --- a/net/base/openssl_util.cc +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (c) 2006-2008 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/openssl_util.h" - -#include <openssl/err.h> - -#include "base/logging.h" -#include "base/platform_thread.h" - -namespace net { - -namespace { - -// We do certificate verification after handshake, so we disable the default -// by registering a no-op verify function. -int NoOpVerifyCallback(X509_STORE_CTX*, void *) { - DVLOG(3) << "skipping cert verify"; - return 1; -} - -unsigned long CurrentThreadId() { - return static_cast<unsigned long>(PlatformThread::CurrentId()); -} - -SSL_CTX* CreateSSL_CTX() { - SSL_load_error_strings(); - SSL_library_init(); - OpenSSL_add_all_algorithms(); - return SSL_CTX_new(SSLv23_client_method()); -} - -} // namespace - -OpenSSLInitSingleton::OpenSSLInitSingleton() - : ssl_ctx_(CreateSSL_CTX()), - store_(X509_STORE_new()) { - CHECK(ssl_ctx_.get()); - CHECK(store_.get()); - - SSL_CTX_set_cert_verify_callback(ssl_ctx_.get(), NoOpVerifyCallback, NULL); - X509_STORE_set_default_paths(store_.get()); - // TODO(bulach): Enable CRL (see X509_STORE_set_flags(X509_V_FLAG_CRL_CHECK)). - int num_locks = CRYPTO_num_locks(); - for (int i = 0; i < num_locks; ++i) - locks_.push_back(new Lock()); - CRYPTO_set_locking_callback(LockingCallback); - CRYPTO_set_id_callback(CurrentThreadId); -} - -OpenSSLInitSingleton::~OpenSSLInitSingleton() { - CRYPTO_set_locking_callback(NULL); - EVP_cleanup(); - ERR_free_strings(); -} - -OpenSSLInitSingleton* GetOpenSSLInitSingleton() { - return Singleton<OpenSSLInitSingleton>::get(); -} - -void EnsureOpenSSLInit() { - Singleton<OpenSSLInitSingleton>::get(); -} - -// static -void OpenSSLInitSingleton::LockingCallback(int mode, - int n, - const char* file, - int line) { - GetOpenSSLInitSingleton()->OnLockingCallback(mode, n, file, line); -} - -void OpenSSLInitSingleton::OnLockingCallback(int mode, - int n, - const char* file, - int line) { - CHECK_LT(static_cast<size_t>(n), locks_.size()); - if (mode & CRYPTO_LOCK) - locks_[n]->Acquire(); - else - locks_[n]->Release(); -} - -} // namespace net - diff --git a/net/base/openssl_util.h b/net/base/openssl_util.h deleted file mode 100644 index d4603c6..0000000 --- a/net/base/openssl_util.h +++ /dev/null @@ -1,59 +0,0 @@ -// Copyright (c) 2006-2008 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 <openssl/ssl.h> - -#include "base/lock.h" -#include "base/scoped_vector.h" -#include "base/singleton.h" - -namespace net { - -// A helper class that takes care of destroying OpenSSL objects when it goes out -// of scope. -template <typename T, void (*destructor)(T*)> -class ScopedSSL { - public: - explicit ScopedSSL(T* ptr_) : ptr_(ptr_) { } - ~ScopedSSL() { if (ptr_) (*destructor)(ptr_); } - - T* get() const { return ptr_; } - - private: - T* ptr_; -}; - -// Singleton for initializing / cleaning up OpenSSL and holding a X509 store. -// Access it via GetOpenSSLInitSingleton(). -class OpenSSLInitSingleton { - public: - SSL_CTX* ssl_ctx() const { return ssl_ctx_.get(); } - X509_STORE* x509_store() const { return store_.get(); } - - private: - friend struct DefaultSingletonTraits<OpenSSLInitSingleton>; - OpenSSLInitSingleton(); - ~OpenSSLInitSingleton(); - - static void LockingCallback(int mode, int n, const char* file, int line); - void OnLockingCallback(int mode, int n, const char* file, int line); - - ScopedSSL<SSL_CTX, SSL_CTX_free> ssl_ctx_; - ScopedSSL<X509_STORE, X509_STORE_free> store_; - // These locks are used and managed by OpenSSL via LockingCallback(). - ScopedVector<Lock> locks_; - - DISALLOW_COPY_AND_ASSIGN(OpenSSLInitSingleton); -}; - -OpenSSLInitSingleton* GetOpenSSLInitSingleton(); - -// Initialize OpenSSL if it isn't already initialized. This must be called -// before any other OpenSSL functions (except GetOpenSSLInitSingleton above). -// This function is thread-safe, and OpenSSL will only ever be initialized once. -// OpenSSL will be properly shut down on program exit. -void EnsureOpenSSLInit(); - -} // namespace net - diff --git a/net/base/ssl_config_service.cc b/net/base/ssl_config_service.cc index 5c38f97..041c720 100644 --- a/net/base/ssl_config_service.cc +++ b/net/base/ssl_config_service.cc @@ -22,6 +22,7 @@ SSLConfig::CertAndStatus::~CertAndStatus() {} SSLConfig::SSLConfig() : rev_checking_enabled(true), ssl2_enabled(false), ssl3_enabled(true), tls1_enabled(true), dnssec_enabled(false), snap_start_enabled(false), + dns_cert_provenance_checking_enabled(false), mitm_proxies_allowed(false), false_start_enabled(true), send_client_cert(false), verify_ev_cert(false), ssl3_fallback(false) { } @@ -31,7 +32,7 @@ SSLConfig::~SSLConfig() { bool SSLConfig::IsAllowedBadCert(X509Certificate* cert) const { for (size_t i = 0; i < allowed_bad_certs.size(); ++i) { - if (cert == allowed_bad_certs[i].cert) + if (cert->Equals(allowed_bad_certs[i].cert)) return true; } return false; diff --git a/net/base/ssl_config_service.h b/net/base/ssl_config_service.h index be50097..0639f48 100644 --- a/net/base/ssl_config_service.h +++ b/net/base/ssl_config_service.h @@ -8,6 +8,7 @@ #include <vector> +#include "base/basictypes.h" #include "base/observer_list.h" #include "base/ref_counted.h" #include "net/base/x509_certificate.h" @@ -31,6 +32,24 @@ struct SSLConfig { // True if we'll do async checks for certificate provenance using DNS. bool dns_cert_provenance_checking_enabled; + // Cipher suites which should be explicitly prevented from being used. By + // default, all cipher suites supported by the underlying SSL implementation + // will be enabled, except for: + // - Null encryption cipher suites. + // - Weak cipher suites: < 80 bits of security strength. + // - FORTEZZA cipher suites (obsolete). + // - IDEA cipher suites (RFC 5469 explains why). + // - Anonymous cipher suites. + // + // Though cipher suites are sent in TLS as "uint8 CipherSuite[2]", in + // big-endian form, they should be declared in host byte order, with the + // first uint8 occupying the most significant byte. + // Ex: To disable TLS_RSA_WITH_RC4_128_MD5, specify 0x0004, while to + // disable TLS_ECDH_ECDSA_WITH_RC4_128_SHA, specify 0xC002. + // + // TODO(rsleevi): Not implemented when using OpenSSL or Schannel. + std::vector<uint16> disabled_cipher_suites; + // True if we allow this connection to be MITM attacked. This sounds a little // worse than it is: large networks sometimes MITM attack all SSL connections // on egress. We want to know this because we might not have the end-to-end diff --git a/net/base/ssl_config_service_mac.cc b/net/base/ssl_config_service_mac.cc index 2ce1d5c..148bba4 100644 --- a/net/base/ssl_config_service_mac.cc +++ b/net/base/ssl_config_service_mac.cc @@ -97,6 +97,8 @@ bool SSLConfigServiceMac::GetSSLConfigNow(SSLConfig* config) { kTLS1EnabledDefaultValue); SSLConfigService::SetSSLConfigFlags(config); + // TODO(rsleevi): http://crbug.com/58831 - Implement preferences for + // disabling cipher suites. return true; } diff --git a/net/base/ssl_config_service_win.cc b/net/base/ssl_config_service_win.cc index debea7d..d4153c3 100644 --- a/net/base/ssl_config_service_win.cc +++ b/net/base/ssl_config_service_win.cc @@ -82,6 +82,13 @@ bool SSLConfigServiceWin::GetSSLConfigNow(SSLConfig* config) { config->tls1_enabled = ((protocols & TLS1) != 0); SSLConfigService::SetSSLConfigFlags(config); + // TODO(rsleevi): Possibly respect the registry keys defined in + // http://support.microsoft.com/kb/245030 (pre-Vista) or + // http://msdn.microsoft.com/en-us/library/bb870930(VS.85).aspx (post-Vista). + // Currently, these values are respected implicitly when using + // SSLClientSocketWin, but they do not propogate to SSLClientSocketNSS + // because we're not currently translating the keys. + return true; } diff --git a/net/base/ssl_false_start_blacklist.txt b/net/base/ssl_false_start_blacklist.txt index d782b5b..26147de 100644 --- a/net/base/ssl_false_start_blacklist.txt +++ b/net/base/ssl_false_start_blacklist.txt @@ -2553,6 +2553,7 @@ mingorp.hr misd.net mishlohim.co.il misim.gov.il +mit.edu mitserv.com mizuho-int.com mizuno.co.jp diff --git a/net/base/x509_certificate.cc b/net/base/x509_certificate.cc index 7bbce5c..d93d270 100644 --- a/net/base/x509_certificate.cc +++ b/net/base/x509_certificate.cc @@ -322,6 +322,10 @@ bool X509Certificate::HasExpired() const { return base::Time::Now() > valid_expiry(); } +bool X509Certificate::Equals(const X509Certificate* other) const { + return IsSameOSCert(cert_handle_, other->cert_handle_); +} + bool X509Certificate::HasIntermediateCertificate(OSCertHandle cert) { #if defined(OS_MACOSX) || defined(OS_WIN) || defined(USE_OPENSSL) for (size_t i = 0; i < intermediate_ca_certs_.size(); ++i) { diff --git a/net/base/x509_certificate.h b/net/base/x509_certificate.h index 2bce631..462758c 100644 --- a/net/base/x509_certificate.h +++ b/net/base/x509_certificate.h @@ -26,6 +26,7 @@ #elif defined(USE_OPENSSL) // Forward declaration; real one in <x509.h> struct x509_st; +typedef struct x509_store_st X509_STORE; #elif defined(USE_NSS) // Forward declaration; real one in <cert.h> struct CERTCertificateStr; @@ -182,6 +183,9 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { // now. bool HasExpired() const; + // Returns true if this object and |other| represent the same certificate. + bool Equals(const X509Certificate* other) const; + // Returns intermediate certificates added via AddIntermediateCertificate(). // Ownership follows the "get" rule: it is the caller's responsibility to // retain the elements of the result. @@ -231,6 +235,12 @@ class X509Certificate : public base::RefCountedThreadSafe<X509Certificate> { static HCERTSTORE cert_store(); #endif +#if defined(USE_OPENSSL) + // Returns a handle to a global, in-memory certificate store. We + // use it for test code, e.g. importing the test server's certificate. + static X509_STORE* cert_store(); +#endif + // Verifies the certificate against the given hostname. Returns OK if // successful or an error code upon failure. // diff --git a/net/base/x509_certificate_openssl.cc b/net/base/x509_certificate_openssl.cc index f09aa6d..f0a3dc1 100644 --- a/net/base/x509_certificate_openssl.cc +++ b/net/base/x509_certificate_openssl.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. @@ -13,13 +13,13 @@ #include <openssl/ssl.h> #include <openssl/x509v3.h> +#include "base/openssl_util.h" #include "base/pickle.h" #include "base/singleton.h" #include "base/string_number_conversions.h" #include "net/base/cert_status_flags.h" #include "net/base/cert_verify_result.h" #include "net/base/net_errors.h" -#include "net/base/openssl_util.h" #include "net/base/x509_openssl_util.h" namespace net { @@ -31,8 +31,9 @@ namespace { void CreateOSCertHandlesFromPKCS7Bytes( const char* data, int length, X509Certificate::OSCertHandles* handles) { + base::EnsureOpenSSLInit(); const unsigned char* der_data = reinterpret_cast<const unsigned char*>(data); - ScopedSSL<PKCS7, PKCS7_free> pkcs7_cert( + base::ScopedOpenSSL<PKCS7, PKCS7_free> pkcs7_cert( d2i_PKCS7(NULL, &der_data, length)); if (!pkcs7_cert.get()) return; @@ -98,7 +99,7 @@ void ParseSubjectAltNames(X509Certificate::OSCertHandle cert, if (!alt_name_ext) return; - ScopedSSL<GENERAL_NAMES, GENERAL_NAMES_free> alt_names( + base::ScopedOpenSSL<GENERAL_NAMES, GENERAL_NAMES_free> alt_names( reinterpret_cast<GENERAL_NAMES*>(X509V3_EXT_d2i(alt_name_ext))); if (!alt_names.get()) return; @@ -206,16 +207,22 @@ void DERCache_free(void* parent, void* ptr, CRYPTO_EX_DATA* ad, int idx, class X509InitSingleton { public: int der_cache_ex_index() const { return der_cache_ex_index_; } + X509_STORE* store() const { return store_.get(); } private: friend struct DefaultSingletonTraits<X509InitSingleton>; - X509InitSingleton() { - der_cache_ex_index_ = X509_get_ex_new_index(0, 0, 0, 0, DERCache_free); + X509InitSingleton() + : der_cache_ex_index_((base::EnsureOpenSSLInit(), + X509_get_ex_new_index(0, 0, 0, 0, + DERCache_free))), + store_(X509_STORE_new()) { 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)). } - ~X509InitSingleton() {} int der_cache_ex_index_; + base::ScopedOpenSSL<X509_STORE, X509_STORE_free> store_; DISALLOW_COPY_AND_ASSIGN(X509InitSingleton); }; @@ -290,6 +297,7 @@ void X509Certificate::FreeOSCertHandle(OSCertHandle cert_handle) { } void X509Certificate::Initialize() { + base::EnsureOpenSSLInit(); fingerprint_ = CalculateFingerprint(cert_handle_); ParsePrincipal(cert_handle_, X509_get_subject_name(cert_handle_), &subject_); ParsePrincipal(cert_handle_, X509_get_issuer_name(cert_handle_), &issuer_); @@ -311,6 +319,7 @@ X509Certificate::OSCertHandle X509Certificate::CreateOSCertHandleFromBytes( const char* data, int length) { if (length < 0) return NULL; + base::EnsureOpenSSLInit(); const unsigned char* d2i_data = reinterpret_cast<const unsigned char*>(data); // Don't cache this data via SetDERCache as this wire format may be not be @@ -346,6 +355,7 @@ X509Certificate::OSCertHandles X509Certificate::CreateOSCertHandlesFromBytes( return results; } +// static X509Certificate* X509Certificate::CreateFromPickle(const Pickle& pickle, void** pickle_iter) { const char* data; @@ -374,16 +384,33 @@ void X509Certificate::GetDNSNames(std::vector<std::string>* dns_names) const { dns_names->push_back(subject_.common_name); } +<<<<<<< HEAD #ifndef ANDROID +======= +// static +X509_STORE* X509Certificate::cert_store() { + return Singleton<X509InitSingleton>::get()->store(); +} +>>>>>>> chromium.org at r66597 int X509Certificate::Verify(const std::string& hostname, int flags, CertVerifyResult* verify_result) const { verify_result->Reset(); - ScopedSSL<X509_STORE_CTX, X509_STORE_CTX_free> ctx(X509_STORE_CTX_new()); + // TODO(joth): We should fetch the subjectAltNames directly rather than via + // GetDNSNames, so we can apply special handling for IP addresses vs DNS + // names, etc. See http://crbug.com/62973. + std::vector<std::string> cert_names; + GetDNSNames(&cert_names); + if (!x509_openssl_util::VerifyHostname(hostname, cert_names)) + verify_result->cert_status |= CERT_STATUS_COMMON_NAME_INVALID; + + base::ScopedOpenSSL<X509_STORE_CTX, X509_STORE_CTX_free> ctx( + X509_STORE_CTX_new()); - ScopedSSL<STACK_OF(X509), sk_X509_free_fn> intermediates(sk_X509_new_null()); + base::ScopedOpenSSL<STACK_OF(X509), sk_X509_free_fn> intermediates( + sk_X509_new_null()); if (!intermediates.get()) return ERR_OUT_OF_MEMORY; @@ -392,8 +419,7 @@ int X509Certificate::Verify(const std::string& hostname, if (!sk_X509_push(intermediates.get(), *it)) return ERR_OUT_OF_MEMORY; } - int rv = X509_STORE_CTX_init(ctx.get(), - GetOpenSSLInitSingleton()->x509_store(), + int rv = X509_STORE_CTX_init(ctx.get(), cert_store(), cert_handle_, intermediates.get()); CHECK_EQ(1, rv); diff --git a/net/base/x509_openssl_util.cc b/net/base/x509_openssl_util.cc index 22ab59a..9e44c4b 100644 --- a/net/base/x509_openssl_util.cc +++ b/net/base/x509_openssl_util.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. @@ -7,6 +7,7 @@ #include "base/logging.h" #include "base/string_number_conversions.h" #include "base/string_piece.h" +#include "base/string_util.h" #include "base/time.h" namespace net { @@ -108,6 +109,114 @@ bool ParseDate(ASN1_TIME* x509_time, base::Time* time) { return true; } +// TODO(joth): Investigate if we can upstream this into the OpenSSL library, +// to avoid duplicating this logic across projects. +bool VerifyHostname(const std::string& hostname, + const std::vector<std::string>& cert_names) { + DCHECK(!hostname.empty()); + + // Simple host name validation. A valid domain name must only contain + // alpha, digits, hyphens, and dots. An IP address may have digits and dots, + // and also square braces and colons for IPv6 addresses. + std::string reference_name; + reference_name.reserve(hostname.length()); + + bool found_alpha = false; + bool found_ip6_chars = false; + bool found_hyphen = false; + int dot_count = 0; + + size_t first_dot_index = std::string::npos; + for (std::string::const_iterator it = hostname.begin(); + it != hostname.end(); ++it) { + char c = *it; + if (IsAsciiAlpha(c)) { + found_alpha = true; + c = base::ToLowerASCII(c); + } else if (c == '.') { + ++dot_count; + if (first_dot_index == std::string::npos) + first_dot_index = reference_name.length(); + } else if (c == ':') { + found_ip6_chars = true; + } else if (c == '-') { + found_hyphen = true; + } else if (!IsAsciiDigit(c)) { + LOG(WARNING) << "Invalid char " << c << " in hostname " << hostname; + return false; + } + reference_name.push_back(c); + } + DCHECK(!reference_name.empty()); + + // TODO(joth): Add IP address support. See http://crbug.com/62973 + if (found_ip6_chars || !found_alpha) { + NOTIMPLEMENTED() << hostname; + return false; + } + + // |wildcard_domain| is the remainder of |host| after the leading host + // component is stripped off, but includes the leading dot e.g. + // "www.f.com" -> ".f.com". + // If there is no meaningful domain part to |host| (e.g. it is an IP address + // or contains no dots) then |wildcard_domain| will be empty. + // We required at least 3 components (i.e. 2 dots) as a basic protection + // against too-broad wild-carding. + base::StringPiece wildcard_domain; + if (found_alpha && !found_ip6_chars && dot_count >= 2) { + DCHECK(first_dot_index != std::string::npos); + wildcard_domain = reference_name; + wildcard_domain.remove_prefix(first_dot_index); + DCHECK(wildcard_domain.starts_with(".")); + } + + for (std::vector<std::string>::const_iterator it = cert_names.begin(); + it != cert_names.end(); ++it) { + // Catch badly corrupt cert names up front. + if (it->empty() || it->find('\0') != std::string::npos) { + LOG(WARNING) << "Bad name in cert: " << *it; + continue; + } + const std::string cert_name_string(StringToLowerASCII(*it)); + base::StringPiece cert_match(cert_name_string); + + // Remove trailing dot, if any. + if (cert_match.ends_with(".")) + cert_match.remove_suffix(1); + + // The hostname must be at least as long as the cert name it is matching, + // as we require the wildcard (if present) to match at least one character. + if (cert_match.length() > reference_name.length()) + continue; + + if (cert_match == reference_name) + return true; + + // Next see if this cert name starts with a wildcard, so long as the + // hostname we're matching against has a valid 'domain' part to match. + // Note the "-10" version of draft-saintandre-tls-server-id-check allows + // the wildcard to appear anywhere in the leftmost label, rather than + // requiring it to be the only character. See also http://crbug.com/60719 + if (wildcard_domain.empty() || !cert_match.starts_with("*")) + continue; + + // Erase the * but not the . from the domain, as we need to include the dot + // in the comparison. + cert_match.remove_prefix(1); + + // Do character by character comparison on the remainder to see + // if we have a wildcard match. This intentionally does no special handling + // for any other wildcard characters in |domain|; alternatively it could + // detect these and skip those candidate cert names. + if (cert_match == wildcard_domain) + return true; + } + DVLOG(1) << "Could not find any match for " << hostname + << " (canonicalized as " << reference_name + << ") in cert names " << JoinString(cert_names, '|'); + return false; +} + } // namespace x509_openssl_util } // namespace net diff --git a/net/base/x509_openssl_util.h b/net/base/x509_openssl_util.h index 5ac511b..4a6fa24 100644 --- a/net/base/x509_openssl_util.h +++ b/net/base/x509_openssl_util.h @@ -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. @@ -10,6 +10,7 @@ #include <openssl/x509v3.h> #include <string> +#include <vector> namespace base { class Time; @@ -32,6 +33,13 @@ bool ParsePrincipalValueByNID(X509_NAME* name, int nid, std::string* value); bool ParseDate(ASN1_TIME* x509_time, base::Time* time); +// Verifies that |hostname| matches one of the names in |cert_names|, based on +// TLS name matching rules, specifically following http://tools.ietf.org/html/draft-saintandre-tls-server-id-check-09#section-4.4.3 +// The members of |cert_names| must have been extracted from the Subject CN or +// SAN fields of a certificate. +bool VerifyHostname(const std::string& hostname, + const std::vector<std::string>& cert_names); + } // namespace x509_openssl_util } // namespace net diff --git a/net/base/x509_openssl_util_unittest.cc b/net/base/x509_openssl_util_unittest.cc new file mode 100644 index 0000000..50589ad --- /dev/null +++ b/net/base/x509_openssl_util_unittest.cc @@ -0,0 +1,102 @@ +// 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/x509_openssl_util.h" + +#include <algorithm> + +#include "base/string_split.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace net { + +namespace x509_openssl_util { + +namespace { + +struct CertificateNameVerifyTestData { + // true iff we expect hostname to match an entry in cert_names. + const bool expected; + // The hostname to match. + const char* const hostname; + // '/' separated list of certificate names to match against. Any occurrence + // of '#' will be replaced with a null character before processing. + const char* const cert_names; +}; + +CertificateNameVerifyTestData kNameVerifyTestData[] = { + { true, "foo.com", "foo.com" }, + { true, "foo.com", "foo.com." }, + { true, "f", "f" }, + { true, "f", "f." }, + { true, "bar.foo.com", "*.foo.com" }, + { true, "www-3.bar.foo.com", "*.bar.foo.com." }, + { true, "www.test.fr", "*.test.com/*.test.co.uk/*.test.de/*.test.fr" }, + { true, "wwW.tESt.fr", "//*.*/*.test.de/*.test.FR/www" }, + { false, "foo.com", "*.com" }, + { false, "f.uk", ".uk" }, + { true, "h.co.uk", "*.co.uk" }, + { false, "192.168.1.11", "*.168.1.11" }, + { false, "foo.us", "*.us" }, + { false, "www.bar.foo.com", + "*.foo.com/*.*.foo.com/*.*.bar.foo.com/*w*.bar.foo.com/*..bar.foo.com" }, + { false, "w.bar.foo.com", "?.bar.foo.com" }, + { false, "www.foo.com", "(www|ftp).foo.com" }, + { false, "www.foo.com", "www.foo.com#*.foo.com/#" }, // # = null char. + { false, "foo", "*" }, + { false, "foo.", "*." }, + { false, "test.org", "www.test.org/*.test.org/*.org" }, + { false, "1.2.3.4.5.6", "*.2.3.4.5.6" }, + // IDN tests + { true, "xn--poema-9qae5a.com.br", "xn--poema-9qae5a.com.br" }, + { true, "www.xn--poema-9qae5a.com.br", "*.xn--poema-9qae5a.com.br" }, + { false, "xn--poema-9qae5a.com.br", "*.xn--poema-9qae5a.com.br" }, + // The following are adapted from the examples in http://tools.ietf.org/html/draft-saintandre-tls-server-id-check-09#section-4.4.3 + { true, "foo.example.com", "*.example.com" }, + { false, "bar.foo.example.com", "*.example.com" }, + { false, "example.com", "*.example.com" }, + { false, "baz1.example.net", "baz*.example.net" }, + { false, "baz2.example.net", "baz*.example.net" }, + { false, "bar.*.example.net", "bar.*.example.net" }, + { false, "bar.f*o.example.net", "bar.f*o.example.net" }, + // IP addresses currently not supported. + { false, "192.168.1.1", "192.168.1.1" }, + { false, "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210", + "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210" }, + { false, "FEDC:BA98:7654:3210:FEDC:BA98:7654:3210", "*.]" }, + { false, "::192.9.5.5", "::192.9.5.5" }, + { false, "::192.9.5.5", "*.9.5.5" }, + { false, "2010:836B:4179::836B:4179", "*:836B:4179::836B:4179" }, + // Invalid host names. + { false, "www%26.foo.com", "www%26.foo.com" }, + { false, "www.*.com", "www.*.com" }, + { false, "w$w.f.com", "w$w.f.com" }, + { false, "www-1.[::FFFF:129.144.52.38]", "*.[::FFFF:129.144.52.38]" }, +}; + +class X509CertificateNameVerifyTest + : public testing::TestWithParam<CertificateNameVerifyTestData> { +}; + +TEST_P(X509CertificateNameVerifyTest, VerifyHostname) { + CertificateNameVerifyTestData test_data(GetParam()); + + std::string cert_name_line(test_data.cert_names); + std::replace(cert_name_line.begin(), cert_name_line.end(), '#', '\0'); + std::vector<std::string> cert_names; + base::SplitString(cert_name_line, '/', &cert_names); + + EXPECT_EQ(test_data.expected, VerifyHostname(test_data.hostname, cert_names)) + << "Host [" << test_data.hostname + << "], cert name [" << test_data.cert_names << "]"; +} + +INSTANTIATE_TEST_CASE_P(, X509CertificateNameVerifyTest, + testing::ValuesIn(kNameVerifyTestData)); + +} // namespace + +} // namespace x509_openssl_util + +} // namespace net |