]> git.ipfire.org Git - thirdparty/squid.git/blobdiff - src/security/PeerConnector.cc
Supply AccessLogEntry (ALE) for more fast ACL checks. (#182)
[thirdparty/squid.git] / src / security / PeerConnector.cc
index 3f5a256d6169b02b4857cd745ed68b1196fc696e..4c63c5d87a37d6d4b7735ba7affeab63f75d8f06 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Copyright (C) 1996-2016 The Squid Software Foundation and contributors
+ * Copyright (C) 1996-2018 The Squid Software Foundation and contributors
  *
  * Squid software is distributed under GPLv2+ license and includes
  * contributions from numerous individuals and organizations.
@@ -131,6 +131,7 @@ Security::PeerConnector::initialize(Security::SessionPointer &serverSession)
         if (acl_access *acl = ::Config.ssl_client.cert_error) {
             ACLFilledChecklist *check = new ACLFilledChecklist(acl, request.getRaw(), dash_str);
             check->al = al;
+            check->syncAle(request.getRaw(), nullptr);
             // check->fd(fd); XXX: need client FD here
             SSL_set_ex_data(serverSession.get(), ssl_ex_index_cert_error_check, check);
         }
@@ -166,7 +167,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
@@ -189,10 +190,21 @@ Security::PeerConnector::negotiate()
     if (result <= 0) {
 #elif USE_GNUTLS
     auto session = fd_table[fd].ssl.get();
-    debugs(83, 5, "gnutls_handshake session=" << (void*)session);
     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) {
-        debugs(83, 5, "gnutls_handshake session=" << (void*)session << ", result=" << result);
+        // 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
     if (const int result = -1) {
 #endif
@@ -230,7 +242,7 @@ Security::PeerConnector::sslFinalized()
         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);
+            Ssl::CertValidationHelper::Submit(validationRequest, call);
             return false;
         } catch (const std::exception &e) {
             debugs(83, DBG_IMPORTANT, "ERROR: Failed to compose ssl_crtvd " <<
@@ -313,6 +325,7 @@ Security::PeerConnector::sslCrtvdCheckForErrors(Ssl::CertValidationResponse cons
     if (acl_access *acl = ::Config.ssl_client.cert_error) {
         check = new ACLFilledChecklist(acl, request.getRaw(), dash_str);
         check->al = al;
+        check->syncAle(request.getRaw(), nullptr);
     }
 
     Security::CertErrors *errs = nullptr;
@@ -327,7 +340,7 @@ Security::PeerConnector::sslCrtvdCheckForErrors(Ssl::CertValidationResponse cons
             bool allowed = false;
             if (check) {
                 check->sslErrors = new Security::CertErrors(Security::CertError(i->error_no, i->cert, i->error_depth));
-                if (check->fastCheck() == ACCESS_ALLOWED)
+                if (check->fastCheck().allowed())
                     allowed = true;
             }
             // else the Config.ssl_client.cert_error access list is not defined
@@ -364,9 +377,18 @@ Security::PeerConnector::sslCrtvdCheckForErrors(Ssl::CertValidationResponse cons
 void
 Security::PeerConnector::NegotiateSsl(int, void *data)
 {
-    PeerConnector *pc = static_cast<Security::PeerConnector *>(data);
+    const auto pc = static_cast<PeerConnector::Pointer*>(data);
+    if (pc->valid())
+        (*pc)->negotiateSsl();
+    delete pc;
+}
+
+/// Comm::SetSelect() callback. Direct calls tickle/resume negotiations.
+void
+Security::PeerConnector::negotiateSsl()
+{
     // Use job calls to add done() checks and other job logic/protections.
-    CallJobHere(83, 7, pc, Security::PeerConnector, negotiate);
+    CallJobHere(83, 7, this, Security::PeerConnector, negotiate);
 }
 
 void
@@ -404,17 +426,17 @@ Security::PeerConnector::handleNegotiateError(const int 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
+        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)
-            noteWantWrite();
-        else
             noteWantRead();
+        else
+            noteWantWrite();
         return;
 
     default:
@@ -437,10 +459,11 @@ 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())
@@ -448,26 +471,27 @@ Security::PeerConnector::noteWantRead()
 
             srvBio->holdRead(false);
             // schedule a negotiateSSl to allow openSSL parse received data
-            Security::PeerConnector::NegotiateSsl(fd, this);
+            negotiateSsl();
             return;
         } else if (srvBio->gotHelloFailed()) {
             srvBio->holdRead(false);
             debugs(83, DBG_IMPORTANT, "Error parsing SSL Server Hello Message on FD " << fd);
             // schedule a negotiateSSl to allow openSSL parse received data
-            Security::PeerConnector::NegotiateSsl(fd, this);
+            negotiateSsl();
             return;
         }
     }
 #endif
     setReadTimeout();
-    Comm::SetSelect(fd, COMM_SELECT_READ, &NegotiateSsl, this, 0);
+    Comm::SetSelect(fd, COMM_SELECT_READ, &NegotiateSsl, new Pointer(this), 0);
 }
 
 void
 Security::PeerConnector::noteWantWrite()
 {
     const int fd = serverConnection()->fd;
-    Comm::SetSelect(fd, COMM_SELECT_WRITE, &NegotiateSsl, this, 0);
+    debugs(83, 5, serverConnection());
+    Comm::SetSelect(fd, COMM_SELECT_WRITE, &NegotiateSsl, new Pointer(this), 0);
     return;
 }
 
@@ -485,17 +509,14 @@ Security::PeerConnector::noteNegotiationError(const int ret, const int ssl_error
     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 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
@@ -537,13 +558,6 @@ Security::PeerConnector::bail(ErrorState *error)
     // the recepient before the fd-closure notification), but we would rather
     // minimize the number of fd-closure notifications and let the recepient
     // manage the TCP state of the connection.
-
-#if USE_GNUTLS
-    // but we do need to release the bad TLS related details in fd_table
-    // ... or GnuTLS will SEGFAULT.
-    const int fd = serverConnection()->fd;
-    Security::SessionClose(fd_table[fd].ssl, fd);
-#endif
 }
 
 void
@@ -626,7 +640,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);
 }
 
