------------------------------------------------------------ revno: 13930 revision-id: squid3@treenet.co.nz-20150927082859-7za4czz7cpqry16n parent: squid3@treenet.co.nz-20150927081853-u23ejmocr3694zyd committer: Amos Jeffries 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' --- src/tunnel.cc 2015-09-09 16:32:08 +0000 +++ src/tunnel.cc 2015-09-27 08:28:59 +0000 @@ -112,7 +112,7 @@ /// 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 { @@ -390,20 +390,36 @@ } void -TunnelStateData::informUserOfPeerError(const char *errMsg) +TunnelStateData::informUserOfPeerError(const char *errMsg, const size_t sz) { server.len = 0; + + if (logTag_ptr) + *logTag_ptr = LOG_TCP_TUNNEL; + 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(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 */ @@ -437,7 +453,7 @@ 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; } @@ -446,7 +462,7 @@ assert(!parseErr); if (!connectRespBuf->hasSpace()) { - informUserOfPeerError("huge CONNECT response from peer"); + informUserOfPeerError("huge CONNECT response from peer", 0); return; } @@ -458,10 +474,16 @@ // 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; }