[squid-dev] [PATCH] GnuTLS session resume

Amos Jeffries squid3 at treenet.co.nz
Wed Jun 22 04:42:45 UTC 2016


This patch shuffles session resume to libsecurity and implements it for
GnuTLS.

As-is it relies on the previous LockingPointer API patch redesign for a
generic reset() method.

Amos
-------------- next part --------------
=== modified file 'src/CachePeer.cc'
--- src/CachePeer.cc	2016-01-01 00:12:18 +0000
+++ src/CachePeer.cc	2016-06-21 10:06:55 +0000
@@ -25,41 +25,40 @@
     typelist(NULL),
     access(NULL),
     weight(1),
     basetime(0),
 #if USE_CACHE_DIGESTS
     digest(NULL),
     digest_url(NULL),
 #endif
     tcp_up(0),
     n_addresses(0),
     rr_count(0),
     next(NULL),
     testing_now(false),
     login(NULL),
     connect_timeout(0),
     connect_fail_limit(0),
     max_conn(0),
     domain(NULL),
 #if USE_OPENSSL
     sslContext(NULL),
-    sslSession(NULL),
 #endif
     front_end_https(0),
     connection_auth(2 /* auto */)
 {
     memset(&stats, 0, sizeof(stats));
     stats.logged_state = PEER_ALIVE;
 
     memset(&icp, 0, sizeof(icp));
     icp.port = CACHE_ICP_PORT;
     icp.version = ICP_VERSION_CURRENT;
 
 #if USE_HTCP
     memset(&htcp, 0, sizeof(htcp));
 #endif
     memset(&options, 0, sizeof(options));
     memset(&mcast, 0, sizeof(mcast));
     memset(&carp, 0, sizeof(carp));
 #if USE_AUTH
     memset(&userhash, 0, sizeof(userhash));
 #endif
@@ -85,26 +84,23 @@
 
 #if USE_CACHE_DIGESTS
     cbdataReferenceDone(digest);
     xfree(digest_url);
 #endif
 
     delete next;
 
     xfree(login);
 
     delete standby.pool;
 
     // the mgr job will notice that its owner is gone and stop
     PeerPoolMgr::Checkpoint(standby.mgr, "peer gone");
 
     xfree(domain);
 
 #if USE_OPENSSL
     if (sslContext)
         SSL_CTX_free(sslContext);
-
-    if (sslSession)
-        SSL_SESSION_free(sslSession);
 #endif
 }
 

=== modified file 'src/CachePeer.h'
--- src/CachePeer.h	2016-01-01 00:12:18 +0000
+++ src/CachePeer.h	2016-06-21 10:06:56 +0000
@@ -167,30 +167,28 @@
         unsigned int hash;
         double load_multiplier;
         double load_factor; /* normalized weight value */
     } sourcehash;
 
     char *login;        /* Proxy authorization */
     time_t connect_timeout;
     int connect_fail_limit;
     int max_conn;
     struct {
         PconnPool *pool; ///< idle connection pool for this peer
         CbcPointer<PeerPoolMgr> mgr; ///< pool manager
         int limit; ///< the limit itself
         bool waitingForClose; ///< a conn must close before we open a standby conn
     } standby; ///< optional "cache_peer standby=limit" feature
     char *domain;       /* Forced domain */
 
     /// security settings for peer connection
     Security::PeerOptions secure;
     Security::ContextPtr sslContext;
-#if USE_OPENSSL
-    SSL_SESSION *sslSession;
-#endif
+    Security::SessionStatePointer sslSession;
 
     int front_end_https;
     int connection_auth;
 };
 
 #endif /* SQUID_CACHEPEER_H_ */
 

