These two patches begin the refactoring work to replace OpenSSL SSL_set/get_ex_data() API with a generic libsecurity API.

Pt1 contains the Security::ExtraData class and the global GetFrom() function used to retrieve it from a session Pointer - initializing one if missing.

Pt2 contains the refactor changes replacing all current uses of ssl_ex_index_server with the ExtraData object SNI member.

Amos

=== modified file 'src/security/Session.cc'
--- src/security/Session.cc	2017-02-05 06:12:19 +0000
+++ src/security/Session.cc	2017-04-19 06:38:34 +0000
@@ -105,40 +105,43 @@
 {
     if (!Comm::IsConnOpen(conn)) {
         debugs(83, DBG_IMPORTANT, "Gone connection");
         return false;
     }
 
 #if USE_OPENSSL || USE_GNUTLS
 
     const char *errAction = "with no TLS/SSL library";
     int errCode = 0;
 #if USE_OPENSSL
     Security::SessionPointer session(Security::NewSessionObject(ctx));
     if (!session) {
         errCode = ERR_get_error();
         errAction = "failed to allocate handle";
     }
 #elif USE_GNUTLS
     gnutls_session_t tmp;
     errCode = gnutls_init(&tmp, static_cast<unsigned int>(type) | GNUTLS_NONBLOCK);
     Security::SessionPointer session(tmp, [](gnutls_session_t p) {
+        // free any ExtraData object associated with this session
+        auto *data = static_cast<Security::ExtraData *>(gnutls_session_get_ptr(p));
+        delete data;
         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) {
@@ -184,40 +187,83 @@
 
 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
     }
 }
 
+#if USE_OPENSSL
+/// "free" function for Security::ExtraData
+static void
+free_ExtraData(void *, void *ptr, CRYPTO_EX_DATA *, int, long, void *)
+{
+    delete static_cast<Security::ExtraData *>(ptr);
+}
+#endif
+
+Security::ExtraData *
+Security::ExtraData::GetFrom(const SessionPointer &session)
+{
+    if (!session)
+        return nullptr;
+
+    Security::ExtraData *data = nullptr;
+
+    // get any data we attached to the session earlier
+#if USE_OPENSSL
+    // initialize extended-data registration with OpenSSL if needed
+    static int data_index = -1;
+    if (data_index == -1) {
+        data_index = SSL_get_ex_new_index(0, const_cast<char *>("Security::ExtraData"), nullptr, nullptr, free_ExtraData);
+    }
+
+    data = static_cast<Security::ExtraData *>(SSL_get_ex_data(session.get(), data_index));
+#elif USE_GNUTLS
+    data = static_cast<Security::ExtraData *>(gnutls_session_get_ptr(session.get()));
+#endif
+
+    // allocate and attach a new data object if missing
+    if (!data) {
+        data = new Security::ExtraData;
+#if USE_OPENSSL
+        SSL_set_ex_data(session.get(), data_index, data);
+#elif USE_GNUTLS
+        gnutls_session_set_ptr(session.get(), data);
+#endif
+    }
+
+    return data;
+}
+
 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-02-10 13:35:05 +0000
+++ src/security/Session.h	2017-04-19 06:38:34 +0000
@@ -1,33 +1,34 @@
 /*
  * 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 "sbuf/SBuf.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.
@@ -39,40 +40,54 @@
 
 #if USE_OPENSSL
 typedef std::shared_ptr<SSL> SessionPointer;
 
 typedef std::unique_ptr<SSL_SESSION, HardFun<void, SSL_SESSION*, &SSL_SESSION_free>> SessionStatePointer;
 
 #elif USE_GNUTLS
 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
 typedef std::shared_ptr<void> SessionPointer;
 
 typedef std::unique_ptr<int> SessionStatePointer;
 
 #endif
 
+/// extra data which we attach to a TLS session
+class ExtraData
+{
+public:
+    SBuf sni;
+
+public:
+    /// The extra data (if any) associated with a session.
+    /// \note Guaranteed to return an object if the session pointer is set,
+    ///       will create if necessary.
+    /// \retval nullptr only if session is nullptr
+    static Security::ExtraData *GetFrom(const SessionPointer &);
+};
+
 /// 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 &);

=== modified file 'src/tests/stub_libsecurity.cc'
--- src/tests/stub_libsecurity.cc	2017-02-05 06:12:19 +0000
+++ src/tests/stub_libsecurity.cc	2017-04-19 06:38:34 +0000
@@ -81,28 +81,29 @@
 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
 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
+Security::ExtraData *Security::ExtraData::GetFrom(const Security::SessionPointer &) STUB_RETVAL(nullptr)
 bool SessionIsResumed(const Security::SessionPointer &) STUB_RETVAL(false)
 void MaybeGetSessionResumeData(const Security::SessionPointer &, Security::SessionStatePointer &) STUB
 void SetSessionResumeData(const Security::SessionPointer &, const Security::SessionStatePointer &) STUB
 #if USE_OPENSSL
 Security::SessionPointer NewSessionObject(const Security::ContextPointer &) STUB_RETVAL(nullptr)
 #endif
 } // namespace Security
 

=== modified file 'src/adaptation/icap/Xaction.cc'
--- src/adaptation/icap/Xaction.cc	2017-02-16 11:51:56 +0000
+++ src/adaptation/icap/Xaction.cc	2017-04-19 06:38:34 +0000
@@ -695,47 +695,48 @@
 {
     if (haveConnection() && commEof)
         buf.appendf("Comm(%d)", connection->fd);
 
     if (stopReason != NULL)
         buf.append("Stopped", 7);
 }
 
 bool Adaptation::Icap::Xaction::fillVirginHttpHeader(MemBuf &) const
 {
     return false;
 }
 
 bool
 Ssl::IcapPeerConnector::initialize(Security::SessionPointer &serverSession)
 {
     if (!Security::PeerConnector::initialize(serverSession))
         return false;
 
     assert(!icapService->cfg().secure.sslDomain.isEmpty());
-#if USE_OPENSSL
-    SBuf *host = new SBuf(icapService->cfg().secure.sslDomain);
-    SSL_set_ex_data(serverSession.get(), ssl_ex_index_server, host);
+    auto *data = Security::ExtraData::GetFrom(serverSession);
+    assert(data);
+    data->sni = icapService->cfg().secure.sslDomain;
 
+#if USE_OPENSSL
     ACLFilledChecklist *check = static_cast<ACLFilledChecklist *>(SSL_get_ex_data(serverSession.get(), ssl_ex_index_cert_error_check));
     if (check)
-        check->dst_peer_name = *host;
+        check->dst_peer_name = data->sni;
 #endif
 
     Security::SetSessionResumeData(serverSession, icapService->sslSession);
     return true;
 }
 
 void
 Ssl::IcapPeerConnector::noteNegotiationDone(ErrorState *error)
 {
     if (error)
         return;
 
     const int fd = serverConnection()->fd;
     Security::MaybeGetSessionResumeData(fd_table[fd].ssl, icapService->sslSession);
 }
 
 void
 Adaptation::Icap::Xaction::handleSecuredPeer(Security::EncryptorAnswer &answer)
 {
     Must(securer != NULL);

=== modified file 'src/globals.h'
--- src/globals.h	2017-01-01 00:12:22 +0000
+++ src/globals.h	2017-04-19 06:38:34 +0000
@@ -81,40 +81,39 @@
 #endif
 
 extern int store_open_disk_fd;  /* 0 */
 extern const char *SwapDirType[];
 extern int store_swap_low;  /* 0 */
 extern int store_swap_high; /* 0 */
 extern size_t store_pages_max;  /* 0 */
 extern int64_t store_maxobjsize;    /* 0 */
 extern int incoming_sockets_accepted;
 #if _SQUID_WINDOWS_
 extern unsigned int WIN32_Socks_initialized;    /* 0 */
 #endif
 #if _SQUID_WINDOWS_
 extern unsigned int WIN32_OS_version;   /* 0 */
 extern char *WIN32_OS_string;           /* NULL */
 extern char *WIN32_Command_Line;        /* NULL */
 extern char *WIN32_Service_Command_Line; /* NULL */
 extern unsigned int WIN32_run_mode;     /* _WIN_SQUID_RUN_MODE_INTERACTIVE */
 #endif
 
-extern int ssl_ex_index_server; /* -1 */
 extern int ssl_ctx_ex_index_dont_verify_domain; /* -1 */
 extern int ssl_ex_index_cert_error_check;   /* -1 */
 extern int ssl_ex_index_ssl_error_detail;      /* -1 */
 extern int ssl_ex_index_ssl_peeked_cert;      /* -1 */
 extern int ssl_ex_index_ssl_errors;   /* -1 */
 extern int ssl_ex_index_ssl_cert_chain;  /* -1 */
 extern int ssl_ex_index_ssl_validation_counter;  /* -1 */
 
 extern const char *external_acl_message;      /* NULL */
 extern int opt_send_signal; /* -1 */
 extern int opt_no_daemon; /* 0 */
 extern int opt_parse_cfg_only; /* 0 */
 
 /// current Squid process number (e.g., 4).
 /// Zero for SMP-unaware code and in no-SMP mode.
 extern int KidIdentifier; /* 0 */
 
 #endif /* SQUID_GLOBALS_H */
 

=== modified file 'src/security/BlindPeerConnector.cc'
--- src/security/BlindPeerConnector.cc	2017-01-12 13:26:45 +0000
+++ src/security/BlindPeerConnector.cc	2017-04-19 06:38:34 +0000
@@ -20,56 +20,53 @@
 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)) {
         debugs(83, 5, "Security::PeerConnector::initialize failed");
         return false;
     }
 
