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