=== modified file 'src/adaptation/icap/ServiceRep.cc'
--- src/adaptation/icap/ServiceRep.cc	2016-01-15 06:47:59 +0000
+++ src/adaptation/icap/ServiceRep.cc	2016-06-21 10:06:56 +0000
@@ -17,43 +17,40 @@
 #include "adaptation/icap/ServiceRep.h"
 #include "base/TextException.h"
 #include "comm/Connection.h"
 #include "ConfigParser.h"
 #include "Debug.h"
 #include "fde.h"
 #include "globals.h"
 #include "HttpReply.h"
 #include "ip/tools.h"
 #include "SquidConfig.h"
 #include "SquidTime.h"
 
 #define DEFAULT_ICAP_PORT   1344
 #define DEFAULT_ICAPS_PORT 11344
 
 CBDATA_NAMESPACED_CLASS_INIT(Adaptation::Icap, ServiceRep);
 
 Adaptation::Icap::ServiceRep::ServiceRep(const ServiceConfigPointer &svcCfg):
     AsyncJob("Adaptation::Icap::ServiceRep"), Adaptation::Service(svcCfg),
     sslContext(NULL),
-#if USE_OPENSSL
-    sslSession(NULL),
-#endif
     theOptions(NULL), theOptionsFetcher(0), theLastUpdate(0),
     theBusyConns(0),
     theAllWaiters(0),
     connOverloadReported(false),
     theIdleConns(NULL),
     isSuspended(0), notifying(false),
     updateScheduled(false),
     wasAnnouncedUp(true), // do not announce an "up" service at startup
     isDetached(false)
 {
     setMaxConnections();
     theIdleConns = new IdleConnList("ICAP Service", NULL);
 }
 
 Adaptation::Icap::ServiceRep::~ServiceRep()
 {
     delete theIdleConns;
     Must(!theOptionsFetcher);
     delete theOptions;
 }

=== modified file 'src/adaptation/icap/ServiceRep.h'
--- src/adaptation/icap/ServiceRep.h	2016-01-01 00:12:18 +0000
+++ src/adaptation/icap/ServiceRep.h	2016-06-21 10:06:56 +0000
@@ -94,43 +94,41 @@
 
     void noteNewWaiter() {theAllWaiters++;} ///< New xaction waiting for service to be up or available
     void noteGoneWaiter(); ///< An xaction is not waiting any more for service to be available
     bool existWaiters() const {return (theAllWaiters > 0);} ///< if there are xactions waiting for the service to be available
 
     //AsyncJob virtual methods
     virtual bool doneAll() const { return Adaptation::Initiator::doneAll() && false;}
     virtual void callException(const std::exception &e);
 
     virtual void detach();
     virtual bool detached() const;
 
 public: // treat these as private, they are for callbacks only
     void noteTimeToUpdate();
     void noteTimeToNotify();
 
     // receive either an ICAP OPTIONS response header or an abort message
     virtual void noteAdaptationAnswer(const Answer &answer);
 
     Security::ContextPtr sslContext;
-#if USE_OPENSSL
-    SSL_SESSION *sslSession;
-#endif
+    Security::SessionStatePointer sslSession;
 
 private:
     // stores Prepare() callback info
 
     struct Client {
         Pointer service; // one for each client to preserve service
         AsyncCall::Pointer callback;
     };
 
     typedef std::vector<Client> Clients;
     // TODO: rename to theUpWaiters
     Clients theClients; // all clients waiting for a call back
 
     Options *theOptions;
     CbcPointer<Adaptation::Initiate> theOptionsFetcher; // pending ICAP OPTIONS transaction
     time_t theLastUpdate; // time the options were last updated
 
     /// FIFO queue of xactions waiting for a connection slot and not yet notified
     /// about it; xaction is removed when notification is scheduled
     std::deque<Client> theNotificationWaiters;

