components/squid/patches/CVE-2015-5400.patch
author Rich Burridge <rich.burridge@oracle.com>
Wed, 13 Apr 2016 10:14:18 -0700
changeset 5756 8233953c0160
permissions -rw-r--r--
21377893 problem in UTILITY/SQUID 23088298 problem in UTILITY/SQUID 23088308 problem in UTILITY/SQUID

Fix for CVE-2015-5400. See:

  http://www.squid-cache.org/Advisories/SQUID-2015_2.txt

for more details. Based on the squid version 3.5.X patch at:

  http://www.squid-cache.org/Versions/v3/3.5/changesets/squid-3.5-13856.patch

--- squid-3.5.5/src/tunnel.cc.orig	2016-04-12 11:11:11.546364864 -0700
+++ squid-3.5.5/src/tunnel.cc	2016-04-12 11:12:40.789271952 -0700
@@ -110,6 +110,10 @@
                  (request->flags.interceptTproxy || request->flags.intercepted));
     }
 
+    /// Sends "502 Bad Gateway" error response to the client,
+    /// if it is waiting for Squid CONNECT response, closing connections.
+    void informUserOfPeerError(const char *errMsg);
+
     class Connection
     {
 
@@ -128,12 +132,13 @@
 
         void error(int const xerrno);
         int debugLevelForError(int const xerrno) const;
-        /// handles a non-I/O error associated with this Connection
-        void logicError(const char *errMsg);
         void closeIfOpen();
         void dataSent (size_t amount);
+        /// writes 'b' buffer, setting the 'writer' member to 'callback'.
+        void write(const char *b, int size, AsyncCall::Pointer &callback, FREE * free_func);
         int len;
         char *buf;
+        AsyncCall::Pointer writer; ///< pending Comm::Write callback
         int64_t *size_ptr;      /* pointer to size in an ConnStateData for logging */
 
         Comm::ConnectionPointer conn;    ///< The currently connected connection.
@@ -223,6 +228,7 @@
     TunnelStateData *tunnelState = (TunnelStateData *)params.data;
     debugs(26, 3, HERE << tunnelState->server.conn);
     tunnelState->server.conn = NULL;
+    tunnelState->server.writer = NULL;
 
     if (tunnelState->request != NULL)
         tunnelState->request->hier.stopPeerClock(false);
@@ -232,7 +238,7 @@
         return;
     }
 
-    if (!tunnelState->server.len) {
+    if (!tunnelState->client.writer) {
         tunnelState->client.conn->close();
         return;
     }
@@ -244,13 +250,14 @@
     TunnelStateData *tunnelState = (TunnelStateData *)params.data;
     debugs(26, 3, HERE << tunnelState->client.conn);
     tunnelState->client.conn = NULL;
+    tunnelState->client.writer = NULL;
 
     if (tunnelState->noConnections()) {
         delete tunnelState;
         return;
     }
 
-    if (!tunnelState->client.len) {
+    if (!tunnelState->server.writer) {
         tunnelState->server.conn->close();
         return;
     }
@@ -381,6 +388,23 @@
         handleConnectResponse(len);
 }
 
+void
+TunnelStateData::informUserOfPeerError(const char *errMsg)
+{
+    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);
+}
+
 /* Read from client side and queue it for writing to the server */
 void
 TunnelStateData::ReadConnectResponseDone(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag errcode, int xerrno, void *data)
@@ -412,7 +436,7 @@
     const bool parsed = rep.parse(connectRespBuf, eof, &parseErr);
     if (!parsed) {
         if (parseErr > 0) { // unrecoverable parsing error
-            server.logicError("malformed CONNECT response from peer");
+            informUserOfPeerError("malformed CONNECT response from peer");
             return;
         }
 
@@ -421,7 +445,7 @@
         assert(!parseErr);
 
         if (!connectRespBuf->hasSpace()) {
-            server.logicError("huge CONNECT response from peer");
+            informUserOfPeerError("huge CONNECT response from peer");
             return;
         }
 
@@ -435,7 +459,8 @@
 
     // bail if we did not get an HTTP 200 (Connection Established) response
     if (rep.sline.status() != Http::scOkay) {
-        server.logicError("unsupported CONNECT response status code");
+        // if we ever decide to reuse the peer connection, we must extract the error response first
+        informUserOfPeerError("unsupported CONNECT response status code");
         return;
     }
 
@@ -454,13 +479,6 @@
 }
 
 void
-TunnelStateData::Connection::logicError(const char *errMsg)
-{
-    debugs(50, 3, conn << " closing on error: " << errMsg);
-    conn->close();
-}
-
-void
 TunnelStateData::Connection::error(int const xerrno)
 {
     /* XXX fixme xstrerror and xerrno... */
@@ -556,7 +574,7 @@
     debugs(26, 3, HERE << "Schedule Write");
     AsyncCall::Pointer call = commCbCall(5,5, "TunnelBlindCopyWriteHandler",
                                          CommIoCbPtrFun(completion, this));
-    Comm::Write(to.conn, from.buf, len, call, NULL);
+    to.write(from.buf, len, call, NULL);
 }
 
 /* Writes data from the client buffer to the server side */
