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

Amos Jeffries squid3 at treenet.co.nz
Thu Jan 19 07:25:56 UTC 2017


Ar, forgot the patch again. Please find it attached.


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-16 12:39:06 +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::SessionSendGoodbye(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::SessionSendGoodbye(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-16 12:35:30 +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,215 @@
     }
     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.
 }
 
 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-17 13:51:44 +0000
@@ -4,91 +4,117 @@
  * 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 "base/Packable.h"
 #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()
+{
+     // init options consistent with an empty sslOptions
+     parseOptions();
+}
+
 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)
 {
     memcpy(&flags, &p.flags, sizeof(flags));
 }
 
+Security::PeerOptions &
+Security::PeerOptions::operator =(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;
+    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();
     } 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 +176,121 @@
         // 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;
 #endif
 #if SSL_OP_NO_TLSv1_1
             if (v > 1)
                 parsedOptions |= SSL_OP_NO_TLSv1_1;
 #endif
 #if SSL_OP_NO_TLSv1_2
             if (v > 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;
+        const char *add = nullptr;
         switch (sslVersion) {
         case 3:
-            add = "NO_TLSv1,NO_TLSv1_1,NO_TLSv1_2";
+#if USE_OPENSSL
+            parsedOptions |= (SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1|SSL_OP_NO_TLSv1_2);
+#elif USE_GNUTLS
+            add = ":-VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2";
+#endif
             break;
         case 4:
-            add = "NO_SSLv3,NO_TLSv1_1,NO_TLSv1_2";
+#if USE_OPENSSL
+            parsedOptions |= (SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1_1|SSL_OP_NO_TLSv1_2);
+#elif USE_GNUTLS
+            add = ":+VERS-TLS1.0:-VERS-TLS1.1:-VERS-TLS1.2";
+#endif
             break;
         case 5:
-            add = "NO_SSLv3,NO_TLSv1,NO_TLSv1_2";
+#if USE_OPENSSL
+            parsedOptions |= (SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_2);
+#elif USE_GNUTLS
+            add = ":-VERS-TLS1.0:+VERS-TLS1.1:-VERS-TLS1.2";
+#endif
             break;
         case 6:
-            add = "NO_SSLv3,NO_TLSv1,NO_TLSv1_1";
+#if USE_OPENSSL
+            parsedOptions |= (SSL_OP_NO_SSLv3|SSL_OP_NO_TLSv1|SSL_OP_NO_TLSv1_1);
+#elif USE_GNUTLS
+            add = ":-VERS-TLS1.0:-VERS-TLS1.1";
+#endif
             break;
         default: // nothing
             break;
         }
         if (add) {
-            if (!sslOptions.isEmpty())
-                sslOptions.append(",",1);
-            sslOptions.append(add, strlen(add));
+#if USE_GNUTLS // dont bother otherwise
+            if (sslOptions.isEmpty())
+                sslOptions.append(add+1, strlen(add+1));
+            else
+                sslOptions.append(add, strlen(add));
+#endif
         }
         sslVersion = 0; // prevent sslOptions being repeatedly appended
     }
 }
 
 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 +304,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,52 +448,54 @@
         "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