+    auto *data = Security::ExtraData::GetFrom(serverSession);
+    assert(data);
+
     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);
+        data->sni = peer->secure.sslDomain;
 
         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
+        data->sni = request->url.host();
     }
 
     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;
     }

=== modified file 'src/security/PeerConnector.cc'
--- src/security/PeerConnector.cc	2017-02-15 03:10:55 +0000
+++ src/security/PeerConnector.cc	2017-04-20 20:42:01 +0000
@@ -216,86 +216,89 @@
     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 '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;
-        if (SBuf *dName = (SBuf *)SSL_get_ex_data(session.get(), ssl_ex_index_server))
-            validationRequest.domainName = dName->c_str();
+        if (auto *data = Security::ExtraData::GetFrom(session)) {
+            if (!data->sni.isEmpty())
+                validationRequest.domainName = data->sni.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);
             serverConn->close();
             return true;
         }
     }
 #endif
 
     noteNegotiationDone(NULL);
     return true;
 }
 
 #if USE_OPENSSL
 void
 Security::PeerConnector::sslCrtvdHandleReply(Ssl::CertValidationResponse::Pointer validationResponse)
 {
     Must(validationResponse != NULL);
 
     Ssl::ErrorDetail *errDetails = NULL;
     bool validatorFailed = false;
     if (!Comm::IsConnOpen(serverConnection())) {
         return;
     }
 
     if (Debug::Enabled(83, 5)) {
         Security::SessionPointer ssl(fd_table[serverConnection()->fd].ssl);
-        SBuf *server = static_cast<SBuf *>(SSL_get_ex_data(ssl.get(), ssl_ex_index_server));
-        debugs(83,5, RawPointer("host", server) << " cert validation result: " << validationResponse->resultCode);
+        const auto *data = Security::ExtraData::GetFrom(ssl);
+        debugs(83,5, "host=" << data->sni << " cert validation result: " << validationResponse->resultCode);
     }
 
     if (validationResponse->resultCode == ::Helper::Error) {
         if (Security::CertErrors *errs = sslCrtvdCheckForErrors(*validationResponse, errDetails)) {
             Security::SessionPointer session(fd_table[serverConnection()->fd].ssl);
             Security::CertErrors *oldErrs = static_cast<Security::CertErrors*>(SSL_get_ex_data(session.get(), ssl_ex_index_ssl_errors));
             SSL_set_ex_data(session.get(), ssl_ex_index_ssl_errors,  (void *)errs);
             delete oldErrs;
         }
     } else if (validationResponse->resultCode != ::Helper::Okay)
         validatorFailed = true;
 
     if (!errDetails && !validatorFailed) {
         noteNegotiationDone(NULL);
         callBack();
         return;
     }
 
     ErrorState *anErr = NULL;
     if (validatorFailed) {

=== modified file 'src/ssl/PeekingPeerConnector.cc'
--- src/ssl/PeekingPeerConnector.cc	2017-02-05 05:57:32 +0000
+++ src/ssl/PeekingPeerConnector.cc	2017-04-19 06:38:34 +0000
@@ -126,90 +126,92 @@
     return Ssl::bumpSplice;
 }
 
 Security::ContextPointer
 Ssl::PeekingPeerConnector::getTlsContext()
 {
     return ::Config.ssl_client.sslContext;
 }
 
 bool
 Ssl::PeekingPeerConnector::initialize(Security::SessionPointer &serverSession)
 {
     if (!Security::PeerConnector::initialize(serverSession))
         return false;
 
     if (ConnStateData *csd = request->clientConnectionManager.valid()) {
 
         // client connection is required in the case we need to splice
         // or terminate client and server connections
         assert(clientConn != NULL);
-        SBuf *hostName = NULL;
+        SBuf hostName;
 
         //Enable Status_request TLS extension, required to bump some clients
         SSL_set_tlsext_status_type(serverSession.get(), TLSEXT_STATUSTYPE_ocsp);
 
         const Security::TlsDetails::Pointer details = csd->tlsParser.details;
         if (details && !details->serverName.isEmpty())
-            hostName = new SBuf(details->serverName);
+            hostName = details->serverName;
 
-        if (!hostName) {
+        if (hostName.isEmpty()) {
             // While we are peeking at the certificate, we may not know the server
             // name that the client will request (after interception or CONNECT)
             // unless it was the CONNECT request with a user-typed address.
             const bool isConnectRequest = !csd->port->flags.isIntercepted();
             if (!request->flags.sslPeek || isConnectRequest)
-                hostName = new SBuf(request->url.host());
+                hostName = request->url.host();
         }
 
-        if (hostName)
-            SSL_set_ex_data(serverSession.get(), ssl_ex_index_server, (void*)hostName);
+        if (hostName.isEmpty()) {
+            if (auto *data = Security::ExtraData::GetFrom(serverSession))
+                data->sni = hostName;
+        }
 
         Must(!csd->serverBump() || csd->serverBump()->step <= Ssl::bumpStep2);
         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);
 
             BIO *b = SSL_get_rbio(serverSession.get());
             Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
             Must(srvBio);
             // inherit client features such as TLS version and SNI
             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);
 
             // 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 char *sniServer = nullptr;
             const bool redirected = request->flags.redirected && ::Config.onoff.redir_rewrites_host;
-            if (!hostName || redirected)
-                sniServer = !request->url.hostIsNumeric() ? request->url.host() : NULL;
+            if (hostName.isEmpty() || redirected)
+                sniServer = !request->url.hostIsNumeric() ? request->url.host() : nullptr;
             else
-                sniServer = hostName->c_str();
+                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);
             }
         }
     }
 
     return true;
 }
 
 void
 Ssl::PeekingPeerConnector::noteNegotiationDone(ErrorState *error)