@@ -565,6 +583,7 @@
 {
     TunnelStateData *tunnelState = (TunnelStateData *)data;
     assert (cbdataReferenceValid (tunnelState));
+    tunnelState->server.writer = NULL;
 
     tunnelState->writeServerDone(buf, len, flag, xerrno);
 }
@@ -614,6 +633,7 @@
 {
     TunnelStateData *tunnelState = (TunnelStateData *)data;
     assert (cbdataReferenceValid (tunnelState));
+    tunnelState->client.writer = NULL;
 
     tunnelState->writeClientDone(buf, len, flag, xerrno);
 }
@@ -631,7 +651,14 @@
 }
 
 void
-TunnelStateData::writeClientDone(char *buf, size_t len, Comm::Flag flag, int xerrno)
+TunnelStateData::Connection::write(const char *b, int size, AsyncCall::Pointer &callback, FREE * free_func)
+{
+    writer = callback;
+    Comm::Write(conn, b, size, callback, free_func);
+}
+
+void
+TunnelStateData::writeClientDone(char *, size_t len, Comm::Flag flag, int xerrno)
 {
     debugs(26, 3, HERE << client.conn << ", " << len << " bytes written, flag=" << flag);
 
@@ -789,6 +816,7 @@
 {
     TunnelStateData *tunnelState = (TunnelStateData *)data;
     debugs(26, 3, HERE << conn << ", flag=" << flag);
+    tunnelState->client.writer = NULL;
 
     if (flag != Comm::OK) {
         *tunnelState->status_ptr = Http::scInternalServerError;
@@ -805,6 +833,7 @@
 {
     TunnelStateData *tunnelState = (TunnelStateData *)data;
     debugs(26, 3, conn << ", flag=" << flag);
+    tunnelState->server.writer = NULL;
     assert(tunnelState->waitingForConnectRequest());
 
     if (flag != Comm::OK) {
@@ -845,7 +874,7 @@
     else {
         AsyncCall::Pointer call = commCbCall(5,5, "tunnelConnectedWriteDone",
                                              CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState));
-        Comm::Write(tunnelState->client.conn, conn_established, strlen(conn_established), call, NULL);
+        tunnelState->client.write(conn_established, strlen(conn_established), call, NULL);
     }
 }
 
@@ -1064,29 +1093,21 @@
     debugs(11, 2, "Tunnel Server REQUEST: " << tunnelState->server.conn << ":\n----------\n" <<
            Raw("tunnelRelayConnectRequest", mb.content(), mb.contentSize()) << "\n----------");
 
-    if (tunnelState->clientExpectsConnectResponse()) {
-        // hack: blindly tunnel peer response (to our CONNECT request) to the client as ours.
-        AsyncCall::Pointer writeCall = commCbCall(5,5, "tunnelConnectedWriteDone",
-                                       CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState));
-        Comm::Write(srv, &mb, writeCall);
-    } else {
-        // we have to eat the connect response from the peer (so that the client
-        // does not see it) and only then start shoveling data to the client
-        AsyncCall::Pointer writeCall = commCbCall(5,5, "tunnelConnectReqWriteDone",
-                                       CommIoCbPtrFun(tunnelConnectReqWriteDone,
-                                               tunnelState));
-        Comm::Write(srv, &mb, writeCall);
-        tunnelState->connectReqWriting = true;
-
-        tunnelState->connectRespBuf = new MemBuf;
-        // SQUID_TCP_SO_RCVBUF: we should not accumulate more than regular I/O buffer
-        // can hold since any CONNECT response leftovers have to fit into server.buf.
-        // 2*SQUID_TCP_SO_RCVBUF: HttpMsg::parse() zero-terminates, which uses space.
-        tunnelState->connectRespBuf->init(SQUID_TCP_SO_RCVBUF, 2*SQUID_TCP_SO_RCVBUF);
-        tunnelState->readConnectResponse();
+    AsyncCall::Pointer writeCall = commCbCall(5,5, "tunnelConnectReqWriteDone",
+                                   CommIoCbPtrFun(tunnelConnectReqWriteDone,
+                                           tunnelState));
+
+    tunnelState->server.write(mb.buf, mb.size, writeCall, mb.freeFunc());
+    tunnelState->connectReqWriting = true;
+
+    tunnelState->connectRespBuf = new MemBuf;
+    // SQUID_TCP_SO_RCVBUF: we should not accumulate more than regular I/O buffer
+    // can hold since any CONNECT response leftovers have to fit into server.buf.
+    // 2*SQUID_TCP_SO_RCVBUF: HttpMsg::parse() zero-terminates, which uses space.
+    tunnelState->connectRespBuf->init(SQUID_TCP_SO_RCVBUF, 2*SQUID_TCP_SO_RCVBUF);
+    tunnelState->readConnectResponse();
 
-        assert(tunnelState->waitingForConnectExchange());
-    }
+    assert(tunnelState->waitingForConnectExchange());
 
     AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout",
                                      CommTimeoutCbPtrFun(tunnelTimeout, tunnelState));
@@ -1219,7 +1240,7 @@
 
     AsyncCall::Pointer call = commCbCall(5,5, "tunnelConnectedWriteDone",
                                          CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState));
-    Comm::Write(tunnelState->client.conn, buf.content(), buf.contentSize(), call, NULL);
+    tunnelState->client.write(buf.content(), buf.contentSize(), call, NULL);
 }
 #endif //USE_OPENSSL