@@ -640,7 +654,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:
@@ -653,8 +667,9 @@ Security::PeerConnector::certDownloadingDone(SBuf &obj, int downloadStatus)
     if (X509 *cert = d2i_X509(NULL, &raw, obj.length())) {
         char buffer[1024];
         debugs(81, 5, "Retrieved certificate: " << X509_NAME_oneline(X509_get_subject_name(cert), buffer, 1024));
+        ContextPointer ctx(getTlsContext());
         const Security::CertList &certsList = srvBio->serverCertificatesIfAny();
-        if (const char *issuerUri = Ssl::uriOfIssuerIfMissing(cert,  certsList)) {
+        if (const char *issuerUri = Ssl::uriOfIssuerIfMissing(cert, certsList, ctx)) {
             urlsOfMissingCerts.push(SBuf(issuerUri));
         }
         Ssl::SSL_add_untrusted_cert(session.get(), cert);
@@ -669,7 +684,7 @@ Security::PeerConnector::certDownloadingDone(SBuf &obj, int downloadStatus)
     }
 
     srvBio->holdRead(false);
-    Security::PeerConnector::NegotiateSsl(serverConnection()->fd, this);
+    negotiateSsl();
 }
 
 bool
@@ -686,12 +701,13 @@ 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()) {
         debugs(83, 5, "SSL server sent " << certs.size() << " certificates");
-        Ssl::missingChainCertificatesUrls(urlsOfMissingCerts, certs);
+        ContextPointer ctx(getTlsContext());
+        Ssl::missingChainCertificatesUrls(urlsOfMissingCerts, certs, ctx);
         if (urlsOfMissingCerts.size()) {
             startCertDownloading(urlsOfMissingCerts.front());
             urlsOfMissingCerts.pop();