On 8/12/2011 11:58 a.m., Amos Jeffries wrote:
On Wed, 07 Dec 2011 18:59:15 +0200, Tsantilas Christos wrote:
On 12/07/2011 01:19 PM, Amos Jeffries wrote:
So, in theory ssl-bump is a form of intercept not a form of
accelerator.  Its use of prepareAcceleratorURL() to convert the partial
to absolute URL unconditionally sets the accel flag with a mix of side
effects. Some bad ones have been identified already.

This patch changes the flag setting, to allow ssl-bump to use the URL
preparation function without the side effects. I'm in half a mind to
make a ssl-bump specific URL preparation function, but only after this
is proven workable.

Christos: as the person who appears to have the best testing ability for
ssl-bump can you run your tests over the resulting Squid and check that
the expected behaviours have not changed for the worse? I am fully
expecting there to be several as yet unknown places needing to add a
test of the sslBumped flag alongside testing accel flag.

Looks OK.
Also because accel and related flags are used in http authentication and
esi, I think too it is better to detach it from sslbumped requests,
where we should handle authentication with a different way...

The only objection is for the Config.onoff.ie_refresh parameter.
Look in the client_side_request.cc line 1027 inside block:
if (Config.onoff.ie_refresh) {
}

But does not look important....

Hmm, maybe. The docs around that code are incorrect "or transparent proxy" is not one of the cases it kicks in. Only (accel && ims). We can either drop it or need to add all of sslbump, intercept, tproxy, and accel flags.

Since it is so screwed I think we can leave it for now and fix separately.


Thank you for the check. I have moved on to splitting the prepare*() function now.

On the SSL intercept case we don't have any backup host:port that I can see easily. If it is saved somewhere please let me know.

For now the order of hot:port locating is:
- use bumped requests Host: header, else
- use CONNECT authority-URL, (http->sslHostName contains the host:port from the CONNECT right?) - reject with "400 Invalid Request" (RFC 2616 mandated response on missing Host:)

I have also added a test case to the transparent intercept case to default port-443 received traffic through this new https:// URL emitting function. port-80 and unknown ports through the old function emitting http:// URLs.

Amos

Here is the mk2 patch. With better re-assembly of the absolute URL for ssl-bump and SSL interception case as well.

The only thing I'm worried about now is that strange ports on CONNECT request being ssl-bumped might be lost with sslHostName. The same is assumed for intercepted SSL, so its not a huge blocker, but could be a problem for some. Ideas?

Amos
=== modified file 'src/client_side.cc'
--- src/client_side.cc  2011-12-06 14:06:38 +0000
+++ src/client_side.cc  2011-12-08 13:22:33 +0000
@@ -1986,6 +1986,7 @@
     }
 }
 
