]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Honor ssl-bump option for https_port.
authorAlex Rousskov <rousskov@measurement-factory.com>
Sat, 3 Dec 2011 22:45:38 +0000 (15:45 -0700)
committerAlex Rousskov <rousskov@measurement-factory.com>
Sat, 3 Dec 2011 22:45:38 +0000 (15:45 -0700)
Initial ssl-bump handling logic mimics that of http_port: If the option is
set, check the slow ssl_bump ACL, and if there is a match, plug into
switchToHttps() code path, generating a dynamic certificate and establishing a
secure connection with the client. If there is no match, Squid becomes a TCP
tunnel for the intercepted connection.

For now, we use the destination IP address of the intercepted connection as
the host name for the certificate (which will trigger browser warnings, of
course).

src/cache_cf.cc
src/cf.data.pre
src/client_side.cc
src/client_side_request.cc
src/tunnel.cc

index 610ab87120dd1c6418aa7f9631be7c6082b41dd4..0234ec13fd0169a8c2942930f8ba66341625c3df 100644 (file)
@@ -4054,6 +4054,16 @@ parse_https_port_list(https_port_list ** head)
         parse_http_port_option(s, token);
     }
 
+    /* ssl-bump requires tproxy and vice versa */
+    if (s->sslBump && !s->spoof_client_ip) {
+        debugs(3, DBG_CRITICAL, "FATAL: ssl-bump on https_port requires tproxy which is missing.");
+        self_destruct();
+    }
+    if (s->spoof_client_ip && !s->sslBump) {
+        debugs(3, DBG_CRITICAL, "FATAL: tproxy on https_port requires ssl-bump which is missing.");
+        self_destruct();
+    }
+
     while (*head) {
         http_port_list ** headTmp = &(*head)->http.next;
         head = (https_port_list **)headTmp;
index 434e94b6d6e6c71b3b3c591923fd43506e1105db..e400801cec64d70d91bd0eb4e668fed1a2e871aa 100644 (file)
@@ -1503,6 +1503,21 @@ DOC_START
 
           accel        Accelerator / reverse proxy mode
 
+          tproxy       Support Linux TPROXY for spoofing outgoing
+                       connections using the client IP address.
+                       NP: disables authentication and maybe IPv6 on the port.
+
+          ssl-bump     Intercept each SSL request matching ssl_bump ACL,
+                       establish secure connection with the client and with
+                       the server, decrypt HTTP messages as they pass through
+                       Squid, and treat them as unencrypted HTTP messages,
+                       becoming the man-in-the-middle.
+
+                       The ssl_bump option is required to fully enable
+                       the SslBump feature.
+
+                       Requires tproxy.
+
        Omitting the mode flag causes default forward proxy mode to be used.
 
 
@@ -1573,6 +1588,25 @@ DOC_START
 
           sslcontext=  SSL session ID context identifier.
 
+          generate-host-certificates[=<on|off>]
+                       Dynamically create SSL server certificates for the
+                       destination hosts of bumped SSL requests.When
+                       enabled, the cert and key options are used to sign
+                       generated certificates. Otherwise generated
+                       certificate will be selfsigned.
+                       If there is CA certificate life time of generated
+                       certificate equals lifetime of CA certificate. If
+                       generated certificate is selfsigned lifetime is three
+                       years.
+                       This option is enabled by default when SslBump is used.
+                       See the sslBump option above for more information.
+
+          dynamic_cert_mem_cache_size=SIZE
+                       Approximate total RAM size spent on cached generated
+                       certificates. If set to zero, caching is disabled. The
+                       default value is 4MB. An average XXX-bit certificate
+                       consumes about XXX bytes of RAM.
+
 DOC_END
 
 NAME: tcp_outgoing_tos tcp_outgoing_ds tcp_outgoing_dscp
index 61e844ec4324c1438f47ae6ec8fe23a5179348c4..22a13bf7e63da5666d42a10d8e575b598355b397 100644 (file)
@@ -2089,15 +2089,15 @@ prepareTransparentURL(ConnStateData * conn, ClientHttpRequest *http, char *url,
         int url_sz = strlen(url) + 32 + Config.appendDomainLen +
                      strlen(host);
         http->uri = (char *)xcalloc(url_sz, 1);
-        snprintf(http->uri, url_sz, "http://%s%s", /*conn->port->protocol,*/ host, url);
+        snprintf(http->uri, url_sz, "%s://%s%s", conn->port->protocol, host, url);
         debugs(33, 5, "TRANSPARENT HOST REWRITE: '" << http->uri <<"'");
     } else {
         /* Put the local socket IP address as the hostname.  */
         int url_sz = strlen(url) + 32 + Config.appendDomainLen;
         http->uri = (char *)xcalloc(url_sz, 1);
         http->getConn()->clientConnection->local.ToHostname(ipbuf,MAX_IPSTRLEN),
-        snprintf(http->uri, url_sz, "http://%s:%d%s",
-                 // http->getConn()->port->protocol,
+        snprintf(http->uri, url_sz, "%s://%s:%d%s",
+                 http->getConn()->port->protocol,
                  ipbuf, http->getConn()->clientConnection->local.GetPort(), url);
         debugs(33, 5, "TRANSPARENT REWRITE: '" << http->uri << "'");
     }
@@ -2541,8 +2541,8 @@ clientProcessRequest(ConnStateData *conn, HttpParser *hp, ClientSocketContext *c
      * from the port settings to the request.
      */
     if (http->clientConnection != NULL) {
-        request->flags.intercepted = (http->clientConnection->flags & COMM_INTERCEPTION);
-        request->flags.spoof_client_ip = (http->clientConnection->flags & COMM_TRANSPARENT);
+        request->flags.intercepted = ((http->clientConnection->flags & COMM_INTERCEPTION) != 0);
+        request->flags.spoof_client_ip = ((http->clientConnection->flags & COMM_TRANSPARENT) != 0 ) ;
     }
 
     if (internalCheck(request->urlpath.termedBuf())) {
@@ -3411,6 +3411,71 @@ clientNegotiateSSL(int fd, void *data)
     conn->readSomeData();
 }
 
+/**
+ * Initializes an ssl connection to squid. 
+ * In the case the SSL_CTX is not given it calls the ConnStateData::switchToHttps method
+ * to start procedure to generate a dynamic SSL_CTX
+ */
+static void
+httpsEstablish(ConnStateData *connState,  SSL_CTX *sslContext)
+{
+    SSL *ssl = NULL;
+    assert(connState);
+    const Comm::ConnectionPointer &details = connState->clientConnection;
+
+    if (sslContext && !(ssl = httpsCreate(details, sslContext)))
+        return;
+
+     typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
+    AsyncCall::Pointer timeoutCall =  JobCallback(33, 5,
+                                      TimeoutDialer, connState, ConnStateData::requestTimeout);
+    commSetConnTimeout(details, Config.Timeout.request, timeoutCall);
+
+    if (ssl) 
+        Comm::SetSelect(details->fd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
+    else {
+        char buf[MAX_IPSTRLEN];
+        debugs(33, 4, HERE << details << " try to generate a Dynamic SSL CTX");
+        connState->switchToHttps(details->local.NtoA(buf, sizeof(buf)));
+    }
+}
+
+/**
+ * A callback function to use with the ACLFilledChecklist callback. 
+ * In the case of ACCES_ALLOWED answer initializes an ssl bumped connection,
+ * else revert the connection to tunnel mode.
+ */
+static void
+httpsSslBumpAccessCheckDone(allow_t answer, void *data)
+{
+    ConnStateData *connState = (ConnStateData *) data;
+
+    //if connection closed/closing just return.
+    if (!connState->isOpen())
+        return;
+
+    if (answer == ACCESS_ALLOWED) {
+        debugs(33, 2, HERE << " sslBump done data: " << connState->clientConnection);
+        httpsEstablish(connState, NULL);
+    } else {
+        // fake a CONNECT request to force connState to tunnel
+
+        debugs(33, 2, HERE << " normal SSL connection: " << connState->clientConnection << " revert to tunnel mode");
+        static char ip[MAX_IPSTRLEN];
+        static char reqStr[MAX_IPSTRLEN + 80];
+        connState->clientConnection->local.NtoA(ip, sizeof(ip));
+        snprintf(reqStr, sizeof(reqStr), "CONNECT %s:%d\r\n\r\n", ip, connState->clientConnection->local.GetPort());
+        bool ret = connState->handleReadData(reqStr, strlen(reqStr));
+        if (ret)
+            ret = connState->clientParseRequests();
+
+        if (!ret) {
+            debugs(33, 2, HERE << "Failed to start fake CONNECT request for ssl bumped connection: " << connState->clientConnection);
+            connState->clientConnection->close();
+        }
+    }
+}
+
 /** handle a new HTTPS connection */
 static void
 httpsAccept(const CommAcceptCbParams &params)
@@ -3423,11 +3488,6 @@ httpsAccept(const CommAcceptCbParams &params)
         return;
     }
 
-    SSL_CTX *sslContext = s->staticSslContext.get();
-    SSL *ssl = NULL;
-    if (!(ssl = httpsCreate(params.conn, sslContext)))
-        return;
-
     debugs(33, 4, HERE << params.conn << " accepted, starting SSL negotiation.");
     fd_note(params.conn->fd, "client https connect");
 
@@ -3440,12 +3500,33 @@ httpsAccept(const CommAcceptCbParams &params)
     // Socket is ready, setup the connection manager to start using it
     ConnStateData *connState = connStateCreate(params.conn, &s->http);
 
-    typedef CommCbMemFunT<ConnStateData, CommTimeoutCbParams> TimeoutDialer;
-    AsyncCall::Pointer timeoutCall =  JobCallback(33, 5,
-                                      TimeoutDialer, connState, ConnStateData::requestTimeout);
-    commSetConnTimeout(params.conn, Config.Timeout.request, timeoutCall);
+    if (s->sslBump) {
+        assert(params.conn->flags & COMM_TRANSPARENT);
+
+        debugs(33, 5, "httpsAccept: accept transparent connection: " << params.conn);
 
-    Comm::SetSelect(params.conn->fd, COMM_SELECT_READ, clientNegotiateSSL, connState, 0);
+        if (!Config.accessList.ssl_bump) {
+            httpsSslBumpAccessCheckDone(ACCESS_DENIED, connState);
+            return;
+        }
+  
+        // Create a fake HTTP request for ssl_bump ACL check,
+        // using tproxy-provided destination IP and port.
+        HttpRequest *request = new HttpRequest();
+        static char ip[MAX_IPSTRLEN];
+        request->SetHost(params.conn->local.NtoA(ip, sizeof(ip)));
+        request->port = params.conn->local.GetPort();
+        request->myportname = s->name;
+        ACLFilledChecklist *acl_checklist = new ACLFilledChecklist(Config.accessList.ssl_bump, request, NULL);
+        acl_checklist->src_addr = params.conn->remote;
+        acl_checklist->my_addr = s->s;
+        acl_checklist->nonBlockingCheck(httpsSslBumpAccessCheckDone, connState);
+        return;
+    } else {
+        SSL_CTX *sslContext = s->staticSslContext.get();
+        httpsEstablish(connState, sslContext);
+    }
 }
 
 void
index d4bbe39ce369a925986c46c4bb491681eba83d14..27b8e842d7339d3fd6541441472f4b43e210c2ce 100644 (file)
@@ -1257,6 +1257,7 @@ bool
 ClientRequestContext::sslBumpAccessCheck()
 {
     if (http->request->method == METHOD_CONNECT &&
+            !http->request->flags.spoof_client_ip && // is not a fake ssl-bumped request from a https port
             Config.accessList.ssl_bump && http->getConn()->port->sslBump) {
         debugs(85, 5, HERE << "SslBump possible, checking ACL");
 
index 3344f799260ffa1df4f9f742df1dc87c7440483d..03a1d814eb76f4160a48edd42c3367220bce7b60 100644 (file)
@@ -479,11 +479,24 @@ TunnelStateData::copyRead(Connection &from, IOCB *completion)
     comm_read(from.conn, from.buf, from.bytesWanted(1, SQUID_TCP_SO_RCVBUF), call);
 }
 
+/**
+ * Set the HTTP status for this request and sets the read handlers for client 
+ * and server side connections.
+ */
+static void
+tunnelStartShoveling(TunnelStateData *tunnelState)
+{
+    *tunnelState->status_ptr = HTTP_OK;
+    if (cbdataReferenceValid(tunnelState)) {
+        tunnelState->copyRead(tunnelState->server, TunnelStateData::ReadServer);
+        tunnelState->copyRead(tunnelState->client, TunnelStateData::ReadClient);
+    }
+}
+
 /**
  * All the pieces we need to write to client and/or server connection
  * have been written.
- * - Set the HTTP status for this request.
- * - Start the blind pump.
+ * Call the tunnelStartShoveling to start the blind pump.
  */
 static void
 tunnelConnectedWriteDone(const Comm::ConnectionPointer &conn, char *buf, size_t size, comm_err_t flag, int xerrno, void *data)
@@ -497,11 +510,7 @@ tunnelConnectedWriteDone(const Comm::ConnectionPointer &conn, char *buf, size_t
         return;
     }
 
-    *tunnelState->status_ptr = HTTP_OK;
-    if (cbdataReferenceValid(tunnelState)) {
-        tunnelState->copyRead(tunnelState->server, TunnelStateData::ReadServer);
-        tunnelState->copyRead(tunnelState->client, TunnelStateData::ReadClient);
-    }
+    tunnelStartShoveling(tunnelState);
 }
 
 /*
@@ -512,9 +521,14 @@ tunnelConnected(const Comm::ConnectionPointer &server, void *data)
 {
     TunnelStateData *tunnelState = (TunnelStateData *)data;
     debugs(26, 3, HERE << server << ", tunnelState=" << tunnelState);
-    AsyncCall::Pointer call = commCbCall(5,5, "tunnelConnectedWriteDone",
-                                         CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState));
-    Comm::Write(tunnelState->client.conn, conn_established, strlen(conn_established), call, NULL);
+
+    if (tunnelState->request && tunnelState->request->flags.spoof_client_ip)
+        tunnelStartShoveling(tunnelState); // ssl-bumped connection, be quiet
+    else {
+        AsyncCall::Pointer call = commCbCall(5,5, "tunnelConnectedWriteDone",
+                                             CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState));
+        Comm::Write(tunnelState->client.conn, conn_established, strlen(conn_established), call, NULL);
+    }
 }
 
 static void