From: Christos Tsantilas Date: Sun, 12 Jan 2014 17:51:12 +0000 (+0200) Subject: SMP SSL session cache implementation X-Git-Tag: SQUID_3_5_0_1~420 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=10a69fc01d42f618979ff92f86dbb04dd978ac44;p=thirdparty%2Fsquid.git SMP SSL session cache implementation This patch implement SSL session cache sharing across SMP workers using shared memory. The following new squid configuration options added: - The "sslproxy_session_cache_size" option which sets the cache size to use for ssl session. Example usage: sslproxy_session_cache_size 4 MB - The "sslproxy_session_ttl" option which defines the time in seconds the ssl session is valid. Example usage: sslproxy_session_ttl 600 This is a Measurement Factory project --- diff --git a/src/Makefile.am b/src/Makefile.am index fccf3e462f..396e47c57a 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -627,6 +627,7 @@ squid_LDADD = \ libsquid.la \ ip/libip.la \ fs/libfs.la \ + $(SSL_LIBS) \ ipc/libipc.la \ mgr/libmgr.la \ anyp/libanyp.la \ @@ -644,7 +645,6 @@ squid_LDADD = \ $(REGEXLIB) \ $(ADAPTATION_LIBS) \ $(ESI_LIBS) \ - $(SSL_LIBS) \ $(SNMP_LIBS) \ $(top_builddir)/lib/libmisccontainers.la \ $(top_builddir)/lib/libmiscencoding.la \ @@ -1793,9 +1793,9 @@ tests_testDiskIO_LDADD = \ $(DISK_OS_LIBS) \ acl/libapi.la \ mgr/libmgr.la \ + $(SSL_LIBS) \ ipc/libipc.la \ base/libbase.la \ - $(SSL_LIBS) \ $(top_builddir)/lib/libmisccontainers.la \ $(top_builddir)/lib/libmiscencoding.la \ $(top_builddir)/lib/libmiscutil.la \ @@ -2807,6 +2807,7 @@ tests_testHttpRequest_LDADD = \ libsquid.la \ ip/libip.la \ fs/libfs.la \ + $(SSL_LIBS) \ ipc/libipc.la \ base/libbase.la \ mgr/libmgr.la \ @@ -2820,7 +2821,6 @@ tests_testHttpRequest_LDADD = \ $(REPL_OBJS) \ $(ADAPTATION_LIBS) \ $(ESI_LIBS) \ - $(SSL_LIBS) \ $(top_builddir)/lib/libmisccontainers.la \ $(top_builddir)/lib/libmiscencoding.la \ $(top_builddir)/lib/libmiscutil.la \ @@ -2994,9 +2994,9 @@ tests_testStore_LDADD= \ ip/libip.la \ fs/libfs.la \ mgr/libmgr.la \ + $(SSL_LIBS) \ ipc/libipc.la \ anyp/libanyp.la \ - $(SSL_LIBS) \ $(top_builddir)/lib/libmisccontainers.la \ $(top_builddir)/lib/libmiscencoding.la \ $(top_builddir)/lib/libmiscutil.la \ @@ -3230,8 +3230,8 @@ tests_testUfs_LDADD = \ $(DISK_LIBS) \ $(DISK_OS_LIBS) \ acl/libapi.la \ - ipc/libipc.la \ $(SSL_LIBS) \ + ipc/libipc.la \ comm/libcomm.la \ anyp/libanyp.la \ base/libbase.la \ @@ -3411,9 +3411,10 @@ tests_testRock_LDADD = \ acl/libacls.la \ acl/libapi.la \ acl/libstate.la \ + eui/libeui.la \ + $(SSL_LIBS) \ ipc/libipc.la \ base/libbase.la \ - $(SSL_LIBS) \ $(top_builddir)/lib/libmisccontainers.la \ $(top_builddir)/lib/libmiscencoding.la \ $(top_builddir)/lib/libmiscutil.la \ @@ -3647,6 +3648,7 @@ tests_testURL_LDADD = \ libsquid.la \ ip/libip.la \ fs/libfs.la \ + $(SSL_LIBS) \ ipc/libipc.la \ mgr/libmgr.la \ $(SNMP_LIBS) \ @@ -3659,7 +3661,6 @@ tests_testURL_LDADD = \ $(REPL_OBJS) \ $(ADAPTATION_LIBS) \ $(ESI_LIBS) \ - $(SSL_LIBS) \ $(top_builddir)/lib/libmisccontainers.la \ $(top_builddir)/lib/libmiscencoding.la \ $(top_builddir)/lib/libmiscutil.la \ diff --git a/src/SquidConfig.h b/src/SquidConfig.h index fea163ecbb..013ae21741 100644 --- a/src/SquidConfig.h +++ b/src/SquidConfig.h @@ -492,6 +492,8 @@ public: struct { int unclean_shutdown; char *ssl_engine; + int session_ttl; + size_t sessionCacheSize; } SSL; #endif diff --git a/src/cf.data.pre b/src/cf.data.pre index b086986690..d6daf26557 100644 --- a/src/cf.data.pre +++ b/src/cf.data.pre @@ -2376,6 +2376,24 @@ DOC_START server certificates while proxying https:// URLs DOC_END +NAME: sslproxy_session_ttl +IFDEF: USE_SSL +DEFAULT: 300 +LOC: Config.SSL.session_ttl +TYPE: int +DOC_START + Sets the timeout value for SSL sessions +DOC_END + +NAME: sslproxy_session_cache_size +IFDEF: USE_SSL +DEFAULT: 2 MB +LOC: Config.SSL.sessionCacheSize +TYPE: b_size_t +DOC_START + Sets the cache size to use for ssl session +DOC_END + NAME: ssl_bump IFDEF: USE_SSL TYPE: sslproxy_ssl_bump diff --git a/src/main.cc b/src/main.cc index 71c481dc42..d72b69b600 100644 --- a/src/main.cc +++ b/src/main.cc @@ -1057,6 +1057,9 @@ mainInitialize(void) #endif #if USE_SSL + if (!configured_once) + Ssl::initialize_session_cache(); + if (Ssl::CertValidationHelper::GetInstance()) Ssl::CertValidationHelper::GetInstance()->Init(); #endif diff --git a/src/ssl/support.cc b/src/ssl/support.cc index 5236ec5592..997d70281e 100644 --- a/src/ssl/support.cc +++ b/src/ssl/support.cc @@ -41,8 +41,10 @@ #include "acl/FilledChecklist.h" #include "anyp/PortCfg.h" #include "fde.h" +#include "ipc/MemMap.h" #include "globals.h" #include "SquidConfig.h" +#include "SquidTime.h" #include "ssl/Config.h" #include "ssl/ErrorDetail.h" #include "ssl/gadgets.h" @@ -53,6 +55,10 @@ #include #endif +static void setSessionCallbacks(SSL_CTX *ctx); +Ipc::MemMap *SslSessionCache = NULL; +const char *SslSessionCacheName = "ssl_session_cache"; + const char *Ssl::BumpModeStr[] = { "none", "client-first", @@ -733,7 +739,6 @@ ssl_initialize(void) } #endif - } ssl_ex_index_server = SSL_get_ex_new_index(0, (void *) "server", NULL, NULL, NULL); @@ -914,6 +919,8 @@ configureSslContext(SSL_CTX *sslContext, AnyP::PortCfg &port) if (port.sslContextFlags & SSL_FLAG_DONT_VERIFY_DOMAIN) SSL_CTX_set_ex_data(sslContext, ssl_ctx_ex_index_dont_verify_domain, (void *) -1); + setSessionCallbacks(sslContext); + return true; } @@ -1673,4 +1680,198 @@ Ssl::CertError::operator != (const CertError &ce) const return code != ce.code || cert.get() != ce.cert.get(); } +static int +store_session_cb(SSL *ssl, SSL_SESSION *session) +{ + if (!SslSessionCache) + return 0; + + debugs(83, 5, "Request to store SSL Session "); + + SSL_SESSION_set_timeout(session, Config.SSL.session_ttl); + + unsigned char *id = session->session_id; + unsigned int idlen = session->session_id_length; + unsigned char key[MEMMAP_SLOT_KEY_SIZE]; + // Session ids are of size 32bytes. They should always fit to a + // MemMap::Slot::key + assert(idlen <= MEMMAP_SLOT_KEY_SIZE); + memset(key, 0, sizeof(key)); + memcpy(key, id, idlen); + int pos; + Ipc::MemMap::Slot *slotW = SslSessionCache->openForWriting((const cache_key*)key, pos); + if (slotW) { + int lenRequired = i2d_SSL_SESSION(session, NULL); + if (lenRequired < MEMMAP_SLOT_DATA_SIZE) { + unsigned char *p = (unsigned char *)slotW->p; + lenRequired = i2d_SSL_SESSION(session, &p); + slotW->set(key, NULL, lenRequired, squid_curtime + Config.SSL.session_ttl); + } + SslSessionCache->closeForWriting(pos); + debugs(83, 5, "wrote an ssl session entry of size " << lenRequired << " at pos " << pos); + } + return 0; +} + +static void +remove_session_cb(SSL_CTX *, SSL_SESSION *sessionID) +{ + if (!SslSessionCache) + return ; + + debugs(83, 5, "Request to remove corrupted or not valid SSL Session "); + int pos; + Ipc::MemMap::Slot const *slot = SslSessionCache->openForReading((const cache_key*)sessionID, pos); + if (slot == NULL) + return; + SslSessionCache->closeForReading(pos); + // TODO: + // What if we are not able to remove the session? + // Maybe schedule a job to remove it later? + // For now we just have an invalid entry in cache until will be expired + // The openSSL will reject it when we try to use it + SslSessionCache->free(pos); +} + +static SSL_SESSION * +get_session_cb(SSL *, unsigned char *sessionID, int len, int *copy) +{ + if (!SslSessionCache) + return NULL; + + SSL_SESSION *session = NULL; + const unsigned int *p; + p = (unsigned int *)sessionID; + debugs(83, 5, "Request to search for SSL Session of len:" << + len << p[0] << ":" << p[1]); + + int pos; + Ipc::MemMap::Slot const *slot = SslSessionCache->openForReading((const cache_key*)sessionID, pos); + if (slot != NULL) { + if (slot->expire > squid_curtime) { + const unsigned char *ptr = slot->p; + session = d2i_SSL_SESSION(NULL, &ptr, slot->pSize); + debugs(83, 5, "Session retrieved from cache at pos " << pos); + } else + debugs(83, 5, "Session in cache expired"); + SslSessionCache->closeForReading(pos); + } + + if (!session) + debugs(83, 5, "Failed to retrieved from cache\n"); + + // With the parameter copy the callback can require the SSL engine + // to increment the reference count of the SSL_SESSION object, Normally + // the reference count is not incremented and therefore the session must + // not be explicitly freed with SSL_SESSION_free(3). + *copy = 0; + return session; +} + +static void +setSessionCallbacks(SSL_CTX *ctx) +{ + if (SslSessionCache) { + SSL_CTX_set_session_cache_mode(ctx, SSL_SESS_CACHE_SERVER|SSL_SESS_CACHE_NO_INTERNAL); + SSL_CTX_sess_set_new_cb(ctx, store_session_cb); + SSL_CTX_sess_set_remove_cb(ctx, remove_session_cb); + SSL_CTX_sess_set_get_cb(ctx, get_session_cb); + } +} + +static bool +isSslServer() +{ + if (Config.Sockaddr.https) + return true; + + for (AnyP::PortCfg *s = Config.Sockaddr.http; s; s = s->next) { + if (s->flags.tunnelSslBumping) + return true; + } + + return false; +} + +#define SSL_SESSION_ID_SIZE 32 +#define SSL_SESSION_MAX_SIZE 10*1024 + +void +Ssl::initialize_session_cache() +{ + + if (!isSslServer()) //no need to configure ssl session cache. + return; + + // Check if the MemMap keys and data are enough big to hold + // session ids and session data + assert(SSL_SESSION_ID_SIZE >= MEMMAP_SLOT_KEY_SIZE); + assert(SSL_SESSION_MAX_SIZE >= MEMMAP_SLOT_DATA_SIZE); + + int configuredItems = ::Config.SSL.sessionCacheSize / sizeof(Ipc::MemMap::Slot); + if (IamWorkerProcess() && configuredItems) + SslSessionCache = new Ipc::MemMap(SslSessionCacheName); + else { + SslSessionCache = NULL; + return; + } + + for (AnyP::PortCfg *s = ::Config.Sockaddr.https; s; s = s->next) { + if (s->staticSslContext.get() != NULL) + setSessionCallbacks(s->staticSslContext.get()); + } + + for (AnyP::PortCfg *s = ::Config.Sockaddr.http; s; s = s->next) { + if (s->staticSslContext.get() != NULL) + setSessionCallbacks(s->staticSslContext.get()); + } +} + +void +destruct_session_cache() +{ + delete SslSessionCache; +} + +/// initializes shared memory segments used by MemStore +class SharedSessionCacheRr: public Ipc::Mem::RegisteredRunner +{ +public: + /* RegisteredRunner API */ + SharedSessionCacheRr(): owner(NULL) {} + virtual void run(const RunnerRegistry &); + virtual ~SharedSessionCacheRr(); + +protected: + virtual void create(const RunnerRegistry &); + +private: + Ipc::MemMap::Owner *owner; +}; + +RunnerRegistrationEntry(rrAfterConfig, SharedSessionCacheRr); + +void +SharedSessionCacheRr::run(const RunnerRegistry &r) +{ + Ipc::Mem::RegisteredRunner::run(r); +} + +void +SharedSessionCacheRr::create(const RunnerRegistry &) +{ + if (!isSslServer()) //no need to configure ssl session cache. + return; + + int items; + items = Config.SSL.sessionCacheSize / sizeof(Ipc::MemMap::Slot); + if (items) + owner = Ipc::MemMap::Init(SslSessionCacheName, items); +} + +SharedSessionCacheRr::~SharedSessionCacheRr() +{ + delete owner; +} + #endif /* USE_SSL */ diff --git a/src/ssl/support.h b/src/ssl/support.h index 9eb9d848ef..7198f5bb81 100644 --- a/src/ssl/support.h +++ b/src/ssl/support.h @@ -277,6 +277,18 @@ int asn1timeToString(ASN1_TIME *tm, char *buf, int len); \return true if SNI set false otherwise */ bool setClientSNI(SSL *ssl, const char *fqdn); + +/** + \ingroup ServerProtocolSSLAPI + * Initializes the shared session cache if configured +*/ +void initialize_session_cache(); + +/** + \ingroup ServerProtocolSSLAPI + * Destroy the shared session cache if configured +*/ +void destruct_session_cache(); } //namespace Ssl #if _SQUID_WINDOWS_