[squid-dev] [PATCH] initial GnuTLS support for encrypted server connections

Amos Jeffries squid3 at treenet.co.nz
Sat Jan 14 17:16:10 UTC 2017


This completes the next milestone in the long journey toward GnuTLS
support: operational server connections for https:// URLs and cache_peer.


I have had to make significant changes to how the options=FOO 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 fd_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/ now.

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


 There is still a fair bit not yet ported. Most notably; SNI, Downloader
for missing certificates, certificate verification functions, and access
to TLS details for error messages, ACL and logging.

Amos
-------------- next part --------------
=== modified file 'src/Makefile.am'
--- src/Makefile.am	2017-01-08 05:12:44 +0000
+++ src/Makefile.am	2017-01-12 13:05:40 +0000
@@ -3022,44 +3022,44 @@
 	$(TESTSOURCES) \
 	SquidMath.cc \
 	SquidMath.h \
 	swap_log_op.cc
 tests_testUfs_LDADD = \
 	http/libhttp.la \
 	parser/libparser.la \
 	CommCalls.o \
 	ident/libident.la \
 	acl/libacls.la \
 	acl/libstate.la \
 	acl/libapi.la \
 	libsquid.la \
 	ip/libip.la \
 	fs/libfs.la \
 	mgr/libmgr.la \
 	$(REPL_OBJS) \
 	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 \
 	mem/libmem.la \
 	store/libstore.la \
 	$(ADAPTATION_LIBS) \
 	sbuf/libsbuf.la \
 	$(top_builddir)/lib/libmisccontainers.la \
 	$(top_builddir)/lib/libmiscencoding.la \
 	$(top_builddir)/lib/libmiscutil.la \
 	$(NETTLELIB) \
 	$(REGEXLIB) \
 	$(SSLLIB) \
 	$(LIBCPPUNIT_LIBS) \
 	$(COMPAT_LIB) \
 	$(XTRA_LIBS)
 tests_testUfs_LDFLAGS = $(LIBADD_DL)
 tests_testUfs_DEPENDENCIES = \
 	$(SWAP_TEST_DS)
 

=== modified file 'src/client_side.cc'
--- src/client_side.cc	2017-01-01 00:12:22 +0000
+++ src/client_side.cc	2017-01-14 15:35:16 +0000
@@ -2540,47 +2540,46 @@
     if (params.flag != Comm::OK) {
         // Its possible the call was still queued when the client disconnected
         debugs(33, 2, s->listenConn << ": accept failure: " << xstrerr(params.xerrno));
         return;
     }
 
     debugs(33, 4, params.conn << ": accepted");
     fd_note(params.conn->fd, "client http connect");
 
     if (s->tcp_keepalive.enabled)
         commSetTcpKeepalive(params.conn->fd, s->tcp_keepalive.idle, s->tcp_keepalive.interval, s->tcp_keepalive.timeout);
 
     ++incoming_sockets_accepted;
 
     // Socket is ready, setup the connection manager to start using it
     auto *srv = Http::NewServer(xact);
     AsyncJob::Start(srv); // usually async-calls readSomeData()
 }
 
 #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;
     }
 
     conn->close();
     return false;
 }
 
 /**
  *
  * \retval 1 on success
  * \retval 0 when needs more data
  * \retval -1 on error
  */
 static int
 Squid_SSL_accept(ConnStateData *conn, PF *callback)
 {
     int fd = conn->clientConnection->fd;
     auto ssl = fd_table[fd].ssl.get();
     int ret;
 

=== modified file 'src/comm.cc'
--- src/comm.cc	2017-01-01 00:12:22 +0000
+++ src/comm.cc	2017-01-12 13:05:40 +0000
@@ -747,44 +747,41 @@
     if (n < 0) {
         int xerrno = errno;
         debugs(5, 3, "FD " << fd << " read: " << xstrerr(xerrno));
     }
     comm_close(fd);
 }
 
 static void
 commLingerTimeout(const FdeCbParams &params)
 {
     debugs(5, 3, "commLingerTimeout: FD " << params.fd);
     comm_close(params.fd);
 }
 
 /*
  * Inspired by apache
  */
 void
 comm_lingering_close(int fd)
 {
-#if USE_OPENSSL
-    if (fd_table[fd].ssl)
-        ssl_shutdown_method(fd_table[fd].ssl);
-#endif
+    Security::SessionClose(fd_table[fd].ssl);
 
     if (shutdown(fd, 1) < 0) {
         comm_close(fd);
         return;
     }
 
     fd_note(fd, "lingering close");
     AsyncCall::Pointer call = commCbCall(5,4, "commLingerTimeout", FdeCbPtrFun(commLingerTimeout, NULL));
 
     debugs(5, 3, HERE << "FD " << fd << " timeout " << timeout);
     assert(fd_table[fd].flags.open);
     if (callback != NULL) {
         typedef FdeCbParams Params;
         Params &params = GetCommParams<Params>(callback);
         params.fd = fd;
         fd_table[fd].timeoutHandler = callback;
         fd_table[fd].timeout = squid_curtime + static_cast<time_t>(10);
     }
 
     Comm::SetSelect(fd, COMM_SELECT_READ, commLingerClose, NULL, 0);
@@ -808,48 +805,45 @@
         debugs(50, DBG_CRITICAL, "ERROR: Closing " << conn << " with TCP RST: " << xstrerr(xerrno));
     }
     conn->close();
 }
 
 // Legacy close function.
 void
 old_comm_reset_close(int fd)
 {
     struct linger L;
     L.l_onoff = 1;
     L.l_linger = 0;
 
     if (setsockopt(fd, SOL_SOCKET, SO_LINGER, (char *) &L, sizeof(L)) < 0) {
         int xerrno = errno;
         debugs(50, DBG_CRITICAL, "ERROR: Closing FD " << fd << " with TCP RST: " << xstrerr(xerrno));
     }
     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::SessionClose(fd_table[params.fd].ssl);
 }
-#endif
 
 void
 comm_close_complete(const FdeCbParams &params)
 {
     fde *F = &fd_table[params.fd];
     F->ssl.reset();
     F->dynamicTlsContext.reset();
     fd_close(params.fd);        /* update fdstat */
     close(params.fd);
 
     ++ statCounter.syscalls.sock.closes;
 
     /* When one connection closes, give accept() a chance, if need be */
     Comm::AcceptLimiter::Instance().kick();
 }
 
 /*
  * Close the socket fd.
  *
  * + call write handlers with ERR_CLOSING
@@ -873,49 +867,47 @@
     if (F->closing())
         return;
 
     /* XXX: is this obsolete behind F->closing() ? */
     if ( (shutting_down || reconfiguring) && (!F->flags.open || F->type == FD_FILE))
         return;
 
     /* The following fails because ipc.c is doing calls to pipe() to create sockets! */
     if (!isOpen(fd)) {
         debugs(50, DBG_IMPORTANT, HERE << "BUG 3556: FD " << fd << " is not an open socket.");
         // XXX: do we need to run close(fd) or fd_close(fd) here?
         return;
     }
 
     assert(F->type != FD_FILE);
 
     PROF_start(comm_close);
 
     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))
         commStopHalfClosedMonitor(fd);
     commUnsetFdTimeout(fd);
 
     // notify read/write handlers after canceling select reservations, if any
     if (COMMIO_FD_WRITECB(fd)->active()) {
         Comm::SetSelect(fd, COMM_SELECT_WRITE, NULL, NULL, 0);
         COMMIO_FD_WRITECB(fd)->finish(Comm::ERR_CLOSING, errno);
     }
     if (COMMIO_FD_READCB(fd)->active()) {
         Comm::SetSelect(fd, COMM_SELECT_READ, NULL, NULL, 0);
         COMMIO_FD_READCB(fd)->finish(Comm::ERR_CLOSING, errno);
     }
 
 #if USE_DELAY_POOLS
     if (ClientInfo *clientInfo = F->clientInfo) {
         if (clientInfo->selectWaiting) {
             clientInfo->selectWaiting = false;

=== modified file 'src/security/BlindPeerConnector.cc'
--- src/security/BlindPeerConnector.cc	2017-01-01 00:12:22 +0000
+++ src/security/BlindPeerConnector.cc	2017-01-14 07:20:47 +0000
@@ -15,63 +15,68 @@
 #include "neighbors.h"
 #include "security/BlindPeerConnector.h"
 #include "security/NegotiationHistory.h"
 #include "SquidConfig.h"
 
 CBDATA_NAMESPACED_CLASS_INIT(Security, BlindPeerConnector);
 
 Security::ContextPointer
 Security::BlindPeerConnector::getTlsContext()
 {
     if (const CachePeer *peer = serverConnection()->getPeer()) {
         assert(peer->secure.encryptTransport);
         return peer->sslContext;
     }
     return ::Config.ssl_client.sslContext;
 }
 
 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);
 
         // NP: domain may be a raw-IP but it is now always set
         assert(!peer->secure.sslDomain.isEmpty());
 
 #if USE_OPENSSL
         // 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(serverSession.get(), ssl_ex_index_server, host);
 
         Security::SetSessionResumeData(serverSession, peer->sslSession);
     } else {
         SBuf *hostName = new SBuf(request->url.host());
         SSL_set_ex_data(serverSession.get(), ssl_ex_index_server, (void*)hostName);
 #endif
     }
