aboutsummaryrefslogtreecommitdiffstats
path: root/main/squid/CVE-2014-0128.patch
blob: 7861df1335d03f6fd08422d2d77fee95a05f810e (plain)
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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
diff -rNU 20 ../squid-3.2.11-o/src/client_side.cc ./src/client_side.cc
--- ../squid-3.2.11-o/src/client_side.cc	2013-04-30 06:47:06.000000000 +0200
+++ ./src/client_side.cc	2014-04-09 15:14:50.000000000 +0200
@@ -1249,43 +1249,41 @@
 
     /* hits only - upstream peer determines correct behaviour on misses, and client_side_reply determines
      * hits candidates
      */
     else if (logTypeIsATcpHit(http->logType) && http->request->header.has(HDR_IF_RANGE) && !clientIfRangeMatch(http, rep))
         range_err = "If-Range match failed";
     else if (!http->request->range->canonize(rep))
         range_err = "canonization failed";
     else if (http->request->range->isComplex())
         range_err = "too complex range header";
     else if (!logTypeIsATcpHit(http->logType) && http->request->range->offsetLimitExceeded(roffLimit))
         range_err = "range outside range_offset_limit";
 
     /* get rid of our range specs on error */
     if (range_err) {
         /* XXX We do this here because we need canonisation etc. However, this current
          * code will lead to incorrect store offset requests - the store will have the
          * offset data, but we won't be requesting it.
          * So, we can either re-request, or generate an error
          */
-        debugs(33, 3, "clientBuildRangeHeader: will not do ranges: " << range_err << ".");
-        delete http->request->range;
-        http->request->range = NULL;
+	http->request->ignoreRange(range_err);
     } else {
         /* XXX: TODO: Review, this unconditional set may be wrong. - TODO: review. */
         httpStatusLineSet(&rep->sline, rep->sline.version,
                           HTTP_PARTIAL_CONTENT, NULL);
         // web server responded with a valid, but unexpected range.
         // will (try-to) forward as-is.
         //TODO: we should cope with multirange request/responses
         bool replyMatchRequest = rep->content_range != NULL ?
                                  request->range->contains(rep->content_range->spec) :
                                  true;
         const int spec_count = http->request->range->specs.count;
         int64_t actual_clen = -1;
 
         debugs(33, 3, "clientBuildRangeHeader: range spec count: " <<
                spec_count << " virgin clen: " << rep->content_length);
         assert(spec_count > 0);
         /* append appropriate header(s) */
 
         if (spec_count == 1) {
             if (!replyMatchRequest) {
diff -rNU 20 ../squid-3.2.11-o/src/client_side_reply.cc ./src/client_side_reply.cc
--- ../squid-3.2.11-o/src/client_side_reply.cc	2013-04-30 06:47:06.000000000 +0200
+++ ./src/client_side_reply.cc	2014-04-09 15:21:57.000000000 +0200
@@ -102,40 +102,43 @@
     err_type err, http_status status, const HttpRequestMethod& method, char const *uri,
     Ip::Address &addr, HttpRequest * failedrequest, const char *unparsedrequest,
 #if USE_AUTH
     Auth::UserRequest::Pointer auth_user_request
 #else
     void*
 #endif
 )
 {
     ErrorState *errstate = clientBuildError(err, status, uri, addr, failedrequest);
 
     if (unparsedrequest)
         errstate->request_hdrs = xstrdup(unparsedrequest);
 
     if (status == HTTP_NOT_IMPLEMENTED && http->request)
         /* prevent confusion over whether we default to persistent or not */
         http->request->flags.proxy_keepalive = 0;
 
     http->al->http.code = errstate->httpStatus;
 
+    if (http->request)
+	http->request->ignoreRange("responding with a Squid-generated error");
+
     createStoreEntry(method, request_flags());
 #if USE_AUTH
     errstate->auth_user_request = auth_user_request;
 #endif
     assert(errstate->callback_data == NULL);
     errorAppendEntry(http->storeEntry(), errstate);
     /* Now the caller reads to get this */
 }
 
 void
 clientReplyContext::removeStoreReference(store_client ** scp,
         StoreEntry ** ep)
 {
     StoreEntry *e;
     store_client *sc_tmp = *scp;
 
     if ((e = *ep) != NULL) {
         *ep = NULL;
         storeUnregister(sc_tmp, e, this);
         *scp = NULL;
diff -rNU 20 ../squid-3.2.11-o/src/client_side_request.cc ./src/client_side_request.cc
--- ../squid-3.2.11-o/src/client_side_request.cc	2014-04-09 14:58:52.000000000 +0200
+++ ./src/client_side_request.cc	2014-04-09 15:22:41.000000000 +0200
@@ -1097,42 +1097,41 @@
             clientStreamNode *node = (clientStreamNode *)http->client_stream.tail->data;
             /* XXX: This is suboptimal. We should give the stream the range set,
              * and thereby let the top of the stream set the offset when the
              * size becomes known. As it is, we will end up requesting from 0
              * for evey -X range specification.
              * RBC - this may be somewhat wrong. We should probably set the range
              * iter up at this point.
              */
             node->readBuffer.offset = request->range->lowestOffset(0);
             http->range_iter.pos = request->range->begin();
             http->range_iter.valid = true;
         }
     }
 
     /* Only HEAD and GET requests permit a Range or Request-Range header.
      * If these headers appear on any other type of request, delete them now.
      */
     else {
         req_hdr->delById(HDR_RANGE);
         req_hdr->delById(HDR_REQUEST_RANGE);
-        delete request->range;
-        request->range = NULL;
+	request->ignoreRange("neither HEAD nor GET");
     }
 
     if (req_hdr->has(HDR_AUTHORIZATION))
         request->flags.auth = 1;
 
     clientCheckPinning(http);
 
     if (request->login[0] != '\0')
         request->flags.auth = 1;
 
     if (req_hdr->has(HDR_VIA)) {
         String s = req_hdr->getList(HDR_VIA);
         /*
          * ThisCache cannot be a member of Via header, "1.1 ThisCache" can.
          * Note ThisCache2 has a space prepended to the hostname so we don't
          * accidentally match super-domains.
          */
 
         if (strListIsSubstr(&s, ThisCache2, ',')) {
             debugObj(33, 1, "WARNING: Forwarding loop detected for:\n",
diff -rNU 20 ../squid-3.2.11-o/src/http.cc ./src/http.cc
--- ../squid-3.2.11-o/src/http.cc	2013-04-30 06:47:06.000000000 +0200
+++ ./src/http.cc	2014-04-09 15:23:31.000000000 +0200
@@ -1710,42 +1710,41 @@
     assert (hdr_out->owner == hoRequest);
 
     /* append our IMS header */
     if (request->lastmod > -1)
         hdr_out->putTime(HDR_IF_MODIFIED_SINCE, request->lastmod);
 
     bool we_do_ranges = decideIfWeDoRanges (request);
 
     String strConnection (hdr_in->getList(HDR_CONNECTION));
 
     while ((e = hdr_in->getEntry(&pos)))
         copyOneHeaderFromClientsideRequestToUpstreamRequest(e, strConnection, request, hdr_out, we_do_ranges, flags);
 
     /* Abstraction break: We should interpret multipart/byterange responses
      * into offset-length data, and this works around our inability to do so.
      */
     if (!we_do_ranges && request->multipartRangeRequest()) {
         /* don't cache the result */
         request->flags.cachable = 0;
         /* pretend it's not a range request */
-        delete request->range;
-        request->range = NULL;
+	request->ignoreRange("want to request the whole object");
         request->flags.range = 0;
     }
 
     /* append Via */
     if (Config.onoff.via) {
         String strVia;
         strVia = hdr_in->getList(HDR_VIA);
         snprintf(bbuf, BBUF_SZ, "%d.%d %s",
                  request->http_ver.major,
                  request->http_ver.minor, ThisCache);
         strListAdd(&strVia, bbuf, ',');
         hdr_out->putStr(HDR_VIA, strVia.termedBuf());
         strVia.clean();
     }
 
     if (request->flags.accelerated) {
         /* Append Surrogate-Capabilities */
         String strSurrogate(hdr_in->getList(HDR_SURROGATE_CAPABILITY));
 #if USE_SQUID_ESI
         snprintf(bbuf, BBUF_SZ, "%s=\"Surrogate/1.0 ESI/1.0\"", Config.Accel.surrogate_id);
diff -rNU 20 ../squid-3.2.11-o/src/HttpRequest.cc ./src/HttpRequest.cc
--- ../squid-3.2.11-o/src/HttpRequest.cc	2013-04-30 06:47:06.000000000 +0200
+++ ./src/HttpRequest.cc	2014-04-09 15:04:05.000000000 +0200
@@ -674,31 +674,46 @@
         return rangeOffsetLimit;
 
     rangeOffsetLimit = 0; // default value for rangeOffsetLimit
 
     ACLFilledChecklist ch(NULL, this, NULL);
     ch.src_addr = client_addr;
     ch.my_addr =  my_addr;
 
     for (acl_size_t *l = Config.rangeOffsetLimit; l; l = l -> next) {
         /* if there is no ACL list or if the ACLs listed match use this limit value */
         if (!l->aclList || ch.fastCheck(l->aclList) == ACCESS_ALLOWED) {
             debugs(58, 4, HERE << "rangeOffsetLimit=" << rangeOffsetLimit);
             rangeOffsetLimit = l->size; // may be -1
             break;
         }
     }
 
     return rangeOffsetLimit;
 }
 
+void
+HttpRequest::ignoreRange(const char *reason)
+{
+    if (range) {
+        debugs(73, 3, static_cast<void*>(range) << " for " << reason);
+        delete range;
+        range = NULL;
+    }
+    // Some callers also reset isRanged but it may not be safe for all callers:
+    // isRanged is used to determine whether a weak ETag comparison is allowed,
+    // and that check should not ignore the Range header if it was present.
+    // TODO: Some callers also delete HDR_RANGE, HDR_REQUEST_RANGE. Should we?
+}
+
+
 bool
 HttpRequest::canHandle1xx() const
 {
     // old clients do not support 1xx unless they sent Expect: 100-continue
     // (we reject all other HDR_EXPECT values so just check for HDR_EXPECT)
     if (http_ver <= HttpVersion(1,0) && !header.has(HDR_EXPECT))
         return false;
 
     // others must support 1xx control messages
     return true;
 }
diff -rNU 20 ../squid-3.2.11-o/src/HttpRequest.h ./src/HttpRequest.h
--- ../squid-3.2.11-o/src/HttpRequest.h	2013-04-30 06:47:06.000000000 +0200
+++ ./src/HttpRequest.h	2014-04-09 15:04:42.000000000 +0200
@@ -223,40 +223,43 @@
     void pack(Packer * p);
 
     static void httpRequestPack(void *obj, Packer *p);
 
     static HttpRequest * CreateFromUrlAndMethod(char * url, const HttpRequestMethod& method);
 
     static HttpRequest * CreateFromUrl(char * url);
 
     ConnStateData *pinnedConnection() {
         if (clientConnectionManager.valid() && clientConnectionManager->pinning.pinned)
             return clientConnectionManager.get();
         return NULL;
     }
 
     /**
      * The client connection manager, if known;
      * Used for any response actions needed directly to the client.
      * ie 1xx forwarding or connection pinning state changes
      */
     CbcPointer<ConnStateData> clientConnectionManager;
+    /// forgets about the cached Range header (for a reason)
+    void ignoreRange(const char *reason);
+
 
     int64_t getRangeOffsetLimit(); /* the result of this function gets cached in rangeOffsetLimit */
 
 private:
     const char *packableURI(bool full_uri) const;
 
     mutable int64_t rangeOffsetLimit;  /* caches the result of getRangeOffsetLimit */
 
 protected:
     virtual void packFirstLineInto(Packer * p, bool full_uri) const;
 
     virtual bool sanityCheckStartLine(MemBuf *buf, const size_t hdr_len, http_status *error);
 
     virtual void hdrCacheInit();
 
     virtual bool inheritProperties(const HttpMsg *aMsg);
 };
 
 MEMPROXY_CLASS_INLINE(HttpRequest);