]> git.pld-linux.org Git - packages/apache.git/blame - byterange-no-merge.2.2.diff
- rel 4; use currently available (unofficial) fix for latest 'Range' vuln.
[packages/apache.git] / byterange-no-merge.2.2.diff
CommitLineData
4d51c0eb
AM
1Index: modules/http/byterange_filter.c
2===================================================================
3--- modules/http/byterange_filter.c (Revision 1162579)
4+++ modules/http/byterange_filter.c (Arbeitskopie)
5@@ -55,66 +55,9 @@
6 #include <unistd.h>
7 #endif
8
9-static int parse_byterange(char *range, apr_off_t clength,
10- apr_off_t *start, apr_off_t *end)
11-{
12- char *dash = strchr(range, '-');
13- char *errp;
14- apr_off_t number;
15+static int ap_set_byterange(request_rec *r, apr_off_t clength,
16+ apr_array_header_t **indexes);
17
18- if (!dash) {
19- return 0;
20- }
21-
22- if ((dash == range)) {
23- /* In the form "-5" */
24- if (apr_strtoff(&number, dash+1, &errp, 10) || *errp) {
25- return 0;
26- }
27- *start = clength - number;
28- *end = clength - 1;
29- }
30- else {
31- *dash++ = '\0';
32- if (apr_strtoff(&number, range, &errp, 10) || *errp) {
33- return 0;
34- }
35- *start = number;
36- if (*dash) {
37- if (apr_strtoff(&number, dash, &errp, 10) || *errp) {
38- return 0;
39- }
40- *end = number;
41- }
42- else { /* "5-" */
43- *end = clength - 1;
44- }
45- }
46-
47- if (*start < 0) {
48- *start = 0;
49- }
50-
51- if (*end >= clength) {
52- *end = clength - 1;
53- }
54-
55- if (*start > *end) {
56- return -1;
57- }
58-
59- return (*start > 0 || *end < clength);
60-}
61-
62-static int ap_set_byterange(request_rec *r);
63-
64-typedef struct byterange_ctx {
65- apr_bucket_brigade *bb;
66- int num_ranges;
67- char *boundary;
68- char *bound_head;
69-} byterange_ctx;
70-
71 /*
72 * Here we try to be compatible with clients that want multipart/x-byteranges
73 * instead of multipart/byteranges (also see above), as per HTTP/1.1. We
74@@ -131,28 +74,205 @@
75 }
76
77 #define BYTERANGE_FMT "%" APR_OFF_T_FMT "-%" APR_OFF_T_FMT "/%" APR_OFF_T_FMT
78-#define PARTITION_ERR_FMT "apr_brigade_partition() failed " \
79- "[%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT "]"
80
81+static apr_status_t copy_brigade_range(apr_bucket_brigade *bb,
82+ apr_bucket_brigade *bbout,
83+ apr_off_t start,
84+ apr_off_t end)
85+{
86+ apr_bucket *first = NULL, *last = NULL, *out_first = NULL, *e;
87+ apr_uint64_t pos = 0, off_first = 0, off_last = 0;
88+ apr_status_t rv;
89+ const char *s;
90+ apr_size_t len;
91+ apr_uint64_t start64, end64;
92+ apr_off_t pofft = 0;
93+
94+ /*
95+ * Once we know that start and end are >= 0 convert everything to apr_uint64_t.
96+ * See the comments in apr_brigade_partition why.
97+ * In short apr_off_t (for values >= 0)and apr_size_t fit into apr_uint64_t.
98+ */
99+ start64 = (apr_uint64_t)start;
100+ end64 = (apr_uint64_t)end;
101+
102+ if (start < 0 || end < 0 || start64 > end64)
103+ return APR_EINVAL;
104+
105+ for (e = APR_BRIGADE_FIRST(bb);
106+ e != APR_BRIGADE_SENTINEL(bb);
107+ e = APR_BUCKET_NEXT(e))
108+ {
109+ apr_uint64_t elen64;
110+ /* we know that no bucket has undefined length (-1) */
111+ AP_DEBUG_ASSERT(e->length != (apr_size_t)(-1));
112+ elen64 = (apr_uint64_t)e->length;
113+ if (!first && (elen64 + pos > start64)) {
114+ first = e;
115+ off_first = pos;
116+ }
117+ if (elen64 + pos > end64) {
118+ last = e;
119+ off_last = pos;
120+ break;
121+ }
122+ pos += elen64;
123+ }
124+ if (!first || !last)
125+ return APR_EINVAL;
126+
127+ e = first;
128+ while (1)
129+ {
130+ apr_bucket *copy;
131+ AP_DEBUG_ASSERT(e != APR_BRIGADE_SENTINEL(bb));
132+ rv = apr_bucket_copy(e, &copy);
133+ if (rv != APR_SUCCESS) {
134+ apr_brigade_cleanup(bbout);
135+ return rv;
136+ }
137+
138+ APR_BRIGADE_INSERT_TAIL(bbout, copy);
139+ if (e == first) {
140+ if (off_first != start64) {
141+ rv = apr_bucket_split(copy, (apr_size_t)(start64 - off_first));
142+ if (rv == APR_ENOTIMPL) {
143+ rv = apr_bucket_read(copy, &s, &len, APR_BLOCK_READ);
144+ if (rv != APR_SUCCESS) {
145+ apr_brigade_cleanup(bbout);
146+ return rv;
147+ }
148+ /*
149+ * The read above might have morphed copy in a bucket
150+ * of shorter length. So read and delete until we reached
151+ * the correct bucket for splitting.
152+ */
153+ while (start64 - off_first > (apr_uint64_t)copy->length) {
154+ apr_bucket *tmp;
155+ int i = 0;
156+ if (i++ >= 99999)
157+ return APR_EINVAL;
158+
159+ tmp = APR_BUCKET_NEXT(copy);
160+ off_first += (apr_uint64_t)copy->length;
161+ APR_BUCKET_REMOVE(copy);
162+ apr_bucket_destroy(copy);
163+ copy = tmp;
164+ rv = apr_bucket_read(copy, &s, &len, APR_BLOCK_READ);
165+ if (rv != APR_SUCCESS) {
166+ apr_brigade_cleanup(bbout);
167+ return rv;
168+ }
169+ }
170+ if (start64 > off_first) {
171+ rv = apr_bucket_split(copy, (apr_size_t)(start64 - off_first));
172+ if (rv != APR_SUCCESS) {
173+ apr_brigade_cleanup(bbout);
174+ return rv;
175+ }
176+ }
177+ else {
178+ copy = APR_BUCKET_PREV(copy);
179+ }
180+ }
181+ else if (rv != APR_SUCCESS) {
182+ apr_brigade_cleanup(bbout);
183+ return rv;
184+ }
185+ out_first = APR_BUCKET_NEXT(copy);
186+ APR_BUCKET_REMOVE(copy);
187+ apr_bucket_destroy(copy);
188+ }
189+ else {
190+ out_first = copy;
191+ }
192+ }
193+ if (e == last) {
194+ if (e == first) {
195+ off_last += start64 - off_first;
196+ copy = out_first;
197+ }
198+ if (end64 - off_last != (apr_uint64_t)e->length) {
199+ rv = apr_bucket_split(copy, (apr_size_t)(end64 + 1 - off_last));
200+ if (rv == APR_ENOTIMPL) {
201+ rv = apr_bucket_read(copy, &s, &len, APR_BLOCK_READ);
202+ if (rv != APR_SUCCESS) {
203+ apr_brigade_cleanup(bbout);
204+ return rv;
205+ }
206+ /*
207+ * The read above might have morphed copy in a bucket
208+ * of shorter length. So read until we reached
209+ * the correct bucket for splitting.
210+ */
211+ while (end64 + 1 - off_last > (apr_uint64_t)copy->length) {
212+ off_last += (apr_uint64_t)copy->length;
213+ copy = APR_BUCKET_NEXT(copy);
214+ rv = apr_bucket_read(copy, &s, &len, APR_BLOCK_READ);
215+ if (rv != APR_SUCCESS) {
216+ apr_brigade_cleanup(bbout);
217+ return rv;
218+ }
219+ }
220+ if (end64 < off_last + (apr_uint64_t)copy->length - 1) {
221+ rv = apr_bucket_split(copy, end64 + 1 - off_last);
222+ if (rv != APR_SUCCESS) {
223+ apr_brigade_cleanup(bbout);
224+ return rv;
225+ }
226+ }
227+ }
228+ else if (rv != APR_SUCCESS) {
229+ apr_brigade_cleanup(bbout);
230+ return rv;
231+ }
232+ copy = APR_BUCKET_NEXT(copy);
233+ if (copy != APR_BRIGADE_SENTINEL(bbout)) {
234+ APR_BUCKET_REMOVE(copy);
235+ apr_bucket_destroy(copy);
236+ }
237+ }
238+ break;
239+ }
240+ e = APR_BUCKET_NEXT(e);
241+ }
242+
243+ AP_DEBUG_ASSERT(APR_SUCCESS == apr_brigade_length(bbout, 1, &pofft));
244+ pos = (apr_uint64_t)pofft;
245+ AP_DEBUG_ASSERT(pos == end64 - start64 + 1);
246+ return APR_SUCCESS;
247+}
248+
249+typedef struct indexes_t {
250+ apr_off_t start;
251+ apr_off_t end;
252+} indexes_t;
253+
254 AP_CORE_DECLARE_NONSTD(apr_status_t) ap_byterange_filter(ap_filter_t *f,
255 apr_bucket_brigade *bb)
256 {
257-#define MIN_LENGTH(len1, len2) ((len1 > len2) ? len2 : len1)
258 request_rec *r = f->r;
259 conn_rec *c = r->connection;
260- byterange_ctx *ctx;
261 apr_bucket *e;
262 apr_bucket_brigade *bsend;
263+ apr_bucket_brigade *tmpbb;
264 apr_off_t range_start;
265 apr_off_t range_end;
266- char *current;
267 apr_off_t clength = 0;
268 apr_status_t rv;
269 int found = 0;
270 int num_ranges;
271+ char *boundary = NULL;
272+ char *bound_head = NULL;
273+ apr_array_header_t *indexes;
274+ indexes_t *idx;
275+ int original_status;
276+ int i;
277
278- /* Iterate through the brigade until reaching EOS or a bucket with
279- * unknown length. */
280+ /*
281+ * Iterate through the brigade until reaching EOS or a bucket with
282+ * unknown length.
283+ */
284 for (e = APR_BRIGADE_FIRST(bb);
285 (e != APR_BRIGADE_SENTINEL(bb) && !APR_BUCKET_IS_EOS(e)
286 && e->length != (apr_size_t)-1);
287@@ -160,90 +280,80 @@
288 clength += e->length;
289 }
290
291- /* Don't attempt to do byte range work if this brigade doesn't
292+ /*
293+ * Don't attempt to do byte range work if this brigade doesn't
294 * contain an EOS, or if any of the buckets has an unknown length;
295 * this avoids the cases where it is expensive to perform
296- * byteranging (i.e. may require arbitrary amounts of memory). */
297+ * byteranging (i.e. may require arbitrary amounts of memory).
298+ */
299 if (!APR_BUCKET_IS_EOS(e) || clength <= 0) {
300 ap_remove_output_filter(f);
301 return ap_pass_brigade(f->next, bb);
302 }
303
304- num_ranges = ap_set_byterange(r);
305+ original_status = r->status;
306+ num_ranges = ap_set_byterange(r, clength, &indexes);
307
308 /* We have nothing to do, get out of the way. */
309 if (num_ranges == 0) {
310+ r->status = original_status;
311 ap_remove_output_filter(f);
312 return ap_pass_brigade(f->next, bb);
313 }
314
315- ctx = apr_pcalloc(r->pool, sizeof(*ctx));
316- ctx->num_ranges = num_ranges;
317- /* create a brigade in case we never call ap_save_brigade() */
318- ctx->bb = apr_brigade_create(r->pool, c->bucket_alloc);
319-
320- if (ctx->num_ranges > 1) {
321+ if (num_ranges > 1) {
322 /* Is ap_make_content_type required here? */
323 const char *orig_ct = ap_make_content_type(r, r->content_type);
324- ctx->boundary = apr_psprintf(r->pool, "%" APR_UINT64_T_HEX_FMT "%lx",
325- (apr_uint64_t)r->request_time, (long) getpid());
326+ boundary = apr_psprintf(r->pool, "%" APR_UINT64_T_HEX_FMT "%lx",
327+ (apr_uint64_t)r->request_time, (long) getpid());
328
329 ap_set_content_type(r, apr_pstrcat(r->pool, "multipart",
330 use_range_x(r) ? "/x-" : "/",
331 "byteranges; boundary=",
332- ctx->boundary, NULL));
333+ boundary, NULL));
334
335 if (strcasecmp(orig_ct, NO_CONTENT_TYPE)) {
336- ctx->bound_head = apr_pstrcat(r->pool,
337- CRLF "--", ctx->boundary,
338- CRLF "Content-type: ",
339- orig_ct,
340- CRLF "Content-range: bytes ",
341- NULL);
342+ bound_head = apr_pstrcat(r->pool,
343+ CRLF "--", boundary,
344+ CRLF "Content-type: ",
345+ orig_ct,
346+ CRLF "Content-range: bytes ",
347+ NULL);
348 }
349 else {
350 /* if we have no type for the content, do our best */
351- ctx->bound_head = apr_pstrcat(r->pool,
352- CRLF "--", ctx->boundary,
353- CRLF "Content-range: bytes ",
354- NULL);
355+ bound_head = apr_pstrcat(r->pool,
356+ CRLF "--", boundary,
357+ CRLF "Content-range: bytes ",
358+ NULL);
359 }
360- ap_xlate_proto_to_ascii(ctx->bound_head, strlen(ctx->bound_head));
361+ ap_xlate_proto_to_ascii(bound_head, strlen(bound_head));
362 }
363
364 /* this brigade holds what we will be sending */
365 bsend = apr_brigade_create(r->pool, c->bucket_alloc);
366+ tmpbb = apr_brigade_create(r->pool, c->bucket_alloc);
367
368- while ((current = ap_getword(r->pool, &r->range, ','))
369- && (rv = parse_byterange(current, clength, &range_start,
370- &range_end))) {
371- apr_bucket *e2;
372- apr_bucket *ec;
373+ idx = (indexes_t *)indexes->elts;
374+ for (i = 0; i < indexes->nelts; i++, idx++) {
375+ range_start = idx->start;
376+ range_end = idx->end;
377
378- if (rv == -1) {
379- continue;
380- }
381-
382- /* These calls to apr_brigage_partition should only fail in
383- * pathological cases, e.g. a file being truncated whilst
384- * being served. */
385- if ((rv = apr_brigade_partition(bb, range_start, &ec)) != APR_SUCCESS) {
386+ rv = copy_brigade_range(bb, tmpbb, range_start, range_end);
387+ if (rv != APR_SUCCESS ) {
388 ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
389- PARTITION_ERR_FMT, range_start, clength);
390+ "copy_brigade_range() failed [%" APR_OFF_T_FMT
391+ "-%" APR_OFF_T_FMT ",%" APR_OFF_T_FMT "]",
392+ range_start, range_end, clength);
393 continue;
394 }
395- if ((rv = apr_brigade_partition(bb, range_end+1, &e2)) != APR_SUCCESS) {
396- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, r,
397- PARTITION_ERR_FMT, range_end+1, clength);
398- continue;
399- }
400-
401 found = 1;
402
403- /* For single range requests, we must produce Content-Range header.
404+ /*
405+ * For single range requests, we must produce Content-Range header.
406 * Otherwise, we need to produce the multipart boundaries.
407 */
408- if (ctx->num_ranges == 1) {
409+ if (num_ranges == 1) {
410 apr_table_setn(r->headers_out, "Content-Range",
411 apr_psprintf(r->pool, "bytes " BYTERANGE_FMT,
412 range_start, range_end, clength));
413@@ -251,7 +361,7 @@
414 else {
415 char *ts;
416
417- e = apr_bucket_pool_create(ctx->bound_head, strlen(ctx->bound_head),
418+ e = apr_bucket_pool_create(bound_head, strlen(bound_head),
419 r->pool, c->bucket_alloc);
420 APR_BRIGADE_INSERT_TAIL(bsend, e);
421
422@@ -263,23 +373,19 @@
423 APR_BRIGADE_INSERT_TAIL(bsend, e);
424 }
425
426- do {
427- apr_bucket *foo;
428- const char *str;
429- apr_size_t len;
430-
431- if (apr_bucket_copy(ec, &foo) != APR_SUCCESS) {
432- /* As above; this should not fail since the bucket has
433- * a known length, but just to be sure, this takes
434- * care of uncopyable buckets that do somehow manage
435- * to slip through. */
436- /* XXX: check for failure? */
437- apr_bucket_read(ec, &str, &len, APR_BLOCK_READ);
438- apr_bucket_copy(ec, &foo);
439- }
440- APR_BRIGADE_INSERT_TAIL(bsend, foo);
441- ec = APR_BUCKET_NEXT(ec);
442- } while (ec != e2);
443+ APR_BRIGADE_CONCAT(bsend, tmpbb);
444+ if (i && i % 32 == 0) {
445+ /*
446+ * Every now and then, pass what we have down the filter chain.
447+ * In this case, the content-length filter cannot calculate and
448+ * set the content length and we must remove any Content-Length
449+ * header already present.
450+ */
451+ apr_table_unset(r->headers_out, "Content-Length");
452+ if ((rv = ap_pass_brigade(f->next, bsend)) != APR_SUCCESS)
453+ return rv;
454+ apr_brigade_cleanup(bsend);
455+ }
456 }
457
458 if (found == 0) {
459@@ -294,11 +400,11 @@
460 return ap_pass_brigade(f->next, bsend);
461 }
462
463- if (ctx->num_ranges > 1) {
464+ if (num_ranges > 1) {
465 char *end;
466
467 /* add the final boundary */
468- end = apr_pstrcat(r->pool, CRLF "--", ctx->boundary, "--" CRLF, NULL);
469+ end = apr_pstrcat(r->pool, CRLF "--", boundary, "--" CRLF, NULL);
470 ap_xlate_proto_to_ascii(end, strlen(end));
471 e = apr_bucket_pool_create(end, strlen(end), r->pool, c->bucket_alloc);
472 APR_BRIGADE_INSERT_TAIL(bsend, e);
473@@ -309,24 +415,32 @@
474
475 /* we're done with the original content - all of our data is in bsend. */
476 apr_brigade_cleanup(bb);
477+ apr_brigade_destroy(tmpbb);
478
479 /* send our multipart output */
480 return ap_pass_brigade(f->next, bsend);
481 }
482
483-static int ap_set_byterange(request_rec *r)
484+static int ap_set_byterange(request_rec *r, apr_off_t clength,
485+ apr_array_header_t **indexes)
486 {
487 const char *range;
488 const char *if_range;
489 const char *match;
490 const char *ct;
491- int num_ranges;
492+ char *cur;
493+ int num_ranges = 0;
494+ apr_off_t sum_lengths = 0;
495+ indexes_t *idx;
496+ int ranges = 1;
497+ const char *it;
498
499 if (r->assbackwards) {
500 return 0;
501 }
502
503- /* Check for Range request-header (HTTP/1.1) or Request-Range for
504+ /*
505+ * Check for Range request-header (HTTP/1.1) or Request-Range for
506 * backwards-compatibility with second-draft Luotonen/Franks
507 * byte-ranges (e.g. Netscape Navigator 2-3).
508 *
509@@ -356,7 +470,8 @@
510 return 0;
511 }
512
513- /* Check the If-Range header for Etag or Date.
514+ /*
515+ * Check the If-Range header for Etag or Date.
516 * Note that this check will return false (as required) if either
517 * of the two etags are weak.
518 */
519@@ -373,17 +488,77 @@
520 }
521 }
522
523- if (!ap_strchr_c(range, ',')) {
524- /* a single range */
525- num_ranges = 1;
526+ range += 6;
527+ it = range;
528+ while (*it) {
529+ if (*it++ == ',') {
530+ ranges++;
531+ }
532 }
533- else {
534- /* a multiple range */
535- num_ranges = 2;
536+ it = range;
537+ *indexes = apr_array_make(r->pool, ranges, sizeof(indexes_t));
538+ while ((cur = ap_getword(r->pool, &range, ','))) {
539+ char *dash;
540+ char *errp;
541+ apr_off_t number, start, end;
542+
543+ if (!(dash = strchr(cur, '-'))) {
544+ break;
545+ }
546+
547+ if (dash == range) {
548+ /* In the form "-5" */
549+ if (apr_strtoff(&number, dash+1, &errp, 10) || *errp) {
550+ break;
551+ }
552+ start = clength - number;
553+ end = clength - 1;
554+ }
555+ else {
556+ *dash++ = '\0';
557+ if (apr_strtoff(&number, cur, &errp, 10) || *errp) {
558+ break;
559+ }
560+ start = number;
561+ if (*dash) {
562+ if (apr_strtoff(&number, dash, &errp, 10) || *errp) {
563+ break;
564+ }
565+ end = number;
566+ }
567+ else { /* "5-" */
568+ end = clength - 1;
569+ }
570+ }
571+
572+ if (start < 0) {
573+ start = 0;
574+ }
575+ if (end >= clength) {
576+ end = clength - 1;
577+ }
578+
579+ if (start > end) {
580+ /* ignore? count? */
581+ break;
582+ }
583+
584+ idx = (indexes_t *)apr_array_push(*indexes);
585+ idx->start = start;
586+ idx->end = end;
587+ sum_lengths += end - start + 1;
588+ /* new set again */
589+ num_ranges++;
590 }
591
592+ if (sum_lengths >= clength) {
593+ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r,
594+ "Sum of ranges not smaller than file, ignoring.");
595+ return 0;
596+ }
597+
598 r->status = HTTP_PARTIAL_CONTENT;
599- r->range = range + 6;
600+ r->range = it;
601
602 return num_ranges;
603 }
This page took 0.134458 seconds and 4 git commands to generate.