+/// Convert a partial-URL into an absolute URL when it has been received on a 
reverse-proxy (accel) configured port
 static void
 prepareAcceleratedURL(ConnStateData * conn, ClientHttpRequest *http, char 
*url, const char *req_hdr)
 {
@@ -1994,8 +1995,6 @@
     char *host;
     char ipbuf[MAX_IPSTRLEN];
 
-    http->flags.accel = 1;
-
     /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
 
     if (strncasecmp(url, "cache_object://", 15) == 0)
@@ -2035,9 +2034,7 @@
     if (vport < 0)
         vport = http->getConn()->clientConnection->local.GetPort();
 
-    const bool switchedToHttps = conn->switchedToHttps();
-    const bool tryHostHeader = vhost || switchedToHttps;
-    if (tryHostHeader && (host = mime_get_header(req_hdr, "Host")) != NULL) {
+    if (vhost && (host = mime_get_header(req_hdr, "Host")) != NULL) {
         debugs(33, 5, "ACCEL VHOST REWRITE: vhost=" << host << " + vport=" << 
vport);
         char thost[256];
         if (vport > 0) {
@@ -2055,9 +2052,7 @@
         int url_sz = strlen(url) + 32 + Config.appendDomainLen +
                      strlen(host);
         http->uri = (char *)xcalloc(url_sz, 1);
-        const char *protocol = switchedToHttps ?
-                               "https" : conn->port->protocol;
-        snprintf(http->uri, url_sz, "%s://%s%s", protocol, host, url);
+        snprintf(http->uri, url_sz, "%s://%s%s", conn->port->protocol, host, 
url);
         debugs(33, 5, "ACCEL VHOST REWRITE: '" << http->uri << "'");
     } else if (conn->port->defaultsite /* && !vhost */) {
         debugs(33, 5, "ACCEL DEFAULTSITE REWRITE: defaultsite=" << 
conn->port->defaultsite << " + vport=" << vport);
@@ -2085,8 +2080,9 @@
     }
 }
 
+/// Convert a partial-URL into an absolute http:// URL when it has been 
intercepted from port 80
 static void
-prepareTransparentURL(ConnStateData * conn, ClientHttpRequest *http, char 
*url, const char *req_hdr)
+prepareInterceptedHttpURL(ConnStateData * conn, ClientHttpRequest *http, char 
*url, const char *req_hdr)
 {
     char *host;
     char ipbuf[MAX_IPSTRLEN];
@@ -2095,13 +2091,13 @@
         return; /* already in good shape */
 
     /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
-    // BUG 2976: Squid only accepts intercepted HTTP.
+    // BUG 2976: Assume http:// scheme if it gets here.
 
     if ((host = mime_get_header(req_hdr, "Host")) != NULL) {
         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, "http://%s%s";, host, url);
         debugs(33, 5, "TRANSPARENT HOST REWRITE: '" << http->uri <<"'");
     } else {
         /* Put the local socket IP address as the hostname.  */
@@ -2109,12 +2105,57 @@
         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,
                  ipbuf, http->getConn()->clientConnection->local.GetPort(), 
url);
         debugs(33, 5, "TRANSPARENT REWRITE: '" << http->uri << "'");
     }
 }
 