+
+    debugs(83, 5, "success");
     return true;
 }
 
 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()
         // 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;
     }
 
     if (auto *peer = serverConnection()->getPeer()) {
         const int fd = serverConnection()->fd;
         Security::MaybeGetSessionResumeData(fd_table[fd].ssl, peer->sslSession);
     }
 }
 

=== modified file 'src/security/PeerConnector.cc'
--- src/security/PeerConnector.cc	2017-01-01 00:12:22 +0000
+++ src/security/PeerConnector.cc	2017-01-14 16:05:55 +0000
@@ -41,116 +41,120 @@
     debugs(83, 5, "Security::PeerConnector constructed, this=" << (void*)this);
     // if this throws, the caller's cb dialer is not our CbDialer
     Must(dynamic_cast<CbDialer*>(callback->getDialer()));
 }
 
 Security::PeerConnector::~PeerConnector()
 {
     debugs(83, 5, "Security::PeerConnector destructed, this=" << (void*)this);
 }
 
 bool Security::PeerConnector::doneAll() const
 {
     return (!callback || callback->canceled()) && AsyncJob::doneAll();
 }
 
 /// Preps connection and SSL state. Calls negotiate().
 void
 Security::PeerConnector::start()
 {
     AsyncJob::start();
+    debugs(83, 5, "this=" << (void*)this);
 
     Security::SessionPointer tmp;
     if (prepareSocket() && initialize(tmp))
         negotiate();
     else
         mustStop("Security::PeerConnector TLS socket initialize failed");
 }
 
 void
 Security::PeerConnector::commCloseHandler(const CommCloseCbParams &params)
 {
     debugs(83, 5, "FD " << params.fd << ", Security::PeerConnector=" << params.data);
     connectionClosed("Security::PeerConnector::commCloseHandler");
 }
 
 void
 Security::PeerConnector::connectionClosed(const char *reason)
 {
+    debugs(83, 5, reason << " socket closed/closing. this=" << (void*)this);
     mustStop(reason);
     callback = NULL;
 }
 
 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;
     }
 
     // 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) {
         // Create the ACL check list now, while we have access to more info.
         // The list is used in ssl_verify_cb() and is freed in ssl_free().
         if (acl_access *acl = ::Config.ssl_client.cert_error) {
             ACLFilledChecklist *check = new ACLFilledChecklist(acl, request.getRaw(), dash_str);
             check->al = al;
             // check->fd(fd); XXX: need client FD here
             SSL_set_ex_data(serverSession.get(), ssl_ex_index_cert_error_check, check);
         }
     }
+#endif
 
     return true;
-#else
-    return false;
-#endif
 }
 
 void
 Security::PeerConnector::setReadTimeout()
 {
     int timeToRead;
     if (negotiationTimeout) {
         const int timeUsed = squid_curtime - startTime;
         const int timeLeft = max(0, static_cast<int>(negotiationTimeout - timeUsed));
         timeToRead = min(static_cast<int>(::Config.Timeout.read), timeLeft);
     } else
         timeToRead = ::Config.Timeout.read;
     AsyncCall::Pointer nil;
     commSetConnTimeout(serverConnection(), timeToRead, nil);
 }
 
 void
 Security::PeerConnector::recordNegotiationDetails()
 {
     const int fd = serverConnection()->fd;
@@ -162,71 +166,90 @@
 #if USE_OPENSSL
     // retrieve TLS parsed extra info
     BIO *b = SSL_get_rbio(session.get());
     Ssl::ServerBio *bio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
     if (const Security::TlsDetails::Pointer &details = bio->receivedHelloDetails())
         serverConnection()->tlsNegotiations()->retrieveParsedInfo(details);
 #endif
 }
 
 void
 Security::PeerConnector::negotiate()
 {
     if (!Comm::IsConnOpen(serverConnection()))
         return;
 
     const int fd = serverConnection()->fd;
     if (fd_table[fd].closing())
         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
     }
 
     recordNegotiationDetails();
 
     if (!sslFinalized())
         return;
 
     callBack();
 }
 
 bool
 Security::PeerConnector::sslFinalized()
 {
 #if USE_OPENSSL
     if (Ssl::TheConfig.ssl_crt_validator && useCertValidator_) {
         const int fd = serverConnection()->fd;
         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)))
             // validationRequest disappears on return so no need to cbdataReference
             validationRequest.errors = errs;
         try {
             debugs(83, 5, "Sending SSL certificate for validation to ssl_crtvd.");
             AsyncCall::Pointer call = asyncCall(83,5, "Security::PeerConnector::sslCrtvdHandleReply", Ssl::CertValidationHelper::CbDialer(this, &Security::PeerConnector::sslCrtvdHandleReply, nullptr));
             Ssl::CertValidationHelper::GetInstance()->sslSubmit(validationRequest, call);
             return false;
         } catch (const std::exception &e) {
             debugs(83, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtvd " <<
                    "request for " << validationRequest.domainName <<
                    " certificate: " << e.what() << "; will now block to " <<
                    "validate that certificate.");
             // fall through to do blocking in-process generation.
             ErrorState *anErr = new ErrorState(ERR_GATEWAY_FAILURE, Http::scInternalServerError, request.getRaw());
 
             noteNegotiationDone(anErr);
             bail(anErr);
@@ -343,178 +366,222 @@
     }
     if (check)
         delete check;
 
     return errs;
 }
 #endif
 
 /// A wrapper for Comm::SetSelect() notifications.
 void
 Security::PeerConnector::NegotiateSsl(int, void *data)
 {
     PeerConnector *pc = static_cast<Security::PeerConnector *>(data);
     // Use job calls to add done() checks and other job logic/protections.
     CallJobHere(83, 7, pc, Security::PeerConnector, negotiate);
 }
 
 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) {
     case SSL_ERROR_WANT_READ:
         noteWantRead();
         return;
 
     case SSL_ERROR_WANT_WRITE:
         noteWantWrite();
         return;
 
     case SSL_ERROR_SSL:
     case SSL_ERROR_SYSCALL:
         ssl_lib_error = ERR_get_error();
         // proceed to the general error handling code
         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());
     Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
     if (srvBio->holdRead()) {
         if (srvBio->gotHello()) {
             if (checkForMissingCertificates())
                 return; // Wait to download certificates before proceed.
 
             srvBio->holdRead(false);
             // schedule a negotiateSSl to allow openSSL parse received data
             Security::PeerConnector::NegotiateSsl(fd, this);
             return;
         } else if (srvBio->gotHelloFailed()) {
             srvBio->holdRead(false);
             debugs(83, DBG_IMPORTANT, "Error parsing SSL Server Hello Message on FD " << fd);
             // schedule a negotiateSSl to allow openSSL parse received data
             Security::PeerConnector::NegotiateSsl(fd, this);
             return;
         }
     }
 #endif
     setReadTimeout();
     Comm::SetSelect(fd, COMM_SELECT_READ, &NegotiateSsl, this, 0);
 }
 
 void
 Security::PeerConnector::noteWantWrite()
 {
     const int fd = serverConnection()->fd;
+    debugs(83, 5, serverConnection());
     Comm::SetSelect(fd, COMM_SELECT_WRITE, &NegotiateSsl, this, 0);
     return;
 }
 
 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)
         anErr = ErrorState::NewForwarding(ERR_SECURE_CONNECT_FAIL, request.getRaw());
     else
         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) {
         // The errFromFailure is attached to the ssl object
         // and will be released when ssl object destroyed.
         // Copy errFromFailure to a new Ssl::ErrorDetail object
         anErr->detail = new Ssl::ErrorDetail(*errFromFailure);
     } else {
         // server_cert can be NULL here
         X509 *server_cert = SSL_get_peer_certificate(session.get());
         anErr->detail = new Ssl::ErrorDetail(SQUID_ERR_SSL_HANDSHAKE, server_cert, NULL);
         X509_free(server_cert);
     }
 
     if (ssl_lib_error != SSL_ERROR_NONE)
         anErr->detail->setLibError(ssl_lib_error);
+#endif
 
     noteNegotiationDone(anErr);
     bail(anErr);
