]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Crypto-NG: initial GnuTLS support for encrypted server connections
authorAmos Jeffries <squid3@treenet.co.nz>
Thu, 23 Feb 2017 13:31:16 +0000 (02:31 +1300)
committerAmos Jeffries <squid3@treenet.co.nz>
Thu, 23 Feb 2017 13:31:16 +0000 (02:31 +1300)
Make significant changes to how the options= config settings are
handled internally since GnuTLS does not expose the priority_t
implementation details like OpenSSL. They are also applied to the
session object instead of to the context.

The Security::SessionPointer is converted to std::shared_ptr. This is
required because GnuTLS does not expose the locking like OpenSSL. Since
we store the SessionPointer to fde::Table::ssl we can always access it
from there one way or another and there is actually no need for OpenSSL
locking sessions now.

Most of the remaining session lifecycle logic is moved to
security/Session.* and given a generic API. Only some client-connection
and SSL-Bump related setup remains in ssl/.

A fair amount more debug is added along with some text changes doing
s/SSL/TLS/ in code comments and debug outputs.

The Security::ContextPointer is also converted to a std::shared_ptr. As
with SessionPointer these are stored in the long-term global config
objects and referenced from there as needed.

Support for https:// URL outgoing connections is enabled with GnuTLS.

23 files changed:
src/Makefile.am
src/cf.data.pre
src/client_side.cc
src/comm.cc
src/security/BlindPeerConnector.cc
src/security/Context.h
src/security/PeerConnector.cc
src/security/PeerOptions.cc
src/security/PeerOptions.h
src/security/ServerOptions.cc
src/security/Session.cc
src/security/Session.h
src/security/forward.h
src/ssl/bio.cc
src/ssl/bio.h
src/ssl/cert_validate_message.cc
src/ssl/cert_validate_message.h
src/ssl/helper.cc
src/ssl/support.cc
src/ssl/support.h
src/tests/stub_libsecurity.cc
src/tests/stub_libsslsquid.cc
src/url.cc

index afd4ad59199e941332769297d8f3b419a34dc1a2..7bfe659d469344c1fb1730ff3facaafa73935e7d 100644 (file)
@@ -3052,10 +3052,10 @@ tests_testUfs_LDADD = \
        acl/libacls.la \
        DiskIO/libdiskio.la \
        acl/libapi.la \
+       anyp/libanyp.la \
        $(SSL_LIBS) \
        ipc/libipc.la \
        comm/libcomm.la \
-       anyp/libanyp.la \
        dns/libdns.la \
        base/libbase.la \
        ip/libip.la \
index 56b32405647389f35099b5ab1ef3560bddc58ce7..e7978d0566ba6475f29049af573674c0764f4ccd 100644 (file)
@@ -2584,15 +2584,11 @@ DOC_START
                        To control SSLv3 use the options= parameter.
                        Supported Values: 1.0 (default), 1.1, 1.2
 
-       options=...     Specify various TLS/SSL implementation options:
+       options=...     Specify various TLS/SSL implementation options.
 
-                           NO_SSLv3    Disallow the use of SSLv3
-
-                           NO_TLSv1    Disallow the use of TLSv1.0
-
-                           NO_TLSv1_1  Disallow the use of TLSv1.1
+                       OpenSSL options most important are:
 
-                           NO_TLSv1_2  Disallow the use of TLSv1.2
+                           NO_SSLv3    Disallow the use of SSLv3
 
                            SINGLE_DH_USE
                                      Always create a new key when using
@@ -2609,8 +2605,21 @@ DOC_START
                                      Be warned that this reduces SSL/TLS
                                      strength to some attacks.
 
-                       See the OpenSSL SSL_CTX_set_options documentation for a
-                       more complete list.
+                               See the OpenSSL SSL_CTX_set_options documentation
+                               for a more complete list.
+
+                       GnuTLS options most important are:
+
+                           %NO_TICKETS
+                                     Disable use of RFC5077 session tickets.
+                                     Some servers may have problems
+                                     understanding the TLS extension due
+                                     to ambiguous specification in RFC4507.
+
+                               See the GnuTLS Priority Strings documentation
+                               for a more complete list.
+                               http://www.gnutls.org/manual/gnutls.html#Priority-Strings
+
        
        cafile=         PEM file containing CA certificates to use when verifying
                        the peer certificate. May be repeated to load multiple files.
@@ -3346,18 +3355,14 @@ DOC_START
 
        tls-min-version=1.N
                        The minimum TLS protocol version to permit. To control
-                       SSLv3 use the ssloptions= parameter.
+                       SSLv3 use the tls-options= parameter.
                        Supported Values: 1.0 (default), 1.1, 1.2
 
-       ssloptions=...  Specify various SSL implementation options:
+       tls-options=... Specify various TLS implementation options.
 
-                           NO_SSLv3    Disallow the use of SSLv3
+                       OpenSSL options most important are:
 
-                           NO_TLSv1    Disallow the use of TLSv1.0
-
-                           NO_TLSv1_1  Disallow the use of TLSv1.1
-
-                           NO_TLSv1_2  Disallow the use of TLSv1.2
+                           NO_SSLv3    Disallow the use of SSLv3
 
                            SINGLE_DH_USE
                                      Always create a new key when using
@@ -3376,7 +3381,19 @@ DOC_START
 
                        See the OpenSSL SSL_CTX_set_options documentation for a
                        more complete list.
-       
+
+                       GnuTLS options most important are:
+
+                           %NO_TICKETS
+                                     Disable use of RFC5077 session tickets.
+                                     Some servers may have problems
+                                     understanding the TLS extension due
+                                     to ambiguous specification in RFC4507.
+
+                               See the GnuTLS Priority Strings documentation
+                               for a more complete list.
+                               http://www.gnutls.org/manual/gnutls.html#Priority-Strings
+
        tls-cafile=     PEM file containing CA certificates to use when verifying
                        the peer certificate. May be repeated to load multiple files.
        
@@ -8671,17 +8688,13 @@ DOC_START
 
        tls-min-version=1.N
                        The minimum TLS protocol version to permit. To control
-                       SSLv3 use the ssloptions= parameter.
+                       SSLv3 use the tls-options= parameter.
                        Supported Values: 1.0 (default), 1.1, 1.2
 
        tls-options=... Specify various OpenSSL library options:
 
                            NO_SSLv3    Disallow the use of SSLv3
 
-                           NO_TLSv1    Disallow the use of TLSv1.0
-                           NO_TLSv1_1  Disallow the use of TLSv1.1
-                           NO_TLSv1_2  Disallow the use of TLSv1.2
-
                            SINGLE_DH_USE
                                      Always create a new key when using
                                      temporary/ephemeral DH key exchanges
index ef6b59d9d7ba7cebdda8c5b10452f8b6e5925823..575f9623fc2e391be6ec7941d448218f5b33e3af 100644 (file)
@@ -2558,13 +2558,12 @@ httpAccept(const CommAcceptCbParams &params)
 }
 
 #if USE_OPENSSL
