Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(399)

Unified Diff: net/socket/ssl_client_socket_openssl.cc

Issue 89623002: net: Implement new SSL session cache for OpenSSL sockets. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Update comments Created 7 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: net/socket/ssl_client_socket_openssl.cc
diff --git a/net/socket/ssl_client_socket_openssl.cc b/net/socket/ssl_client_socket_openssl.cc
index 549f4ed1621e87a0970100a5fba7d54b48364009..0b9bcbfd29ec689717723249c5882f175760144d 100644
--- a/net/socket/ssl_client_socket_openssl.cc
+++ b/net/socket/ssl_client_socket_openssl.cc
@@ -23,6 +23,7 @@
#include "net/cert/single_request_cert_verifier.h"
#include "net/cert/x509_certificate_net_log_param.h"
#include "net/socket/ssl_error_params.h"
+#include "net/socket/ssl_session_cache_openssl.h"
#include "net/ssl/openssl_client_key_store.h"
#include "net/ssl/ssl_cert_request_info.h"
#include "net/ssl/ssl_connection_status_flags.h"
@@ -41,9 +42,6 @@ namespace {
#define GotoState(s) next_handshake_state_ = s
#endif
-const int kSessionCacheTimeoutSeconds = 60 * 60;
-const size_t kSessionCacheMaxEntires = 1024;
-
// This constant can be any non-negative/non-zero value (eg: it does not
// overlap with any value of the net::Error range, including net::OK).
const int kNoPendingReadResult = 1;
@@ -215,108 +213,6 @@ int NoOpVerifyCallback(X509_STORE_CTX*, void *) {
return 1;
}
-// 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, per shard.
-class SSLSessionCache {
- public:
- SSLSessionCache() {}
-
- 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(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 << " => "
- << 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);
- }
-
- void OnSessionRemoved(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_);
-
- SessionMap::iterator it = session_map_.find(session);
- if (it == session_map_.end())
- return;
- DVLOG(2) << "Remove session " << session << " => " << it->second->first;
- DCHECK(it->second->second == session);
- host_port_map_.erase(it->second);
- session_map_.erase(it);
- session_to_free.reset(session);
- DCHECK_EQ(host_port_map_.size(), session_map_.size());
- }
-
- // 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,
- const std::string& shard) {
- base::AutoLock lock(lock_);
- 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 << " => " << cache_key;
- SSL_SESSION* session = it->second;
- DCHECK(session);
- DCHECK(session_map_[session] == it);
- // Ideally we'd release |lock_| before calling into OpenSSL here, however
- // that opens a small risk |session| will go out of scope before it is used.
- // Alternatively we would take a temporary local refcount on |session|,
- // except OpenSSL does not provide a public API for adding a ref (c.f.
- // SSL_SESSION_free which decrements the ref).
- 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() {
- base::AutoLock lock(lock_);
- 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.
- typedef std::map<std::string, SSL_SESSION*> HostPortMap;
- typedef std::map<SSL_SESSION*, HostPortMap::iterator> SessionMap;
- HostPortMap host_port_map_;
- SessionMap session_map_;
-
- // Protects access to both the above maps.
- base::Lock lock_;
-
- DISALLOW_COPY_AND_ASSIGN(SSLSessionCache);
-};
-
// Utility to construct the appropriate set & clear masks for use the OpenSSL
// options and mode configuration functions. (SSL_set_options etc)
struct SslSetClearMask {
@@ -330,15 +226,24 @@ struct SslSetClearMask {
long clear_mask;
};
+// Compute a unique key string for the SSL session cache. |socket| is an
+// input socket object. Return a string.
+std::string GetSocketSessionCacheKey(const SSLClientSocketOpenSSL& socket) {
+ std::string result = socket.host_and_port().ToString();
+ result.append("/");
+ result.append(socket.ssl_session_cache_shard());
+ return result;
+}
+
} // namespace
class SSLClientSocketOpenSSL::SSLContext {
public:
static SSLContext* GetInstance() { return Singleton<SSLContext>::get(); }
SSL_CTX* ssl_ctx() { return ssl_ctx_.get(); }
- SSLSessionCache* session_cache() { return &session_cache_; }
+ SSLSessionCacheOpenSSL* session_cache() { return &session_cache_; }
- SSLClientSocketOpenSSL* GetClientSocketFromSSL(SSL* ssl) {
+ SSLClientSocketOpenSSL* GetClientSocketFromSSL(const SSL* ssl) {
DCHECK(ssl);
SSLClientSocketOpenSSL* socket = static_cast<SSLClientSocketOpenSSL*>(
SSL_get_ex_data(ssl, ssl_socket_data_index_));
@@ -358,12 +263,8 @@ class SSLClientSocketOpenSSL::SSLContext {
ssl_socket_data_index_ = SSL_get_ex_new_index(0, 0, 0, 0, 0);
DCHECK_NE(ssl_socket_data_index_, -1);
ssl_ctx_.reset(SSL_CTX_new(SSLv23_client_method()));
+ session_cache_.Reset(ssl_ctx_.get(), kDefaultSessionCacheConfig);
SSL_CTX_set_cert_verify_callback(ssl_ctx_.get(), NoOpVerifyCallback, NULL);
- SSL_CTX_set_session_cache_mode(ssl_ctx_.get(), SSL_SESS_CACHE_CLIENT);
- SSL_CTX_sess_set_new_cb(ssl_ctx_.get(), NewSessionCallbackStatic);
- SSL_CTX_sess_set_remove_cb(ssl_ctx_.get(), RemoveSessionCallbackStatic);
- SSL_CTX_set_timeout(ssl_ctx_.get(), kSessionCacheTimeoutSeconds);
- SSL_CTX_sess_set_cache_size(ssl_ctx_.get(), kSessionCacheMaxEntires);
SSL_CTX_set_client_cert_cb(ssl_ctx_.get(), ClientCertCallback);
SSL_CTX_set_channel_id_cb(ssl_ctx_.get(), ChannelIDCallback);
#if defined(OPENSSL_NPN_NEGOTIATED)
@@ -375,26 +276,13 @@ class SSLClientSocketOpenSSL::SSLContext {
#endif
}
- static int NewSessionCallbackStatic(SSL* ssl, SSL_SESSION* session) {
- return GetInstance()->NewSessionCallback(ssl, session);
- }
-
- int NewSessionCallback(SSL* ssl, SSL_SESSION* session) {
- SSLClientSocketOpenSSL* socket = GetClientSocketFromSSL(ssl);
- session_cache_.OnSessionAdded(socket->host_and_port(),
- socket->ssl_session_cache_shard(),
- session);
- return 1; // 1 => We took ownership of |session|.
- }
-
- static void RemoveSessionCallbackStatic(SSL_CTX* ctx, SSL_SESSION* session) {
- return GetInstance()->RemoveSessionCallback(ctx, session);
+ static std::string GetSessionCacheKey(const SSL* ssl) {
+ SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl);
+ DCHECK(socket);
+ return GetSocketSessionCacheKey(*socket);
}
- void RemoveSessionCallback(SSL_CTX* ctx, SSL_SESSION* session) {
- DCHECK(ctx == ssl_ctx());
- session_cache_.OnSessionRemoved(session);
- }
+ static SSLSessionCacheOpenSSL::Config kDefaultSessionCacheConfig;
static int ClientCertCallback(SSL* ssl, X509** x509, EVP_PKEY** pkey) {
SSLClientSocketOpenSSL* socket = GetInstance()->GetClientSocketFromSSL(ssl);
@@ -420,11 +308,18 @@ class SSLClientSocketOpenSSL::SSLContext {
// SSLClientSocketOpenSSL object from an SSL instance.
int ssl_socket_data_index_;
- // 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_;
+ // |session_cache_| must be destroyed before |ssl_ctx_|.
+ SSLSessionCacheOpenSSL session_cache_;
+};
+
+// static
+SSLSessionCacheOpenSSL::Config
+ SSLClientSocketOpenSSL::SSLContext::kDefaultSessionCacheConfig = {
+ &GetSessionCacheKey, // key_func
+ 1024, // max_entries
+ 256, // expiration_check_count
+ 60 * 60, // timeout_seconds
};
// static
@@ -753,9 +648,8 @@ bool SSLClientSocketOpenSSL::Init() {
if (!SSL_set_tlsext_host_name(ssl_, host_and_port_.host().c_str()))
return false;
- trying_cached_session_ =
- context->session_cache()->SetSSLSession(ssl_, host_and_port_,
- ssl_session_cache_shard_);
+ trying_cached_session_ = context->session_cache()->SetSSLSessionWithKey(
+ ssl_, GetSocketSessionCacheKey(*this));
BIO* ssl_bio = NULL;
// 0 => use default buffer sizes.

Powered by Google App Engine
This is Rietveld 408576698