-#endif
 }
 
 void
 Security::PeerConnector::bail(ErrorState *error)
 {
     Must(error); // or the recepient will not know there was a problem
     Must(callback != NULL);
     CbDialer *dialer = dynamic_cast<CbDialer*>(callback->getDialer());
     Must(dialer);
     dialer->answer().error = error;
 
     callBack();
     // Our job is done. The callabck recepient will probably close the failed
     // peer connection and try another peer or go direct (if possible). We
     // can close the connection ourselves (our error notification would reach
     // the recepient before the fd-closure notification), but we would rather
     // minimize the number of fd-closure notifications and let the recepient
     // manage the TCP state of the connection.
+
+#if USE_GNUTLS
+    // but we do need to release the bad TLS related details in fd_table
+    // ... or GnuTLS will SEGFAULT.
+    const int fd = serverConnection()->fd;
+    Security::SessionClose(fd_table[fd].ssl, fd);
+#endif
 }
 
 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.
     callback = NULL; // this should make done() true
 
     // remove close handler
     comm_remove_close_handler(serverConnection()->fd, closeHandler);
 
     CbDialer *dialer = dynamic_cast<CbDialer*>(cb->getDialer());
     Must(dialer);
     dialer->answer().conn = serverConnection();
     ScheduleCallHere(cb);
 }
 
 void
 Security::PeerConnector::swanSong()
 {
     // XXX: unregister fd-closure monitoring and CommSetSelect interest, if any
     AsyncJob::swanSong();
     if (callback != NULL) { // paranoid: we have left the caller waiting

=== modified file 'src/security/PeerOptions.cc'
--- src/security/PeerOptions.cc	2017-01-01 00:12:22 +0000
+++ src/security/PeerOptions.cc	2017-01-14 09:37:52 +0000
@@ -11,84 +11,106 @@
 #include "Debug.h"
 #include "fatal.h"
 #include "globals.h"
 #include "parser/Tokenizer.h"
 #include "Parsing.h"
 #include "security/PeerOptions.h"
 
 #if USE_OPENSSL
 #include "ssl/support.h"
 #endif
 
 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)
 {
+    if (!sslOptions.isEmpty())
+        parseOptions(parsedOptions); // re-parse after sslOptions copied.
     memcpy(&flags, &p.flags, sizeof(flags));
 }
 
+Security::PeerOptions &
+Security::PeerOptions::operator =(const Security::PeerOptions &p)
+{
+    sslOptions = p.sslOptions;
+    if (!sslOptions.isEmpty())
+        parseOptions(parsedOptions); // re-parse after sslOptions copied.
+    caDir = p.caDir;
+    crlFile = p.crlFile;
+    sslCipher = p.sslCipher;
+    sslFlags = p.sslFlags;
+    sslDomain = p.sslDomain;
+    parsedFlags = p.parsedFlags;
+    certs = p.certs;
+    caFiles = p.caFiles;
+    parsedCrl = p.parsedCrl;
+    sslVersion = p.sslVersion;
+    encryptTransport = p.encryptTransport;
+    memcpy(&flags, &p.flags, sizeof(flags));
+    return *this;
+}
+
 void
 Security::PeerOptions::parse(const char *token)
 {
     if (!*token) {
         // config says just "ssl" or "tls" (or "tls-")
         encryptTransport = true;
         return;
     }
 
     if (strncmp(token, "disable", 7) == 0) {
         clear();
         return;
     }
 
     if (strncmp(token, "cert=", 5) == 0) {
         KeyData t;
         t.privateKeyFile = t.certFile = SBuf(token + 5);
         certs.emplace_back(t);
     } else if (strncmp(token, "key=", 4) == 0) {
         if (certs.empty() || certs.back().certFile.isEmpty()) {
             fatal("cert= option must be set before key= is used.");
             return;
         }
         KeyData &t = certs.back();
         t.privateKeyFile = SBuf(token + 4);
     } else if (strncmp(token, "version=", 8) == 0) {
         debugs(0, DBG_PARSE_NOTE(1), "UPGRADE WARNING: SSL version= is deprecated. Use options= to limit protocols instead.");
         sslVersion = xatoi(token + 8);
     } else if (strncmp(token, "min-version=", 12) == 0) {
         tlsMinVersion = SBuf(token + 12);
     } else if (strncmp(token, "options=", 8) == 0) {
         sslOptions = SBuf(token + 8);
-        parsedOptions = parseOptions();
+        parseOptions(parsedOptions);
     } else if (strncmp(token, "cipher=", 7) == 0) {
         sslCipher = SBuf(token + 7);
     } else if (strncmp(token, "cafile=", 7) == 0) {
         caFiles.emplace_back(SBuf(token + 7));
     } else if (strncmp(token, "capath=", 7) == 0) {
         caDir = SBuf(token + 7);
 #if !USE_OPENSSL
         debugs(3, DBG_PARSE_NOTE(1), "WARNING: capath= option requires --with-openssl.");
 #endif
     } else if (strncmp(token, "crlfile=", 8) == 0) {
         crlFile = SBuf(token + 8);
         loadCrlFile();
     } else if (strncmp(token, "flags=", 6) == 0) {
         if (parsedFlags != 0) {
             debugs(3, DBG_PARSE_NOTE(1), "WARNING: Overwriting flags=" << sslFlags << " with " << SBuf(token + 6));
         }
         sslFlags = SBuf(token + 6);
         parsedFlags = parseFlags();
     } else if (strncmp(token, "default-ca=off", 14) == 0 || strncmp(token, "no-default-ca", 13) == 0) {
         if (flags.tlsDefaultCa.configured() && flags.tlsDefaultCa)
@@ -150,83 +172,119 @@
         // default OFF for listening ports
         if (flags.tlsDefaultCa)
             p->appendf(" %sdefault-ca", pfx);
         else
             p->appendf(" %sdefault-ca=off", pfx);
     }
 
     if (!flags.tlsNpn)
         p->appendf(" %sno-npn", pfx);
 }
 
 void
 Security::PeerOptions::updateTlsVersionLimits()
 {
     if (!tlsMinVersion.isEmpty()) {
         ::Parser::Tokenizer tok(tlsMinVersion);
         int64_t v = 0;
         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;
+                *parsedOptions |= SSL_OP_NO_TLSv1;
 #endif
 #if SSL_OP_NO_TLSv1_1
             if (v > 1)
-                parsedOptions |= SSL_OP_NO_TLSv1_1;
+                *parsedOptions |= SSL_OP_NO_TLSv1_1;
 #endif
 #if SSL_OP_NO_TLSv1_2
             if (v > 2)
-                parsedOptions |= SSL_OP_NO_TLSv1_2;
+                *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;
         switch (sslVersion) {
         case 3:
-            add = "NO_TLSv1,NO_TLSv1_1,NO_TLSv1_2";
+#if USE_OPENSSL
+            add = ",NO_TLSv1,NO_TLSv1_1,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
+            add = ",NO_SSLv3,NO_TLSv1_1,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
+            add = ",NO_SSLv3,NO_TLSv1,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
+            add = ",NO_SSLv3,NO_TLSv1,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 (sslOptions.isEmpty())
+                sslOptions.append(add+1, strlen(add+1));
+            else
+                sslOptions.append(add, strlen(add));
         }
         sslVersion = 0; // prevent sslOptions being repeatedly appended
     }
 }
 
 Security::ContextPointer
 Security::PeerOptions::createBlankContext() const
 {
     Security::ContextPointer ctx;
 #if USE_OPENSSL
     Ssl::Initialize();
 
 #if (OPENSSL_VERSION_NUMBER >= 0x10100000L)
     SSL_CTX *t = SSL_CTX_new(TLS_client_method());
 #else
     SSL_CTX *t = SSL_CTX_new(SSLv23_client_method());
 #endif
     if (!t) {
         const auto x = ERR_get_error();
         fatalf("Failed to allocate TLS client context: %s\n", Security::ErrorString(x));
@@ -240,51 +298,55 @@
         fatalf("Failed to allocate TLS client context: %s\n", Security::ErrorString(x));
     }
     ctx.resetWithoutLocking(t);
 
 #else
     debugs(83, 1, "WARNING: Failed to allocate TLS client context: No TLS library");
 
 #endif
 
     return ctx;
 }
 
 Security::ContextPointer
 Security::PeerOptions::createClientContext(bool setOptions)
 {
     updateTlsVersionLimits();
 
     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);
         updateContextCrl(t);
     }
 
     return t;
 }
 
