Background:
This problem only occurs when we are handling an intercepted request
on a TCP connection destined to a machine not listed on the Host header
domains IP address list. To fix this bug properly we are going to have
to wrap untrusted requests in a CONNECT request and pass it to peers.
Who are also going to have to be updated to un-wrap the CONNECT and
treat the internal request as intercepted traffic themselves. This is
turning into a big project all on its own.
In order to prevent Squid-3.2 taking another year or two, we have
decided to defer the fix to a later Squid series.
The workaround:
This patch re-enables Squid peer selection algorithms for intercepted
traffic which has failed Host header verification.
When host verification fails Squid will use, in order of preference:
* an already PINNED server connection
* the client ORIGINAL_DST details
* cache_peer as chosen by selection algorithms
NOTE: whenever DIRECT is selected by routing algorithms the
ORIGINAL_DST is used instead.
Peer selection results are updated to display PINNED and
ORIGINAL_DST alongside DIRECT and cache_peer.
SECURITY NOTE:
At this point Squid will pass the request to cache_peer using the
non-trusted Host header in their URLs. Meaning that the peers
may still be poisoned by CVE-2009-0801 attacks. Only the initial
intercepting proxy is protected.
Full protection against CVE-2009-0801 can be enjoyed by building
Squid with the -DSTRICT_HOST_VERIFY compile-time flag. This will
make the peers unreachable for intercepted traffic where the
Host verification has failed.
PS. The notion was floated of re-writing the Host header or URL domain
to the original
destination IP and passing that through. However that method is known
to break
virtual hosted websites, which unfortunately do overlap a great deal
with the hosting
companies use of Geo-DNS on their CDNs.
Amos
Bug 3478: Allow peer selection
This re-enables Squid peer selection algorithms for intercepted
traffic which has failed Host header verification.
When host verification fails Squid will use, in order of preference:
* an already PINNED server connection
* the client ORIGINAL_DST details
* cache_peer as chosen by selection algorithms
NOTE: whenever DIRECT is selected by routing algorithms the
ORIGINAL_DST is used instead.
Peer selection results are updated to display PINNED and
ORIGINAL_DST alongside DIRECT and cache_peer.
SECURITY NOTE:
At this point Squid will pass the request to cache_peer using the
non-trusted Host header in their URLs. Meaning that the peers
may still be poisoned by CVE-2009-0801 attacks. Only the initial
intercepting proxy is protected.
Full protection against CVE-2009-0801 can be enjoyed by building
Squid with the -DSTRICT_HOST_VERIFY compile-time flag. This will
make the peers unreachable for intercepted traffic where the
Host verification has failed.
=== modified file 'src/cf.data.pre'
--- src/cf.data.pre 2012-07-18 16:21:47 +0000
+++ src/cf.data.pre 2012-07-26 14:05:21 +0000
@@ -1922,9 +1922,9 @@
* Intercepted traffic which passes verification is handled
normally.
- For now it also forces suspicious requests to go DIRECT to the
- original destination, overriding client_dst_passthru for
- intercepted requests which fail Host: verification.
+ * Intercepted requests which fail verification are sent
+ to the client original destination instead of DIRECT.
+ This overrides client_dst_passthru.
For now suspicious intercepted CONNECT requests are always
responded to with an HTTP 409 (Conflict) error page.
@@ -1939,13 +1939,26 @@
directly to the original client destination IP or seek a faster
source.
- This option (on by default) prevents cache_peer and alternative DNS
- entries being used on intercepted traffic. Both of which lead to
- the security vulnerability outlined below.
-
- SECURITY WARNING:
-
- This directive should only be disabled if cache_peer are required.
+ Using alternative server DNS entries can provide potentially faster
+ connectivity with a range of failure recovery options.
+ But can also lead to connectivity trouble when the client and
+ server are attempting stateful interactions unaware of the proxy.
+
+ This option (on by default) prevents alternative DNS entries being
+ located to send intercepted traffic DIRECT to an origin server.
+ The clients original destination IP and port will be used instead.
+
+ To allow Squid to locate potentially faster destinations or to
+ recover from connection failures to the clients original destination
+ set this to OFF.
+
+ Regardless of this option setting, when dealing with intercepted
+ traffic Squid will verify the Host: header and any traffic which
+ fails Host verification will be treated as if this option were ON.
+
+ see host_verify_strict for details on the verification process.
+
+ SECURITY NOTE:
As described in CVE-2009-0801 when the Host: header alone is used
to determine the destination of a request it becomes trivial for
@@ -1957,7 +1970,7 @@
sandbox only verifies that the applet tries to contact the same IP
as from where it was loaded at the IP level. The Host: header may
be different from the connected IP and approved origin.
-
+
DOC_END
COMMENT_START
=== modified file 'src/forward.cc'
--- src/forward.cc 2012-07-23 07:02:06 +0000
+++ src/forward.cc 2012-07-27 08:08:26 +0000
@@ -134,12 +134,15 @@
const bool useOriginalDst = Config.onoff.client_dst_passthru || (request
&& !request->flags.hostVerified);
if (isIntercepted && useOriginalDst) {
selectPeerForIntercepted();
- // destination "found". continue with the forwarding.
+#if STRICT_ORIGINAL_DST
+ // 3.2 does not suppro re-wrapping inside CONNECT.
+ // our only alternative is to fake destination "found" and continue
with the forwarding.
startConnectionOrFail();
- } else {
- // do full route options selection
- peerSelect(&serverDestinations, request, entry,
fwdPeerSelectionCompleteWrapper, this);
+ return;
+#endif
}
+ // do full route options selection
+ peerSelect(&serverDestinations, request, entry,
fwdPeerSelectionCompleteWrapper, this);
}
/// bypasses peerSelect() when dealing with intercepted requests
@@ -151,19 +154,22 @@
if (ConnStateData *client = request->pinnedConnection())
p = client->validatePinnedConnection(request, NULL);
- if (p != NULL && Comm::IsConnOpen(p)) {
- debugs(17, 3, HERE << "reusing a pinned conn: " << *p);
+ if (Comm::IsConnOpen(p)) {
/* duplicate peerSelectPinned() effects */
p->peerType = PINNED;
entry->ping_status = PING_DONE; /* Skip ICP */
- } else {
- p = new Comm::Connection();
- p->peerType = ORIGINAL_DST;
- p->remote = clientConn->local;
- getOutgoingAddress(request, p);
- debugs(17, 3, HERE << "opening a new conn: " << *p);
+
+ debugs(17, 3, HERE << "reusing a pinned conn: " << *p);
+ serverDestinations.push_back(p);
}
+ // use client original destination as second preferred choice
+ p = new Comm::Connection();
+ p->peerType = ORIGINAL_DST;
+ p->remote = clientConn->local;
+ getOutgoingAddress(request, p);
+
+ debugs(17, 3, HERE << "using client original destination: " << *p);
serverDestinations.push_back(p);
}
=== modified file 'src/peer_select.cc'
--- src/peer_select.cc 2012-07-23 07:02:06 +0000
+++ src/peer_select.cc 2012-07-27 03:50:49 +0000
@@ -222,6 +222,33 @@
{
FwdServer *fs = psstate->servers;
+ // Bug 3243: CVE 2009-0801
+ // Bypass of browser same-origin access control in intercepted
communication
+ // To resolve this we must force DIRECT and only to the original client
destination.
+ const HttpRequest *req = psstate->request;
+ const bool isIntercepted = !req->flags.redirected &&
+ (req->flags.intercepted ||
req->flags.spoof_client_ip);
+ const bool useOriginalDst = Config.onoff.client_dst_passthru ||
!req->flags.hostVerified;
+ const bool choseDirect = fs && fs->code == HIER_DIRECT;
+ if (isIntercepted && useOriginalDst && choseDirect) {
+ // construct a "result" adding the ORIGINAL_DST to the set instead of
DIRECT
+ Comm::ConnectionPointer p = new Comm::Connection();
+ p->remote = req->clientConnectionManager->clientConnection->local;
+ p->peerType = fs->code;
+ p->setPeer(fs->_peer);
+
+ // check for a configured outgoing address for this destination...
+ getOutgoingAddress(psstate->request, p);
+ psstate->paths->push_back(p);
+
+ // clear the used fs and continue
+ psstate->servers = fs->next;
+ cbdataReferenceDone(fs->_peer);
+ memFree(fs, MEM_FWD_SERVER);
+ peerSelectDnsPaths(psstate);
+ return;
+ }
+
// convert the list of FwdServer destinations into destinations IP
addresses
if (fs && psstate->paths->size() < (unsigned int)Config.forward_max_tries)
{
// send the next one off for DNS lookup.
@@ -247,6 +274,10 @@
for (size_t i = 0; i < psstate->paths->size(); ++i) {
if ((*psstate->paths)[i]->peerType == HIER_DIRECT)
debugs(44, 2, " DIRECT = " << (*psstate->paths)[i]);
+ else if ((*psstate->paths)[i]->peerType == ORIGINAL_DST)
+ debugs(44, 2, " ORIGINAL_DST = " << (*psstate->paths)[i]);
+ else if ((*psstate->paths)[i]->peerType == PINNED)
+ debugs(44, 2, " PINNED = " << (*psstate->paths)[i]);
else
debugs(44, 2, " cache_peer = " << (*psstate->paths)[i]);
}