summaryrefslogtreecommitdiffstats
path: root/net/base
diff options
context:
space:
mode:
Diffstat (limited to 'net/base')
-rw-r--r--net/base/cert_test_util.cc22
-rw-r--r--net/base/cookie_monster.cc23
-rw-r--r--net/base/cookie_monster_unittest.cc28
-rw-r--r--net/base/cookie_options.h4
-rw-r--r--net/base/ev_root_ca_metadata.cc2
-rw-r--r--net/base/net_errors.cc5
-rw-r--r--net/base/net_test_suite.h6
-rw-r--r--net/base/net_util.cc4
-rw-r--r--net/base/openssl_util.cc86
-rw-r--r--net/base/openssl_util.h59
-rw-r--r--net/base/ssl_config_service.cc3
-rw-r--r--net/base/ssl_config_service.h19
-rw-r--r--net/base/ssl_config_service_mac.cc2
-rw-r--r--net/base/ssl_config_service_win.cc7
-rw-r--r--net/base/ssl_false_start_blacklist.txt1
-rw-r--r--net/base/x509_certificate.cc4
-rw-r--r--net/base/x509_certificate.h10
-rw-r--r--net/base/x509_certificate_openssl.cc48
-rw-r--r--net/base/x509_openssl_util.cc111
-rw-r--r--net/base/x509_openssl_util.h10
-rw-r--r--net/base/x509_openssl_util_unittest.cc102
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