=== modified file 'src/ssl/support.cc'
--- src/ssl/support.cc	2017-02-05 05:57:32 +0000
+++ src/ssl/support.cc	2017-04-19 06:38:34 +0000
@@ -236,73 +236,74 @@
     return matchX509CommonNames(cert, (void *)server, check_domain);
 }
 
 #if (OPENSSL_VERSION_NUMBER < 0x10100000L)
 static inline X509 *X509_STORE_CTX_get0_cert(X509_STORE_CTX *ctx)
 {
     return ctx->cert;
 }
 #endif
 
 /// \ingroup ServerProtocolSSLInternal
 static int
 ssl_verify_cb(int ok, X509_STORE_CTX * ctx)
 {
     // preserve original ctx->error before SSL_ calls can overwrite it
     Security::ErrorCode error_no = ok ? SSL_ERROR_NONE : X509_STORE_CTX_get_error(ctx);
 
     char buffer[256] = "";
     SSL *ssl = (SSL *)X509_STORE_CTX_get_ex_data(ctx, SSL_get_ex_data_X509_STORE_CTX_idx());
     SSL_CTX *sslctx = SSL_get_SSL_CTX(ssl);
-    SBuf *server = (SBuf *)SSL_get_ex_data(ssl, ssl_ex_index_server);
+    Security::SessionPointer session(ssl, [](SSL *){/* do not destroy */});
+    auto *data = Security::ExtraData::GetFrom(session);
     void *dont_verify_domain = SSL_CTX_get_ex_data(sslctx, ssl_ctx_ex_index_dont_verify_domain);
     ACLChecklist *check = (ACLChecklist*)SSL_get_ex_data(ssl, ssl_ex_index_cert_error_check);
     X509 *peeked_cert = (X509 *)SSL_get_ex_data(ssl, ssl_ex_index_ssl_peeked_cert);
     Security::CertPointer peer_cert;
     peer_cert.resetAndLock(X509_STORE_CTX_get0_cert(ctx));
 
     X509_NAME_oneline(X509_get_subject_name(peer_cert.get()), buffer, sizeof(buffer));
 
     // detect infinite loops
     uint32_t *validationCounter = static_cast<uint32_t *>(SSL_get_ex_data(ssl, ssl_ex_index_ssl_validation_counter));
     if (!validationCounter) {
         validationCounter = new uint32_t(1);
         SSL_set_ex_data(ssl, ssl_ex_index_ssl_validation_counter, validationCounter);
     } else {
         // overflows allowed if SQUID_CERT_VALIDATION_ITERATION_MAX >= UINT32_MAX
         (*validationCounter)++;
     }
 
     if ((*validationCounter) >= SQUID_CERT_VALIDATION_ITERATION_MAX) {
         ok = 0; // or the validation loop will never stop
         error_no = SQUID_X509_V_ERR_INFINITE_VALIDATION;
         debugs(83, 2, "SQUID_X509_V_ERR_INFINITE_VALIDATION: " <<
                *validationCounter << " iterations while checking " << buffer);
     }
 
     if (ok) {
         debugs(83, 5, "SSL Certificate signature OK: " << buffer);
 
         // Check for domain mismatch only if the current certificate is the peer certificate.
-        if (!dont_verify_domain && server && peer_cert.get() == X509_STORE_CTX_get_current_cert(ctx)) {
-            if (!Ssl::checkX509ServerValidity(peer_cert.get(), server->c_str())) {
-                debugs(83, 2, "SQUID_X509_V_ERR_DOMAIN_MISMATCH: Certificate " << buffer << " does not match domainname " << server);
+        if (!dont_verify_domain && !data->sni.isEmpty() && peer_cert.get() == X509_STORE_CTX_get_current_cert(ctx)) {
+            if (!Ssl::checkX509ServerValidity(peer_cert.get(), data->sni.c_str())) {
+                debugs(83, 2, "SQUID_X509_V_ERR_DOMAIN_MISMATCH: Certificate " << buffer << " does not match domainname " << data->sni);
                 ok = 0;
                 error_no = SQUID_X509_V_ERR_DOMAIN_MISMATCH;
             }
         }
     }
 
     if (ok && peeked_cert) {
         // Check whether the already peeked certificate matches the new one.
         if (X509_cmp(peer_cert.get(), peeked_cert) != 0) {
             debugs(83, 2, "SQUID_X509_V_ERR_CERT_CHANGE: Certificate " << buffer << " does not match peeked certificate");
             ok = 0;
             error_no =  SQUID_X509_V_ERR_CERT_CHANGE;
         }
     }
 
     if (!ok) {
         Security::CertPointer broken_cert;
         broken_cert.resetAndLock(X509_STORE_CTX_get_current_cert(ctx));
         if (!broken_cert)
             broken_cert = peer_cert;
@@ -433,82 +434,72 @@
 /// \ingroup ServerProtocolSSLInternal
 /// Callback handler function to release STACK_OF(X509) "ex" data stored
 /// in an SSL object.
 static void
 ssl_free_CertChain(void *, void *ptr, CRYPTO_EX_DATA *,
                    int, long, void *)
 {
     STACK_OF(X509) *certsChain = static_cast <STACK_OF(X509) *>(ptr);
     sk_X509_pop_free(certsChain,X509_free);
 }
 
 // "free" function for X509 certificates
 static void
 ssl_free_X509(void *, void *ptr, CRYPTO_EX_DATA *,
               int, long, void *)
 {
     X509  *cert = static_cast <X509 *>(ptr);
     X509_free(cert);
 }
 
-// "free" function for SBuf
-static void
-ssl_free_SBuf(void *, void *ptr, CRYPTO_EX_DATA *,
-              int, long, void *)
-{
-    SBuf  *buf = static_cast <SBuf *>(ptr);
-    delete buf;
-}
-
 void
 Ssl::Initialize(void)
 {
     static bool initialized = false;
     if (initialized)
         return;
     initialized = true;
 
     SSL_load_error_strings();
     SSLeay_add_ssl_algorithms();
 
 #if HAVE_OPENSSL_ENGINE_H
     if (::Config.SSL.ssl_engine) {
         ENGINE *e;
         if (!(e = ENGINE_by_id(::Config.SSL.ssl_engine)))
             fatalf("Unable to find SSL engine '%s'\n", ::Config.SSL.ssl_engine);
 
         if (!ENGINE_set_default(e, ENGINE_METHOD_ALL)) {
             const int ssl_error = ERR_get_error();
             fatalf("Failed to initialise SSL engine: %s\n", Security::ErrorString(ssl_error));
         }
     }
 #else
     if (::Config.SSL.ssl_engine)
         fatalf("Your OpenSSL has no SSL engine support\n");
 #endif
 
     const char *defName = ::Config.SSL.certSignHash ? ::Config.SSL.certSignHash : SQUID_SSL_SIGN_HASH_IF_NONE;
     Ssl::DefaultSignHash = EVP_get_digestbyname(defName);
     if (!Ssl::DefaultSignHash)
         fatalf("Sign hash '%s' is not supported\n", defName);
 
-    ssl_ex_index_server = SSL_get_ex_new_index(0, (void *) "server", NULL, NULL, ssl_free_SBuf);
     ssl_ctx_ex_index_dont_verify_domain = SSL_CTX_get_ex_new_index(0, (void *) "dont_verify_domain", NULL, NULL, NULL);
     ssl_ex_index_cert_error_check = SSL_get_ex_new_index(0, (void *) "cert_error_check", NULL, &ssl_dupAclChecklist, &ssl_freeAclChecklist);
     ssl_ex_index_ssl_error_detail = SSL_get_ex_new_index(0, (void *) "ssl_error_detail", NULL, NULL, &ssl_free_ErrorDetail);
     ssl_ex_index_ssl_peeked_cert  = SSL_get_ex_new_index(0, (void *) "ssl_peeked_cert", NULL, NULL, &ssl_free_X509);
     ssl_ex_index_ssl_errors =  SSL_get_ex_new_index(0, (void *) "ssl_errors", NULL, NULL, &ssl_free_SslErrors);
     ssl_ex_index_ssl_cert_chain = SSL_get_ex_new_index(0, (void *) "ssl_cert_chain", NULL, NULL, &ssl_free_CertChain);
     ssl_ex_index_ssl_validation_counter = SSL_get_ex_new_index(0, (void *) "ssl_validation_counter", NULL, NULL, &ssl_free_int);
     ssl_ex_index_ssl_untrusted_chain = SSL_get_ex_new_index(0, (void *) "ssl_untrusted_chain", NULL, NULL, &ssl_free_CertChain);
 }
 
 static bool
 configureSslContext(Security::ContextPointer &ctx, AnyP::PortCfg &port)
 {
     int ssl_error;
     SSL_CTX_set_options(ctx.get(), port.secure.parsedOptions);
 
     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) {

_______________________________________________
squid-dev mailing list
[email protected]
http://lists.squid-cache.org/listinfo/squid-dev

Reply via email to