-
-/** Create SSL connection structure and update fd_table */
+/// Create TLS connection structure and update fd_table
 static bool
 httpsCreate(const Comm::ConnectionPointer &conn, const Security::ContextPointer &ctx)
 {
-    if (Ssl::CreateServer(ctx, conn, "client https start")) {
-        debugs(33, 5, "will negotate SSL on " << conn);
+    if (Security::CreateServerSession(ctx, conn, "client https start")) {
+        debugs(33, 5, "will negotiate TLS on " << conn);
         return true;
     }
 
@@ -2853,8 +2852,7 @@ ConnStateData::sslCrtdHandleReply(const Helper::Reply &reply)
                     if (!ret)
                         debugs(33, 5, "Failed to set certificates to ssl object for PeekAndSplice mode");
 
-                    Security::ContextPointer ctx;
-                    ctx.resetAndLock(SSL_get_SSL_CTX(ssl));
+                    Security::ContextPointer ctx(Security::GetFrom(fd_table[clientConnection->fd].ssl));
                     Ssl::configureUnconfiguredSslContext(ctx, signAlgorithm, *port);
                 } else {
                     Security::ContextPointer ctx(Ssl::generateSslContextUsingPkeyAndCertFromMemory(reply_message.getBody().c_str(), *port));
@@ -3009,8 +3007,7 @@ ConnStateData::getSslContextStart()
             if (!Ssl::configureSSL(ssl, certProperties, *port))
                 debugs(33, 5, "Failed to set certificates to ssl object for PeekAndSplice mode");
 
-            Security::ContextPointer ctx;
-            ctx.resetAndLock(SSL_get_SSL_CTX(ssl));
+            Security::ContextPointer ctx(Security::GetFrom(fd_table[clientConnection->fd].ssl));
             Ssl::configureUnconfiguredSslContext(ctx, certProperties.signAlgorithm, *port);
         } else {
             Security::ContextPointer dynCtx(Ssl::generateSslContext(certProperties, *port));
index 1b62d7d19a37d0fcc1cbfd0ff7a4195e400c78c6..56f3965f177b0ab085e3b65596a543e7c3764c9e 100644 (file)
@@ -764,10 +764,7 @@ commLingerTimeout(const FdeCbParams &params)
 void
 comm_lingering_close(int fd)
 {
-#if USE_OPENSSL
-    if (fd_table[fd].ssl)
-        ssl_shutdown_method(fd_table[fd].ssl);
-#endif
+    Security::SessionSendGoodbye(fd_table[fd].ssl);
 
     if (shutdown(fd, 1) < 0) {
         comm_close(fd);
@@ -825,14 +822,11 @@ old_comm_reset_close(int fd)
     comm_close(fd);
 }
 
-#if USE_OPENSSL
 void
-commStartSslClose(const FdeCbParams &params)
+commStartTlsClose(const FdeCbParams &params)
 {
-    assert(fd_table[params.fd].ssl);
-    ssl_shutdown_method(fd_table[params.fd].ssl.get());
+    Security::SessionSendGoodbye(fd_table[params.fd].ssl);
 }
-#endif
 
 void
 comm_close_complete(const FdeCbParams &params)
@@ -890,15 +884,13 @@ _comm_close(int fd, char const *file, int line)
 
     F->flags.close_request = true;
 
-#if USE_OPENSSL
     if (F->ssl) {
-        AsyncCall::Pointer startCall=commCbCall(5,4, "commStartSslClose",
-                                                FdeCbPtrFun(commStartSslClose, NULL));
+        AsyncCall::Pointer startCall=commCbCall(5,4, "commStartTlsClose",
+                                                FdeCbPtrFun(commStartTlsClose, nullptr));
         FdeCbParams &startParams = GetCommParams<FdeCbParams>(startCall);
         startParams.fd = fd;
         ScheduleCallHere(startCall);
     }
-#endif
 
     // a half-closed fd may lack a reader, so we stop monitoring explicitly
     if (commHasHalfClosedMonitor(fd))
index ea1247ac73dbba9d24df6302be7707acdb74682b..747da785e735fdcc987c048eb6e0cab6b73c5e99 100644 (file)
@@ -32,8 +32,10 @@ Security::BlindPeerConnector::getTlsContext()
 bool
 Security::BlindPeerConnector::initialize(Security::SessionPointer &serverSession)
 {
-    if (!Security::PeerConnector::initialize(serverSession))
+    if (!Security::PeerConnector::initialize(serverSession)) {
+        debugs(83, 5, "Security::PeerConnector::initialize failed");
         return false;
+    }
 
     if (const CachePeer *peer = serverConnection()->getPeer()) {
         assert(peer);
@@ -52,6 +54,8 @@ Security::BlindPeerConnector::initialize(Security::SessionPointer &serverSession
         SSL_set_ex_data(serverSession.get(), ssl_ex_index_server, (void*)hostName);
 #endif
     }
+
+    debugs(83, 5, "success");
     return true;
 }
 
@@ -59,6 +63,7 @@ void
 Security::BlindPeerConnector::noteNegotiationDone(ErrorState *error)
 {
     if (error) {
+        debugs(83, 5, "error=" << (void*)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()
index 337c3b2c6e2d2edbf98f8a675c513255463f79ed..73a4d97e8a9bf8f2164914d4f6cfd33439481ef9 100644 (file)
@@ -9,8 +9,7 @@
 #ifndef SQUID_SRC_SECURITY_CONTEXT_H
 #define SQUID_SRC_SECURITY_CONTEXT_H
 
-#include "security/forward.h"
-#include "security/LockingPointer.h"
+#include <memory>
 
 #if USE_OPENSSL
 #if HAVE_OPENSSL_SSL_H
 namespace Security {
 
 #if USE_OPENSSL
-CtoCpp1(SSL_CTX_free, SSL_CTX *);
-#if defined(CRYPTO_LOCK_SSL_CTX) // OpenSSL 1.0
-inline int SSL_CTX_up_ref(SSL_CTX *t) {if (t) CRYPTO_add(&t->references, 1, CRYPTO_LOCK_SSL_CTX); return 0;}
-#endif
-typedef Security::LockingPointer<SSL_CTX, SSL_CTX_free_cpp, HardFun<int, SSL_CTX *, SSL_CTX_up_ref> > ContextPointer;
+typedef std::shared_ptr<SSL_CTX> ContextPointer;
 
 #elif USE_GNUTLS
-CtoCpp1(gnutls_certificate_free_credentials, gnutls_certificate_credentials_t);
-typedef Security::LockingPointer<struct gnutls_certificate_credentials_st, gnutls_certificate_free_credentials_cpp> ContextPointer;
+typedef std::shared_ptr<struct gnutls_certificate_credentials_st> ContextPointer;
 
 #else
 // use void* so we can check against nullptr
-typedef Security::LockingPointer<void, nullptr> ContextPointer;
+typedef std::shared_ptr<void> ContextPointer;
 
 #endif
 
index 4a20ae457bac3adb1da7ea7a8d8aa40c7c00a298..bf1169def1a097806d7635f9d882eef06de62555 100644 (file)
@@ -58,6 +58,7 @@ void
 Security::PeerConnector::start()
 {
     AsyncJob::start();
+    debugs(83, 5, "this=" << (void*)this);
 
     Security::SessionPointer tmp;
     if (prepareSocket() && initialize(tmp))
@@ -76,6 +77,7 @@ Security::PeerConnector::commCloseHandler(const CommCloseCbParams &params)
 void
 Security::PeerConnector::connectionClosed(const char *reason)
 {
+    debugs(83, 5, reason << " socket closed/closing. this=" << (void*)this);
     mustStop(reason);
     callback = NULL;
 }
@@ -83,32 +85,34 @@ Security::PeerConnector::connectionClosed(const char *reason)
 bool
 Security::PeerConnector::prepareSocket()
 {
-    const int fd = serverConnection()->fd;
-    if (!Comm::IsConnOpen(serverConn) || fd_table[serverConn->fd].closing()) {
+    debugs(83, 5, serverConnection() << ", this=" << (void*)this);
+    if (!Comm::IsConnOpen(serverConnection()) || fd_table[serverConnection()->fd].closing()) {
         connectionClosed("Security::PeerConnector::prepareSocket");
         return false;
     }
 
+    debugs(83, 5, serverConnection());
+
     // watch for external connection closures
     typedef CommCbMemFunT<Security::PeerConnector, CommCloseCbParams> Dialer;
     closeHandler = JobCallback(9, 5, Dialer, this, Security::PeerConnector::commCloseHandler);
-    comm_add_close_handler(fd, closeHandler);
+    comm_add_close_handler(serverConnection()->fd, closeHandler);
     return true;
 }
 
 bool
 Security::PeerConnector::initialize(Security::SessionPointer &serverSession)
 {
-#if USE_OPENSSL
     Security::ContextPointer ctx(getTlsContext());
-    assert(ctx);
+    debugs(83, 5, serverConnection() << ", ctx=" << (void*)ctx.get());
 
-    if (!Ssl::CreateClient(ctx, serverConnection(), "server https start")) {
+    if (!ctx || !Security::CreateClientSession(ctx, serverConnection(), "server https start")) {
         const auto xerrno = errno;
-        const auto ssl_error = ERR_get_error();
+        if (!ctx) {
+            debugs(83, DBG_IMPORTANT, "Error initializing TLS connection: No security context.");
+        } // else CreateClientSession() did the appropriate debugs() already
         ErrorState *anErr = new ErrorState(ERR_SOCKET_FAILURE, Http::scInternalServerError, request.getRaw());
         anErr->xerrno = xerrno;
-        debugs(83, DBG_IMPORTANT, "Error allocating TLS handle: " << Security::ErrorString(ssl_error));
         noteNegotiationDone(anErr);
         bail(anErr);
         return false;
@@ -116,7 +120,9 @@ Security::PeerConnector::initialize(Security::SessionPointer &serverSession)
 
     // A TLS/SSL session has now been created for the connection and stored in fd_table
     serverSession = fd_table[serverConnection()->fd].ssl;
+    debugs(83, 5, serverConnection() << ", session=" << (void*)serverSession.get());
 
+#if USE_OPENSSL
     // If CertValidation Helper used do not lookup checklist for errors,
     // but keep a list of errors to send it to CertValidator
     if (!Ssl::TheConfig.ssl_crt_validator) {
@@ -129,11 +135,9 @@ Security::PeerConnector::initialize(Security::SessionPointer &serverSession)
             SSL_set_ex_data(serverSession.get(), ssl_ex_index_cert_error_check, check);
         }
     }
+#endif
 
     return true;
-#else
-    return false;
-#endif
 }
 
 void
@@ -179,11 +183,30 @@ Security::PeerConnector::negotiate()
         return;
 
 #if USE_OPENSSL
-    const int result = SSL_connect(fd_table[fd].ssl.get());
+    auto session = fd_table[fd].ssl.get();
+    debugs(83, 5, "SSL_connect session=" << (void*)session);
+    const int result = SSL_connect(session);
+    if (result <= 0) {
+#elif USE_GNUTLS
+    auto session = fd_table[fd].ssl.get();
+    const int result = gnutls_handshake(session);
+    debugs(83, 5, "gnutls_handshake session=" << (void*)session << ", result=" << result);
+
+    if (result == GNUTLS_E_SUCCESS) {
+        char *desc = gnutls_session_get_desc(session);
+        debugs(83, 2, serverConnection() << " TLS Session info: " << desc);
+        gnutls_free(desc);
+    }
+
+    if (result != GNUTLS_E_SUCCESS) {
+        // debug the TLS session state so far
+        auto descIn = gnutls_handshake_get_last_in(session);
+        debugs(83, 2, "handshake IN: " << gnutls_handshake_description_get_name(descIn));
+        auto descOut = gnutls_handshake_get_last_out(session);
+        debugs(83, 2, "handshake OUT: " << gnutls_handshake_description_get_name(descOut));
 #else
-    const int result = -1;
+    if (const int result = -1) {
 #endif
-    if (result <= 0) {
         handleNegotiateError(result);
         return; // we might be gone by now
     }
@@ -205,11 +228,11 @@ Security::PeerConnector::sslFinalized()
         Security::SessionPointer session(fd_table[fd].ssl);
 
         Ssl::CertValidationRequest validationRequest;
-        // WARNING: Currently we do not use any locking for any of the
-        // members of the Ssl::CertValidationRequest class. In this code the
+        // WARNING: Currently we do not use any locking for 'errors' member
+        // of the Ssl::CertValidationRequest class. In this code the
         // Ssl::CertValidationRequest object used only to pass data to
         // Ssl::CertValidationHelper::submit method.
-        validationRequest.ssl = session.get();
+        validationRequest.ssl = session;
         if (SBuf *dName = (SBuf *)SSL_get_ex_data(session.get(), ssl_ex_index_server))
             validationRequest.domainName = dName->c_str();
         if (Security::CertErrors *errs = static_cast<Security::CertErrors *>(SSL_get_ex_data(session.get(), ssl_ex_index_ssl_errors)))
@@ -360,10 +383,11 @@ Security::PeerConnector::NegotiateSsl(int, void *data)
 void
 Security::PeerConnector::handleNegotiateError(const int ret)
 {
-#if USE_OPENSSL
     const int fd = serverConnection()->fd;
-    unsigned long ssl_lib_error = SSL_ERROR_NONE;
-    Security::SessionPointer session(fd_table[fd].ssl);
+    const Security::SessionPointer session(fd_table[fd].ssl);
+    unsigned long ssl_lib_error = ret;
+
+#if USE_OPENSSL
     const int ssl_error = SSL_get_error(session.get(), ret);
 
     switch (ssl_error) {
@@ -382,19 +406,49 @@ Security::PeerConnector::handleNegotiateError(const int ret)
         break;
     default:
         // no special error handling for all other errors
+        ssl_lib_error = SSL_ERROR_NONE;
         break;
     }
 
+#elif USE_GNUTLS
+    const int ssl_error = ret;
+
+    switch (ret) {
+    case GNUTLS_E_WARNING_ALERT_RECEIVED: {
+        auto alert = gnutls_alert_get(session.get());
+        debugs(83, DBG_IMPORTANT, "TLS ALERT: " << gnutls_alert_get_name(alert));
+    }
+    // drop through to next case
+
+    case GNUTLS_E_AGAIN:
+    case GNUTLS_E_INTERRUPTED:
+        if (gnutls_record_get_direction(session.get()) == 0)
+            noteWantRead();
+        else
+            noteWantWrite();
+        return;
+
+    default:
+        // no special error handling for all other errors
+        break;
+    }
+
+#else
+    // this avoids unused variable compiler warnings.
+    Must(!session);
+    const int ssl_error = ret;
+#endif
+
     // Log connection details, if any
     recordNegotiationDetails();
     noteNegotiationError(ret, ssl_error, ssl_lib_error);
-#endif
 }
 
 void
 Security::PeerConnector::noteWantRead()
 {
     const int fd = serverConnection()->fd;
+    debugs(83, 5, serverConnection());
 #if USE_OPENSSL
     Security::SessionPointer session(fd_table[fd].ssl);
     BIO *b = SSL_get_rbio(session.get());
@@ -425,6 +479,7 @@ void
 Security::PeerConnector::noteWantWrite()
 {
     const int fd = serverConnection()->fd;
+    debugs(83, 5, serverConnection());
     Comm::SetSelect(fd, COMM_SELECT_WRITE, &NegotiateSsl, this, 0);
     return;
 }
@@ -432,21 +487,23 @@ Security::PeerConnector::noteWantWrite()
 void
 Security::PeerConnector::noteNegotiationError(const int ret, const int ssl_error, const int ssl_lib_error)
 {
-#if USE_OPENSSL // not used unless OpenSSL enabled.
 #if defined(EPROTO)
     int sysErrNo = EPROTO;
 #else
     int sysErrNo = EACCES;
 #endif
 
+#if USE_OPENSSL
     // store/report errno when ssl_error is SSL_ERROR_SYSCALL, ssl_lib_error is 0, and ret is -1
     if (ssl_error == SSL_ERROR_SYSCALL && ret == -1 && ssl_lib_error == 0)
         sysErrNo = errno;
+#endif
+    int xerr = errno;
 
     const int fd = serverConnection()->fd;
-    debugs(83, DBG_IMPORTANT, "Error negotiating SSL on FD " << fd <<
+    debugs(83, DBG_IMPORTANT, "ERROR: negotiating TLS on FD " << fd <<
            ": " << Security::ErrorString(ssl_lib_error) << " (" <<
-           ssl_error << "/" << ret << "/" << errno << ")");
+           ssl_error << "/" << ret << "/" << xerr << ")");
 
     ErrorState *anErr = NULL;
     if (request != NULL)
@@ -455,6 +512,7 @@ Security::PeerConnector::noteNegotiationError(const int ret, const int ssl_error
         anErr = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, NULL);
     anErr->xerrno = sysErrNo;
 
+#if USE_OPENSSL
     Security::SessionPointer session(fd_table[fd].ssl);
     Ssl::ErrorDetail *errFromFailure = static_cast<Ssl::ErrorDetail *>(SSL_get_ex_data(session.get(), ssl_ex_index_ssl_error_detail));
     if (errFromFailure != NULL) {
@@ -471,10 +529,10 @@ Security::PeerConnector::noteNegotiationError(const int ret, const int ssl_error
 
     if (ssl_lib_error != SSL_ERROR_NONE)
         anErr->detail->setLibError(ssl_lib_error);
+#endif
 
     noteNegotiationDone(anErr);
     bail(anErr);
-#endif
 }
 
 void
@@ -498,6 +556,8 @@ Security::PeerConnector::bail(ErrorState *error)
 void
 Security::PeerConnector::callBack()
 {
+    debugs(83, 5, "TLS setup ended for " << serverConnection());
+
     AsyncCall::Pointer cb = callback;
     // Do this now so that if we throw below, swanSong() assert that we _tried_
     // to call back holds.
index 044952be6a75df9a90d30f5b35b14e64c59ba404..46a43a8474a840bab39396712a33da815f802262 100644 (file)
 
 Security::PeerOptions Security::ProxyOutgoingConfig;
 
-Security::PeerOptions::PeerOptions(const Security::PeerOptions &p) :
-    sslOptions(p.sslOptions),
-    caDir(p.caDir),
-    crlFile(p.crlFile),
-    sslCipher(p.sslCipher),
-    sslFlags(p.sslFlags),
-    sslDomain(p.sslDomain),
-    parsedOptions(p.parsedOptions),
-    parsedFlags(p.parsedFlags),
-    certs(p.certs),
-    caFiles(p.caFiles),
-    parsedCrl(p.parsedCrl),
-    sslVersion(p.sslVersion),
-    encryptTransport(p.encryptTransport)
+Security::PeerOptions::PeerOptions()
 {
-    memcpy(&flags, &p.flags, sizeof(flags));
+    // init options consistent with an empty sslOptions
+    parseOptions();
 }
 
 void
@@ -71,7 +59,7 @@ Security::PeerOptions::parse(const char *token)
         tlsMinVersion = SBuf(token + 12);
     } else if (strncmp(token, "options=", 8) == 0) {
         sslOptions = SBuf(token + 8);
-        parsedOptions = parseOptions();
+        parseOptions();
     } else if (strncmp(token, "cipher=", 7) == 0) {
         sslCipher = SBuf(token + 7);
     } else if (strncmp(token, "cafile=", 7) == 0) {
@@ -167,6 +155,7 @@ Security::PeerOptions::updateTlsVersionLimits()
         if (tok.skip('1') && tok.skip('.') && tok.int64(v, 10, false, 1) && v <= 3) {
             // only account for TLS here - SSL versions are handled by options= parameter
             // avoid affecting options= parameter in cachemgr config report
+#if USE_OPENSSL
 #if SSL_OP_NO_TLSv1
             if (v > 0)
                 parsedOptions |= SSL_OP_NO_TLSv1;
@@ -180,36 +169,73 @@ Security::PeerOptions::updateTlsVersionLimits()
                 parsedOptions |= SSL_OP_NO_TLSv1_2;
 #endif
 
+#elif USE_GNUTLS
+            // XXX: update parsedOptions directly to avoid polluting 'options=' dumps
+            SBuf add;
+            if (v > 0)
+                add.append(":-VERS-TLS1.0");
+            if (v > 1)
+                add.append(":-VERS-TLS1.1");
+            if (v > 2)
+                add.append(":-VERS-TLS1.2");
+
+            if (sslOptions.isEmpty())
+                add.chop(1); // remove the initial ':'
+            sslOptions.append(add);
+#endif
+
         } else {
             debugs(0, DBG_PARSE_NOTE(1), "WARNING: Unknown TLS minimum version: " << tlsMinVersion);
         }
 
-    } else if (sslVersion > 2) {
+        return;
+    }
+
+    if (sslVersion > 2) {
         // backward compatibility hack for sslversion= configuration
         // only use if tls-min-version=N.N is not present
         // values 0-2 for auto and SSLv2 are not supported any longer.
         // Do it this way so we DO cause changes to options= in cachemgr config report
-        const char *add = NULL;
+        const char *add = nullptr;
         switch (sslVersion) {
         case 3:
-            add = "NO_TLSv1,NO_TLSv1_1,NO_TLSv1_2";
+#if USE_OPENSSL
+            parsedOptions |= (SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1|SSL_OP_NO_TLSv1_2);
+#elif USE_GNUTLS
+            add = ":-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2";
+#endif
             break;
         case 4:
-            add = "NO_SSLv3,NO_TLSv1_1,NO_TLSv1_2";
+#if USE_OPENSSL
+            parsedOptions |= (SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1_1|SSL_OP_NO_TLSv1_2);
+#elif USE_GNUTLS
+            add = ":+VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2";
+#endif
             break;
         case 5:
-            add = "NO_SSLv3,NO_TLSv1,NO_TLSv1_2";
+#if USE_OPENSSL
+            parsedOptions |= (SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_2);
+#elif USE_GNUTLS
+            add = ":-VERS-TLS1.0:+VERS-TLS1.1:-VERS-TLS1.2";
+#endif
             break;
         case 6:
-            add = "NO_SSLv3,NO_TLSv1,NO_TLSv1_1";
+#if USE_OPENSSL
+            parsedOptions |= (SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1);
+#elif USE_GNUTLS
+            add = ":-VERS-TLS1.0:-VERS-TLS1.1";
+#endif
             break;
         default: // nothing
             break;
         }
         if (add) {
-            if (!sslOptions.isEmpty())
-                sslOptions.append(",",1);
-            sslOptions.append(add, strlen(add));
+#if USE_GNUTLS // dont bother otherwise
+            if (sslOptions.isEmpty())
+                sslOptions.append(add+1, strlen(add+1));
+            else
+                sslOptions.append(add, strlen(add));
+#endif
         }
         sslVersion = 0; // prevent sslOptions being repeatedly appended
     }
@@ -231,7 +257,7 @@ Security::PeerOptions::createBlankContext() const
         const auto x = ERR_get_error();
         fatalf("Failed to allocate TLS client context: %s\n", Security::ErrorString(x));
     }
-    ctx.resetWithoutLocking(t);
+    ctx = convertContextFromRawPtr(t);
 
 #elif USE_GNUTLS
     // Initialize for X.509 certificate exchange
@@ -239,7 +265,7 @@ Security::PeerOptions::createBlankContext() const
     if (const int x = gnutls_certificate_allocate_credentials(&t)) {
         fatalf("Failed to allocate TLS client context: %s\n", Security::ErrorString(x));
     }
-    ctx.resetWithoutLocking(t);
+    ctx = convertContextFromRawPtr(t);
 
 #else
     debugs(83, 1, "WARNING: Failed to allocate TLS client context: No TLS library");
@@ -257,8 +283,11 @@ Security::PeerOptions::createClientContext(bool setOptions)
     Security::ContextPointer t(createBlankContext());
     if (t) {
 #if USE_OPENSSL
+        // NP: GnuTLS uses 'priorities' which are set per-session instead.
+        SSL_CTX_set_options(t.get(), (setOptions ? parsedOptions : 0));
+
         // XXX: temporary performance regression. c_str() data copies and prevents this being a const method
-        Ssl::InitClientContext(t, *this, (setOptions ? parsedOptions : 0), parsedFlags);
+        Ssl::InitClientContext(t, *this, parsedFlags);
 #endif
         updateContextNpn(t);
         updateContextCa(t);
@@ -268,6 +297,7 @@ Security::PeerOptions::createClientContext(bool setOptions)
     return t;
 }
 
+#if USE_OPENSSL
 /// set of options we can parse and what they map to
 static struct ssl_option {
     const char *name;
@@ -397,18 +427,20 @@ static struct ssl_option {
         NULL, 0
     }
 };
+#endif /* USE_OPENSSL */
 
 /**
  * Pre-parse TLS options= parameter to be applied when the TLS objects created.
  * Options must not used in the case of peek or stare bump mode.
  */
-long
+void
 Security::PeerOptions::parseOptions()
 {
-    long op = 0;
+#if USE_OPENSSL
     ::Parser::Tokenizer tok(sslOptions);
+    long op = 0;
 
-    do {
+    while (!tok.atEnd()) {
         enum {
             MODE_ADD, MODE_REMOVE
         } mode;
@@ -461,13 +493,31 @@ Security::PeerOptions::parseOptions()
             fatalf("Unknown TLS option '" SQUIDSBUFPH "'", SQUIDSBUFPRINT(tok.remaining()));
         }
 
-    } while (!tok.atEnd());
+    }
 
 #if SSL_OP_NO_SSLv2
     // compliance with RFC 6176: Prohibiting Secure Sockets Layer (SSL) Version 2.0
     op = op | SSL_OP_NO_SSLv2;
 #endif
-    return op;
+    parsedOptions = op;
+
+#elif USE_GNUTLS
+    if (sslOptions.isEmpty()) {
+        parsedOptions.reset();
+        return;
+    }
+
+    const char *err = nullptr;
+    const char *priorities = sslOptions.c_str();
+    gnutls_priority_t op;
+    if (gnutls_priority_init(&op, priorities, &err) != GNUTLS_E_SUCCESS) {
+        fatalf("Unknown TLS option '%s'", err);
+    }
+    parsedOptions = Security::ParsedOptions(op, [](gnutls_priority_t p) {
+        debugs(83, 5, "gnutls_priority_deinit p=" << (void*)p);
+        gnutls_priority_deinit(p);
+    });
+#endif
 }
 
 /**
@@ -572,6 +622,7 @@ Security::PeerOptions::updateContextNpn(Security::ContextPointer &ctx)
 static const char *
 loadSystemTrustedCa(Security::ContextPointer &ctx)
 {
+    debugs(83, 8, "Setting default system Trusted CA. ctx=" << (void*)ctx.get());
 #if USE_OPENSSL
     if (SSL_CTX_set_default_verify_paths(ctx.get()) == 0)
         return Security::ErrorString(ERR_get_error());
@@ -641,6 +692,31 @@ Security::PeerOptions::updateContextCrl(Security::ContextPointer &ctx)
 #endif /* USE_OPENSSL */
 }
 
+void
+Security::PeerOptions::updateSessionOptions(Security::SessionPointer &s)
+{
+#if USE_OPENSSL
+    // 'options=' value being set to session is a GnuTLS specific thing.
+#elif USE_GNUTLS
+    int x;
+    SBuf errMsg;
+    if (!parsedOptions) {
+        debugs(83, 5, "set GnuTLS default priority/options for session=" << s);
+        x = gnutls_set_default_priority(s.get());
+        static const SBuf defaults("default");
+        errMsg = defaults;
+    } else {
+        debugs(83, 5, "set GnuTLS options '" << sslOptions << "' for session=" << s);
+        x = gnutls_priority_set(s.get(), parsedOptions.get());
+        errMsg = sslOptions;
+    }
+
+    if (x != GNUTLS_E_SUCCESS) {
+        debugs(83, DBG_IMPORTANT, "ERROR: Failed to set TLS options (" << errMsg << "). error: " << Security::ErrorString(x));
+    }
+#endif
+}
+
 void
 parse_securePeerOptions(Security::PeerOptions *opt)
 {
index 18c4e4766bc0ace31525180baf51cc1528686edc..cbe848ecd6d87a5f343104e532122a47b0549e54 100644 (file)
@@ -22,9 +22,10 @@ namespace Security
 class PeerOptions
 {
 public:
-    PeerOptions() : parsedOptions(0), parsedFlags(0), sslVersion(0), encryptTransport(false) {}
-    PeerOptions(const PeerOptions &);
-    virtual ~PeerOptions() = default;
+    PeerOptions();
+    PeerOptions(const PeerOptions &) = default;
+    PeerOptions &operator =(const PeerOptions &) = default;
+    virtual ~PeerOptions() {}
 
     /// parse a TLS squid.conf option
     virtual void parse(const char *);
@@ -50,11 +51,14 @@ public:
     /// setup the CRL details for the given context
     void updateContextCrl(Security::ContextPointer &);
 
+    /// setup any library-specific options that can be set for the given session
+    void updateSessionOptions(Security::SessionPointer &);
+
     /// output squid.conf syntax with 'pfx' prefix on parameters for the stored settings
     virtual void dumpCfg(Packable *, const char *pfx) const;
 
 private:
-    long parseOptions();
+    void parseOptions(); ///< parsed value of sslOptions
     long parseFlags();
     void loadCrlFile();
 
@@ -69,19 +73,39 @@ public:
 
     SBuf tlsMinVersion;  ///< version label for minimum TLS version to permit
 
-    long parsedOptions; ///< parsed value of sslOptions
-    long parsedFlags;   ///< parsed value of sslFlags
+    Security::ParsedOptions parsedOptions; ///< parsed value of sslOptions
+    long parsedFlags = 0;   ///< parsed value of sslFlags
 
     std::list<Security::KeyData> certs; ///< details from the cert= and file= config parameters
     std::list<SBuf> caFiles;  ///< paths of files containing trusted Certificate Authority
     Security::CertRevokeList parsedCrl; ///< CRL to use when verifying the remote end certificate
 
 protected:
-    int sslVersion;
+    template<typename T>
+    Security::ContextPointer convertContextFromRawPtr(T ctx) const {
+#if USE_OPENSSL
+        return ContextPointer(ctx, [](SSL_CTX *p) {
+            debugs(83, 5, "SSL_free ctx=" << (void*)p);
+            SSL_CTX_free(p);
+        });
+#elif USE_GNUTLS
+        return Security::ContextPointer(ctx, [](gnutls_certificate_credentials_t p) {
+            debugs(83, 5, "gnutls_certificate_free_credentials ctx=" << (void*)p);
+            gnutls_certificate_free_credentials(p);
+        });
+#else
+        assert(!ctx);
+        return Security::ContextPointer();
+#endif
+    }
+
+    int sslVersion = 0;
 
     /// flags governing Squid internal TLS operations
     struct flags_ {
         flags_() : tlsDefaultCa(true), tlsNpn(true) {}
+        flags_(const flags_ &) = default;
+        flags_ &operator =(const flags_ &) = default;
 
         /// whether to use the system default Trusted CA when verifying the remote end certificate
         YesNoNone tlsDefaultCa;
@@ -92,7 +116,7 @@ protected:
 
 public:
     /// whether transport encryption (TLS/SSL) is to be used on connections to the peer
-    bool encryptTransport;
+    bool encryptTransport = false;
 };
 
 /// configuration options for DIRECT server access
index d8c16e887ace5c6a9b0137d7f3eb589dd82f73e1..a40e89d525f9b39e03b0dc6f057233780ad30898 100644 (file)
@@ -101,7 +101,7 @@ Security::ServerOptions::createBlankContext() const
         const auto x = ERR_get_error();
         debugs(83, DBG_CRITICAL, "ERROR: Failed to allocate TLS server context: " << Security::ErrorString(x));
     }
-    ctx.resetWithoutLocking(t);
+    ctx = convertContextFromRawPtr(t);
 
 #elif USE_GNUTLS
     // Initialize for X.509 certificate exchange
@@ -109,7 +109,7 @@ Security::ServerOptions::createBlankContext() const
     if (const int x = gnutls_certificate_allocate_credentials(&t)) {
         debugs(83, DBG_CRITICAL, "ERROR: Failed to allocate TLS server context: " << Security::ErrorString(x));
     }
-    ctx.resetWithoutLocking(t);
+    ctx = convertContextFromRawPtr(t);
 
 #else
     debugs(83, DBG_CRITICAL, "ERROR: Failed to allocate TLS server context: No TLS library");
index 8f3a61b448522201b5c524f944067f2cf1cc2943..b34bdc2961adb8fc03863454761e0f5ee3424734 100644 (file)
 #include "squid.h"
 #include "anyp/PortCfg.h"
 #include "base/RunnersRegistry.h"
+#include "CachePeer.h"
 #include "Debug.h"
+#include "fd.h"
+#include "fde.h"
 #include "ipc/MemMap.h"
 #include "security/Session.h"
 #include "SquidConfig.h"
+#include "ssl/bio.h"
 
 #define SSL_SESSION_ID_SIZE 32
 #define SSL_SESSION_MAX_SIZE 10*1024
 
+#if USE_OPENSSL || USE_GNUTLS
+static int
+tls_read_method(int fd, char *buf, int len)
+{
+    auto session = fd_table[fd].ssl.get();
+    debugs(83, 3, "started for session=" << (void*)session);
+
+#if DONT_DO_THIS && USE_OPENSSL
+    if (!SSL_is_init_finished(session)) {
+        errno = ENOTCONN;
+        return -1;
+    }
+#endif
+
+#if USE_OPENSSL
+    int i = SSL_read(session, buf, len);
+#elif USE_GNUTLS
+    int i = gnutls_record_recv(session, buf, len);
+#endif
+
+    if (i > 0) {
+        debugs(83, 8, "TLS FD " << fd << " session=" << (void*)session << " " << i << " bytes");
+        (void)VALGRIND_MAKE_MEM_DEFINED(buf, i);
+    }
+
+#if USE_OPENSSL
+    if (i > 0 && SSL_pending(session) > 0) {
+#elif USE_GNUTLS
+    if (i > 0 && gnutls_record_check_pending(session) > 0) {
+#endif
+        debugs(83, 2, "TLS FD " << fd << " is pending");
+        fd_table[fd].flags.read_pending = true;
+    } else
+        fd_table[fd].flags.read_pending = false;
+
+    return i;
+}
+
+static int
+tls_write_method(int fd, const char *buf, int len)
+{
+    auto session = fd_table[fd].ssl.get();
+    debugs(83, 3, "started for session=" << (void*)session);
+
+#if USE_OPENSSL
+    if (!SSL_is_init_finished(session)) {
+        errno = ENOTCONN;
+        return -1;
+    }
+#endif
+
+#if USE_OPENSSL
+    int i = SSL_write(session, buf, len);
+#elif USE_GNUTLS
+    int i = gnutls_record_send(session, buf, len);
+#endif
+
+    if (i > 0) {
+        debugs(83, 8, "TLS FD " << fd << " session=" << (void*)session << " " << i << " bytes");
+    }
+    return i;
+}
+#endif
+
+#if USE_OPENSSL
+Security::SessionPointer
+Security::NewSessionObject(const Security::ContextPointer &ctx)
+{
+    Security::SessionPointer session(SSL_new(ctx.get()), [](SSL *p) {
+        debugs(83, 5, "SSL_free session=" << (void*)p);
+        SSL_free(p);
+    });
+    debugs(83, 5, "SSL_new session=" << (void*)session.get());
+    return session;
+}
+#endif
+
+static bool
+CreateSession(const Security::ContextPointer &ctx, const Comm::ConnectionPointer &conn, Security::Io::Type type, const char *squidCtx)
+{
+    if (!Comm::IsConnOpen(conn)) {
+        debugs(83, DBG_IMPORTANT, "Gone connection");
+        return false;
+    }
+
+#if USE_OPENSSL || USE_GNUTLS
+
+    const char *errAction = "with no TLS/SSL library";
+    int errCode = 0;
+#if USE_OPENSSL
+    Security::SessionPointer session(Security::NewSessionObject(ctx));
+    if (!session) {
+        errCode = ERR_get_error();
+        errAction = "failed to allocate handle";
+    }
+#elif USE_GNUTLS
+    gnutls_session_t tmp;
+    errCode = gnutls_init(&tmp, static_cast<unsigned int>(type) | GNUTLS_NONBLOCK);
+    Security::SessionPointer session(tmp, [](gnutls_session_t p) {
+        debugs(83, 5, "gnutls_deinit session=" << (void*)p);
+        gnutls_deinit(p);
+    });
+    debugs(83, 5, "gnutls_init " << (type == Security::Io::BIO_TO_SERVER ? "client" : "server" )<< " session=" << (void*)session.get());
+    if (errCode != GNUTLS_E_SUCCESS) {
+        session.reset();
+        errAction = "failed to initialize session";
+    }
+#endif
+
+    if (session) {
+        const int fd = conn->fd;
+
+#if USE_OPENSSL
+        // without BIO, we would call SSL_set_fd(ssl.get(), fd) instead
+        if (BIO *bio = Ssl::Bio::Create(fd, type)) {
+            Ssl::Bio::Link(session.get(), bio); // cannot fail
+#elif USE_GNUTLS
+        errCode = gnutls_credentials_set(session.get(), GNUTLS_CRD_CERTIFICATE, ctx.get());
+        if (errCode == GNUTLS_E_SUCCESS) {
+
+            if (auto *peer = conn->getPeer())
+                peer->secure.updateSessionOptions(session);
+            else
+                Security::ProxyOutgoingConfig.updateSessionOptions(session);
+
+            // NP: GnuTLS does not yet support the BIO operations
+            //     this does the equivalent of SSL_set_fd() for now.
+            gnutls_transport_set_int(session.get(), fd);
+            gnutls_handshake_set_timeout(session.get(), GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT);
+#endif
+
+            debugs(83, 5, "link FD " << fd << " to TLS session=" << (void*)session.get());
+            fd_table[fd].ssl = session;
+            fd_table[fd].read_method = &tls_read_method;
+            fd_table[fd].write_method = &tls_write_method;
+            fd_note(fd, squidCtx);
+            return true;
+        }
+
+#if USE_OPENSSL
+        errCode = ERR_get_error();
+        errAction = "failed to initialize I/O";
+#elif USE_GNUTLS
+        errAction = "failed to assign credentials";
+#endif
+    }
+
+    debugs(83, DBG_IMPORTANT, "ERROR: " << squidCtx << ' ' << errAction <<
+           ": " << (errCode != 0 ? Security::ErrorString(errCode) : ""));
+#endif
+    return false;
+}
+
+bool
+Security::CreateClientSession(const Security::ContextPointer &ctx, const Comm::ConnectionPointer &c, const char *squidCtx)
+{
+    return CreateSession(ctx, c, Security::Io::BIO_TO_SERVER, squidCtx);
+}
+
+bool
+Security::CreateServerSession(const Security::ContextPointer &ctx, const Comm::ConnectionPointer &c, const char *squidCtx)
+{
+    return CreateSession(ctx, c, Security::Io::BIO_TO_CLIENT, squidCtx);
+}
+
+void
+Security::SessionSendGoodbye(const Security::SessionPointer &s)
+{
+    debugs(83, 5, "session=" << (void*)s.get());
+    if (s) {
+#if USE_OPENSSL
+        SSL_shutdown(s.get());
+#elif USE_GNUTLS
+        gnutls_bye(s.get(), GNUTLS_SHUT_RDWR);
+#endif
+    }
+}
+
 bool
 Security::SessionIsResumed(const Security::SessionPointer &s)
 {
index 0dea35acdf36f6d14473e31118d49dc74af637f3..6551bed601953333f5690d01c649a9c9e0228c00 100644 (file)
@@ -9,6 +9,8 @@
 #ifndef SQUID_SRC_SECURITY_SESSION_H
 #define SQUID_SRC_SECURITY_SESSION_H
 
+#include "base/HardFun.h"
+#include "comm/forward.h"
 #include "security/LockingPointer.h"
 
 #include <memory>
 
 namespace Security {
 
+/// Creates TLS Client connection structure (aka 'session' state) and initializes TLS/SSL I/O (Comm and BIO).
+/// On errors, emits DBG_IMPORTANT with details and returns false.
+bool CreateClientSession(const Security::ContextPointer &, const Comm::ConnectionPointer &, const char *squidCtx);
+
+/// Creates TLS Server connection structure (aka 'session' state) and initializes TLS/SSL I/O (Comm and BIO).
+/// On errors, emits DBG_IMPORTANT with details and returns false.
+bool CreateServerSession(const Security::ContextPointer &, const Comm::ConnectionPointer &, const char *squidCtx);
+
 #if USE_OPENSSL
-CtoCpp1(SSL_free, SSL *);
-#if defined(CRYPTO_LOCK_SSL) // OpenSSL 1.0
-inline int SSL_up_ref(SSL *t) {if (t) CRYPTO_add(&t->references, 1, CRYPTO_LOCK_SSL); return 0;}
-#endif
-typedef Security::LockingPointer<SSL, Security::SSL_free_cpp, HardFun<int, SSL *, SSL_up_ref> > SessionPointer;
+typedef std::shared_ptr<SSL> SessionPointer;
 
 typedef std::unique_ptr<SSL_SESSION, HardFun<void, SSL_SESSION*, &SSL_SESSION_free>> SessionStatePointer;
 
 #elif USE_GNUTLS
-// Locks can be implemented attaching locks counter to gnutls_session_t
-// objects using the gnutls_session_set_ptr()/gnutls_session_get_ptr ()
-// library functions
-CtoCpp1(gnutls_deinit, gnutls_session_t);
-typedef Security::LockingPointer<struct gnutls_session_int, gnutls_deinit_cpp> SessionPointer;
+typedef std::shared_ptr<struct gnutls_session_int> SessionPointer;
 
 // wrapper function to get around gnutls_free being a typedef
 inline void squid_gnutls_free(void *d) {gnutls_free(d);}
 typedef std::unique_ptr<gnutls_datum_t, HardFun<void, void*, &Security::squid_gnutls_free>> SessionStatePointer;
 
 #else
-// use void* so we can check against NULL
-CtoCpp1(xfree, void *);
-typedef Security::LockingPointer<void, xfree_cpp> SessionPointer;
+typedef std::shared_ptr<void> SessionPointer;
 
 typedef std::unique_ptr<int> SessionStatePointer;
 
 #endif
 
+/// send the shutdown/bye notice for an active TLS session.
+void SessionSendGoodbye(const Security::SessionPointer &);
+
 /// whether the session is a resumed one
 bool SessionIsResumed(const Security::SessionPointer &);
 
@@ -74,6 +77,21 @@ void MaybeGetSessionResumeData(const Security::SessionPointer &, Security::Sessi
 /// Needs to be done before using the SessionPointer for a handshake.
 void SetSessionResumeData(const Security::SessionPointer &, const Security::SessionStatePointer &);
 
+#if USE_OPENSSL
+/// Helper function to retrieve a (non-locked) ContextPointer from a SessionPointer
+inline Security::ContextPointer
+GetFrom(Security::SessionPointer &s)
+{
+    auto *ctx = SSL_get_SSL_CTX(s.get());
+    return Security::ContextPointer(ctx, [](SSL_CTX *) {/* nothing to unlock/free */});
+}
+
+/// \deprecated use the PeerOptions/ServerOptions API methods instead.
+/// Wraps SessionPointer value creation to reduce risk of
+/// a nasty hack in ssl/support.cc.
+Security::SessionPointer NewSessionObject(const Security::ContextPointer &);
+#endif
+
 } // namespace Security
 
 #endif /* SQUID_SRC_SECURITY_SESSION_H */
index cd721751155cad4dbb1aab82495b75b2cdec8680..f95f7b3638e8c5f74409ec5fb86b36c09a679f5b 100644 (file)
@@ -108,7 +108,35 @@ inline const char *ErrorString(const ErrorCode code) {
 /// \note using std::unordered_set ensures values are unique, with fast lookup
 typedef std::unordered_set<Security::ErrorCode> Errors;
 
+namespace Io
+{
+enum Type {
+#if USE_OPENSSL
+    BIO_TO_CLIENT = 6000,
+    BIO_TO_SERVER
+#elif USE_GNUTLS
+    // NP: this is odd looking but correct.
+    // 'to-client' means we are a server, and vice versa.
+    BIO_TO_CLIENT = GNUTLS_SERVER,
+    BIO_TO_SERVER = GNUTLS_CLIENT
+#else
+    BIO_TO_CLIENT = 6000,
+    BIO_TO_SERVER
+#endif
+};
+
+} // namespace Io
+
 class KeyData;
+
+#if USE_OPENSSL
+typedef long ParsedOptions;
+#elif USE_GNUTLS
+typedef std::shared_ptr<struct gnutls_priority_st> ParsedOptions;
+#else
+class ParsedOptions {}; // we never parse/use TLS options in this case
+#endif
+
 class PeerConnector;
 class PeerOptions;
 class ServerOptions;
index a91b03cbbdc3f48ccf47addbd38784391a597a79..f691cd918b58483d143f77f2b2b6a4c949907f58 100644 (file)
@@ -63,7 +63,7 @@ static BIO_METHOD *SquidMethods = NULL;
 #endif
 
 BIO *
-Ssl::Bio::Create(const int fd, Ssl::Bio::Type type)
+Ssl::Bio::Create(const int fd, Security::Io::Type type)
 {
 #if (OPENSSL_VERSION_NUMBER < 0x10100000L)
     BIO_METHOD *useMethod = &SquidMethods;
@@ -620,7 +620,7 @@ squid_bio_ctrl(BIO *table, int cmd, long arg1, void *arg2)
         assert(arg2);
         const int fd = *static_cast<int*>(arg2);
         Ssl::Bio *bio;
-        if (arg1 == Ssl::Bio::BIO_TO_SERVER)
+        if (arg1 == Security::Io::BIO_TO_SERVER)
             bio = new Ssl::ServerBio(fd);
         else
             bio = new Ssl::ClientBio(fd);
index 512a3b5a4503cf53fdf007bb4d987668405ba20b..c99d565b749991d284f814e1796031861abbb120 100644 (file)
@@ -9,6 +9,8 @@
 #ifndef SQUID_SSL_BIO_H
 #define SQUID_SSL_BIO_H
 
+#if USE_OPENSSL
+
 #include "FadingCounter.h"
 #include "fd.h"
 #include "security/Handshake.h"
@@ -28,11 +30,6 @@ namespace Ssl
 class Bio
 {
 public:
-    enum Type {
-        BIO_TO_CLIENT = 6000,
-        BIO_TO_SERVER
-    };
-
     explicit Bio(const int anFd);
     virtual ~Bio();
 
@@ -54,7 +51,7 @@ public:
 
     /// Creates a low-level BIO table, creates a high-level Ssl::Bio object
     /// for a given socket, and then links the two together via BIO_C_SET_FD.
-    static BIO *Create(const int fd, Type type);
+    static BIO *Create(const int fd, Security::Io::Type type);
     /// Tells ssl connection to use BIO and monitor state via stateChanged()
     static void Link(SSL *ssl, BIO *bio);
 
@@ -213,5 +210,6 @@ inline int BIO_get_init(BIO *table) { return table->init; }
 inline void BIO_set_init(BIO *table, int init) { table->init = init; }
 #endif
 
+#endif /* USE_OPENSSL */
 #endif /* SQUID_SSL_BIO_H */
 
index 235d2563f7ec64633377926fd94d00bc5f191d73..74d98409c3bed0e394528c0f171ea0d3a5212ff3 100644 (file)
@@ -21,16 +21,16 @@ Ssl::CertValidationMsg::composeRequest(CertValidationRequest const &vcert)
 {
     body.clear();
     body += Ssl::CertValidationMsg::param_host + "=" + vcert.domainName;
-    STACK_OF(X509) *peerCerts = static_cast<STACK_OF(X509) *>(SSL_get_ex_data(vcert.ssl, ssl_ex_index_ssl_cert_chain));
+    STACK_OF(X509) *peerCerts = static_cast<STACK_OF(X509) *>(SSL_get_ex_data(vcert.ssl.get(), ssl_ex_index_ssl_cert_chain));
 
-    if (const char *sslVersion = SSL_get_version(vcert.ssl))
+    if (const char *sslVersion = SSL_get_version(vcert.ssl.get()))
         body += "\n" +  Ssl::CertValidationMsg::param_proto_version + "=" + sslVersion;
 
-    if (const char *cipherName = SSL_CIPHER_get_name(SSL_get_current_cipher(vcert.ssl)))
+    if (const char *cipherName = SSL_CIPHER_get_name(SSL_get_current_cipher(vcert.ssl.get())))
         body += "\n" +  Ssl::CertValidationMsg::param_cipher + "=" + cipherName;
 
     if (!peerCerts)
-        peerCerts = SSL_get_peer_cert_chain(vcert.ssl);
+        peerCerts = SSL_get_peer_cert_chain(vcert.ssl.get());
 
     if (peerCerts) {
         Ssl::BIO_Pointer bio(BIO_new(BIO_s_mem()));
index 21d26f75946dc068077dd1d0e2645e3a89b34b62..ad5b886ca741cbd5e754b7b6567fa0cff3a3f3b5 100644 (file)
@@ -26,10 +26,9 @@ namespace Ssl
 class CertValidationRequest
 {
 public:
-    SSL *ssl;
-    Security::CertErrors *errors; ///< The list of errors detected
+    Security::SessionPointer ssl;
+    Security::CertErrors *errors = nullptr; ///< The list of errors detected
     std::string domainName; ///< The server name
-    CertValidationRequest() : ssl(NULL), errors(NULL) {}
 };
 
 /**
index 04f2589691e63b1a9b99c2aa15d1b4806b82335c..01efccea931bf1ca1fcf64ef03c258142c425de6 100644 (file)
@@ -236,7 +236,7 @@ void Ssl::CertValidationHelper::sslSubmit(Ssl::CertValidationRequest const &requ
     crtdvdData->query = message.compose();
     crtdvdData->query += '\n';
     crtdvdData->callback = callback;
-    crtdvdData->ssl.resetAndLock(request.ssl);
+    crtdvdData->ssl = request.ssl;
     Ssl::CertValidationResponse::Pointer const*validationResponse;
 
     if (CertValidationHelper::HelperCache &&
index 8cecb599c8b2fca683f2bf4ceedc51478bd7b515..12d0cc12bc583073c2b386dfa31b7e00d76609ba 100644 (file)
@@ -23,6 +23,7 @@
 #include "globals.h"
 #include "ipc/MemMap.h"
 #include "security/CertError.h"
+#include "security/Session.h"
 #include "SquidConfig.h"
 #include "SquidTime.h"
 #include "ssl/bio.h"
@@ -627,13 +628,11 @@ Ssl::InitServerContext(Security::ContextPointer &ctx, AnyP::PortCfg &port)
 }
 
 bool
-Ssl::InitClientContext(Security::ContextPointer &ctx, Security::PeerOptions &peer, long options, long fl)
+Ssl::InitClientContext(Security::ContextPointer &ctx, Security::PeerOptions &peer, long fl)
 {
     if (!ctx)
         return false;
 
-    SSL_CTX_set_options(ctx.get(), options);
-
     if (!peer.sslCipher.isEmpty()) {
         debugs(83, 5, "Using chiper suite " << peer.sslCipher << ".");
 
@@ -691,55 +690,6 @@ Ssl::InitClientContext(Security::ContextPointer &ctx, Security::PeerOptions &pee
     return true;
 }
 
-/// \ingroup ServerProtocolSSLInternal
-int
-ssl_read_method(int fd, char *buf, int len)
-{
-    auto ssl = fd_table[fd].ssl.get();
-
-#if DONT_DO_THIS
-
-    if (!SSL_is_init_finished(ssl)) {
-        errno = ENOTCONN;
-        return -1;
-    }
-
-#endif
-
-    int i = SSL_read(ssl, buf, len);
-    if (i > 0) {
-        (void)VALGRIND_MAKE_MEM_DEFINED(buf, i);
-    }
-
-    if (i > 0 && SSL_pending(ssl) > 0) {
-        debugs(83, 2, "SSL FD " << fd << " is pending");
-        fd_table[fd].flags.read_pending = true;
-    } else
-        fd_table[fd].flags.read_pending = false;
-
-    return i;
-}
-
-/// \ingroup ServerProtocolSSLInternal
-int
-ssl_write_method(int fd, const char *buf, int len)
-{
-    auto ssl = fd_table[fd].ssl.get();
-    if (!SSL_is_init_finished(ssl)) {
-        errno = ENOTCONN;
-        return -1;
-    }
-
-    int i = SSL_write(ssl, buf, len);
-    return i;
-}
-
-void
-ssl_shutdown_method(SSL *ssl)
-{
-    SSL_shutdown(ssl);
-}
-
 /// \ingroup ServerProtocolSSLInternal
 static const char *
 ssl_get_attribute(X509_NAME * name, const char *attribute_name)
@@ -1046,7 +996,7 @@ Ssl::verifySslCertificate(Security::ContextPointer &ctx, CertificateProperties c
     assert(0);
 #else
     // Temporary ssl for getting X509 certificate from SSL_CTX.
-    Security::SessionPointer ssl(SSL_new(ctx.get()));
+    Security::SessionPointer ssl(Security::NewSessionObject(ctx));
     X509 * cert = SSL_get_certificate(ssl.get());
 #endif
     if (!cert)
@@ -1429,53 +1379,6 @@ bool Ssl::generateUntrustedCert(Security::CertPointer &untrustedCert, EVP_PKEY_P
     return Ssl::generateSslCertificate(untrustedCert, untrustedPkey, certProperties);
 }
 
-static bool
-SslCreate(const Security::ContextPointer &ctx, const Comm::ConnectionPointer &conn, Ssl::Bio::Type type, const char *squidCtx)
-{
-    if (!Comm::IsConnOpen(conn)) {
-        debugs(83, DBG_IMPORTANT, "Gone connection");
-        return false;
-    }
-
-    const char *errAction = NULL;
-    int errCode = 0;
-    if (auto ssl = SSL_new(ctx.get())) {
-        const int fd = conn->fd;
-        // without BIO, we would call SSL_set_fd(ssl, fd) instead
-        if (BIO *bio = Ssl::Bio::Create(fd, type)) {
-            Ssl::Bio::Link(ssl, bio); // cannot fail
-
-            fd_table[fd].ssl.resetWithoutLocking(ssl);
-            fd_table[fd].read_method = &ssl_read_method;
-            fd_table[fd].write_method = &ssl_write_method;
-            fd_note(fd, squidCtx);
-            return true;
-        }
-        errCode = ERR_get_error();
-        errAction = "failed to initialize I/O";
-        SSL_free(ssl);
-    } else {
-        errCode = ERR_get_error();
-        errAction = "failed to allocate handle";
-    }
-
-    debugs(83, DBG_IMPORTANT, "ERROR: " << squidCtx << ' ' << errAction <<
-           ": " << Security::ErrorString(errCode));
-    return false;
-}
-
-bool
-Ssl::CreateClient(const Security::ContextPointer &ctx, const Comm::ConnectionPointer &c, const char *squidCtx)
-{
-    return SslCreate(ctx, c, Ssl::Bio::BIO_TO_SERVER, squidCtx);
-}
-
-bool
-Ssl::CreateServer(const Security::ContextPointer &ctx, const Comm::ConnectionPointer &c, const char *squidCtx)
-{
-    return SslCreate(ctx, c, Ssl::Bio::BIO_TO_CLIENT, squidCtx);
-}
-
 static int
 store_session_cb(SSL *ssl, SSL_SESSION *session)
 {
index 16bb7ef7f6efed3f650182150cf735d39be8ffea..e8f566209eed81a8392207a3a0efbe6479b5f411 100644 (file)
@@ -73,14 +73,6 @@ class ErrorDetail;
 class CertValidationResponse;
 typedef RefCount<CertValidationResponse> CertValidationResponsePointer;
 
-/// Creates SSL Client connection structure and initializes SSL I/O (Comm and BIO).
-/// On errors, emits DBG_IMPORTANT with details and returns false.
-bool CreateClient(const Security::ContextPointer &, const Comm::ConnectionPointer &, const char *squidCtx);
-
-/// Creates SSL Server connection structure and initializes SSL I/O (Comm and BIO).
-/// On errors, emits DBG_IMPORTANT with details and returns false.
-bool CreateServer(const Security::ContextPointer &, const Comm::ConnectionPointer &, const char *squidCtx);
-
 void SetSessionCallbacks(Security::ContextPointer &);
 extern Ipc::MemMap *SessionCache;
 extern const char *SessionCacheName;
@@ -89,7 +81,7 @@ extern const char *SessionCacheName;
 bool InitServerContext(Security::ContextPointer &, AnyP::PortCfg &);
 
 /// initialize a TLS client context with OpenSSL specific settings
-bool InitClientContext(Security::ContextPointer &, Security::PeerOptions &, long options, long flags);
+bool InitClientContext(Security::ContextPointer &, Security::PeerOptions &, long flags);
 
 #if defined(CRYPTO_LOCK_X509)
 // portability wrapper for OpenSSL 1.0 vs 1.1
@@ -99,15 +91,6 @@ inline int X509_up_ref(X509 *t) {if (t) CRYPTO_add(&t->references, 1, CRYPTO_LOC
 
 } //namespace Ssl
 
-/// \ingroup ServerProtocolSSLAPI
-int ssl_read_method(int, char *, int);
-
-/// \ingroup ServerProtocolSSLAPI
-int ssl_write_method(int, const char *, int);
-
-/// \ingroup ServerProtocolSSLAPI
-void ssl_shutdown_method(SSL *ssl);
-
 /// \ingroup ServerProtocolSSLAPI
 const char *sslGetUserEmail(SSL *ssl);
 
index 2aff6f2674beda1549bba97da19369d15c7ce5b9..835c8cdc38e24045e330abcc787a361804c7a08f 100644 (file)
@@ -68,15 +68,21 @@ void PeerConnector::recordNegotiationDetails() STUB
 
 #include "security/PeerOptions.h"
 Security::PeerOptions Security::ProxyOutgoingConfig;
+Security::PeerOptions::PeerOptions() {
+#if USE_OPENSSL
+    parsedOptions = 0;
+#endif
+    STUB_NOP
+}
 void Security::PeerOptions::parse(char const*) STUB
 Security::ContextPointer Security::PeerOptions::createClientContext(bool) STUB_RETVAL(Security::ContextPointer())
 void Security::PeerOptions::updateTlsVersionLimits() STUB
 Security::ContextPointer Security::PeerOptions::createBlankContext() const STUB_RETVAL(Security::ContextPointer())
 void Security::PeerOptions::updateContextCa(Security::ContextPointer &) STUB
 void Security::PeerOptions::updateContextCrl(Security::ContextPointer &) STUB
+void Security::PeerOptions::updateSessionOptions(Security::SessionPointer &) STUB
 void Security::PeerOptions::dumpCfg(Packable*, char const*) const STUB
-long Security::PeerOptions::parseOptions() STUB_RETVAL(0)
-long Security::PeerOptions::parseFlags() STUB_RETVAL(0)
+void Security::PeerOptions::parseOptions() STUB
 void parse_securePeerOptions(Security::PeerOptions *) STUB
 
 #include "security/ServerOptions.h"
@@ -89,8 +95,14 @@ void Security::ServerOptions::updateContextEecdh(Security::ContextPointer &) STU
 
 #include "security/Session.h"
 namespace Security {
+bool CreateClientSession(const Security::ContextPointer &, const Comm::ConnectionPointer &, const char *) STUB_RETVAL(false)
+bool CreateServerSession(const Security::ContextPointer &, const Comm::ConnectionPointer &, const char *) STUB_RETVAL(false)
+void SessionSendGoodbye(const Security::SessionPointer &) STUB
 bool SessionIsResumed(const Security::SessionPointer &) STUB_RETVAL(false)
 void MaybeGetSessionResumeData(const Security::SessionPointer &, Security::SessionStatePointer &) STUB
 void SetSessionResumeData(const Security::SessionPointer &, const Security::SessionStatePointer &) STUB
+#if USE_OPENSSL
+Security::SessionPointer NewSessionObject(const Security::ContextPointer &) STUB_RETVAL(nullptr)
+#endif
 } // namespace Security
 
index 3f7c1ac2be90754037b75a9292328d4a55ec44a7..4481cef3bfe047353b55417933b9f2b7535b28d1 100644 (file)
@@ -51,11 +51,8 @@ const String & Ssl::ErrorDetail::toString() const STUB_RETSTATREF(String)
 namespace Ssl
 {
 bool InitServerContext(Security::ContextPointer &, AnyP::PortCfg &) STUB_RETVAL(false)
-bool InitClientContext(Security::ContextPointer &, Security::PeerOptions &, long, const char *) STUB_RETVAL(false)
+bool InitClientContext(Security::ContextPointer &, Security::PeerOptions &, const char *) STUB_RETVAL(false)
 } // namespace Ssl
-int ssl_read_method(int, char *, int) STUB_RETVAL(0)
-int ssl_write_method(int, const char *, int) STUB_RETVAL(0)
-void ssl_shutdown_method(SSL *ssl) STUB
 const char *sslGetUserEmail(SSL *ssl) STUB_RETVAL(NULL)
 const char *sslGetUserAttribute(SSL *ssl, const char *attribute_name) STUB_RETVAL(NULL)
 const char *sslGetCAAttribute(SSL *ssl, const char *attribute_name) STUB_RETVAL(NULL)
index 56c7854e5519dd256d793212beedd79d74235a80..08e1e1bd320badfb16ed36a3a3b61f9d07a31f01 100644 (file)
@@ -832,6 +832,8 @@ urlCheckRequest(const HttpRequest * r)
     case AnyP::PROTO_HTTPS:
 #if USE_OPENSSL
         rc = 1;
+#elif USE_GNUTLS
+        rc = 1;
 #else
         /*
         * Squid can't originate an SSL connection, so it should