1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118
|
------------------------------------------------------------
revno: 13930
revision-id: squid3@treenet.co.nz-20150927082859-7za4czz7cpqry16n
parent: squid3@treenet.co.nz-20150927081853-u23ejmocr3694zyd
committer: Amos Jeffries <squid3@treenet.co.nz>
branch nick: 3.5
timestamp: Sun 2015-09-27 01:28:59 -0700
message:
Fix cache_peer login=PASS(THRU) after CVE-2015-5400
The patch for CVE-2015-5400 converts all non-200 peer responses
into 502 Bad Gateway responses when relaying a CONNECT to a peer.
This happens to break login=PASS and login=PASSTHRU behaviour
which relies on the 401 and 407 status being relayed transparently.
We need to relay the auth server responses as-is when login= is
set to PASS or PASSTHRU but then unconditionally close the
connections to prevent CVE-2015-5400 from occuring.
------------------------------------------------------------
# Bazaar merge directive format 2 (Bazaar 0.90)
# revision_id: squid3@treenet.co.nz-20150927082859-7za4czz7cpqry16n
# target_branch: http://bzr.squid-cache.org/bzr/squid3/3.5
# testament_sha1: 57d2ad15fd181cd054567c3028663f0f9eb07197
# timestamp: 2015-09-27 08:51:01 +0000
# source_branch: http://bzr.squid-cache.org/bzr/squid3/3.5
# base_revision_id: squid3@treenet.co.nz-20150927081853-\
# u23ejmocr3694zyd
#
# Begin patch
=== modified file 'src/tunnel.cc'
--- a/src/tunnel.cc
+++ b/src/tunnel.cc
@@ -124,7 +124,7 @@ public:
/// Sends "502 Bad Gateway" error response to the client,
/// if it is waiting for Squid CONNECT response, closing connections.
- void informUserOfPeerError(const char *errMsg);
+ void informUserOfPeerError(const char *errMsg, size_t);
class Connection
{
@@ -351,20 +351,33 @@ TunnelStateData::readConnectResponseDone
}
void
-TunnelStateData::informUserOfPeerError(const char *errMsg)
+TunnelStateData::informUserOfPeerError(const char *errMsg, const size_t sz)
{
server.len = 0;
+
if (!clientExpectsConnectResponse()) {
// closing the connection is the best we can do here
debugs(50, 3, server.conn << " closing on error: " << errMsg);
server.conn->close();
return;
}
- ErrorState *err = new ErrorState(ERR_CONNECT_FAIL, Http::scBadGateway, request.getRaw());
- err->callback = tunnelErrorComplete;
- err->callback_data = this;
- *status_ptr = Http::scBadGateway;
- errorSend(http->getConn()->clientConnection, err);
+
+ // if we have no reply suitable to relay, use 502 Bad Gateway
+ if (!sz || sz > static_cast<size_t>(connectRespBuf->contentSize())) {
+ ErrorState *err = new ErrorState(ERR_CONNECT_FAIL, Http::scBadGateway, request.getRaw());
+ *status_ptr = Http::scBadGateway;
+ err->callback = tunnelErrorComplete;
+ err->callback_data = this;
+ errorSend(http->getConn()->clientConnection, err);
+ return;
+ }
+
+ // if we need to send back the server response. write its headers to the client
+ server.len = sz;
+ memcpy(server.buf, connectRespBuf->content(), server.len);
+ copy(server.len, server, client, TunnelStateData::WriteClientDone);
+ // then close the server FD to prevent any relayed keep-alive causing CVE-2015-5400
+ server.closeIfOpen();
}
/* Read from client side and queue it for writing to the server */
@@ -398,7 +411,7 @@ TunnelStateData::handleConnectResponse(c
const bool parsed = rep.parse(connectRespBuf, eof, &parseErr);
if (!parsed) {
if (parseErr > 0) { // unrecoverable parsing error
- informUserOfPeerError("malformed CONNECT response from peer");
+ informUserOfPeerError("malformed CONNECT response from peer", 0);
return;
}
@@ -407,7 +420,7 @@ TunnelStateData::handleConnectResponse(c
assert(!parseErr);
if (!connectRespBuf->hasSpace()) {
- informUserOfPeerError("huge CONNECT response from peer");
+ informUserOfPeerError("huge CONNECT response from peer", 0);
return;
}
@@ -419,10 +432,16 @@ TunnelStateData::handleConnectResponse(c
// CONNECT response was successfully parsed
*status_ptr = rep.sline.status();
+ // we need to relay the 401/407 responses when login=PASS(THRU)
+ const char *pwd = server.conn->getPeer()->login;
+ const bool relay = pwd && (strcmp(pwd, "PASS") != 0 || strcmp(pwd, "PASSTHRU") != 0) &&
+ (*status_ptr == Http::scProxyAuthenticationRequired ||
+ *status_ptr == Http::scUnauthorized);
+
// bail if we did not get an HTTP 200 (Connection Established) response
if (rep.sline.status() != Http::scOkay) {
// if we ever decide to reuse the peer connection, we must extract the error response first
- informUserOfPeerError("unsupported CONNECT response status code");
+ informUserOfPeerError("unsupported CONNECT response status code", (relay ? rep.hdr_sz : 0));
return;
}
|