summaryrefslogtreecommitdiffstats
path: root/net
diff options
context:
space:
mode:
authoragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-12 22:22:19 +0000
committeragl@chromium.org <agl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-12-12 22:22:19 +0000
commitc3456bbc289c54765e89d628ba03505425fe372a (patch)
tree7d027edc8ea0a358bd5dcfb227fbc48ea8ca26ab /net
parent9b10d2a051ec36c4e7f012d31b8214221db16eaa (diff)
downloadchromium_src-c3456bbc289c54765e89d628ba03505425fe372a.zip
chromium_src-c3456bbc289c54765e89d628ba03505425fe372a.tar.gz
chromium_src-c3456bbc289c54765e89d628ba03505425fe372a.tar.bz2
net: split the SSL session cache between incognito and normal.
This change causes incognito requests to effectively have a different SSL session cache from other requests. SSL session information will therefore not leak into or out of incognito mode. BUG=30877 TEST=net_unittests Review URL: http://codereview.chromium.org/8857002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@114098 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'net')
-rw-r--r--net/http/http_cache.cc4
-rw-r--r--net/http/http_cache.h1
-rw-r--r--net/http/http_network_session.cc1
-rw-r--r--net/http/http_network_session.h1
-rw-r--r--net/http/http_network_transaction_unittest.cc2
-rw-r--r--net/http/http_proxy_client_socket_pool_unittest.cc1
-rw-r--r--net/http/http_stream_factory_impl_unittest.cc2
-rw-r--r--net/net.gyp2
-rw-r--r--net/socket/client_socket_factory.cc16
-rw-r--r--net/socket/client_socket_pool_manager_impl.cc5
-rw-r--r--net/socket/client_socket_pool_manager_impl.h2
-rw-r--r--net/socket/ssl_client_socket.h14
-rw-r--r--net/socket/ssl_client_socket_nss.cc10
-rw-r--r--net/socket/ssl_client_socket_nss.h7
-rw-r--r--net/socket/ssl_client_socket_nss_unittest.cc17
-rw-r--r--net/socket/ssl_client_socket_openssl.cc68
-rw-r--r--net/socket/ssl_client_socket_openssl.h7
-rw-r--r--net/socket/ssl_client_socket_pool.cc4
-rw-r--r--net/socket/ssl_client_socket_pool.h1
-rw-r--r--net/socket/ssl_client_socket_pool_unittest.cc1
-rw-r--r--net/socket/ssl_client_socket_unittest.cc6
-rw-r--r--net/test/test_server.cc9
-rw-r--r--net/test/test_server.h5
-rwxr-xr-xnet/tools/testserver/testserver.py49
-rw-r--r--net/url_request/url_request_unittest.cc149
25 files changed, 320 insertions, 64 deletions
diff --git a/net/http/http_cache.cc b/net/http/http_cache.cc
index 4aa37fa..8d79918 100644
--- a/net/http/http_cache.cc
+++ b/net/http/http_cache.cc
@@ -49,6 +49,7 @@ HttpNetworkSession* CreateNetworkSession(
DnsCertProvenanceChecker* dns_cert_checker,
ProxyService* proxy_service,
SSLHostInfoFactory* ssl_host_info_factory,
+ const std::string& ssl_session_cache_shard,
SSLConfigService* ssl_config_service,
HttpAuthHandlerFactory* http_auth_handler_factory,
NetworkDelegate* network_delegate,
@@ -62,6 +63,7 @@ HttpNetworkSession* CreateNetworkSession(
params.dns_cert_checker = dns_cert_checker;
params.proxy_service = proxy_service;
params.ssl_host_info_factory = ssl_host_info_factory;
+ params.ssl_session_cache_shard = ssl_session_cache_shard;
params.ssl_config_service = ssl_config_service;
params.http_auth_handler_factory = http_auth_handler_factory;
params.network_delegate = network_delegate;
@@ -321,6 +323,7 @@ HttpCache::HttpCache(HostResolver* host_resolver,
TransportSecurityState* transport_security_state,
DnsCertProvenanceChecker* dns_cert_checker_,
ProxyService* proxy_service,
+ const std::string& ssl_session_cache_shard,
SSLConfigService* ssl_config_service,
HttpAuthHandlerFactory* http_auth_handler_factory,
NetworkDelegate* network_delegate,
@@ -344,6 +347,7 @@ HttpCache::HttpCache(HostResolver* host_resolver,
dns_cert_checker_,
proxy_service,
ssl_host_info_factory_.get(),
+ ssl_session_cache_shard,
ssl_config_service,
http_auth_handler_factory,
network_delegate,
diff --git a/net/http/http_cache.h b/net/http/http_cache.h
index 0e12443..62c4668 100644
--- a/net/http/http_cache.h
+++ b/net/http/http_cache.h
@@ -126,6 +126,7 @@ class NET_EXPORT HttpCache : public HttpTransactionFactory,
TransportSecurityState* transport_security_state,
DnsCertProvenanceChecker* dns_cert_checker,
ProxyService* proxy_service,
+ const std::string& ssl_session_cache_shard,
SSLConfigService* ssl_config_service,
HttpAuthHandlerFactory* http_auth_handler_factory,
NetworkDelegate* network_delegate,
diff --git a/net/http/http_network_session.cc b/net/http/http_network_session.cc
index cc3fdd1f..1c56bc5 100644
--- a/net/http/http_network_session.cc
+++ b/net/http/http_network_session.cc
@@ -43,6 +43,7 @@ HttpNetworkSession::HttpNetworkSession(const Params& params)
params.transport_security_state,
params.dns_cert_checker,
params.ssl_host_info_factory,
+ params.ssl_session_cache_shard,
params.proxy_service,
params.ssl_config_service)),
spdy_session_pool_(params.host_resolver,
diff --git a/net/http/http_network_session.h b/net/http/http_network_session.h
index 083d807..5dcf825 100644
--- a/net/http/http_network_session.h
+++ b/net/http/http_network_session.h
@@ -74,6 +74,7 @@ class NET_EXPORT HttpNetworkSession
DnsCertProvenanceChecker* dns_cert_checker;
ProxyService* proxy_service;
SSLHostInfoFactory* ssl_host_info_factory;
+ std::string ssl_session_cache_shard;
SSLConfigService* ssl_config_service;
HttpAuthHandlerFactory* http_auth_handler_factory;
NetworkDelegate* network_delegate;
diff --git a/net/http/http_network_transaction_unittest.cc b/net/http/http_network_transaction_unittest.cc
index 60febd5..72a648f 100644
--- a/net/http/http_network_transaction_unittest.cc
+++ b/net/http/http_network_transaction_unittest.cc
@@ -371,7 +371,7 @@ CaptureGroupNameSSLSocketPool::CaptureGroupNameSocketPool(
HostResolver* host_resolver,
CertVerifier* cert_verifier)
: SSLClientSocketPool(0, 0, NULL, host_resolver, cert_verifier, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL) {}
+ NULL, NULL, "", NULL, NULL, NULL, NULL, NULL, NULL) {}
//-----------------------------------------------------------------------------
diff --git a/net/http/http_proxy_client_socket_pool_unittest.cc b/net/http/http_proxy_client_socket_pool_unittest.cc
index d7b56867..e68de73 100644
--- a/net/http/http_proxy_client_socket_pool_unittest.cc
+++ b/net/http/http_proxy_client_socket_pool_unittest.cc
@@ -71,6 +71,7 @@ class HttpProxyClientSocketPoolTest : public TestWithHttpParam {
NULL /* dnsrr_resolver */,
NULL /* dns_cert_checker */,
NULL /* ssl_host_info_factory */,
+ "" /* ssl_session_cache_shard */,
&socket_factory_,
&transport_socket_pool_,
NULL,
diff --git a/net/http/http_stream_factory_impl_unittest.cc b/net/http/http_stream_factory_impl_unittest.cc
index 3b47b1d..0c5a8e4 100644
--- a/net/http/http_stream_factory_impl_unittest.cc
+++ b/net/http/http_stream_factory_impl_unittest.cc
@@ -270,7 +270,7 @@ template<>
CapturePreconnectsSSLSocketPool::CapturePreconnectsSocketPool(
HostResolver* host_resolver, CertVerifier* cert_verifier)
: SSLClientSocketPool(0, 0, NULL, host_resolver, cert_verifier, NULL, NULL,
- NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL),
+ NULL, NULL, "", NULL, NULL, NULL, NULL, NULL, NULL),
last_num_streams_(-1) {}
TEST(HttpStreamFactoryTest, PreconnectDirect) {
diff --git a/net/net.gyp b/net/net.gyp
index 446c628..033ac21 100644
--- a/net/net.gyp
+++ b/net/net.gyp
@@ -1147,7 +1147,6 @@
'socket/socks5_client_socket_unittest.cc',
'socket/socks_client_socket_pool_unittest.cc',
'socket/socks_client_socket_unittest.cc',
- 'socket/ssl_client_socket_nss_unittest.cc',
'socket/ssl_client_socket_pool_unittest.cc',
'socket/ssl_client_socket_unittest.cc',
'socket/ssl_server_socket_unittest.cc',
@@ -1243,7 +1242,6 @@
'base/x509_util_nss_unittest.cc',
'base/cert_database_nss_unittest.cc',
'base/dnssec_unittest.cc',
- 'socket/ssl_client_socket_nss_unittest.cc',
],
}, { # else !use_openssl: remove the unneeded files
'sources!': [
diff --git a/net/socket/client_socket_factory.cc b/net/socket/client_socket_factory.cc
index dd7746d..60daef4 100644
--- a/net/socket/client_socket_factory.cc
+++ b/net/socket/client_socket_factory.cc
@@ -102,22 +102,8 @@ class DefaultClientSocketFactory : public ClientSocketFactory,
#endif
}
- // TODO(rch): This is only implemented for the NSS SSL library, which is the
- /// default for Windows, Mac and Linux, but we should implement it everywhere.
void ClearSSLSessionCache() {
-#if defined(OS_WIN)
- if (!g_use_system_ssl)
- SSLClientSocketNSS::ClearSessionCache();
-#elif defined(USE_OPENSSL)
- // no-op
-#elif defined(USE_NSS)
- SSLClientSocketNSS::ClearSessionCache();
-#elif defined(OS_MACOSX)
- if (!g_use_system_ssl)
- SSLClientSocketNSS::ClearSessionCache();
-#else
- NOTIMPLEMENTED();
-#endif
+ SSLClientSocket::ClearSessionCache();
}
};
diff --git a/net/socket/client_socket_pool_manager_impl.cc b/net/socket/client_socket_pool_manager_impl.cc
index 3159c09..a4f9b72 100644
--- a/net/socket/client_socket_pool_manager_impl.cc
+++ b/net/socket/client_socket_pool_manager_impl.cc
@@ -41,6 +41,7 @@ ClientSocketPoolManagerImpl::ClientSocketPoolManagerImpl(
TransportSecurityState* transport_security_state,
DnsCertProvenanceChecker* dns_cert_checker,
SSLHostInfoFactory* ssl_host_info_factory,
+ const std::string& ssl_session_cache_shard,
ProxyService* proxy_service,
SSLConfigService* ssl_config_service)
: net_log_(net_log),
@@ -51,6 +52,7 @@ ClientSocketPoolManagerImpl::ClientSocketPoolManagerImpl(
transport_security_state_(transport_security_state),
dns_cert_checker_(dns_cert_checker),
ssl_host_info_factory_(ssl_host_info_factory),
+ ssl_session_cache_shard_(ssl_session_cache_shard),
proxy_service_(proxy_service),
ssl_config_service_(ssl_config_service),
transport_pool_histograms_("TCP"),
@@ -70,6 +72,7 @@ ClientSocketPoolManagerImpl::ClientSocketPoolManagerImpl(
transport_security_state,
dns_cert_checker,
ssl_host_info_factory,
+ ssl_session_cache_shard,
socket_factory,
transport_socket_pool_.get(),
NULL /* no socks proxy */,
@@ -290,6 +293,7 @@ ClientSocketPoolManagerImpl::GetSocketPoolForHTTPProxy(
transport_security_state_,
dns_cert_checker_,
ssl_host_info_factory_,
+ ssl_session_cache_shard_,
socket_factory_,
tcp_https_ret.first->second /* https proxy */,
NULL /* no socks proxy */,
@@ -329,6 +333,7 @@ SSLClientSocketPool* ClientSocketPoolManagerImpl::GetSocketPoolForSSLWithProxy(
transport_security_state_,
dns_cert_checker_,
ssl_host_info_factory_,
+ ssl_session_cache_shard_,
socket_factory_,
NULL, /* no tcp pool, we always go through a proxy */
GetSocketPoolForSOCKSProxy(proxy_server),
diff --git a/net/socket/client_socket_pool_manager_impl.h b/net/socket/client_socket_pool_manager_impl.h
index a4ba519..72c1a0c 100644
--- a/net/socket/client_socket_pool_manager_impl.h
+++ b/net/socket/client_socket_pool_manager_impl.h
@@ -66,6 +66,7 @@ class ClientSocketPoolManagerImpl : public base::NonThreadSafe,
TransportSecurityState* transport_security_state,
DnsCertProvenanceChecker* dns_cert_checker,
SSLHostInfoFactory* ssl_host_info_factory,
+ const std::string& ssl_session_cache_shard,
ProxyService* proxy_service,
SSLConfigService* ssl_config_service);
virtual ~ClientSocketPoolManagerImpl();
@@ -112,6 +113,7 @@ class ClientSocketPoolManagerImpl : public base::NonThreadSafe,
TransportSecurityState* const transport_security_state_;
DnsCertProvenanceChecker* const dns_cert_checker_;
SSLHostInfoFactory* const ssl_host_info_factory_;
+ const std::string ssl_session_cache_shard_;
ProxyService* const proxy_service_;
const scoped_refptr<SSLConfigService> ssl_config_service_;
diff --git a/net/socket/ssl_client_socket.h b/net/socket/ssl_client_socket.h
index 433470b..64ccd78 100644
--- a/net/socket/ssl_client_socket.h
+++ b/net/socket/ssl_client_socket.h
@@ -39,18 +39,24 @@ struct SSLClientSocketContext {
OriginBoundCertService* origin_bound_cert_service_arg,
TransportSecurityState* transport_security_state_arg,
DnsCertProvenanceChecker* dns_cert_checker_arg,
- SSLHostInfoFactory* ssl_host_info_factory_arg)
+ SSLHostInfoFactory* ssl_host_info_factory_arg,
+ const std::string& ssl_session_cache_shard_arg)
: cert_verifier(cert_verifier_arg),
origin_bound_cert_service(origin_bound_cert_service_arg),
transport_security_state(transport_security_state_arg),
dns_cert_checker(dns_cert_checker_arg),
- ssl_host_info_factory(ssl_host_info_factory_arg) {}
+ ssl_host_info_factory(ssl_host_info_factory_arg),
+ ssl_session_cache_shard(ssl_session_cache_shard_arg) {}
CertVerifier* cert_verifier;
OriginBoundCertService* origin_bound_cert_service;
TransportSecurityState* transport_security_state;
DnsCertProvenanceChecker* dns_cert_checker;
SSLHostInfoFactory* ssl_host_info_factory;
+ // ssl_session_cache_shard is an opaque string that identifies a shard of the
+ // SSL session cache. SSL sockets with the same ssl_session_cache_shard may
+ // resume each other's SSL sessions but we'll never sessions between shards.
+ const std::string ssl_session_cache_shard;
};
// A client socket that uses SSL as the transport layer.
@@ -120,6 +126,10 @@ class NET_EXPORT SSLClientSocket : public SSLSocket {
static bool IgnoreCertError(int error, int load_flags);
+ // ClearSessionCache clears the SSL session cache, used to resume SSL
+ // sessions.
+ static void ClearSessionCache();
+
virtual bool was_npn_negotiated() const;
virtual bool set_was_npn_negotiated(bool negotiated);
diff --git a/net/socket/ssl_client_socket_nss.cc b/net/socket/ssl_client_socket_nss.cc
index eb8c662..c63d56a 100644
--- a/net/socket/ssl_client_socket_nss.cc
+++ b/net/socket/ssl_client_socket_nss.cc
@@ -450,6 +450,7 @@ SSLClientSocketNSS::SSLClientSocketNSS(ClientSocketHandle* transport_socket,
ob_cert_request_handle_(NULL),
handshake_callback_called_(false),
completed_handshake_(false),
+ ssl_session_cache_shard_(context.ssl_session_cache_shard),
eset_mitm_detected_(false),
kaspersky_mitm_detected_(false),
predicted_cert_chain_correct_(false),
@@ -471,7 +472,7 @@ SSLClientSocketNSS::~SSLClientSocketNSS() {
}
// static
-void SSLClientSocketNSS::ClearSessionCache() {
+void SSLClientSocket::ClearSessionCache() {
// SSL_ClearSessionCache can't be called before NSS is initialized. Don't
// bother initializing NSS just to clear an empty SSL session cache.
if (!NSS_IsInitialized())
@@ -1044,6 +1045,13 @@ int SSLClientSocketNSS::InitializeSSLPeerName() {
// SSL tunnel through a proxy -- GetPeerName returns the proxy's address
// rather than the destination server's address in that case.
std::string peer_id = host_and_port_.ToString();
+ // If the ssl_session_cache_shard_ is non-empty, we append it to the peer id.
+ // This will cause session cache misses between sockets with different values
+ // of ssl_session_cache_shard_ and this is used to partition the session cache
+ // for incognito mode.
+ if (!ssl_session_cache_shard_.empty()) {
+ peer_id += "/" + ssl_session_cache_shard_;
+ }
SECStatus rv = SSL_SetSockPeerID(nss_fd_, const_cast<char*>(peer_id.c_str()));
if (rv != SECSuccess)
LogFailedNSSFunction(net_log_, "SSL_SetSockPeerID", peer_id.c_str());
diff --git a/net/socket/ssl_client_socket_nss.h b/net/socket/ssl_client_socket_nss.h
index 366aa7f..784b92d 100644
--- a/net/socket/ssl_client_socket_nss.h
+++ b/net/socket/ssl_client_socket_nss.h
@@ -57,8 +57,6 @@ class SSLClientSocketNSS : public SSLClientSocket {
const SSLClientSocketContext& context);
virtual ~SSLClientSocketNSS();
- NET_EXPORT_PRIVATE static void ClearSessionCache();
-
// SSLClientSocket implementation.
virtual void GetSSLInfo(SSLInfo* ssl_info) OVERRIDE;
virtual void GetSSLCertRequestInfo(
@@ -269,6 +267,11 @@ class SSLClientSocketNSS : public SSLClientSocket {
// True if the SSL handshake has been completed.
bool completed_handshake_;
+ // ssl_session_cache_shard_ is an opaque string that partitions the SSL
+ // session cache. i.e. sessions created with one value will not attempt to
+ // resume on the socket with a different value.
+ const std::string ssl_session_cache_shard_;
+
// True iff we believe that the user has an ESET product intercepting our
// HTTPS connections.
bool eset_mitm_detected_;
diff --git a/net/socket/ssl_client_socket_nss_unittest.cc b/net/socket/ssl_client_socket_nss_unittest.cc
deleted file mode 100644
index 2e85d31..0000000
--- a/net/socket/ssl_client_socket_nss_unittest.cc
+++ /dev/null
@@ -1,17 +0,0 @@
-// Copyright (c) 2011 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/socket/ssl_client_socket_nss.h"
-
-#include "testing/gtest/include/gtest/gtest.h"
-
-namespace net {
-
-// Verifies that SSLClientSocketNSS::ClearSessionCache can be called without
-// explicit NSS initialization.
-TEST(SSLClientSocketNSSTest, ClearSessionCache) {
- SSLClientSocketNSS::ClearSessionCache();
-}
-
-} // namespace net
diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc
index f933031..547de44 100644
--- a/net/socket/ssl_client_socket_openssl.cc
+++ b/net/socket/ssl_client_socket_openssl.cc
@@ -200,28 +200,32 @@ int NoOpVerifyCallback(X509_STORE_CTX*, void *) {
// OpenSSL manages a cache of SSL_SESSION, this class provides the application
// side policy for that cache about session re-use: we retain one session per
-// unique HostPortPair.
+// unique HostPortPair, per shard.
class SSLSessionCache {
public:
SSLSessionCache() {}
- void OnSessionAdded(const HostPortPair& host_and_port, SSL_SESSION* session) {
+ void OnSessionAdded(const HostPortPair& host_and_port,
+ const std::string& shard,
+ SSL_SESSION* session) {
// Declare the session cleaner-upper before the lock, so any call into
// OpenSSL to free the session will happen after the lock is released.
crypto::ScopedOpenSSL<SSL_SESSION, SSL_SESSION_free> session_to_free;
base::AutoLock lock(lock_);
DCHECK_EQ(0U, session_map_.count(session));
+ const std::string cache_key = GetCacheKey(host_and_port, shard);
+
std::pair<HostPortMap::iterator, bool> res =
- host_port_map_.insert(std::make_pair(host_and_port, session));
+ host_port_map_.insert(std::make_pair(cache_key, session));
if (!res.second) { // Already exists: replace old entry.
session_to_free.reset(res.first->second);
session_map_.erase(session_to_free.get());
res.first->second = session;
}
DVLOG(2) << "Adding session " << session << " => "
- << host_and_port.ToString() << ", new entry = " << res.second;
- DCHECK(host_port_map_[host_and_port] == session);
+ << cache_key << ", new entry = " << res.second;
+ DCHECK(host_port_map_[cache_key] == session);
session_map_[session] = res.first;
DCHECK_EQ(host_port_map_.size(), session_map_.size());
DCHECK_LE(host_port_map_.size(), kSessionCacheMaxEntires);
@@ -236,8 +240,7 @@ class SSLSessionCache {
SessionMap::iterator it = session_map_.find(session);
if (it == session_map_.end())
return;
- DVLOG(2) << "Remove session " << session << " => "
- << it->second->first.ToString();
+ DVLOG(2) << "Remove session " << session << " => " << it->second->first;
DCHECK(it->second->second == session);
host_port_map_.erase(it->second);
session_map_.erase(it);
@@ -247,13 +250,14 @@ class SSLSessionCache {
// Looks up the host:port in the cache, and if a session is found it is added
// to |ssl|, returning true on success.
- bool SetSSLSession(SSL* ssl, const HostPortPair& host_and_port) {
+ bool SetSSLSession(SSL* ssl, const HostPortPair& host_and_port,
+ const std::string& shard) {
base::AutoLock lock(lock_);
- HostPortMap::iterator it = host_port_map_.find(host_and_port);
+ const std::string cache_key = GetCacheKey(host_and_port, shard);
+ HostPortMap::iterator it = host_port_map_.find(cache_key);
if (it == host_port_map_.end())
return false;
- DVLOG(2) << "Lookup session: " << it->second << " => "
- << host_and_port.ToString();
+ DVLOG(2) << "Lookup session: " << it->second << " => " << cache_key;
SSL_SESSION* session = it->second;
DCHECK(session);
DCHECK(session_map_[session] == it);
@@ -265,12 +269,26 @@ class SSLSessionCache {
return SSL_set_session(ssl, session) == 1;
}
+ // Flush removes all entries from the cache. This is called when a client
+ // certificate is added.
+ void Flush() {
+ for (HostPortMap::iterator i = host_port_map_.begin();
+ i != host_port_map_.end(); i++) {
+ SSL_SESSION_free(i->second);
+ }
+ host_port_map_.clear();
+ session_map_.clear();
+ }
+
private:
+ static std::string GetCacheKey(const HostPortPair& host_and_port,
+ const std::string& shard) {
+ return host_and_port.ToString() + "/" + shard;
+ }
+
// A pair of maps to allow bi-directional lookups between host:port and an
// associated session.
- // TODO(joth): When client certificates are implemented we should key the
- // cache on the client certificate used in addition to the host-port pair.
- typedef std::map<HostPortPair, SSL_SESSION*> HostPortMap;
+ typedef std::map<std::string, SSL_SESSION*> HostPortMap;
typedef std::map<SSL_SESSION*, HostPortMap::iterator> SessionMap;
HostPortMap host_port_map_;
SessionMap session_map_;
@@ -329,7 +347,9 @@ class SSLContext {
int NewSessionCallback(SSL* ssl, SSL_SESSION* session) {
SSLClientSocketOpenSSL* socket = GetClientSocketFromSSL(ssl);
- session_cache_.OnSessionAdded(socket->host_and_port(), session);
+ session_cache_.OnSessionAdded(socket->host_and_port(),
+ socket->ssl_session_cache_shard(),
+ session);
return 1; // 1 => We took ownership of |session|.
}
@@ -360,8 +380,11 @@ class SSLContext {
// SSLClientSocketOpenSSL object from an SSL instance.
int ssl_socket_data_index_;
- crypto::ScopedOpenSSL<SSL_CTX, SSL_CTX_free> ssl_ctx_;
+ // session_cache_ must appear before |ssl_ctx_| because the destruction of
+ // |ssl_ctx_| may trigger callbacks into |session_cache_|. Therefore,
+ // |session_cache_| must be destructed after |ssl_ctx_|.
SSLSessionCache session_cache_;
+ crypto::ScopedOpenSSL<SSL_CTX, SSL_CTX_free> ssl_ctx_;
};
// Utility to construct the appropriate set & clear masks for use the OpenSSL
@@ -379,6 +402,12 @@ struct SslSetClearMask {
} // namespace
+// static
+void SSLClientSocket::ClearSessionCache() {
+ SSLContext* context = SSLContext::GetInstance();
+ context->session_cache()->Flush();
+}
+
SSLClientSocketOpenSSL::SSLClientSocketOpenSSL(
ClientSocketHandle* transport_socket,
const HostPortPair& host_and_port,
@@ -394,6 +423,7 @@ SSLClientSocketOpenSSL::SSLClientSocketOpenSSL(
transport_(transport_socket),
host_and_port_(host_and_port),
ssl_config_(ssl_config),
+ ssl_session_cache_shard_(context.ssl_session_cache_shard),
trying_cached_session_(false),
npn_status_(kNextProtoUnsupported),
net_log_(transport_socket->socket()->NetLog()) {
@@ -418,7 +448,8 @@ bool SSLClientSocketOpenSSL::Init() {
return false;
trying_cached_session_ =
- context->session_cache()->SetSSLSession(ssl_, host_and_port_);
+ context->session_cache()->SetSSLSession(ssl_, host_and_port_,
+ ssl_session_cache_shard_);
BIO* ssl_bio = NULL;
// 0 => use default buffer sizes.
@@ -651,6 +682,9 @@ int SSLClientSocketOpenSSL::Connect(const CompletionCallback& callback) {
void SSLClientSocketOpenSSL::Disconnect() {
if (ssl_) {
+ // Calling SSL_shutdown prevents the session from being marked as
+ // unresumable.
+ SSL_shutdown(ssl_);
SSL_free(ssl_);
ssl_ = NULL;
}
diff --git a/net/socket/ssl_client_socket_openssl.h b/net/socket/ssl_client_socket_openssl.h
index e1e1778..98b3fe1 100644
--- a/net/socket/ssl_client_socket_openssl.h
+++ b/net/socket/ssl_client_socket_openssl.h
@@ -43,6 +43,9 @@ class SSLClientSocketOpenSSL : public SSLClientSocket {
~SSLClientSocketOpenSSL();
const HostPortPair& host_and_port() const { return host_and_port_; }
+ const std::string& ssl_session_cache_shard() const {
+ return ssl_session_cache_shard_;
+ }
// Callback from the SSL layer that indicates the remote server is requesting
// a certificate for this client.
@@ -151,6 +154,10 @@ class SSLClientSocketOpenSSL : public SSLClientSocket {
scoped_ptr<ClientSocketHandle> transport_;
const HostPortPair host_and_port_;
SSLConfig ssl_config_;
+ // ssl_session_cache_shard_ is an opaque string that partitions the SSL
+ // session cache. i.e. sessions created with one value will not attempt to
+ // resume on the socket with a different value.
+ const std::string ssl_session_cache_shard_;
// Used for session cache diagnostics.
bool trying_cached_session_;
diff --git a/net/socket/ssl_client_socket_pool.cc b/net/socket/ssl_client_socket_pool.cc
index 1ec0453..744eb38 100644
--- a/net/socket/ssl_client_socket_pool.cc
+++ b/net/socket/ssl_client_socket_pool.cc
@@ -452,6 +452,7 @@ SSLClientSocketPool::SSLClientSocketPool(
TransportSecurityState* transport_security_state,
DnsCertProvenanceChecker* dns_cert_checker,
SSLHostInfoFactory* ssl_host_info_factory,
+ const std::string& ssl_session_cache_shard,
ClientSocketFactory* client_socket_factory,
TransportClientSocketPool* transport_pool,
SOCKSClientSocketPool* socks_pool,
@@ -474,7 +475,8 @@ SSLClientSocketPool::SSLClientSocketPool(
origin_bound_cert_service,
transport_security_state,
dns_cert_checker,
- ssl_host_info_factory),
+ ssl_host_info_factory,
+ ssl_session_cache_shard),
net_log)),
ssl_config_service_(ssl_config_service) {
if (ssl_config_service_)
diff --git a/net/socket/ssl_client_socket_pool.h b/net/socket/ssl_client_socket_pool.h
index cec2e25..6b4de78 100644
--- a/net/socket/ssl_client_socket_pool.h
+++ b/net/socket/ssl_client_socket_pool.h
@@ -183,6 +183,7 @@ class NET_EXPORT_PRIVATE SSLClientSocketPool
TransportSecurityState* transport_security_state,
DnsCertProvenanceChecker* dns_cert_checker,
SSLHostInfoFactory* ssl_host_info_factory,
+ const std::string& ssl_session_cache_shard,
ClientSocketFactory* client_socket_factory,
TransportClientSocketPool* transport_pool,
SOCKSClientSocketPool* socks_pool,
diff --git a/net/socket/ssl_client_socket_pool_unittest.cc b/net/socket/ssl_client_socket_pool_unittest.cc
index 7da8aaa..05dd93d 100644
--- a/net/socket/ssl_client_socket_pool_unittest.cc
+++ b/net/socket/ssl_client_socket_pool_unittest.cc
@@ -98,6 +98,7 @@ class SSLClientSocketPoolTest : public testing::Test {
NULL /* dnsrr_resolver */,
NULL /* dns_cert_checker */,
NULL /* ssl_host_info_factory */,
+ "" /* ssl_session_cache_shard */,
&socket_factory_,
transport_pool ? &transport_socket_pool_ : NULL,
socks_pool ? &socks_socket_pool_ : NULL,
diff --git a/net/socket/ssl_client_socket_unittest.cc b/net/socket/ssl_client_socket_unittest.cc
index aadffe1..7b9436d 100644
--- a/net/socket/ssl_client_socket_unittest.cc
+++ b/net/socket/ssl_client_socket_unittest.cc
@@ -753,3 +753,9 @@ TEST_F(SSLClientSocketTest, ClientSocketHandleNotFromPool) {
rv = callback.WaitForResult();
EXPECT_EQ(net::OK, rv);
}
+
+// Verifies that SSLClientSocket::ClearSessionCache can be called without
+// explicit NSS initialization.
+TEST(SSLClientSocket, ClearSessionCache) {
+ net::SSLClientSocket::ClearSessionCache();
+}
diff --git a/net/test/test_server.cc b/net/test/test_server.cc
index c27f73b..07575a26 100644
--- a/net/test/test_server.cc
+++ b/net/test/test_server.cc
@@ -56,13 +56,15 @@ std::string GetHostname(TestServer::Type type,
TestServer::HTTPSOptions::HTTPSOptions()
: server_certificate(CERT_OK),
request_client_certificate(false),
- bulk_ciphers(HTTPSOptions::BULK_CIPHER_ANY) {}
+ bulk_ciphers(HTTPSOptions::BULK_CIPHER_ANY),
+ record_resume(false) {}
TestServer::HTTPSOptions::HTTPSOptions(
TestServer::HTTPSOptions::ServerCertificate cert)
: server_certificate(cert),
request_client_certificate(false),
- bulk_ciphers(HTTPSOptions::BULK_CIPHER_ANY) {}
+ bulk_ciphers(HTTPSOptions::BULK_CIPHER_ANY),
+ record_resume(false) {}
TestServer::HTTPSOptions::~HTTPSOptions() {}
@@ -409,6 +411,9 @@ bool TestServer::AddCommandLineArguments(CommandLine* command_line) const {
command_line->AppendArg(kBulkCipherSwitch + "=aes256");
if (https_options_.bulk_ciphers & HTTPSOptions::BULK_CIPHER_3DES)
command_line->AppendArg(kBulkCipherSwitch + "=3des");
+
+ if (https_options_.record_resume)
+ command_line->AppendArg("--https-record-resume");
}
return true;
diff --git a/net/test/test_server.h b/net/test/test_server.h
index 8d6d8a2..18c1420 100644
--- a/net/test/test_server.h
+++ b/net/test/test_server.h
@@ -103,6 +103,11 @@ class TestServer {
// HTTPS server, or BULK_CIPHER_ANY to indicate that all implemented
// ciphers are acceptable.
int bulk_ciphers;
+
+ // If true, pass the --https-record-resume argument to testserver.py which
+ // causes it to log session cache actions and echo the log on
+ // /ssl-session-cache.
+ bool record_resume;
};
TestServer(Type type, const FilePath& document_root);
diff --git a/net/tools/testserver/testserver.py b/net/tools/testserver/testserver.py
index 0d4a430..35a08c4 100755
--- a/net/tools/testserver/testserver.py
+++ b/net/tools/testserver/testserver.py
@@ -68,6 +68,20 @@ def debug(str):
debug_output.write(str + "\n")
debug_output.flush()
+class RecordingSSLSessionCache(object):
+ """RecordingSSLSessionCache acts as a TLS session cache and maintains a log of
+ lookups and inserts in order to test session cache behaviours."""
+
+ def __init__(self):
+ self.log = []
+
+ def __getitem__(self, sessionID):
+ self.log.append(('lookup', sessionID))
+ raise KeyError()
+
+ def __setitem__(self, sessionID, session):
+ self.log.append(('insert', sessionID))
+
class StoppableHTTPServer(BaseHTTPServer.HTTPServer):
"""This is a specialization of of BaseHTTPServer to allow it
to be exited cleanly (by setting its "stop" member to True)."""
@@ -83,7 +97,8 @@ class HTTPSServer(tlslite.api.TLSSocketServerMixIn, StoppableHTTPServer):
"""This is a specialization of StoppableHTTPerver that add https support."""
def __init__(self, server_address, request_hander_class, cert_path,
- ssl_client_auth, ssl_client_cas, ssl_bulk_ciphers):
+ ssl_client_auth, ssl_client_cas, ssl_bulk_ciphers,
+ record_resume_info):
s = open(cert_path).read()
x509 = tlslite.api.X509()
x509.parse(s)
@@ -101,7 +116,12 @@ class HTTPSServer(tlslite.api.TLSSocketServerMixIn, StoppableHTTPServer):
if ssl_bulk_ciphers is not None:
self.ssl_handshake_settings.cipherNames = ssl_bulk_ciphers
- self.session_cache = tlslite.api.SessionCache()
+ if record_resume_info:
+ # If record_resume_info is true then we'll replace the session cache with
+ # an object that records the lookups and inserts that it sees.
+ self.session_cache = RecordingSSLSessionCache()
+ else:
+ self.session_cache = tlslite.api.SessionCache()
StoppableHTTPServer.__init__(self, server_address, request_hander_class)
def handshake(self, tlsConnection):
@@ -357,6 +377,7 @@ class TestPageHandler(BasePageHandler):
self.ClientRedirectHandler,
self.MultipartHandler,
self.MultipartSlowHandler,
+ self.GetSSLSessionCacheHandler,
self.DefaultResponseHandler]
post_handlers = [
self.EchoTitleHandler,
@@ -1380,6 +1401,23 @@ class TestPageHandler(BasePageHandler):
self.wfile.write('--' + bound + '--')
return True
+ def GetSSLSessionCacheHandler(self):
+ """Send a reply containing a log of the session cache operations."""
+
+ if not self._ShouldHandleRequest('/ssl-session-cache'):
+ return False
+
+ self.send_response(200)
+ self.send_header('Content-Type', 'text/plain')
+ self.end_headers()
+ try:
+ for (action, sessionID) in self.server.session_cache.log:
+ self.wfile.write('%s\t%s\n' % (action, sessionID.encode('hex')))
+ except AttributeError, e:
+ self.wfile.write('Pass --https-record-resume in order to use' +
+ ' this request')
+ return True
+
def DefaultResponseHandler(self):
"""This is the catch-all response handler for requests that aren't handled
by one of the special handlers above.
@@ -1805,7 +1843,7 @@ def main(options, args):
return
server = HTTPSServer(('127.0.0.1', port), TestPageHandler, options.cert,
options.ssl_client_auth, options.ssl_client_ca,
- options.ssl_bulk_cipher)
+ options.ssl_bulk_cipher, options.record_resume)
print 'HTTPS server started on port %d...' % server.server_port
else:
server = StoppableHTTPServer(('127.0.0.1', port), TestPageHandler)
@@ -1921,6 +1959,11 @@ if __name__ == '__main__':
help='Specify that https should be used, specify '
'the path to the cert containing the private key '
'the server should use.')
+ option_parser.add_option('', '--https-record-resume', dest='record_resume',
+ const=True, default=False, action='store_const',
+ help='Record resumption cache events rather than'
+ ' resuming as normal. Allows the use of the'
+ ' /ssl-session-cache request')
option_parser.add_option('', '--ssl-client-auth', action='store_true',
help='Require SSL client auth on every connection.')
option_parser.add_option('', '--ssl-client-ca', action='append', default=[],
diff --git a/net/url_request/url_request_unittest.cc b/net/url_request/url_request_unittest.cc
index 811f885..8dcdbb1 100644
--- a/net/url_request/url_request_unittest.cc
+++ b/net/url_request/url_request_unittest.cc
@@ -23,6 +23,7 @@
#include "base/process_util.h"
#include "base/string_number_conversions.h"
#include "base/string_piece.h"
+#include "base/string_split.h"
#include "base/string_util.h"
#include "base/stringprintf.h"
#include "base/utf_string_conversions.h"
@@ -41,9 +42,11 @@
#include "net/ftp/ftp_network_layer.h"
#include "net/http/http_cache.h"
#include "net/http/http_network_layer.h"
+#include "net/http/http_network_session.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_response_headers.h"
#include "net/proxy/proxy_service.h"
+#include "net/socket/ssl_client_socket.h"
#include "net/test/test_server.h"
#include "net/url_request/url_request.h"
#include "net/url_request/url_request_file_dir_job.h"
@@ -1181,6 +1184,152 @@ TEST_F(HTTPSRequestTest, ClientAuthTest) {
}
}
+TEST_F(HTTPSRequestTest, ResumeTest) {
+ // Test that we attempt a session resume when making two connections to the
+ // same host.
+ TestServer::HTTPSOptions https_options;
+ https_options.record_resume = true;
+ TestServer test_server(https_options,
+ FilePath(FILE_PATH_LITERAL("net/data/ssl")));
+ ASSERT_TRUE(test_server.Start());
+
+ SSLClientSocket::ClearSessionCache();
+
+ {
+ TestDelegate d;
+ TestURLRequest r(test_server.GetURL("ssl-session-cache"), &d);
+ r.set_context(default_context_);
+
+ r.Start();
+ EXPECT_TRUE(r.is_pending());
+
+ MessageLoop::current()->Run();
+
+ EXPECT_EQ(1, d.response_started_count());
+ }
+
+ reinterpret_cast<HttpCache*>(default_context_->http_transaction_factory())->
+ CloseAllConnections();
+
+ {
+ TestDelegate d;
+ TestURLRequest r(test_server.GetURL("ssl-session-cache"), &d);
+ r.set_context(default_context_);
+
+ r.Start();
+ EXPECT_TRUE(r.is_pending());
+
+ MessageLoop::current()->Run();
+
+ // The response will look like;
+ // insert abc
+ // lookup abc
+ // insert xyz
+ //
+ // With a newline at the end which makes the split think that there are
+ // four lines.
+
+ EXPECT_EQ(1, d.response_started_count());
+ std::vector<std::string> lines;
+ base::SplitString(d.data_received(), '\n', &lines);
+ ASSERT_EQ(4u, lines.size()) << d.data_received();
+
+ std::string session_id;
+
+ for (size_t i = 0; i < 2; i++) {
+ std::vector<std::string> parts;
+ base::SplitString(lines[i], '\t', &parts);
+ ASSERT_EQ(2u, parts.size());
+ if (i == 0) {
+ EXPECT_EQ("insert", parts[0]);
+ session_id = parts[1];
+ } else {
+ EXPECT_EQ("lookup", parts[0]);
+ EXPECT_EQ(session_id, parts[1]);
+ }
+ }
+ }
+}
+
+TEST_F(HTTPSRequestTest, SSLSessionCacheShardTest) {
+ // Test that sessions aren't resumed when the value of ssl_session_cache_shard
+ // differs.
+ TestServer::HTTPSOptions https_options;
+ https_options.record_resume = true;
+ TestServer test_server(https_options,
+ FilePath(FILE_PATH_LITERAL("net/data/ssl")));
+ ASSERT_TRUE(test_server.Start());
+
+ SSLClientSocket::ClearSessionCache();
+
+ {
+ TestDelegate d;
+ TestURLRequest r(test_server.GetURL("ssl-session-cache"), &d);
+ r.set_context(default_context_);
+
+ r.Start();
+ EXPECT_TRUE(r.is_pending());
+
+ MessageLoop::current()->Run();
+
+ EXPECT_EQ(1, d.response_started_count());
+ }
+
+ // Now create a new HttpCache with a different ssl_session_cache_shard value.
+ HttpNetworkSession::Params params;
+ params.host_resolver = default_context_->host_resolver();
+ params.cert_verifier = default_context_->cert_verifier();
+ params.proxy_service = default_context_->proxy_service();
+ params.ssl_config_service = default_context_->ssl_config_service();
+ params.http_auth_handler_factory =
+ default_context_->http_auth_handler_factory();
+ params.network_delegate = default_context_->network_delegate();
+ params.http_server_properties = default_context_->http_server_properties();
+ params.ssl_session_cache_shard = "alternate";
+
+ scoped_ptr<net::HttpCache> cache(new net::HttpCache(
+ new net::HttpNetworkSession(params),
+ net::HttpCache::DefaultBackend::InMemory(0)));
+
+ default_context_->set_http_transaction_factory(cache.get());
+
+ {
+ TestDelegate d;
+ TestURLRequest r(test_server.GetURL("ssl-session-cache"), &d);
+ r.set_context(default_context_);
+
+ r.Start();
+ EXPECT_TRUE(r.is_pending());
+
+ MessageLoop::current()->Run();
+
+ // The response will look like;
+ // insert abc
+ // insert xyz
+ //
+ // With a newline at the end which makes the split think that there are
+ // three lines.
+
+ EXPECT_EQ(1, d.response_started_count());
+ std::vector<std::string> lines;
+ base::SplitString(d.data_received(), '\n', &lines);
+ ASSERT_EQ(3u, lines.size());
+
+ std::string session_id;
+ for (size_t i = 0; i < 2; i++) {
+ std::vector<std::string> parts;
+ base::SplitString(lines[i], '\t', &parts);
+ ASSERT_EQ(2u, parts.size());
+ EXPECT_EQ("insert", parts[0]);
+ if (i == 0) {
+ session_id = parts[1];
+ } else {
+ EXPECT_NE(session_id, parts[1]);
+ }
+ }
+ }
+}
+
TEST_F(URLRequestTestHTTP, CancelTest) {
TestDelegate d;
{