+/// Convert a partial-URL into an absolute https:// URL when it has been 
intercepted from port 443 or a decrypted CONNECT request
+static void
+prepareInterceptedHttpsURL(ConnStateData * conn, ClientHttpRequest *http, char 
*url, const char *req_hdr)
+{
+    if (*url != '/')
+        return; /* already in good shape */
+
+    /* BUG: Squid cannot deal with '*' URLs (RFC2616 5.1.2) */
+    // BUG 2976: Assume https:// scheme if it gets here.
+
+    const char *host;
+    if ((host = mime_get_header(req_hdr, "Host")) != NULL) {
+        int url_sz = strlen(url) + 32 + Config.appendDomainLen + strlen(host);
+        http->uri = (char *)xcalloc(url_sz, 1);
+        snprintf(http->uri, url_sz, "https://%s%s";, host, url);
+        debugs(33, 5, "SSL INTERCEPT HOST REWRITE: '" << http->uri << "'");
+
+    } else if(conn->sslHostName.size()>0) {
+        // Put the CONNECT request authority details (host:port) as the 
hostname.
+        int url_sz = strlen(url) + 32 + Config.appendDomainLen + 
conn->sslHostName.size();
+        http->uri = (char *)xcalloc(url_sz, 1);
+        snprintf(http->uri, url_sz, "https://"; SQUIDSTRINGPH "%s", 
SQUIDSTRINGPRINT(conn->sslHostName), url);
+        debugs(33, 5, "SSL INTERCEPT REWRITE: '" << http->uri << "'");
+
+    } else {
+        // Intercepted traffic with no Host: is unable to be safely repaired 
(yet, maybe use SNI later).
+        // RFC 2616 section 14.32 'MUST respond with a 400'
+        clientStreamNode *node = (clientStreamNode 
*)http->client_stream.tail->prev->data;
+        clientReplyContext *repContext = dynamic_cast<clientReplyContext 
*>(node->data.getRaw());
+        assert(repContext);
+        repContext->setReplyToError(ERR_INVALID_REQ, HTTP_BAD_REQUEST,
+                                    http->request->method, NULL,
+                                    http->getConn()->clientConnection->remote,
+                                    http->request,
+                                    NULL,
+#if USE_AUTH
+                                    http->getConn() != NULL && 
http->getConn()->auth_user_request != NULL ?
+                                    http->getConn()->auth_user_request : 
http->request->auth_user_request);
+#else
+                                    NULL);
+#endif
+        node = (clientStreamNode *)http->client_stream.tail->data;
+        clientStreamRead(node, http, node->readBuffer);
+    }
+}
+
 /**
  *  parseHttpRequest()
  *
@@ -2264,25 +2305,48 @@
 
 #endif
 
-    debugs(33,5, HERE << "repare absolute URL from " << 
(csd->transparent()?"intercept":(csd->port->accel ? "accel":"")));
+    debugs(33, 5, "parseHttpRequest: Complete request received");
+
+    // XXX: crop this dump at the end of headers. No need for extras
+    debugs(11, 2, "HTTP Client " << csd->clientConnection);
+    debugs(11, 2, "HTTP Client REQUEST:\n---------\n" << (hp->buf) + 
hp->req.m_start << "\n----------");
+
+
+    debugs(33,5, HERE << "Repair absolute URL from " << 
(csd->switchedToHttps()?"SSL ":"") <<
+           (csd->transparent()?"intercept":(csd->port->accel ? "accel":"")));
     /* Rewrite the URL in transparent or accelerator mode */
     /* NP: there are several cases to traverse here:
      *  - standard mode (forward proxy)
-     *  - transparent mode (TPROXY)
+     *  - transparent mode (TPROXY port 80)
+     *  - transparent mode (TPROXY port 443)
      *  - transparent mode with failures
-     *  - intercept mode (NAT)
+     *  - intercept mode (NAT port 80)
+     *  - intercept mode (NAT port 443)
+     *  - intercept mode (SSL CONNECT requests)
      *  - intercept mode with failures
      *  - accelerator mode (reverse proxy)
      *  - internal URL
      *  - mixed combos of the above with internal URL
      */
     if (csd->transparent()) {
-        /* intercept or transparent mode, properly working with no failures */
-        prepareTransparentURL(csd, http, url, req_hdr);
-
-    } else if (csd->port->accel || csd->switchedToHttps()) {
+        /* NAT intercept or TPROXY mode, properly working with no failures */
+        unsigned short port = 
http->getConn()->clientConnection->local.GetPort();
+        if (port == 80)
+            prepareInterceptedHttpURL(csd, http, url, req_hdr);
+        else if (port == 443)
+            prepareInterceptedHttpsURL(csd, http, url, req_hdr);
+        else {
+            debugs(33, 4, HERE << "Intercepted port " << port << ". Assuming 
its HTTP.");
+            prepareInterceptedHttpURL(csd, http, url, req_hdr);
+        }
+    } else if (csd->port->accel) {
         /* accelerator mode */
         prepareAcceleratedURL(csd, http, url, req_hdr);
+        http->flags.accel = 1;
+
+    } else if (csd->switchedToHttps()) {
+        /* CONNECT 'ssl-bump' interception mode */
+        prepareInterceptedHttpsURL(csd, http, url, req_hdr);
 
     } else if (internalCheck(url)) {
         /* internal URL mode */
@@ -2299,12 +2363,6 @@
         strcpy(http->uri, url);
     }
 
-    debugs(33, 5, "parseHttpRequest: Complete request received");
-
-    // XXX: crop this dump at the end of headers. No need for extras
-    debugs(11, 2, "HTTP Client " << csd->clientConnection);
-    debugs(11, 2, "HTTP Client REQUEST:\n---------\n" << (hp->buf) + 
hp->req.m_start << "\n----------");
-
     result->flags.parsed_ok = 1;
     xfree(url);
     return result;
@@ -3593,7 +3651,7 @@
 
     // We are going to read new request
     flags.readMore = true;
-    debugs(33, 5, HERE << "converting " << clientConnection << " to SSL");
+    debugs(33, 5, HERE << "converting " << clientConnection << " (for " << 
sslHostName << ") to SSL");
 
     return getSslContextStart();
 }

Reply via email to