| 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.
|
|
|