+#if USE_OPENSSL
 /// set of options we can parse and what they map to
 static struct ssl_option {
     const char *name;
     long value;
 
 } ssl_options[] = {
 
 #if SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
     {
         "NETSCAPE_REUSE_CIPHER_CHANGE_BUG", SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
     },
 #endif
 #if SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
     {
         "SSLREF2_REUSE_CERT_TYPE_BUG", SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
     },
 #endif
 #if SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
     {
         "MICROSOFT_BIG_SSLV3_BUFFER", SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
@@ -380,50 +442,52 @@
         "No_Compression", SSL_OP_NO_COMPRESSION
     },
 #endif
 #if SSL_OP_NO_TICKET
     {
         "NO_TICKET", SSL_OP_NO_TICKET
     },
 #endif
 #if SSL_OP_SINGLE_ECDH_USE
     {
         "SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE
     },
 #endif
     {
         "", 0
     },
     {
         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
-Security::PeerOptions::parseOptions()
+void
+Security::PeerOptions::parseOptions(Security::ParsedOptionsPointer &theOut)
 {
-    long op = 0;
+#if USE_OPENSSL
     ::Parser::Tokenizer tok(sslOptions);
+    long op = 0;
 
     do {
         enum {
             MODE_ADD, MODE_REMOVE
         } mode;
 
         if (tok.skip('-') || tok.skip('!'))
             mode = MODE_REMOVE;
         else {
             (void)tok.skip('+'); // default action is add. ignore if missing operator
             mode = MODE_ADD;
         }
 
         static const CharacterSet optChars = CharacterSet("TLS-option", "_") + CharacterSet::ALPHA + CharacterSet::DIGIT;
         int64_t hex = 0;
         SBuf option;
         long value = 0;
 
         // Bug 4429: identify the full option name before determining text or numeric
         if (tok.prefix(option, optChars)) {
@@ -450,41 +514,53 @@
                 break;
             case MODE_REMOVE:
                 op &= ~value;
                 break;
             }
         } else {
             debugs(83, DBG_PARSE_NOTE(1), "ERROR: Unknown TLS option " << option);
         }
 
         static const CharacterSet delims("TLS-option-delim",":,");
         if (!tok.skipAll(delims) && !tok.atEnd()) {
             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;
+    theOut.reset(new long(op));
+
+#elif USE_GNUTLS
+    const char *err = nullptr;
+    const char *priorities = (sslOptions.isEmpty() ? nullptr : sslOptions.c_str());
+    gnutls_priority_t op;
+    int x = gnutls_priority_init(&op, priorities, &err);
+    if (x != GNUTLS_E_SUCCESS) {
+        fatalf("Unknown TLS option '%s'", err);
+    }
+    theOut.reset(op);
+
+#endif
 }
 
 /**
  * Parses the TLS flags squid.conf parameter
  */
 long
 Security::PeerOptions::parseFlags()
 {
     if (sslFlags.isEmpty())
         return 0;
 
     static struct {
         SBuf label;
         long mask;
     } flagTokens[] = {
         { SBuf("NO_DEFAULT_CA"), SSL_FLAG_NO_DEFAULT_CA },
         { SBuf("DELAYED_AUTH"), SSL_FLAG_DELAYED_AUTH },
         { SBuf("DONT_VERIFY_PEER"), SSL_FLAG_DONT_VERIFY_PEER },
         { SBuf("DONT_VERIFY_DOMAIN"), SSL_FLAG_DONT_VERIFY_DOMAIN },
         { SBuf("NO_SESSION_REUSE"), SSL_FLAG_NO_SESSION_REUSE },
@@ -625,26 +701,46 @@
     if (parsedCrl.size()) {
         for (auto &i : parsedCrl) {
             if (!X509_STORE_add_crl(st, i.get()))
                 debugs(83, 2, "WARNING: Failed to add CRL");
             else
                 verifyCrl = true;
         }
     }
 
 #if X509_V_FLAG_CRL_CHECK
     if ((parsedFlags & SSL_FLAG_VERIFY_CRL_ALL))
         X509_STORE_set_flags(st, X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL);
     else if (verifyCrl || (parsedFlags & SSL_FLAG_VERIFY_CRL))
         X509_STORE_set_flags(st, X509_V_FLAG_CRL_CHECK);
 #endif
 
 #endif /* USE_OPENSSL */
 }
 
 void
+Security::PeerOptions::updateSessionOptions(Security::SessionPointer &s)
+{
+    // 'options=' value being set to session is a GnuTLS specific thing.
+#if !USE_OPENSSL && USE_GNUTLS
+    int x;
+    if (!parsedOptions) {
+        debugs(83, 5, "set GnuTLS default priority/options for session=" << s);
+        x = gnutls_set_default_priority(s.get());
+    } else {
+        debugs(83, 5, "set GnuTLS options '" << sslOptions << "' for session=" << s);
+        x = gnutls_priority_set(s.get(), parsedOptions.get());
+    }
+
+    if (x != GNUTLS_E_SUCCESS) {
+        debugs(83, 1, "Failed to set TLS options. error: " << Security::ErrorString(x));
+    }
+#endif
+}
+
+void
 parse_securePeerOptions(Security::PeerOptions *opt)
 {
     while(const char *token = ConfigParser::NextToken())
         opt->parse(token);
 }
 

=== modified file 'src/security/PeerOptions.h'
--- src/security/PeerOptions.h	2017-01-01 00:12:22 +0000
+++ src/security/PeerOptions.h	2017-01-12 13:05:40 +0000
@@ -5,105 +5,109 @@
  * contributions from numerous individuals and organizations.
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 #ifndef SQUID_SRC_SECURITY_PEEROPTIONS_H
 #define SQUID_SRC_SECURITY_PEEROPTIONS_H
 
 #include "base/YesNoNone.h"
 #include "ConfigParser.h"
 #include "security/KeyData.h"
 
 class Packable;
 
 namespace Security
 {
 
 /// TLS squid.conf settings for a remote server peer
 class PeerOptions
 {
 public:
-    PeerOptions() : parsedOptions(0), parsedFlags(0), sslVersion(0), encryptTransport(false) {}
+    PeerOptions() = default;
     PeerOptions(const PeerOptions &);
-    virtual ~PeerOptions() = default;
+    PeerOptions &operator =(const PeerOptions &);
+    virtual ~PeerOptions() {}
 
     /// parse a TLS squid.conf option
     virtual void parse(const char *);
 
     /// reset the configuration details to default
     virtual void clear() {*this = PeerOptions();}
 
     /// generate an unset security context object
     virtual Security::ContextPointer createBlankContext() const;
 
     /// generate a security client-context from these configured options
     Security::ContextPointer createClientContext(bool setOptions);
 
     /// sync the context options with tls-min-version=N configuration
     void updateTlsVersionLimits();
 
     /// setup the NPN extension details for the given context
     void updateContextNpn(Security::ContextPointer &);
 
     /// setup the CA details for the given context
     void updateContextCa(Security::ContextPointer &);
 
     /// 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(Security::ParsedOptionsPointer &); ///< parsed value of sslOptions
     long parseFlags();
     void loadCrlFile();
 
 public:
     SBuf sslOptions;     ///< library-specific options string
     SBuf caDir;          ///< path of directory containing a set of trusted Certificate Authorities
     SBuf crlFile;        ///< path of file containing Certificate Revoke List
 
     SBuf sslCipher;
     SBuf sslFlags;       ///< flags defining what TLS operations Squid performs
     SBuf sslDomain;
 
     SBuf tlsMinVersion;  ///< version label for minimum TLS version to permit
 
-    long parsedOptions; ///< parsed value of sslOptions
-    long parsedFlags;   ///< parsed value of sslFlags
+    Security::ParsedOptionsPointer 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;
+    int sslVersion = 0;
 
     /// flags governing Squid internal TLS operations
     struct flags_ {
         flags_() : tlsDefaultCa(true), tlsNpn(true) {}
 
         /// whether to use the system default Trusted CA when verifying the remote end certificate
         YesNoNone tlsDefaultCa;
 
         /// whether to use the TLS NPN extension on these connections
         bool tlsNpn;
     } flags;
 
 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
 extern PeerOptions ProxyOutgoingConfig;
 
 } // namespace Security
 
 // parse the tls_outgoing_options directive
 void parse_securePeerOptions(Security::PeerOptions *);
 #define free_securePeerOptions(x) Security::ProxyOutgoingConfig.clear()
 #define dump_securePeerOptions(e,n,x) do { (e)->appendf(n); (x).dumpCfg((e),""); (e)->append("\n",1); } while(false)
 
 #endif /* SQUID_SRC_SECURITY_PEEROPTIONS_H */
 

=== modified file 'src/security/Session.cc'
--- src/security/Session.cc	2017-01-01 00:12:22 +0000
+++ src/security/Session.cc	2017-01-14 14:51:42 +0000
@@ -1,41 +1,226 @@
 /*
  * Copyright (C) 1996-2017 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.
  */
 
 /* DEBUG: section 83    TLS session management */
 
 #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, 2, "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
+    debugs(83, 1, MYNAME << ": TLS FD " << fd << " read " << i << " bytes");
+
+    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, 2, "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
+
+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(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());
+    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::SessionClose(const Security::SessionPointer &s, const int fdOnError)
+{
+    debugs(83, 5, "session=" << (void*)s.get());
+    if (s && fdOnError == -1) {
+#if USE_OPENSSL
+        SSL_shutdown(s.get());
+#elif USE_GNUTLS
+        gnutls_bye(s.get(), GNUTLS_SHUT_RDWR);
+#endif
+    }
+
+#if USE_GNUTLS
+    // XXX: should probably be done for OpenSSL too, but that needs testing.
+    if (fdOnError != -1) {
+        debugs(83, 5, "unlink FD " << fdOnError << " from TLS session=" << (void*)fd_table[fdOnError].ssl.get());
+        fd_table[fdOnError].ssl.reset();
+        fd_table[fdOnError].read_method = &default_read_method;
+        fd_table[fdOnError].write_method = &default_write_method;
+        fd_note(fdOnError, "TLS error");
+    }
+#endif
+}
+
 bool
 Security::SessionIsResumed(const Security::SessionPointer &s)
 {
     bool result = false;
 #if USE_OPENSSL
     result = SSL_session_reused(s.get()) == 1;
 #elif USE_GNUTLS
     result = gnutls_session_is_resumed(s.get()) != 0;
 #endif
     debugs(83, 7, "session=" << (void*)s.get() << ", query? answer: " << (result ? 'T' : 'F') );
     return result;
 }
 
 void
 Security::MaybeGetSessionResumeData(const Security::SessionPointer &s, Security::SessionStatePointer &data)
 {
     if (!SessionIsResumed(s)) {
 #if USE_OPENSSL
         // nil is valid for SSL_get1_session(), it cannot fail.
         data.reset(SSL_get1_session(s.get()));

=== modified file 'src/security/Session.h'
--- src/security/Session.h	2017-01-01 00:12:22 +0000
+++ src/security/Session.h	2017-01-12 13:05:40 +0000
@@ -1,78 +1,83 @@
 /*
  * Copyright (C) 1996-2017 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.
  */
 
 #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>
 
 #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 {
 
+/// 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
 
+/// close an active TLS session.
+/// set fdOnError to the connection FD when the session is being closed
+/// due to an encryption error, otherwise omit.
+void SessionClose(const Security::SessionPointer &, int fdOnError = -1);
+
 /// whether the session is a resumed one
 bool SessionIsResumed(const Security::SessionPointer &);
 
 /**
  * When the session is not a resumed session, retrieve the details needed to
  * resume a later connection and store them in 'data'. This may result in 'data'
  * becoming a nil Pointer if no details exist or an error occurs.
  *
  * When the session is already a resumed session, do nothing and leave 'data'
  * unhanged.
  * XXX: is this latter behaviour always correct?
  */
 void MaybeGetSessionResumeData(const Security::SessionPointer &, Security::SessionStatePointer &data);
 
 /// Set the data for resuming a previous session.
 /// Needs to be done before using the SessionPointer for a handshake.
 void SetSessionResumeData(const Security::SessionPointer &, const Security::SessionStatePointer &);
 
 } // namespace Security
 

=== modified file 'src/security/forward.h'
--- src/security/forward.h	2017-01-01 00:12:22 +0000
+++ src/security/forward.h	2017-01-14 11:43:34 +0000
@@ -91,29 +91,52 @@
 
 class EncryptorAnswer;
 
 /// Squid defined error code (<0), an error code returned by X.509 API, or SSL_ERROR_NONE
 typedef int ErrorCode;
 
 inline const char *ErrorString(const ErrorCode code) {
 #if USE_OPENSSL
     return ERR_error_string(code, nullptr);
 #elif USE_GNUTLS
     return gnutls_strerror(code);
 #else
     return "[no TLS library]";
 #endif
 }
 
 /// set of Squid defined TLS error codes
 /// \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_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 && USE_GNUTLS
+typedef std::unique_ptr<struct gnutls_priority_st, HardFun<void, gnutls_priority_t, &gnutls_priority_deinit>> ParsedOptionsPointer;
+#else
+typedef std::unique_ptr<long> ParsedOptionsPointer;
+#endif
+
 class PeerConnector;
 class PeerOptions;
 class ServerOptions;
 
 } // namespace Security
 
 #endif /* SQUID_SRC_SECURITY_FORWARD_H */
 

=== modified file 'src/ssl/PeekingPeerConnector.cc'
--- src/ssl/PeekingPeerConnector.cc	2017-01-01 00:12:22 +0000
+++ src/ssl/PeekingPeerConnector.cc	2017-01-12 13:05:40 +0000
@@ -168,41 +168,41 @@
         if (csd->sslBumpMode == Ssl::bumpPeek || csd->sslBumpMode == Ssl::bumpStare) {
             auto clientSession = fd_table[clientConn->fd].ssl.get();
             Must(clientSession);
             BIO *bc = SSL_get_rbio(clientSession);
             Ssl::ClientBio *cltBio = static_cast<Ssl::ClientBio *>(BIO_get_data(bc));
             Must(cltBio);
             if (details && details->tlsVersion.protocol != AnyP::PROTO_NONE) {
                 applyTlsDetailsToSSL(serverSession.get(), details, csd->sslBumpMode);
                 // Should we allow it for all protocols?
                 if (details->tlsVersion.protocol == AnyP::PROTO_TLS || details->tlsVersion == AnyP::ProtocolVersion(AnyP::PROTO_SSL, 3, 0)) {
                     BIO *b = SSL_get_rbio(serverSession.get());
                     Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
                     // Inherite client features, like SSL version, SNI and other
                     srvBio->setClientFeatures(details, cltBio->rBufData());
                     srvBio->recordInput(true);
                     srvBio->mode(csd->sslBumpMode);
                 }
             }
         } else {
             // Set client SSL options
-            SSL_set_options(serverSession.get(), ::Security::ProxyOutgoingConfig.parsedOptions);
+            SSL_set_options(serverSession.get(), *::Security::ProxyOutgoingConfig.parsedOptions);
 
             // Use SNI TLS extension only when we connect directly
             // to the origin server and we know the server host name.
             const char *sniServer = NULL;
             const bool redirected = request->flags.redirected && ::Config.onoff.redir_rewrites_host;
             if (!hostName || redirected)
                 sniServer = !request->url.hostIsNumeric() ? request->url.host() : NULL;
             else
                 sniServer = hostName->c_str();
 
             if (sniServer)
                 Ssl::setClientSNI(serverSession.get(), sniServer);
         }
 
         if (Ssl::ServerBump *serverBump = csd->serverBump()) {
             serverBump->attachServerSession(serverSession);
             // store peeked cert to check SQUID_X509_V_ERR_CERT_CHANGE
             if (X509 *peeked_cert = serverBump->serverCert.get()) {
                 X509_up_ref(peeked_cert);
                 SSL_set_ex_data(serverSession.get(), ssl_ex_index_ssl_peeked_cert, peeked_cert);

=== modified file 'src/ssl/bio.cc'
--- src/ssl/bio.cc	2017-01-01 00:12:22 +0000
+++ src/ssl/bio.cc	2017-01-12 13:05:40 +0000
@@ -45,41 +45,41 @@
 #if (OPENSSL_VERSION_NUMBER < 0x10100000L)
 /// Initialization structure for the BIO table with
 /// Squid-specific methods and BIO method wrappers.
 static BIO_METHOD SquidMethods = {
     BIO_TYPE_SOCKET,
     "squid",
     squid_bio_write,
     squid_bio_read,
     squid_bio_puts,
     NULL, // squid_bio_gets not supported
     squid_bio_ctrl,
     squid_bio_create,
     squid_bio_destroy,
     NULL // squid_callback_ctrl not supported
 };
 #else
 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;
 #else
     if (!SquidMethods) {
         SquidMethods = BIO_meth_new(BIO_TYPE_SOCKET, "squid");
         BIO_meth_set_write(SquidMethods, squid_bio_write);
         BIO_meth_set_read(SquidMethods, squid_bio_read);
         BIO_meth_set_puts(SquidMethods, squid_bio_puts);
         BIO_meth_set_gets(SquidMethods, NULL);
         BIO_meth_set_ctrl(SquidMethods, squid_bio_ctrl);
         BIO_meth_set_create(SquidMethods, squid_bio_create);
         BIO_meth_set_destroy(SquidMethods, squid_bio_destroy);
     }
     const BIO_METHOD *useMethod = SquidMethods;
 #endif
 
     if (BIO *bio = BIO_new(useMethod)) {
         BIO_int_ctrl(bio, BIO_C_SET_FD, type, fd);
         return bio;
@@ -558,41 +558,41 @@
 
 /// implements puts() via write()
 static int
 squid_bio_puts(BIO *table, const char *str)
 {
     assert(str);
     return squid_bio_write(table, str, strlen(str));
 }
 
 /// other BIO manipulations (those without dedicated callbacks in BIO table)
 static long
 squid_bio_ctrl(BIO *table, int cmd, long arg1, void *arg2)
 {
     debugs(83, 5, table << ' ' << cmd << '(' << arg1 << ", " << arg2 << ')');
 
     switch (cmd) {
     case BIO_C_SET_FD: {
         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);
         assert(!BIO_get_data(table));
         BIO_set_data(table, bio);
         BIO_set_init(table, 1);
         return 0;
     }
 
     case BIO_C_GET_FD:
         if (BIO_get_init(table)) {
             Ssl::Bio *bio = static_cast<Ssl::Bio*>(BIO_get_data(table));
             assert(bio);
             if (arg2)
                 *static_cast<int*>(arg2) = bio->fd();
             return bio->fd();
         }
         return -1;
 
     case BIO_CTRL_DUP:

=== modified file 'src/ssl/bio.h'
--- src/ssl/bio.h	2017-01-01 00:12:22 +0000
+++ src/ssl/bio.h	2017-01-12 13:05:40 +0000
@@ -1,76 +1,73 @@
 /*
  * Copyright (C) 1996-2017 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.
  */
 
 #ifndef SQUID_SSL_BIO_H
 #define SQUID_SSL_BIO_H
 
+#if USE_OPENSSL
+
 #include "fd.h"
 #include "security/Handshake.h"
 
 #include <iosfwd>
 #include <list>
 #if HAVE_OPENSSL_BIO_H
 #include <openssl/bio.h>
 #endif
 #include <string>
 #include <type_traits>
 
 namespace Ssl
 {
 
 /// BIO source and sink node, handling socket I/O and monitoring SSL state
 class Bio
 {
 public:
-    enum Type {
-        BIO_TO_CLIENT = 6000,
-        BIO_TO_SERVER
-    };
-
     explicit Bio(const int anFd);
     virtual ~Bio();
 
     /// Writes the given data to socket
     virtual int write(const char *buf, int size, BIO *table);
 
     /// Reads data from socket
     virtual int read(char *buf, int size, BIO *table);
 
     /// Flushes any buffered data to socket.
     /// The Ssl::Bio does not buffer any data, so this method has nothing to do
     virtual void flush(BIO *table) {}
 
     int fd() const { return fd_; } ///< The SSL socket descriptor
 
     /// Called by linked SSL connection whenever state changes, an alert
     /// appears, or an error occurs. See SSL_set_info_callback().
     virtual void stateChanged(const SSL *ssl, int where, int ret);
 
     /// 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);
 
     const SBuf &rBufData() {return rbuf;} ///< The buffered input data
 protected:
     const int fd_; ///< the SSL socket we are reading and writing
     SBuf rbuf;  ///< Used to buffer input data.
 };
 
 /// BIO node to handle socket IO for squid client side
 /// If bumping is enabled  this Bio detects and analyses client hello message
 /// to retrieve the SSL features supported by the client
 class ClientBio: public Bio
 {
 public:
     explicit ClientBio(const int anFd): Bio(anFd), holdRead_(false), holdWrite_(false), helloSize(0) {}
 
     /// The ClientBio version of the Ssl::Bio::stateChanged method
     /// When the client hello message retrieved, fill the
     /// "features" member with the client provided informations.
@@ -185,22 +182,23 @@
     Ssl::BumpMode bumpMode_;
 
     /// The size of data stored in rbuf which passed to the openSSL
     size_t rbufConsumePos;
     Security::HandshakeParser parser_; ///< The TLS/SSL messages parser.
 };
 
 } // namespace Ssl
 
 void
 applyTlsDetailsToSSL(SSL *ssl, Security::TlsDetails::Pointer const &details, Ssl::BumpMode bumpMode);
 
 #if (OPENSSL_VERSION_NUMBER < 0x10100000L)
 // OpenSSL v1.0 bio compatibility functions
 inline void *BIO_get_data(BIO *table) { return table->ptr; }
 inline void BIO_set_data(BIO *table, void *data) { table->ptr = data; }
 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 */
 

=== modified file 'src/ssl/cert_validate_message.cc'
--- src/ssl/cert_validate_message.cc	2017-01-01 00:12:22 +0000
+++ src/ssl/cert_validate_message.cc	2017-01-12 13:05:40 +0000
@@ -4,50 +4,50 @@
  * 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 "acl/FilledChecklist.h"
 #include "globals.h"
 #include "helper.h"
 #include "security/CertError.h"
 #include "ssl/cert_validate_message.h"
 #include "ssl/ErrorDetail.h"
 #include "ssl/support.h"
 #include "util.h"
 
 void
 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()));
         for (int i = 0; i < sk_X509_num(peerCerts); ++i) {
             X509 *cert = sk_X509_value(peerCerts, i);
             PEM_write_bio_X509(bio.get(), cert);
             body = body + "\n" + param_cert + xitoa(i) + "=";
             char *ptr;
             long len = BIO_get_mem_data(bio.get(), &ptr);
             body.append(ptr, (ptr[len-1] == '\n' ? len - 1 : len));
             if (!BIO_reset(bio.get())) {
                 // print an error?
             }
         }
     }
 
     if (vcert.errors) {
         int i = 0;
         for (const Security::CertErrors *err = vcert.errors; err; err = err->next, ++i) {
             body +="\n";

=== modified file 'src/ssl/cert_validate_message.h'
--- src/ssl/cert_validate_message.h	2017-01-01 00:12:22 +0000
+++ src/ssl/cert_validate_message.h	2017-01-12 13:05:40 +0000
@@ -9,44 +9,43 @@
 #ifndef SQUID_SSL_CERT_VALIDATE_MESSAGE_H
 #define SQUID_SSL_CERT_VALIDATE_MESSAGE_H
 
 #include "base/RefCount.h"
 #include "helper/ResultCode.h"
 #include "ssl/crtd_message.h"
 #include "ssl/support.h"
 
 #include <vector>
 
 namespace Ssl
 {
 
 /**
  * This class is used to hold the required informations to build
  * a request message for the certificate validator helper
  */
 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) {}
 };
 
 /**
  * This class is used to store informations found in certificate validation
  * response messages read from certificate validator helper
  */
 class CertValidationResponse: public RefCountable
 {
 public:
     typedef RefCount<CertValidationResponse> Pointer;
 
     /**
      * This class used to hold error informations returned from
      * cert validator helper.
      */
     class  RecvdError
     {
     public:
         RecvdError(): id(0), error_no(SSL_ERROR_NONE), cert(NULL), error_depth(-1) {}
         RecvdError(const RecvdError &);

=== modified file 'src/ssl/helper.cc'
--- src/ssl/helper.cc	2017-01-01 00:12:22 +0000
+++ src/ssl/helper.cc	2017-01-12 13:05:40 +0000
@@ -219,41 +219,41 @@
         if (!Ssl::CertValidationHelper::HelperCache->add(crtdvdData->query.c_str(), item))
             delete item;
     }
 
     delete crtdvdData;
 }
 
 void Ssl::CertValidationHelper::sslSubmit(Ssl::CertValidationRequest const &request, AsyncCall::Pointer &callback)
 {
     assert(ssl_crt_validator);
 
     Ssl::CertValidationMsg message(Ssl::CrtdMessage::REQUEST);
     message.setCode(Ssl::CertValidationMsg::code_cert_validate);
     message.composeRequest(request);
     debugs(83, 5, "SSL crtvd request: " << message.compose().c_str());
 
     submitData *crtdvdData = new submitData;
     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 &&
             (validationResponse = CertValidationHelper::HelperCache->get(crtdvdData->query.c_str()))) {
 
         CertValidationHelper::CbDialer *dialer = dynamic_cast<CertValidationHelper::CbDialer*>(callback->getDialer());
         Must(dialer);
         dialer->arg1 = *validationResponse;
         ScheduleCallHere(callback);
         delete crtdvdData;
         return;
     }
 
     if (!ssl_crt_validator->trySubmit(crtdvdData->query.c_str(), sslCrtvdHandleReplyWrapper, crtdvdData)) {
         Ssl::CertValidationResponse::Pointer resp = new Ssl::CertValidationResponse;;
         resp->resultCode = ::Helper::BrokenHelper;
         Ssl::CertValidationHelper::CbDialer *dialer = dynamic_cast<Ssl::CertValidationHelper::CbDialer*>(callback->getDialer());
         Must(dialer);
         dialer->arg1 = resp;
         ScheduleCallHere(callback);

=== modified file 'src/ssl/support.cc'
--- src/ssl/support.cc	2017-01-01 00:12:22 +0000
+++ src/ssl/support.cc	2017-01-13 03:01:41 +0000
@@ -6,40 +6,41 @@
  * Please see the COPYING and CONTRIBUTORS files for details.
  */
 
 /* DEBUG: section 83    SSL accelerator support */
 
 #include "squid.h"
 
 /* MS Visual Studio Projects are monolithic, so we need the following
  * #if to exclude the SSL code from compile process when not needed.
  */
 #if USE_OPENSSL
 
 #include "acl/FilledChecklist.h"
 #include "anyp/PortCfg.h"
 #include "fatal.h"
 #include "fd.h"
 #include "fde.h"
 #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"
 #include "ssl/Config.h"
 #include "ssl/ErrorDetail.h"
 #include "ssl/gadgets.h"
 #include "ssl/support.h"
 #include "URL.h"
 
 #include <cerrno>
 
 // TODO: Move ssl_ex_index_* global variables from global.cc here.
 int ssl_ex_index_ssl_untrusted_chain = -1;
 
 Ipc::MemMap *Ssl::SessionCache = NULL;
 const char *Ssl::SessionCacheName = "ssl_session_cache";
 
 static Ssl::CertsIndexedList SquidUntrustedCerts;
 
 const EVP_MD *Ssl::DefaultSignHash = NULL;
@@ -508,41 +509,41 @@
     (void)ret;
     if ((where & SSL_CB_HANDSHAKE_DONE) != 0) {
         // disable renegotiation (CVE-2009-3555)
         ssl->s3->flags |= SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS;
     }
 }
 #endif
 
 static void
 maybeDisableRenegotiate(Security::ContextPointer &ctx)
 {
 #if defined(SSL3_FLAGS_NO_RENEGOTIATE_CIPHERS) && (OPENSSL_VERSION_NUMBER < 0x10100000L)
     SSL_CTX_set_info_callback(ctx.get(), ssl_info_cb);
 #endif
 }
 
 static bool
 configureSslContext(Security::ContextPointer &ctx, AnyP::PortCfg &port)
 {
     int ssl_error;
-    SSL_CTX_set_options(ctx.get(), port.secure.parsedOptions);
+    SSL_CTX_set_options(ctx.get(), *port.secure.parsedOptions);
 
     maybeDisableRenegotiate(ctx);
 
     if (port.sslContextSessionId)
         SSL_CTX_set_session_id_context(ctx.get(), (const unsigned char *)port.sslContextSessionId, strlen(port.sslContextSessionId));
 
     if (port.secure.parsedFlags & SSL_FLAG_NO_SESSION_REUSE) {
         SSL_CTX_set_session_cache_mode(ctx.get(), SSL_SESS_CACHE_OFF);
     }
 
     if (Config.SSL.unclean_shutdown) {
         debugs(83, 5, "Enabling quiet SSL shutdowns (RFC violation).");
 
         SSL_CTX_set_quiet_shutdown(ctx.get(), 1);
     }
 
     if (!port.secure.sslCipher.isEmpty()) {
         debugs(83, 5, "Using chiper suite " << port.secure.sslCipher << ".");
 
         if (!SSL_CTX_set_cipher_list(ctx.get(), port.secure.sslCipher.c_str())) {
@@ -632,47 +633,45 @@
 
         debugs(83, 5, "Comparing private and public SSL keys.");
 
         if (!SSL_CTX_check_private_key(ctx.get())) {
             ssl_error = ERR_get_error();
             debugs(83, DBG_CRITICAL, "ERROR: SSL private key '" << certfile << "' does not match public key '" <<
                    keyfile << "': " << Security::ErrorString(ssl_error));
             return false;
         }
     */
 
     if (!configureSslContext(ctx, port)) {
         debugs(83, DBG_CRITICAL, "ERROR: Configuring static SSL context");
         return false;
     }
 
     return true;
 }
 
 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);
-
     maybeDisableRenegotiate(ctx);
 
     if (!peer.sslCipher.isEmpty()) {
         debugs(83, 5, "Using chiper suite " << peer.sslCipher << ".");
 
         const char *cipher = peer.sslCipher.c_str();
         if (!SSL_CTX_set_cipher_list(ctx.get(), cipher)) {
             const int ssl_error = ERR_get_error();
             fatalf("Failed to set SSL cipher suite '%s': %s\n",
                    cipher, Security::ErrorString(ssl_error));
         }
     }
 
     if (!peer.certs.empty()) {
         // TODO: support loading multiple cert/key pairs
         auto &keys = peer.certs.front();
         if (!keys.certFile.isEmpty()) {
             debugs(83, DBG_IMPORTANT, "Using certificate in " << keys.certFile);
 
             const char *certfile = keys.certFile.c_str();
@@ -699,89 +698,40 @@
                 fatalf("SSL private key '%s' does not match public key '%s': %s\n",
                        certfile, keyfile, Security::ErrorString(ssl_error));
             }
         }
     }
 
     maybeSetupRsaCallback(ctx);
 
     if (fl & SSL_FLAG_DONT_VERIFY_PEER) {
         debugs(83, 2, "NOTICE: Peer certificates are not verified for validity!");
         SSL_CTX_set_verify(ctx.get(), SSL_VERIFY_NONE, NULL);
     } else {
         debugs(83, 9, "Setting certificate verification callback.");
         SSL_CTX_set_verify(ctx.get(), SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, ssl_verify_cb);
     }
 
     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)
 {
     static char buffer[1024];
     buffer[0] = '\0';
 
     if (strcmp(attribute_name, "DN") == 0) {
         X509_NAME_oneline(name, buffer, sizeof(buffer));
     } else {
         int nid = OBJ_txt2nid(const_cast<char *>(attribute_name));
         if (nid == 0) {
             debugs(83, DBG_IMPORTANT, "WARNING: Unknown SSL attribute name '" << attribute_name << "'");
             return nullptr;
         }
         X509_NAME_get_text_by_NID(name, nid, buffer, sizeof(buffer));
     }
 
     return *buffer ? buffer : nullptr;
 }
 
@@ -1053,41 +1003,45 @@
 
     if (!SSL_use_PrivateKey(ssl, pkey.get()))
         return false;
 
     return true;
 }
 
 bool
 Ssl::verifySslCertificate(Security::ContextPointer &ctx, CertificateProperties const &properties)
 {
     // SSL_get_certificate is buggy in openssl versions 1.0.1d and 1.0.1e
     // Try to retrieve certificate directly from Security::ContextPointer object
 #if SQUID_USE_SSLGETCERTIFICATE_HACK
     X509 ***pCert = (X509 ***)ctx->cert;
     X509 * cert = pCert && *pCert ? **pCert : NULL;
 #elif SQUID_SSLGETCERTIFICATE_BUGGY
     X509 * cert = NULL;
     assert(0);
 #else
     // Temporary ssl for getting X509 certificate from SSL_CTX.
-    Security::SessionPointer ssl(SSL_new(ctx.get()));
+    Security::SessionPointer ssl(SSL_new(ctx.get()), [](SSL *p) {
+            debugs(83, 5, "SSL_free session=" << (void*)p);
+            SSL_free(p);
+        });
+    debugs(83, 5, "SSL_new session=" << (void*)ssl.get());
     X509 * cert = SSL_get_certificate(ssl.get());
 #endif
     if (!cert)
         return false;
     ASN1_TIME * time_notBefore = X509_get_notBefore(cert);
     ASN1_TIME * time_notAfter = X509_get_notAfter(cert);
     bool ret = (X509_cmp_current_time(time_notBefore) < 0 && X509_cmp_current_time(time_notAfter) > 0);
     if (!ret)
         return false;
 
     return certificateMatchesProperties(cert, properties);
 }
 
 bool
 Ssl::setClientSNI(SSL *ssl, const char *fqdn)
 {
     //The SSL_CTRL_SET_TLSEXT_HOSTNAME is a openssl macro which indicates
     // if the TLS servername extension (SNI) is enabled in openssl library.
 #if defined(SSL_CTRL_SET_TLSEXT_HOSTNAME)
     if (!SSL_set_tlsext_host_name(ssl, fqdn)) {
@@ -1436,87 +1390,40 @@
     Ssl::CertificateProperties certProperties;
     if (const char *cn = CommonHostName(cert.get())) {
         certProperties.commonName = "Not trusted by \"";
         certProperties.commonName += cn;
         certProperties.commonName += "\"";
     } else if (const char *org = getOrganization(cert.get())) {
         certProperties.commonName =  "Not trusted by \"";
         certProperties.commonName += org;
         certProperties.commonName += "\"";
     } else
         certProperties.commonName =  "Not trusted";
     certProperties.setCommonName = true;
     // O, OU, and other CA subject fields will be mimicked
     // Expiration date and other common properties will be mimicked
     certProperties.signAlgorithm = Ssl::algSignSelf;
     certProperties.signWithPkey.resetAndLock(pkey.get());
     certProperties.mimicCert.resetAndLock(cert.get());
     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)
 {
     if (!Ssl::SessionCache)
         return 0;
 
     debugs(83, 5, "Request to store SSL Session ");
 
     SSL_SESSION_set_timeout(session, Config.SSL.session_ttl);
 
 #if (OPENSSL_VERSION_NUMBER < 0x10100000L)
     unsigned char *id = session->session_id;
     unsigned int idlen = session->session_id_length;
 #else
     unsigned int idlen;
     const unsigned char *id = SSL_SESSION_get_id(session, &idlen);
 #endif
     unsigned char key[MEMMAP_SLOT_KEY_SIZE];
     // Session ids are of size 32bytes. They should always fit to a
     // MemMap::Slot::key

=== modified file 'src/ssl/support.h'
--- src/ssl/support.h	2017-01-01 00:12:22 +0000
+++ src/ssl/support.h	2017-01-12 13:05:40 +0000
@@ -56,76 +56,59 @@
 namespace AnyP
 {
 class PortCfg;
 };
 
 namespace Ipc
 {
 class MemMap;
 }
 
 namespace Ssl
 {
 /// initialize the SSL library global state.
 /// call before generating any SSL context
 void Initialize();
 
 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;
 
 /// initialize a TLS server context with OpenSSL specific settings
 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
 // use Security::CertPointer instead where possible
 inline int X509_up_ref(X509 *t) {if (t) CRYPTO_add(&t->references, 1, CRYPTO_LOCK_X509); return 0;}
 #endif
 
 } //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);
 
 /// \ingroup ServerProtocolSSLAPI
 const char *sslGetUserAttribute(SSL *ssl, const char *attribute_name);
 
 /// \ingroup ServerProtocolSSLAPI
 const char *sslGetCAAttribute(SSL *ssl, const char *attribute_name);
 
 /// \ingroup ServerProtocolSSLAPI
 const char *sslGetUserCertificatePEM(SSL *ssl);
 
 /// \ingroup ServerProtocolSSLAPI
 const char *sslGetUserCertificateChainPEM(SSL *ssl);
 
 namespace Ssl
 {
 /// \ingroup ServerProtocolSSLAPI
 typedef char const *GETX509ATTRIBUTE(X509 *, const char *);
 
 /// \ingroup ServerProtocolSSLAPI

=== modified file 'src/tests/stub_libsecurity.cc'
--- src/tests/stub_libsecurity.cc	2017-01-01 00:12:22 +0000
+++ src/tests/stub_libsecurity.cc	2017-01-12 13:05:40 +0000
@@ -51,46 +51,50 @@
 const char *PeerConnector::status() const STUB_RETVAL("")
 void PeerConnector::commCloseHandler(const CommCloseCbParams &) STUB
 void PeerConnector::connectionClosed(const char *) STUB
 bool PeerConnector::prepareSocket() STUB_RETVAL(false)
 void PeerConnector::setReadTimeout() STUB
 bool PeerConnector::initialize(Security::SessionPointer &) STUB_RETVAL(false)
 void PeerConnector::negotiate() STUB
 bool PeerConnector::sslFinalized() STUB_RETVAL(false)
 void PeerConnector::handleNegotiateError(const int) STUB
 void PeerConnector::noteWantRead() STUB
 void PeerConnector::noteWantWrite() STUB
 void PeerConnector::noteNegotiationError(const int, const int, const int) STUB
 //    virtual Security::ContextPointer getTlsContext() = 0;
 void PeerConnector::bail(ErrorState *) STUB
 void PeerConnector::callBack() STUB
 void PeerConnector::recordNegotiationDetails() STUB
 }
 
 #include "security/PeerOptions.h"
 Security::PeerOptions Security::ProxyOutgoingConfig;
+Security::PeerOptions &Security::PeerOptions::operator =(const Security::PeerOptions &) STUB_RETVAL(*this)
 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 parse_securePeerOptions(Security::PeerOptions *) STUB
 
 #include "security/ServerOptions.h"
 //Security::ServerOptions::ServerOptions(const Security::ServerOptions &) STUB
 void Security::ServerOptions::parse(const char *) STUB
 void Security::ServerOptions::dumpCfg(Packable *, const char *) const STUB
 Security::ContextPointer Security::ServerOptions::createBlankContext() const STUB_RETVAL(Security::ContextPointer())
 bool Security::ServerOptions::createStaticServerContext(AnyP::PortCfg &) STUB_RETVAL(false)
 void Security::ServerOptions::updateContextEecdh(Security::ContextPointer &) STUB
 
 #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 SessionClose(const Security::SessionPointer &) STUB
+void SessionClose(const Security::SessionPointer &, int) 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
 } // namespace Security
 

=== modified file 'src/tests/stub_libsslsquid.cc'
--- src/tests/stub_libsslsquid.cc	2017-01-01 00:12:22 +0000
+++ src/tests/stub_libsslsquid.cc	2017-01-12 13:05:40 +0000
@@ -34,45 +34,42 @@
 //Ssl::CertificateStorageAction::CertificateStorageAction(const Mgr::Command::Pointer &cmd) STUB
 Ssl::CertificateStorageAction::Pointer Ssl::CertificateStorageAction::Create(const Mgr::Command::Pointer &cmd) STUB_RETSTATREF(Ssl::CertificateStorageAction::Pointer)
 void Ssl::CertificateStorageAction::dump(StoreEntry *sentry) STUB
 void Ssl::GlobalContextStorage::addLocalStorage(Ip::Address const & address, size_t size_of_store) STUB
 Ssl::LocalContextStorage *Ssl::GlobalContextStorage::getLocalStorage(Ip::Address const & address)
 { fatal(STUB_API " required"); static Ssl::LocalContextStorage v(0,0); return &v; }
 void Ssl::GlobalContextStorage::reconfigureStart() STUB
 //Ssl::GlobalContextStorage Ssl::TheGlobalContextStorage;
 
 #include "ssl/ErrorDetail.h"
 Security::ErrorCode parseErrorString(const char *name) STUB_RETVAL(0)
 //const char *Ssl::getErrorName(Security::ErrorCode value) STUB_RETVAL(NULL)
 Ssl::ErrorDetail::ErrorDetail(Security::ErrorCode, X509 *, X509 *, const char *) STUB
 Ssl::ErrorDetail::ErrorDetail(ErrorDetail const &) STUB
 const String & Ssl::ErrorDetail::toString() const STUB_RETSTATREF(String)
 
 #include "ssl/support.h"
 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)
 const char *sslGetUserCertificatePEM(SSL *ssl) STUB_RETVAL(NULL)
 const char *sslGetUserCertificateChainPEM(SSL *ssl) STUB_RETVAL(NULL)
 namespace Ssl
 {
 //GETX509ATTRIBUTE GetX509UserAttribute;
 //GETX509ATTRIBUTE GetX509CAAttribute;
 //GETX509ATTRIBUTE GetX509Fingerprint;
 std::vector<const char *> BumpModeStr = {""};
 bool generateUntrustedCert(Security::CertPointer & untrustedCert, EVP_PKEY_Pointer & untrustedPkey, Security::CertPointer const & cert, EVP_PKEY_Pointer const & pkey) STUB_RETVAL(false)
 Security::ContextPointer generateSslContext(CertificateProperties const &, AnyP::PortCfg &) STUB_RETVAL(Security::ContextPointer())
 bool verifySslCertificate(Security::ContextPointer &, CertificateProperties const &) STUB_RETVAL(false)
 Security::ContextPointer generateSslContextUsingPkeyAndCertFromMemory(const char *, AnyP::PortCfg &) STUB_RETVAL(Security::ContextPointer())
 void addChainToSslContext(Security::ContextPointer &, STACK_OF(X509) *) STUB
 void readCertChainAndPrivateKeyFromFiles(Security::CertPointer & cert, EVP_PKEY_Pointer & pkey, X509_STACK_Pointer & chain, char const * certFilename, char const * keyFilename) STUB
 int matchX509CommonNames(X509 *peer_cert, void *check_data, int (*check_func)(void *check_data,  ASN1_STRING *cn_data)) STUB_RETVAL(0)
 bool checkX509ServerValidity(X509 *cert, const char *server) STUB_RETVAL(false)
 int asn1timeToString(ASN1_TIME *tm, char *buf, int len) STUB_RETVAL(0)

=== modified file 'src/url.cc'
--- src/url.cc	2017-01-01 00:12:22 +0000
+++ src/url.cc	2017-01-14 07:26:47 +0000
@@ -813,41 +813,41 @@
         break;
 
     case AnyP::PROTO_FTP:
 
         if (r->method == Http::METHOD_PUT)
             rc = 1;
 
     case AnyP::PROTO_GOPHER:
 
     case AnyP::PROTO_WAIS:
 
     case AnyP::PROTO_WHOIS:
         if (r->method == Http::METHOD_GET)
             rc = 1;
         else if (r->method == Http::METHOD_HEAD)
             rc = 1;
 
         break;
 
     case AnyP::PROTO_HTTPS:
-#if USE_OPENSSL
+#if USE_OPENSSL || USE_GNUTLS
         rc = 1;
 #else
         /*
         * Squid can't originate an SSL connection, so it should
         * never receive an "https:" URL.  It should always be
         * CONNECT instead.
         */
         rc = 0;
 #endif
         break;
 
     default:
         break;
     }
 
     return rc;
 }
 
 /*
  * Quick-n-dirty host extraction from a URL.  Steps:



More information about the squid-dev mailing list