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