=== modified file 'src/adaptation/icap/Xaction.cc'
--- src/adaptation/icap/Xaction.cc	2016-02-02 15:39:23 +0000
+++ src/adaptation/icap/Xaction.cc	2016-06-21 12:14:42 +0000
@@ -708,60 +708,53 @@
 {
     return false;
 }
 
 #if USE_OPENSSL
 Security::SessionPtr
 Ssl::IcapPeerConnector::initializeSsl()
 {
     auto ssl = Ssl::PeerConnector::initializeSsl();
     if (!ssl)
         return nullptr;
 
     assert(!icapService->cfg().secure.sslDomain.isEmpty());
     SBuf *host = new SBuf(icapService->cfg().secure.sslDomain);
     SSL_set_ex_data(ssl, ssl_ex_index_server, host);
 
     ACLFilledChecklist *check = (ACLFilledChecklist *)SSL_get_ex_data(ssl, ssl_ex_index_cert_error_check);
     if (check)
         check->dst_peer_name = *host;
 
-    if (icapService->sslSession)
-        SSL_set_session(ssl, icapService->sslSession);
+    Security::GetSessionResumeData(Security::SessionPointer(ssl), icapService->sslSession);
 
     return ssl;
 }
 
 void
 Ssl::IcapPeerConnector::noteNegotiationDone(ErrorState *error)
 {
     if (error)
         return;
 
     const int fd = serverConnection()->fd;
-    auto ssl = fd_table[fd].ssl.get();
-    assert(ssl);
-    if (!SSL_session_reused(ssl)) {
-        if (icapService->sslSession)
-            SSL_SESSION_free(icapService->sslSession);
-        icapService->sslSession = SSL_get1_session(ssl);
-    }
+    Security::GetSessionResumeData(fd_table[fd].ssl, icapService->sslSession);
 }
 
 void
 Adaptation::Icap::Xaction::handleSecuredPeer(Security::EncryptorAnswer &answer)
 {
     Must(securer != NULL);
     securer = NULL;
 
     if (closer != NULL) {
         if (answer.conn != NULL)
             comm_remove_close_handler(answer.conn->fd, closer);
         else
             closer->cancel("securing completed");
         closer = NULL;
     }
 
     if (answer.error.get()) {
         if (answer.conn != NULL)
             answer.conn->close();
         debugs(93, 2, typeName <<

=== modified file 'src/client_side.cc'
--- src/client_side.cc	2016-05-20 13:20:27 +0000
+++ src/client_side.cc	2016-06-21 10:06:56 +0000
@@ -2640,41 +2640,41 @@
         /* NOTREACHED */
     }
     return 1;
 }
 
 /** negotiate an SSL connection */
 static void
 clientNegotiateSSL(int fd, void *data)
 {
     ConnStateData *conn = (ConnStateData *)data;
     X509 *client_cert;
     auto ssl = fd_table[fd].ssl.get();
 
     int ret;
     if ((ret = Squid_SSL_accept(conn, clientNegotiateSSL)) <= 0) {
         if (ret < 0) // An error
             conn->clientConnection->close();
         return;
     }
 
-    if (SSL_session_reused(ssl)) {
+    if (Security::SessionIsResumed(fd_table[fd].ssl)) {
         debugs(83, 2, "clientNegotiateSSL: Session " << SSL_get_session(ssl) <<
                " reused on FD " << fd << " (" << fd_table[fd].ipaddr << ":" << (int)fd_table[fd].remote_port << ")");
     } else {
         if (Debug::Enabled(83, 4)) {
             /* Write out the SSL session details.. actually the call below, but
              * OpenSSL headers do strange typecasts confusing GCC.. */
             /* PEM_write_SSL_SESSION(debug_log, SSL_get_session(ssl)); */
 #if defined(OPENSSL_VERSION_NUMBER) && OPENSSL_VERSION_NUMBER >= 0x00908000L
             PEM_ASN1_write((i2d_of_void *)i2d_SSL_SESSION, PEM_STRING_SSL_SESSION, debug_log, (char *)SSL_get_session(ssl), NULL,NULL,0,NULL,NULL);
 
 #elif (ALLOW_ALWAYS_SSL_SESSION_DETAIL == 1)
 
             /* When using gcc 3.3.x and OpenSSL 0.9.7x sometimes a compile error can occur here.
             * This is caused by an unpredicatble gcc behaviour on a cast of the first argument
             * of PEM_ASN1_write(). For this reason this code section is disabled. To enable it,
             * define ALLOW_ALWAYS_SSL_SESSION_DETAIL=1.
             * Because there are two possible usable cast, if you get an error here, try the other
             * commented line. */
 
             PEM_ASN1_write((int(*)())i2d_SSL_SESSION, PEM_STRING_SSL_SESSION, debug_log, (char *)SSL_get_session(ssl), NULL,NULL,0,NULL,NULL);
=== modified file 'src/security/Session.cc'
--- src/security/Session.cc	2016-02-17 21:03:29 +0000
+++ src/security/Session.cc	2016-06-21 15:21:41 +0000
@@ -1,38 +1,77 @@
 /*
  * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 #include "squid.h"
 #include "anyp/PortCfg.h"
 #include "base/RunnersRegistry.h"
 #include "ipc/MemMap.h"
 #include "security/Session.h"
 #include "SquidConfig.h"
 
 #define SSL_SESSION_ID_SIZE 32
 #define SSL_SESSION_MAX_SIZE 10*1024
 
+bool
+Security::SessionIsResumed(const Security::SessionPointer &s)
+{
+    return
+#if USE_OPENSSL
+        SSL_session_reused(s.get()) == 1;
+#elif USE_GNUTLS
+        gnutls_session_is_resumed(s.get()) != 0;
+#else
+        false;
+#endif
+}
+
+void
+Security::GetSessionResumeData(const Security::SessionPointer &s, Security::SessionStatePointer &data)
+{
+    if (!SessionIsResumed(s)) {
+#if USE_OPENSSL
+        data.reset(SSL_get1_session(s.get()));
+#elif USE_GNUTLS
+        gnutls_datum_t *tmp = nullptr;
+        (void)gnutls_session_get_data2(s.get(), tmp);
+        data.reset(tmp);
+#endif
+    }
+}
+
+void
+Security::SetSessionResumeData(const Security::SessionPtr &s, const Security::SessionStatePointer &data)
+{
+    if (s) {
+#if USE_OPENSSL
+        (void)SSL_set_session(s, data.get());
+#elif USE_GNUTLS
+        (void)gnutls_session_set_data(s, data->data, data->size);
+#endif
+    }
+}
+
 static bool
 isTlsServer()
 {
     for (AnyP::PortCfgPointer s = HttpPortList; s != nullptr; s = s->next) {
         if (s->secure.encryptTransport)
             return true;
         if (s->flags.tunnelSslBumping)
             return true;
     }
 
     return false;
 }
 
 void
 initializeSessionCache()
 {
 #if USE_OPENSSL
     // 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);

=== modified file 'src/security/Session.h'
--- src/security/Session.h	2016-02-13 12:12:10 +0000
+++ src/security/Session.h	2016-06-21 10:06:56 +0000
@@ -14,40 +14,59 @@
 
 #if USE_OPENSSL
 #if HAVE_OPENSSL_SSL_H
 #include <openssl/ssl.h>
 #endif
 #endif
 
 #if USE_GNUTLS
 #if HAVE_GNUTLS_GNUTLS_H
 #include <gnutls/gnutls.h>
 #endif
 #endif
 
 namespace Security {
 
 #if USE_OPENSSL
 typedef SSL* SessionPtr;
 CtoCpp1(SSL_free, SSL *);
 typedef LockingPointer<SSL, Security::SSL_free_cpp, CRYPTO_LOCK_SSL> SessionPointer;
 
+typedef SSL_SESSION* SessionStatePtr;
+CtoCpp1(SSL_SESSION_free, SSL_SESSION *);
+typedef LockingPointer<SSL_SESSION, Security::SSL_SESSION_free_cpp, CRYPTO_LOCK_SSL_SESSION> SessionStatePointer;
+
 #elif USE_GNUTLS
 typedef gnutls_session_t SessionPtr;
 CtoCpp1(gnutls_deinit, gnutls_session_t);
 // TODO: Convert to Locking pointer.
 // Locks can be implemented attaching locks counter to gnutls_session_t
 // objects using the gnutls_session_set_ptr()/gnutls_session_get_ptr ()
 // library functions
 typedef TidyPointer<struct gnutls_session_int, Security::gnutls_deinit_cpp> SessionPointer;
 
+typedef gnutls_datum_t *SessionStatePtr;
+CtoCpp1(gnutls_free, gnutls_datum_t *);
+typedef TidyPointer<gnutls_datum_t, Security::gnutls_free_cpp> SessionStatePointer;
+
 #else
 // use void* so we can check against NULL
 typedef void* SessionPtr;
 typedef TidyPointer<void, nullptr> SessionPointer;
+typedef TidyPointer<void, nullptr> SessionStatePointer;
 
 #endif
 
+/// whether the session is a resumed one
+bool SessionIsResumed(const Security::SessionPointer &);
+
+/// Retrieve the data needed to resume this session on a later connection
+void GetSessionResumeData(const Security::SessionPointer &, Security::SessionStatePointer &);
+
+/// Set the data for resuming a previous session.
+/// Needs to be done before using the SessionPointer for a handshake.
+void SetSessionResumeData(const Security::SessionPtr &, const Security::SessionStatePointer &);
+
 } // namespace Security
 
 #endif /* SQUID_SRC_SECURITY_SESSION_H */
 

=== modified file 'src/ssl/BlindPeerConnector.cc'
--- src/ssl/BlindPeerConnector.cc	2016-01-27 16:56:38 +0000
+++ src/ssl/BlindPeerConnector.cc	2016-06-21 10:06:56 +0000
@@ -29,54 +29,49 @@
     return ::Config.ssl_client.sslContext;
 }
 
 Security::SessionPtr
 Ssl::BlindPeerConnector::initializeSsl()
 {
     auto ssl = Ssl::PeerConnector::initializeSsl();
     if (!ssl)
         return nullptr;
 
     if (const CachePeer *peer = serverConnection()->getPeer()) {
         assert(peer);
 
         // NP: domain may be a raw-IP but it is now always set
         assert(!peer->secure.sslDomain.isEmpty());
 
         // const loss is okay here, ssl_ex_index_server is only read and not assigned a destructor
         SBuf *host = new SBuf(peer->secure.sslDomain);
         SSL_set_ex_data(ssl, ssl_ex_index_server, host);
 
-        if (peer->sslSession)
-            SSL_set_session(ssl, peer->sslSession);
+        Security::SetSessionResumeData(ssl, peer->sslSession);
     } else {
         SBuf *hostName = new SBuf(request->url.host());
         SSL_set_ex_data(ssl, ssl_ex_index_server, (void*)hostName);
     }
 
     return ssl;
 }
 
 void
 Ssl::BlindPeerConnector::noteNegotiationDone(ErrorState *error)
 {
     if (error) {
         // XXX: forward.cc calls peerConnectSucceeded() after an OK TCP connect but
         // we call peerConnectFailed() if SSL failed afterwards. Is that OK?
         // It is not clear whether we should call peerConnectSucceeded/Failed()
         // based on TCP results, SSL results, or both. And the code is probably not
         // consistent in this aspect across tunnelling and forwarding modules.
         if (CachePeer *p = serverConnection()->getPeer())
             peerConnectFailed(p);
         return;
     }
 
-    const int fd = serverConnection()->fd;
-    Security::SessionPtr ssl = fd_table[fd].ssl.get();
-    if (serverConnection()->getPeer() && !SSL_session_reused(ssl)) {
-        if (serverConnection()->getPeer()->sslSession)
-            SSL_SESSION_free(serverConnection()->getPeer()->sslSession);
-
-        serverConnection()->getPeer()->sslSession = SSL_get1_session(ssl);
+    if (auto *peer = serverConnection()->getPeer()) {
+        const int fd = serverConnection()->fd;
+        Security::GetSessionResumeData(fd_table[fd].ssl, peer->sslSession);
     }
 }
 



More information about the squid-dev mailing list