]> git.pld-linux.org Git - packages/apache.git/blob - 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
1 Index: 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.113179 seconds and 3 git commands to generate.