]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/security/PeerConnector.cc
transaction_initiator ACL for detecting various unusual transactions
[thirdparty/squid.git] / src / security / PeerConnector.cc
index 14bbbcd07a04185ca401a45dc86c855a70857dd6..4b3b6ceaca519989246bc765f35cdba232174a62 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
+ * 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.
@@ -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 &params)
 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<Security::PeerConnector, CommCloseCbParams> Dialer;
     closeHandler = JobCallback(9, 5, Dialer, this, Security::PeerConnector::commCloseHandler);
-    comm_add_close_handler(fd, closeHandler);
+    comm_add_close_handler(serverConnection()->fd, closeHandler);
     return true;
 }
 
 bool
 Security::PeerConnector::initialize(Security::SessionPointer &serverSession)
 {
-#if USE_OPENSSL
     Security::ContextPointer ctx(getTlsContext());
-    assert(ctx);
+    debugs(83, 5, serverConnection() << ", ctx=" << (void*)ctx.get());
 
-    if (!Ssl::CreateClient(ctx, serverConnection(), "server https start")) {
+    if (!ctx || !Security::CreateClientSession(ctx, serverConnection(), "server https start")) {
         const auto xerrno = errno;
-        const auto ssl_error = ERR_get_error();
+        if (!ctx) {
+            debugs(83, DBG_IMPORTANT, "Error initializing TLS connection: No security context.");
+        } // else CreateClientSession() did the appropriate debugs() already
         ErrorState *anErr = new ErrorState(ERR_SOCKET_FAILURE, Http::scInternalServerError, request.getRaw());
         anErr->xerrno = xerrno;
-        debugs(83, DBG_IMPORTANT, "Error allocating TLS handle: " << Security::ErrorString(ssl_error));
         noteNegotiationDone(anErr);
         bail(anErr);
         return false;
@@ -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
@@ -162,7 +166,7 @@ Security::PeerConnector::recordNegotiationDetails()
 #if USE_OPENSSL
     // retrieve TLS parsed extra info
     BIO *b = SSL_get_rbio(session.get());
-    Ssl::ServerBio *bio = static_cast<Ssl::ServerBio *>(b->ptr);
+    Ssl::ServerBio *bio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
     if (const Security::TlsDetails::Pointer &details = bio->receivedHelloDetails())
         serverConnection()->tlsNegotiations()->retrieveParsedInfo(details);
 #endif
@@ -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<Security::CertErrors *>(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,23 +406,53 @@ 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());
-    Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
+    Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
     if (srvBio->holdRead()) {
         if (srvBio->gotHello()) {
             if (checkForMissingCertificates())
@@ -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,29 +487,28 @@ 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)
-        anErr = ErrorState::NewForwarding(ERR_SECURE_CONNECT_FAIL, request.getRaw());
-    else
-        anErr = new ErrorState(ERR_SECURE_CONNECT_FAIL, Http::scServiceUnavailable, NULL);
+    ErrorState *anErr = ErrorState::NewForwarding(ERR_SECURE_CONNECT_FAIL, request);
     anErr->xerrno = sysErrNo;
 
+#if USE_OPENSSL
     Security::SessionPointer session(fd_table[fd].ssl);
     Ssl::ErrorDetail *errFromFailure = static_cast<Ssl::ErrorDetail *>(SSL_get_ex_data(session.get(), ssl_ex_index_ssl_error_detail));
     if (errFromFailure != NULL) {
@@ -471,10 +525,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 +552,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.
@@ -573,7 +629,7 @@ Security::PeerConnector::startCertDownloading(SBuf &url)
                                       PeerConnectorCertDownloaderDialer(&Security::PeerConnector::certDownloadingDone, this));
 
     const Downloader *csd = (request ? dynamic_cast<const Downloader*>(request->downloader.valid()) : nullptr);
-    Downloader *dl = new Downloader(url, certCallback, csd ? csd->nestedLevel() + 1 : 1);
+    Downloader *dl = new Downloader(url, certCallback, XactionInitiator::initCertFetcher, csd ? csd->nestedLevel() + 1 : 1);
     AsyncJob::Start(dl);
 }
 
@@ -587,7 +643,7 @@ Security::PeerConnector::certDownloadingDone(SBuf &obj, int downloadStatus)
     const int fd = serverConnection()->fd;
     Security::SessionPointer session(fd_table[fd].ssl);
     BIO *b = SSL_get_rbio(session.get());
-    Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
+    Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
 
     // Parse Certificate. Assume that it is in DER format.
     // According to RFC 4325:
@@ -633,7 +689,7 @@ Security::PeerConnector::checkForMissingCertificates()
     const int fd = serverConnection()->fd;
     Security::SessionPointer session(fd_table[fd].ssl);
     BIO *b = SSL_get_rbio(session.get());
-    Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(b->ptr);
+    Ssl::ServerBio *srvBio = static_cast<Ssl::ServerBio *>(BIO_get_data(b));
     const Security::CertList &certs = srvBio->serverCertificatesIfAny();
 
     if (certs.size()) {