|
1 Fix for CVE-2015-5400. See: |
|
2 |
|
3 http://www.squid-cache.org/Advisories/SQUID-2015_2.txt |
|
4 |
|
5 for more details. Based on the squid version 3.5.X patch at: |
|
6 |
|
7 http://www.squid-cache.org/Versions/v3/3.5/changesets/squid-3.5-13856.patch |
|
8 |
|
9 --- squid-3.5.5/src/tunnel.cc.orig 2016-04-12 11:11:11.546364864 -0700 |
|
10 +++ squid-3.5.5/src/tunnel.cc 2016-04-12 11:12:40.789271952 -0700 |
|
11 @@ -110,6 +110,10 @@ |
|
12 (request->flags.interceptTproxy || request->flags.intercepted)); |
|
13 } |
|
14 |
|
15 + /// Sends "502 Bad Gateway" error response to the client, |
|
16 + /// if it is waiting for Squid CONNECT response, closing connections. |
|
17 + void informUserOfPeerError(const char *errMsg); |
|
18 + |
|
19 class Connection |
|
20 { |
|
21 |
|
22 @@ -128,12 +132,13 @@ |
|
23 |
|
24 void error(int const xerrno); |
|
25 int debugLevelForError(int const xerrno) const; |
|
26 - /// handles a non-I/O error associated with this Connection |
|
27 - void logicError(const char *errMsg); |
|
28 void closeIfOpen(); |
|
29 void dataSent (size_t amount); |
|
30 + /// writes 'b' buffer, setting the 'writer' member to 'callback'. |
|
31 + void write(const char *b, int size, AsyncCall::Pointer &callback, FREE * free_func); |
|
32 int len; |
|
33 char *buf; |
|
34 + AsyncCall::Pointer writer; ///< pending Comm::Write callback |
|
35 int64_t *size_ptr; /* pointer to size in an ConnStateData for logging */ |
|
36 |
|
37 Comm::ConnectionPointer conn; ///< The currently connected connection. |
|
38 @@ -223,6 +228,7 @@ |
|
39 TunnelStateData *tunnelState = (TunnelStateData *)params.data; |
|
40 debugs(26, 3, HERE << tunnelState->server.conn); |
|
41 tunnelState->server.conn = NULL; |
|
42 + tunnelState->server.writer = NULL; |
|
43 |
|
44 if (tunnelState->request != NULL) |
|
45 tunnelState->request->hier.stopPeerClock(false); |
|
46 @@ -232,7 +238,7 @@ |
|
47 return; |
|
48 } |
|
49 |
|
50 - if (!tunnelState->server.len) { |
|
51 + if (!tunnelState->client.writer) { |
|
52 tunnelState->client.conn->close(); |
|
53 return; |
|
54 } |
|
55 @@ -244,13 +250,14 @@ |
|
56 TunnelStateData *tunnelState = (TunnelStateData *)params.data; |
|
57 debugs(26, 3, HERE << tunnelState->client.conn); |
|
58 tunnelState->client.conn = NULL; |
|
59 + tunnelState->client.writer = NULL; |
|
60 |
|
61 if (tunnelState->noConnections()) { |
|
62 delete tunnelState; |
|
63 return; |
|
64 } |
|
65 |
|
66 - if (!tunnelState->client.len) { |
|
67 + if (!tunnelState->server.writer) { |
|
68 tunnelState->server.conn->close(); |
|
69 return; |
|
70 } |
|
71 @@ -381,6 +388,23 @@ |
|
72 handleConnectResponse(len); |
|
73 } |
|
74 |
|
75 +void |
|
76 +TunnelStateData::informUserOfPeerError(const char *errMsg) |
|
77 +{ |
|
78 + server.len = 0; |
|
79 + if (!clientExpectsConnectResponse()) { |
|
80 + // closing the connection is the best we can do here |
|
81 + debugs(50, 3, server.conn << " closing on error: " << errMsg); |
|
82 + server.conn->close(); |
|
83 + return; |
|
84 + } |
|
85 + ErrorState *err = new ErrorState(ERR_CONNECT_FAIL, Http::scBadGateway, request.getRaw()); |
|
86 + err->callback = tunnelErrorComplete; |
|
87 + err->callback_data = this; |
|
88 + *status_ptr = Http::scBadGateway; |
|
89 + errorSend(http->getConn()->clientConnection, err); |
|
90 +} |
|
91 + |
|
92 /* Read from client side and queue it for writing to the server */ |
|
93 void |
|
94 TunnelStateData::ReadConnectResponseDone(const Comm::ConnectionPointer &, char *buf, size_t len, Comm::Flag errcode, int xerrno, void *data) |
|
95 @@ -412,7 +436,7 @@ |
|
96 const bool parsed = rep.parse(connectRespBuf, eof, &parseErr); |
|
97 if (!parsed) { |
|
98 if (parseErr > 0) { // unrecoverable parsing error |
|
99 - server.logicError("malformed CONNECT response from peer"); |
|
100 + informUserOfPeerError("malformed CONNECT response from peer"); |
|
101 return; |
|
102 } |
|
103 |
|
104 @@ -421,7 +445,7 @@ |
|
105 assert(!parseErr); |
|
106 |
|
107 if (!connectRespBuf->hasSpace()) { |
|
108 - server.logicError("huge CONNECT response from peer"); |
|
109 + informUserOfPeerError("huge CONNECT response from peer"); |
|
110 return; |
|
111 } |
|
112 |
|
113 @@ -435,7 +459,8 @@ |
|
114 |
|
115 // bail if we did not get an HTTP 200 (Connection Established) response |
|
116 if (rep.sline.status() != Http::scOkay) { |
|
117 - server.logicError("unsupported CONNECT response status code"); |
|
118 + // if we ever decide to reuse the peer connection, we must extract the error response first |
|
119 + informUserOfPeerError("unsupported CONNECT response status code"); |
|
120 return; |
|
121 } |
|
122 |
|
123 @@ -454,13 +479,6 @@ |
|
124 } |
|
125 |
|
126 void |
|
127 -TunnelStateData::Connection::logicError(const char *errMsg) |
|
128 -{ |
|
129 - debugs(50, 3, conn << " closing on error: " << errMsg); |
|
130 - conn->close(); |
|
131 -} |
|
132 - |
|
133 -void |
|
134 TunnelStateData::Connection::error(int const xerrno) |
|
135 { |
|
136 /* XXX fixme xstrerror and xerrno... */ |
|
137 @@ -556,7 +574,7 @@ |
|
138 debugs(26, 3, HERE << "Schedule Write"); |
|
139 AsyncCall::Pointer call = commCbCall(5,5, "TunnelBlindCopyWriteHandler", |
|
140 CommIoCbPtrFun(completion, this)); |
|
141 - Comm::Write(to.conn, from.buf, len, call, NULL); |
|
142 + to.write(from.buf, len, call, NULL); |
|
143 } |
|
144 |
|
145 /* Writes data from the client buffer to the server side */ |
|
146 @@ -565,6 +583,7 @@ |
|
147 { |
|
148 TunnelStateData *tunnelState = (TunnelStateData *)data; |
|
149 assert (cbdataReferenceValid (tunnelState)); |
|
150 + tunnelState->server.writer = NULL; |
|
151 |
|
152 tunnelState->writeServerDone(buf, len, flag, xerrno); |
|
153 } |
|
154 @@ -614,6 +633,7 @@ |
|
155 { |
|
156 TunnelStateData *tunnelState = (TunnelStateData *)data; |
|
157 assert (cbdataReferenceValid (tunnelState)); |
|
158 + tunnelState->client.writer = NULL; |
|
159 |
|
160 tunnelState->writeClientDone(buf, len, flag, xerrno); |
|
161 } |
|
162 @@ -631,7 +651,14 @@ |
|
163 } |
|
164 |
|
165 void |
|
166 -TunnelStateData::writeClientDone(char *buf, size_t len, Comm::Flag flag, int xerrno) |
|
167 +TunnelStateData::Connection::write(const char *b, int size, AsyncCall::Pointer &callback, FREE * free_func) |
|
168 +{ |
|
169 + writer = callback; |
|
170 + Comm::Write(conn, b, size, callback, free_func); |
|
171 +} |
|
172 + |
|
173 +void |
|
174 +TunnelStateData::writeClientDone(char *, size_t len, Comm::Flag flag, int xerrno) |
|
175 { |
|
176 debugs(26, 3, HERE << client.conn << ", " << len << " bytes written, flag=" << flag); |
|
177 |
|
178 @@ -789,6 +816,7 @@ |
|
179 { |
|
180 TunnelStateData *tunnelState = (TunnelStateData *)data; |
|
181 debugs(26, 3, HERE << conn << ", flag=" << flag); |
|
182 + tunnelState->client.writer = NULL; |
|
183 |
|
184 if (flag != Comm::OK) { |
|
185 *tunnelState->status_ptr = Http::scInternalServerError; |
|
186 @@ -805,6 +833,7 @@ |
|
187 { |
|
188 TunnelStateData *tunnelState = (TunnelStateData *)data; |
|
189 debugs(26, 3, conn << ", flag=" << flag); |
|
190 + tunnelState->server.writer = NULL; |
|
191 assert(tunnelState->waitingForConnectRequest()); |
|
192 |
|
193 if (flag != Comm::OK) { |
|
194 @@ -845,7 +874,7 @@ |
|
195 else { |
|
196 AsyncCall::Pointer call = commCbCall(5,5, "tunnelConnectedWriteDone", |
|
197 CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState)); |
|
198 - Comm::Write(tunnelState->client.conn, conn_established, strlen(conn_established), call, NULL); |
|
199 + tunnelState->client.write(conn_established, strlen(conn_established), call, NULL); |
|
200 } |
|
201 } |
|
202 |
|
203 @@ -1064,29 +1093,21 @@ |
|
204 debugs(11, 2, "Tunnel Server REQUEST: " << tunnelState->server.conn << ":\n----------\n" << |
|
205 Raw("tunnelRelayConnectRequest", mb.content(), mb.contentSize()) << "\n----------"); |
|
206 |
|
207 - if (tunnelState->clientExpectsConnectResponse()) { |
|
208 - // hack: blindly tunnel peer response (to our CONNECT request) to the client as ours. |
|
209 - AsyncCall::Pointer writeCall = commCbCall(5,5, "tunnelConnectedWriteDone", |
|
210 - CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState)); |
|
211 - Comm::Write(srv, &mb, writeCall); |
|
212 - } else { |
|
213 - // we have to eat the connect response from the peer (so that the client |
|
214 - // does not see it) and only then start shoveling data to the client |
|
215 - AsyncCall::Pointer writeCall = commCbCall(5,5, "tunnelConnectReqWriteDone", |
|
216 - CommIoCbPtrFun(tunnelConnectReqWriteDone, |
|
217 - tunnelState)); |
|
218 - Comm::Write(srv, &mb, writeCall); |
|
219 - tunnelState->connectReqWriting = true; |
|
220 - |
|
221 - tunnelState->connectRespBuf = new MemBuf; |
|
222 - // SQUID_TCP_SO_RCVBUF: we should not accumulate more than regular I/O buffer |
|
223 - // can hold since any CONNECT response leftovers have to fit into server.buf. |
|
224 - // 2*SQUID_TCP_SO_RCVBUF: HttpMsg::parse() zero-terminates, which uses space. |
|
225 - tunnelState->connectRespBuf->init(SQUID_TCP_SO_RCVBUF, 2*SQUID_TCP_SO_RCVBUF); |
|
226 - tunnelState->readConnectResponse(); |
|
227 + AsyncCall::Pointer writeCall = commCbCall(5,5, "tunnelConnectReqWriteDone", |
|
228 + CommIoCbPtrFun(tunnelConnectReqWriteDone, |
|
229 + tunnelState)); |
|
230 + |
|
231 + tunnelState->server.write(mb.buf, mb.size, writeCall, mb.freeFunc()); |
|
232 + tunnelState->connectReqWriting = true; |
|
233 + |
|
234 + tunnelState->connectRespBuf = new MemBuf; |
|
235 + // SQUID_TCP_SO_RCVBUF: we should not accumulate more than regular I/O buffer |
|
236 + // can hold since any CONNECT response leftovers have to fit into server.buf. |
|
237 + // 2*SQUID_TCP_SO_RCVBUF: HttpMsg::parse() zero-terminates, which uses space. |
|
238 + tunnelState->connectRespBuf->init(SQUID_TCP_SO_RCVBUF, 2*SQUID_TCP_SO_RCVBUF); |
|
239 + tunnelState->readConnectResponse(); |
|
240 |
|
241 - assert(tunnelState->waitingForConnectExchange()); |
|
242 - } |
|
243 + assert(tunnelState->waitingForConnectExchange()); |
|
244 |
|
245 AsyncCall::Pointer timeoutCall = commCbCall(5, 4, "tunnelTimeout", |
|
246 CommTimeoutCbPtrFun(tunnelTimeout, tunnelState)); |
|
247 @@ -1219,7 +1240,7 @@ |
|
248 |
|
249 AsyncCall::Pointer call = commCbCall(5,5, "tunnelConnectedWriteDone", |
|
250 CommIoCbPtrFun(tunnelConnectedWriteDone, tunnelState)); |
|
251 - Comm::Write(tunnelState->client.conn, buf.content(), buf.contentSize(), call, NULL); |
|
252 + tunnelState->client.write(buf.content(), buf.contentSize(), call, NULL); |
|
253 } |
|
254 #endif //USE_OPENSSL |
|
255 |