+void
 Security::PeerOptions::parseOptions()
 {
-    long op = 0;
+#if USE_OPENSSL
     ::Parser::Tokenizer tok(sslOptions);
+    long op = 0;
 
-    do {
+    while (!tok.atEnd()) {
         enum {
             MODE_ADD, MODE_REMOVE
         } mode;
 
         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)) {
 
             // find the named option in our supported set
@@ -444,47 +514,64 @@
         }
 
         if (value) {
             switch (mode) {
             case MODE_ADD:
                 op |= value;
                 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;
+    parsedOptions = op;
+
+#elif USE_GNUTLS
+    if (sslOptions.isEmpty()) {
+        parsedOptions.reset();
+        return;
+    }
+
+    const char *err = nullptr;
+    const char *priorities = sslOptions.c_str();
+    gnutls_priority_t op;
+    if (gnutls_priority_init(&op, priorities, &err) != GNUTLS_E_SUCCESS) {
+        fatalf("Unknown TLS option '%s'", err);
+    }
+    parsedOptions = Security::ParsedOptions(op, [](gnutls_priority_t p) {
+            gnutls_priority_deinit(p);
+    });
+#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 +712,50 @@
     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;
+    SBuf errMsg;
+    if (!parsedOptions) {
+        debugs(83, 5, "set GnuTLS default priority/options for session=" << s);
+        x = gnutls_set_default_priority(s.get());
+        static const SBuf defaults("default");
+        errMsg = defaults;
+    } else {
+        debugs(83, 5, "set GnuTLS options '" << sslOptions << "' for session=" << s);
+        x = gnutls_priority_set(s.get(), parsedOptions.get());
+        errMsg = sslOptions;
+    }
+
+    if (x != GNUTLS_E_SUCCESS) {
+        debugs(83, DBG_IMPORTANT, "ERROR: Failed to set TLS options (" << errMsg << "). error: " << Security::ErrorString(x));
+    }
+#endif
+}
+
+void
 parse_securePeerOptions(Security::PeerOptions *opt)
 {
     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-17 13:54:04 +0000
@@ -5,105 +5,111 @@
  * 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();
     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(); ///< 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::ParsedOptions parsedOptions; ///< parsed value of sslOptions
+    long parsedFlags = 0;   ///< parsed value of sslFlags
 
     std::list<Security::KeyData> certs; ///< details from the cert= and file= config parameters
     std::list<SBuf> caFiles;  ///< paths of files containing trusted Certificate Authority
     Security::CertRevokeList parsedCrl; ///< CRL to use when verifying the remote end certificate
 
 protected:
-    int sslVersion;
+    int sslVersion = 0;
 
     /// flags governing Squid internal TLS operations
     struct flags_ {
         flags_() : tlsDefaultCa(true), tlsNpn(true) {}
+        flags_(const flags_ &) = default;
+        flags_ &operator =(const flags_ &) = default;
 
         /// whether to use the system default Trusted CA when verifying the remote end certificate
         YesNoNone tlsDefaultCa;
 
         /// 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-16 15:16:35 +0000
@@ -1,41 +1,214 @@
 /*
  * 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, 3, "started for session=" << (void*)session);
+
+#if DONT_DO_THIS && USE_OPENSSL
+    if (!SSL_is_init_finished(session)) {
+        errno = ENOTCONN;
+        return -1;
+    }
+#endif
+
+#if USE_OPENSSL
+    int i = SSL_read(session, buf, len);
+#elif USE_GNUTLS
+    int i = gnutls_record_recv(session, buf, len);
+#endif
+
+    if (i > 0) {
+        debugs(83, 8, "TLS FD " << fd << " session=" << (void*)session << " " << i << " bytes");
+        (void)VALGRIND_MAKE_MEM_DEFINED(buf, i);
+    }
+
+#if USE_OPENSSL
+    if (i > 0 && SSL_pending(session) > 0) {
+#elif USE_GNUTLS
+    if (i > 0 && gnutls_record_check_pending(session) > 0) {
+#endif
+        debugs(83, 2, "TLS FD " << fd << " is pending");
+        fd_table[fd].flags.read_pending = true;
+    } else
+        fd_table[fd].flags.read_pending = false;
+
+    return i;
+}
+
+static int
+tls_write_method(int fd, const char *buf, int len)
+{
+    auto session = fd_table[fd].ssl.get();
+    debugs(83, 3, "started for session=" << (void*)session);
+
+#if USE_OPENSSL
+    if (!SSL_is_init_finished(session)) {
+        errno = ENOTCONN;
+        return -1;
+    }
+#endif
+
+#if USE_OPENSSL
+    int i = SSL_write(session, buf, len);
+#elif USE_GNUTLS
+    int i = gnutls_record_send(session, buf, len);
+#endif
+
+    if (i > 0) {
+        debugs(83, 8, "TLS FD " << fd << " session=" << (void*)session << " " << i << " bytes");
+    }
+    return i;
+}
+#endif
+
+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::SessionSendGoodbye(const Security::SessionPointer &s)
+{
+    debugs(83, 5, "session=" << (void*)s.get());
+    if (s) {
+#if USE_OPENSSL
+        SSL_shutdown(s.get());
+#elif USE_GNUTLS
+        gnutls_bye(s.get(), GNUTLS_SHUT_RDWR);
+#endif
+    }
+}
+
 bool
 Security::SessionIsResumed(const Security::SessionPointer &s)
 {
     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-16 12:37:44 +0000
@@ -1,78 +1,81 @@
 /*
  * 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
 
+/// send the shutdown/bye notice for an active TLS session.
+void SessionSendGoodbye(const Security::SessionPointer &);
+
 /// whether the session is a resumed one
 bool SessionIsResumed(const Security::SessionPointer &);
 
 /**
  * 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-17 04:47:37 +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::shared_ptr<struct gnutls_priority_st> ParsedOptions;
+#else
+typedef long ParsedOptions;
+#endif
+
 class PeerConnector;
 class PeerOptions;
 class ServerOptions;
 
 } // namespace Security
 
 #endif /* SQUID_SRC_SECURITY_FORWARD_H */
 

=== 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-17 04:13:26 +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;
@@ -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-16 15:21:04 +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-18 16:14:36 +0000
@@ -51,46 +51,57 @@
 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::PeerOptions() {
+#if !USE_GNUTLS
+    parsedOptions = 0;
+#endif
+     STUB_NOP
+}
+Security::PeerOptions::PeerOptions(const Security::PeerOptions &) {STUB}
+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 Security::PeerOptions::parseOptions() STUB
 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 SessionSendGoodbye(const Security::SessionPointer &) STUB
 bool SessionIsResumed(const Security::SessionPointer &) STUB_RETVAL(false)
 void MaybeGetSessionResumeData(const Security::SessionPointer &, Security::SessionStatePointer &) STUB
 void SetSessionResumeData(const Security::SessionPointer &, const Security::SessionStatePointer &) STUB
 } // 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