=== modified file 'src/ClientRequestContext.h'
--- src/ClientRequestContext.h	2011-07-16 15:21:48 +0000
+++ src/ClientRequestContext.h	2011-07-17 12:02:14 +0000
@@ -24,6 +24,9 @@
     ~ClientRequestContext();
 
     bool httpStateIsValid();
+    void hostHeaderVerify();
+    void hostHeaderIpVerify(const ipcache_addrs* ia);
+    void hostHeaderVerifyFailed();
     void clientAccessCheck();
     void clientAccessCheck2();
     void clientAccessCheckDone(const allow_t &answer);
@@ -51,6 +54,7 @@
     ACLChecklist *acl_checklist;        /* need ptr back so we can unreg if needed */
     int redirect_state;
 
+    bool host_header_verify_done;
     bool http_access_done;
     bool adapted_http_access_done;
 #if USE_ADAPTATION

=== modified file 'src/client_side_request.cc'
--- src/client_side_request.cc	2011-07-16 15:21:48 +0000
+++ src/client_side_request.cc	2011-07-17 13:07:58 +0000
@@ -519,6 +519,134 @@
 }
 #endif /* FOLLOW_X_FORWARDED_FOR */
 
+static void
+hostHeaderIpVerifyWrapper(const ipcache_addrs* ia, const DnsLookupDetails &, void *data)
+{
+    ClientRequestContext *c = static_cast<ClientRequestContext*>(data);
+    // XXX: note the DNS details for the transaction stats.
+    c->hostHeaderIpVerify(ia);
+}
+
+void
+ClientRequestContext::hostHeaderIpVerify(const ipcache_addrs* ia)
+{
+    Comm::ConnectionPointer clientConn = http->getConn()->clientConnection;
+
+    if (ia != NULL && ia->count > 0) {
+        // Is the NAT destination IP in DNS?
+        for (int i = 0; i < ia->count; i++) {
+            if (clientConn->local.matchIPAddr(ia->in_addrs[i]) == 0) {
+                debugs(85, 3, HERE << "validate IP " << clientConn->local << " possible from Host:");
+                http->doCallouts();
+                return;
+            }
+            debugs(85, 3, HERE << "validate IP " << clientConn->local << " non-match from Host: IP " << ia->in_addrs[i]);
+        }
+    }
+    debugs(85, 3, HERE << "FAIL: validate IP " << clientConn->local << " possible from Host:");
+    hostHeaderVerifyFailed();
+}
+
+void
+ClientRequestContext::hostHeaderVerifyFailed()
+{
+    debugs(85, 1, "SECURITY ALERT: Host: header forgery detected from " << http->getConn()->clientConnection);
+
+    // IP address validation for Host: failed. reject the connection.
+    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_CONFLICT,
+                                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);
+}
+
+void
+ClientRequestContext::hostHeaderVerify()
+{
+    // Require a Host: header.
+    const char *host = http->request->header.getStr(HDR_HOST);
+    char *hostB = NULL;
+
+    if (!host) {
+        // TODO: dump out the HTTP/1.1 error about missing host header.
+        // otherwise this is fine, can't forge a header value when its not even set.
+        debugs(85, 3, HERE << "validate skipped with no Host: header present.");
+        http->doCallouts();
+        return;
+    }
+
+    // Locate if there is a port attached, strip ready for IP lookup
+    char *portStr = NULL;
+    uint16_t port = 0;
+    if (host[0] == '[') {
+        // IPv6 literal.
+        // check for a port?
+        hostB = xstrdup(host+1);
+        portStr = strchr(hostB, ']');
+        if (!portStr) {
+            safe_free(hostB); // well, that wasn't an IPv6 literal.
+        } else {
+            *portStr = '\0';
+            if (*(++portStr) == ':')
+                port = xatoi(++portStr);
+            else
+                portStr=NULL; // no port to check.
+        }
+        if (hostB)
+            host = hostB; // point host at the local version for lookup
+    } else if (strrchr(host, ':') != NULL) {
+        // Domain or IPv4 literal with port
+        hostB = xstrdup(host);
+        portStr = strrchr(hostB, ':');
+        *portStr = '\0';
+        port = xatoi(++portStr);
+        host = hostB; // point host at the local version for lookup
+    }
+
+    debugs(85, 3, HERE << "validate host=" << host << ", port=" << port << ", portStr=" << (portStr?portStr:"NULL"));
+    if (http->request->flags.intercepted || http->request->flags.spoof_client_ip) {
+        // verify the port (if any) matches the apparent destination
+        if (portStr && port != http->getConn()->clientConnection->local.GetPort()) {
+            debugs(85, 3, HERE << "FAIL on validate port " << http->getConn()->clientConnection->local.GetPort() << " matches Host: port " << port << "((" << portStr);
+            hostHeaderVerifyFailed();
+            safe_free(hostB);
+            return;
+        }
+        // XXX: match the scheme default port against the apparent destination
+
+        // verify the destination DNS is one of the Host: headers IPs
+        ipcache_nbgethostbyname(host, hostHeaderIpVerifyWrapper, this);
+        safe_free(hostB);
+        return;
+    }
+
+    // TODO: verify front-end accelerated requests as well?
+
+    // Verify forward-proxy requested URL domain matches the Host: header
+    host = http->request->header.getStr(HDR_HOST);
+    if (strcmp(host, http->request->GetHost()) != 0) {
+        debugs(85, 3, HERE << "FAIL on validate URL domain " << http->request->GetHost() << " matches Host: " << host);
+        hostHeaderVerifyFailed();
+        safe_free(hostB);
+        return;
+    }
+
+    debugs(85, 3, HERE << "validate passed.");
+    safe_free(hostB);
+    http->doCallouts();
+}
+
 /* This is the entry point for external users of the client_side routines */
 void
 ClientRequestContext::clientAccessCheck()
@@ -1332,6 +1460,14 @@
     if (!calloutContext->http->al.request)
         calloutContext->http->al.request = HTTPMSGLOCK(request);
 
+    // CVE-2009-0801: verify the Host: header is consistent with other known details.
+    if (!calloutContext->host_header_verify_done) {
+        debugs(83, 3, HERE << "Doing calloutContext->hostHeaderVerify()");
+        calloutContext->host_header_verify_done = true;
+        calloutContext->hostHeaderVerify();
+        return;
+    }
+
     if (!calloutContext->http_access_done) {
         debugs(83, 3, HERE << "Doing calloutContext->clientAccessCheck()");
         calloutContext->http_access_done = true;


