]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
SMP SSL session cache implementation
authorChristos Tsantilas <chtsanti@users.sourceforge.net>
Sun, 12 Jan 2014 17:51:12 +0000 (19:51 +0200)
committerChristos Tsantilas <chtsanti@users.sourceforge.net>
Sun, 12 Jan 2014 17:51:12 +0000 (19:51 +0200)
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

src/Makefile.am
src/SquidConfig.h
src/cf.data.pre
src/main.cc
src/ssl/support.cc
src/ssl/support.h

index fccf3e462fceb845067b27287c466d6e649c6ebc..396e47c57a283be4e2edd1f625c571ef589c9bcc 100644 (file)
@@ -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 \
index fea163ecbb5fce474de7c7b4a4caaaf2270e52a9..013ae217419cfdf99629c60c6264d8a74e21f59a 100644 (file)
@@ -492,6 +492,8 @@ public:
     struct {
         int unclean_shutdown;
         char *ssl_engine;
+        int session_ttl;
+        size_t sessionCacheSize;
     } SSL;
 #endif
 
index b086986690e6467e6e4a30737f90d892b1e8f84e..d6daf26557a5c0a753e0888e17278411de22060d 100644 (file)
@@ -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
index 71c481dc42d548868f243eefd8638d51dbddeef8..d72b69b60069ffd6e5b07513f75dfbab1ff03489 100644 (file)
@@ -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
index 5236ec559276656068389271cc831d65c368c407..997d70281e47a4c9ef518d6f2661897371ac0da7 100644 (file)
 #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"
 #include <errno.h>
 #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 */
index 9eb9d848ef6f004d82fe24a1122a3dba86ebf43d..7198f5bb81ac65d07e7210b7b4a36ce30fe7f137 100644 (file)
@@ -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_