From: William A. Rowe Jr Date: Fri, 22 Aug 2014 11:37:19 +0000 (+0000) Subject: mod_deflate: Handle Zlib header and validation bytes received in multiple X-Git-Tag: 2.2.28~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=84741df70dbdc9259c380adeb324732524b6a17d;p=thirdparty%2Fapache%2Fhttpd.git mod_deflate: Handle Zlib header and validation bytes received in multiple chunks. PR: 46146, 55666 Submitted by: Yann Ylavic Reviewed by: ylavic, wrowe, rpluem Backports: r1572655, r1572663, r1572668, r1572669, r1572670, r1572671, r1573224, r1586745, r1587594, r1587639, r1590509, r1603156, r1604353 git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.2.x@1619753 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index d38b8383677..0a232afd79f 100644 --- a/CHANGES +++ b/CHANGES @@ -20,6 +20,9 @@ Changes with Apache 2.2.28 *) SECURITY: CVE-2014-0226 (cve.mitre.org) Fix a race condition in scoreboard handling, which could lead to a heap buffer overflow. [Joe Orton, Eric Covener, Jeff Trawick] + + *) mod_deflate: Handle Zlib header and validation bytes received in multiple + chunks. PR 46146. [Yann Ylavic] *) SECURITY: CVE-2013-5704 (cve.mitre.org) core: HTTP trailers could be used to replace HTTP headers diff --git a/modules/filters/mod_deflate.c b/modules/filters/mod_deflate.c index 38cd4545f49..de3387d6aa3 100644 --- a/modules/filters/mod_deflate.c +++ b/modules/filters/mod_deflate.c @@ -360,9 +360,15 @@ typedef struct deflate_ctx_t int (*libz_end_func)(z_streamp); unsigned char *validation_buffer; apr_size_t validation_buffer_length; - int inflate_init; + char header[10]; /* sizeof(gzip_header) */ + apr_size_t header_len; + int zlib_flags; int ratio_hits; apr_off_t inflate_total; + unsigned int consume_pos, + consume_len; + unsigned int filter_init:1; + unsigned int done:1; } deflate_ctx; /* Check whether the (inflate) ratio exceeds the configured limit/burst. */ @@ -749,6 +755,9 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, zRC = flush_libz_buffer(ctx, c, f->c->bucket_alloc, deflate, Z_SYNC_FLUSH, NO_UPDATE_CRC); if (zRC != Z_OK) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Zlib error %d flushing zlib output buffer (%s)", + zRC, ctx->stream.msg); return APR_EGENERAL; } @@ -774,6 +783,14 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, /* read */ apr_bucket_read(e, &data, &len, APR_BLOCK_READ); + if (!len) { + apr_bucket_delete(e); + continue; + } + if (len > APR_INT32_MAX) { + apr_bucket_split(e, APR_INT32_MAX); + apr_bucket_read(e, &data, &len, APR_BLOCK_READ); + } /* This crc32 function is from zlib. */ ctx->crc = crc32(ctx->crc, (const Bytef *)data, len); @@ -805,6 +822,9 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, zRC = deflate(&(ctx->stream), Z_NO_FLUSH); if (zRC != Z_OK) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Zlib error %d deflating data (%s)", zRC, + ctx->stream.msg); return APR_EGENERAL; } } @@ -816,6 +836,96 @@ static apr_status_t deflate_out_filter(ap_filter_t *f, return APR_SUCCESS; } +static apr_status_t consume_zlib_flags(deflate_ctx *ctx, + const char **data, apr_size_t *len) +{ + if ((ctx->zlib_flags & EXTRA_FIELD)) { + /* Consume 2 bytes length prefixed data. */ + if (ctx->consume_pos == 0) { + if (!*len) { + return APR_INCOMPLETE; + } + ctx->consume_len = (unsigned int)**data; + ctx->consume_pos++; + ++*data; + --*len; + } + if (ctx->consume_pos == 1) { + if (!*len) { + return APR_INCOMPLETE; + } + ctx->consume_len += ((unsigned int)**data) << 8; + ctx->consume_pos++; + ++*data; + --*len; + } + if (*len < ctx->consume_len) { + ctx->consume_len -= *len; + *len = 0; + return APR_INCOMPLETE; + } + *data += ctx->consume_len; + *len -= ctx->consume_len; + + ctx->consume_len = ctx->consume_pos = 0; + ctx->zlib_flags &= ~EXTRA_FIELD; + } + + if ((ctx->zlib_flags & ORIG_NAME)) { + /* Consume nul terminated string. */ + while (*len && **data) { + ++*data; + --*len; + } + if (!*len) { + return APR_INCOMPLETE; + } + /* .. and nul. */ + ++*data; + --*len; + + ctx->zlib_flags &= ~ORIG_NAME; + } + + if ((ctx->zlib_flags & COMMENT)) { + /* Consume nul terminated string. */ + while (*len && **data) { + ++*data; + --*len; + } + if (!*len) { + return APR_INCOMPLETE; + } + /* .. and nul. */ + ++*data; + --*len; + + ctx->zlib_flags &= ~COMMENT; + } + + if ((ctx->zlib_flags & HEAD_CRC)) { + /* Consume CRC16 (2 octets). */ + if (ctx->consume_pos == 0) { + if (!*len) { + return APR_INCOMPLETE; + } + ctx->consume_pos++; + ++*data; + --*len; + } + if (!*len) { + return APR_INCOMPLETE; + } + ++*data; + --*len; + + ctx->consume_pos = 0; + ctx->zlib_flags &= ~HEAD_CRC; + } + + return APR_SUCCESS; +} + /* This is the deflate input filter (inflates). */ static apr_status_t deflate_in_filter(ap_filter_t *f, apr_bucket_brigade *bb, @@ -840,64 +950,98 @@ static apr_status_t deflate_in_filter(ap_filter_t *f, c = ap_get_module_config(r->server->module_config, &deflate_module); dc = ap_get_module_config(r->per_dir_config, &deflate_module); - if (!ctx) { - char deflate_hdr[10]; + if (!ctx || ctx->header_len < sizeof(ctx->header)) { apr_size_t len; - /* only work on main request/no subrequests */ - if (!ap_is_initial_req(r)) { - ap_remove_input_filter(f); - return ap_get_brigade(f->next, bb, mode, block, readbytes); - } + if (!ctx) { + /* only work on main request/no subrequests */ + if (!ap_is_initial_req(r)) { + ap_remove_input_filter(f); + return ap_get_brigade(f->next, bb, mode, block, readbytes); + } - /* We can't operate on Content-Ranges */ - if (apr_table_get(r->headers_in, "Content-Range") != NULL) { - ap_remove_input_filter(f); - return ap_get_brigade(f->next, bb, mode, block, readbytes); - } + /* We can't operate on Content-Ranges */ + if (apr_table_get(r->headers_in, "Content-Range") != NULL) { + ap_remove_input_filter(f); + return ap_get_brigade(f->next, bb, mode, block, readbytes); + } - /* Check whether request body is gzipped. - * - * If it is, we're transforming the contents, invalidating - * some request headers including Content-Encoding. - * - * If not, we just remove ourself. - */ - if (check_gzip(r, r->headers_in, NULL) == 0) { - ap_remove_input_filter(f); - return ap_get_brigade(f->next, bb, mode, block, readbytes); + /* Check whether request body is gzipped. + * + * If it is, we're transforming the contents, invalidating + * some request headers including Content-Encoding. + * + * If not, we just remove ourself. + */ + if (check_gzip(r, r->headers_in, NULL) == 0) { + ap_remove_input_filter(f); + return ap_get_brigade(f->next, bb, mode, block, readbytes); + } + + f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx)); + ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc); + ctx->proc_bb = apr_brigade_create(r->pool, f->c->bucket_alloc); + ctx->buffer = apr_palloc(r->pool, c->bufferSize); } - f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx)); - ctx->bb = apr_brigade_create(r->pool, f->c->bucket_alloc); - ctx->proc_bb = apr_brigade_create(r->pool, f->c->bucket_alloc); - ctx->buffer = apr_palloc(r->pool, c->bufferSize); + do { + apr_brigade_cleanup(ctx->bb); - rv = ap_get_brigade(f->next, ctx->bb, AP_MODE_READBYTES, block, 10); - if (rv != APR_SUCCESS) { - return rv; - } + len = sizeof(ctx->header) - ctx->header_len; + rv = ap_get_brigade(f->next, ctx->bb, AP_MODE_READBYTES, block, + len); - apr_table_unset(r->headers_in, "Content-Length"); - apr_table_unset(r->headers_in, "Content-MD5"); + /* ap_get_brigade may return success with an empty brigade for + * a non-blocking read which would block (an empty brigade for + * a blocking read is an issue which is simply forwarded here). + */ + if (rv != APR_SUCCESS || APR_BRIGADE_EMPTY(ctx->bb)) { + return rv; + } - len = 10; - rv = apr_brigade_flatten(ctx->bb, deflate_hdr, &len); - if (rv != APR_SUCCESS) { - return rv; - } + /* zero length body? step aside */ + bkt = APR_BRIGADE_FIRST(ctx->bb); + if (APR_BUCKET_IS_EOS(bkt)) { + if (ctx->header_len) { + /* If the header was (partially) read it's an error, this + * is not a gzip Content-Encoding, as claimed. + */ + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Encountered premature end-of-stream while " + "reading inflate header"); + return APR_EGENERAL; + } + APR_BUCKET_REMOVE(bkt); + APR_BRIGADE_INSERT_TAIL(bb, bkt); + ap_remove_input_filter(f); + return APR_SUCCESS; + } + + rv = apr_brigade_flatten(ctx->bb, + ctx->header + ctx->header_len, &len); + if (rv != APR_SUCCESS) { + return rv; + } + if (len && !ctx->header_len) { + apr_table_unset(r->headers_in, "Content-Length"); + apr_table_unset(r->headers_in, "Content-MD5"); + } + ctx->header_len += len; + + } while (ctx->header_len < sizeof(ctx->header)); /* We didn't get the magic bytes. */ - if (len != 10 || - deflate_hdr[0] != deflate_magic[0] || - deflate_hdr[1] != deflate_magic[1]) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Failed to inflate input: wrong/partial magic bytes"); + if (ctx->header[0] != deflate_magic[0] || + ctx->header[1] != deflate_magic[1]) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, + "Zlib: Invalid header"); return APR_EGENERAL; } - /* We can't handle flags for now. */ - if (deflate_hdr[3] != 0) { - ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Failed to inflate input: cannot handle deflate flags"); + ctx->zlib_flags = ctx->header[3]; + if ((ctx->zlib_flags & RESERVED)) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, + "Zlib: Invalid flags %02x", ctx->zlib_flags); return APR_EGENERAL; } @@ -930,8 +1074,15 @@ static apr_status_t deflate_in_filter(ap_filter_t *f, if (APR_BRIGADE_EMPTY(ctx->proc_bb)) { rv = ap_get_brigade(f->next, ctx->bb, mode, block, readbytes); + /* Don't terminate on EAGAIN (or success with an empty brigade in + * non-blocking mode), just return focus. + */ + if (block == APR_NONBLOCK_READ + && (APR_STATUS_IS_EAGAIN(rv) + || (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(ctx->bb)))) { + return rv; + } if (rv != APR_SUCCESS) { - /* What about APR_EAGAIN errors? */ inflateEnd(&ctx->stream); return rv; } @@ -945,14 +1096,26 @@ static apr_status_t deflate_in_filter(ap_filter_t *f, /* If we actually see the EOS, that means we screwed up! */ if (APR_BUCKET_IS_EOS(bkt)) { - inflateEnd(&ctx->stream); - return APR_EGENERAL; + if (!ctx->done) { + inflateEnd(&ctx->stream); + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Encountered premature end-of-stream while inflating"); + return APR_EGENERAL; + } + + /* Move everything to the returning brigade. */ + APR_BUCKET_REMOVE(bkt); + APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, bkt); + break; } if (APR_BUCKET_IS_FLUSH(bkt)) { - apr_bucket *tmp_heap; + apr_bucket *tmp_b; zRC = inflate(&(ctx->stream), Z_SYNC_FLUSH); if (zRC != Z_OK) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, + "Zlib error %d inflating data (%s)", zRC, + ctx->stream.msg); inflateEnd(&ctx->stream); return APR_EGENERAL; } @@ -972,72 +1135,130 @@ static apr_status_t deflate_in_filter(ap_filter_t *f, } ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len); - tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len, - NULL, f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap); + tmp_b = apr_bucket_heap_create((char *)ctx->buffer, len, + NULL, f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_b); ctx->stream.avail_out = c->bufferSize; - /* Move everything to the returning brigade. */ + /* Flush everything so far in the returning brigade, but continue + * reading should EOS/more follow (don't lose them). + */ + tmp_b = APR_BUCKET_PREV(bkt); APR_BUCKET_REMOVE(bkt); - APR_BRIGADE_CONCAT(bb, ctx->bb); - break; + APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, bkt); + bkt = tmp_b; + continue; + } + + /* sanity check - data after completed compressed body and before eos? */ + if (ctx->done) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Encountered extra data after compressed data"); + return APR_EGENERAL; } /* read */ apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ); + if (!len) { + continue; + } + if (len > APR_INT32_MAX) { + apr_bucket_split(bkt, APR_INT32_MAX); + apr_bucket_read(bkt, &data, &len, APR_BLOCK_READ); + } + + if (ctx->zlib_flags) { + rv = consume_zlib_flags(ctx, &data, &len); + if (rv == APR_SUCCESS) { + ctx->zlib_flags = 0; + } + if (!len) { + continue; + } + } /* pass through zlib inflate. */ ctx->stream.next_in = (unsigned char *)data; - ctx->stream.avail_in = len; + ctx->stream.avail_in = (int)len; zRC = Z_OK; - while (ctx->stream.avail_in != 0) { - if (ctx->stream.avail_out == 0) { - apr_bucket *tmp_heap; - ctx->stream.next_out = ctx->buffer; - len = c->bufferSize - ctx->stream.avail_out; - - ctx->inflate_total += len; - if (inflate_limit && ctx->inflate_total > inflate_limit) { - inflateEnd(&ctx->stream); - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, - "Inflated content length of %" APR_OFF_T_FMT - " is larger than the configured limit" - " of %" APR_OFF_T_FMT, - ctx->inflate_total, inflate_limit); - return APR_ENOSPC; - } - - if (!check_ratio(r, ctx, dc)) { - inflateEnd(&ctx->stream); - ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, - "Inflated content ratio is larger than the " - "configured limit %i by %i time(s)", - dc->ratio_limit, dc->ratio_burst); - return APR_EINVAL; - } - - ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len); - tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len, - NULL, f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap); - ctx->stream.avail_out = c->bufferSize; - } + if (!ctx->validation_buffer) { + while (ctx->stream.avail_in != 0) { + if (ctx->stream.avail_out == 0) { + apr_bucket *tmp_heap; + ctx->stream.next_out = ctx->buffer; + len = c->bufferSize - ctx->stream.avail_out; + + ctx->inflate_total += len; + if (inflate_limit && ctx->inflate_total > inflate_limit) { + inflateEnd(&ctx->stream); + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, + "Inflated content length of %" APR_OFF_T_FMT + " is larger than the configured limit" + " of %" APR_OFF_T_FMT, + ctx->inflate_total, inflate_limit); + return APR_ENOSPC; + } - zRC = inflate(&ctx->stream, Z_NO_FLUSH); + if (!check_ratio(r, ctx, dc)) { + inflateEnd(&ctx->stream); + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, + "Inflated content ratio is larger than the " + "configured limit %i by %i time(s)", + dc->ratio_limit, dc->ratio_burst); + return APR_EINVAL; + } - if (zRC == Z_STREAM_END) { - break; - } + ctx->crc = crc32(ctx->crc, (const Bytef *)ctx->buffer, len); + tmp_heap = apr_bucket_heap_create((char *)ctx->buffer, len, + NULL, f->c->bucket_alloc); + APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap); + ctx->stream.avail_out = c->bufferSize; + } - if (zRC != Z_OK) { - inflateEnd(&ctx->stream); - return APR_EGENERAL; + zRC = inflate(&ctx->stream, Z_NO_FLUSH); + + if (zRC == Z_STREAM_END) { + ctx->validation_buffer = apr_pcalloc(r->pool, + VALIDATION_SIZE); + ctx->validation_buffer_length = 0; + break; + } + + if (zRC != Z_OK) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, + "Zlib error %d inflating data (%s)", zRC, + ctx->stream.msg); + inflateEnd(&ctx->stream); + return APR_EGENERAL; + } } } - if (zRC == Z_STREAM_END) { - apr_bucket *tmp_heap, *eos; + + if (ctx->validation_buffer) { + apr_bucket *tmp_heap; + apr_size_t avail, valid; + unsigned char *buf = ctx->validation_buffer; + + avail = ctx->stream.avail_in; + valid = (apr_size_t)VALIDATION_SIZE - + ctx->validation_buffer_length; + + /* + * We have inflated all data. Now try to capture the + * validation bytes. We may not have them all available + * right now, but capture what is there. + */ + if (avail < valid) { + memcpy(buf + ctx->validation_buffer_length, + ctx->stream.next_in, avail); + ctx->validation_buffer_length += avail; + continue; + } + memcpy(buf + ctx->validation_buffer_length, + ctx->stream.next_in, valid); + ctx->validation_buffer_length += valid; ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "Zlib: Inflated %ld to %ld : URL %s", @@ -1052,34 +1273,37 @@ static apr_status_t deflate_in_filter(ap_filter_t *f, APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, tmp_heap); ctx->stream.avail_out = c->bufferSize; - /* Is the remaining 8 bytes already in the avail stream? */ - if (ctx->stream.avail_in >= 8) { + { unsigned long compCRC, compLen; - compCRC = getLong(ctx->stream.next_in); + compCRC = getLong(buf); if (ctx->crc != compCRC) { inflateEnd(&ctx->stream); + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, + "Zlib: CRC error inflating data"); return APR_EGENERAL; } - ctx->stream.next_in += 4; - compLen = getLong(ctx->stream.next_in); + compLen = getLong(buf + VALIDATION_SIZE / 2); /* gzip stores original size only as 4 byte value */ if ((ctx->stream.total_out & 0xFFFFFFFF) != compLen) { inflateEnd(&ctx->stream); + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, + "Zlib: Length %ld of inflated data does " + "not match expected value %ld", + ctx->stream.total_out, compLen); return APR_EGENERAL; } } - else { - /* FIXME: We need to grab the 8 verification bytes - * from the wire! */ - inflateEnd(&ctx->stream); - return APR_EGENERAL; - } inflateEnd(&ctx->stream); - eos = apr_bucket_eos_create(f->c->bucket_alloc); - APR_BRIGADE_INSERT_TAIL(ctx->proc_bb, eos); - break; + ctx->done = 1; + + /* Did we have trailing data behind the closing 8 bytes? */ + if (avail > valid) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Encountered extra data after compressed data"); + return APR_EGENERAL; + } } } @@ -1090,8 +1314,8 @@ static apr_status_t deflate_in_filter(ap_filter_t *f, * some data in our zlib buffer, flush it out so we can return something. */ if (block == APR_BLOCK_READ && - APR_BRIGADE_EMPTY(ctx->proc_bb) && - ctx->stream.avail_out < c->bufferSize) { + APR_BRIGADE_EMPTY(ctx->proc_bb) && + ctx->stream.avail_out < c->bufferSize) { apr_bucket *tmp_heap; apr_size_t len; ctx->stream.next_out = ctx->buffer; @@ -1113,6 +1337,9 @@ static apr_status_t deflate_in_filter(ap_filter_t *f, newbb = apr_brigade_split(ctx->proc_bb, bkt); APR_BRIGADE_CONCAT(bb, ctx->proc_bb); APR_BRIGADE_CONCAT(ctx->proc_bb, newbb); + if (APR_BUCKET_IS_EOS(APR_BRIGADE_LAST(bb))) { + ap_remove_input_filter(f); + } } return APR_SUCCESS; @@ -1123,8 +1350,6 @@ static apr_status_t deflate_in_filter(ap_filter_t *f, static apr_status_t inflate_out_filter(ap_filter_t *f, apr_bucket_brigade *bb) { - int zlib_method; - int zlib_flags; apr_bucket *e; request_rec *r = f->r; deflate_ctx *ctx = f->ctx; @@ -1209,8 +1434,6 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, /* initialize inflate output buffer */ ctx->stream.next_out = ctx->buffer; ctx->stream.avail_out = c->bufferSize; - - ctx->inflate_init = 0; } while (!APR_BRIGADE_EMPTY(bb)) @@ -1320,56 +1543,68 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, /* read */ apr_bucket_read(e, &data, &len, APR_BLOCK_READ); + if (!len) { + apr_bucket_delete(e); + continue; + } + if (len > APR_INT32_MAX) { + apr_bucket_split(e, APR_INT32_MAX); + apr_bucket_read(e, &data, &len, APR_BLOCK_READ); + } /* first bucket contains zlib header */ - if (!ctx->inflate_init++) { - if (len < 10) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "Insufficient data for inflate"); - return APR_EGENERAL; + if (ctx->header_len < sizeof(ctx->header)) { + apr_size_t rem; + + rem = sizeof(ctx->header) - ctx->header_len; + if (len < rem) { + memcpy(ctx->header + ctx->header_len, data, len); + ctx->header_len += len; + apr_bucket_delete(e); + continue; } - else { - zlib_method = data[2]; - zlib_flags = data[3]; + memcpy(ctx->header + ctx->header_len, data, rem); + ctx->header_len += rem; + { + int zlib_method; + zlib_method = ctx->header[2]; if (zlib_method != Z_DEFLATED) { ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, "inflate: data not deflated!"); ap_remove_output_filter(f); return ap_pass_brigade(f->next, bb); } - if (data[0] != deflate_magic[0] || - data[1] != deflate_magic[1] || - (zlib_flags & RESERVED) != 0) { + if (ctx->header[0] != deflate_magic[0] || + ctx->header[1] != deflate_magic[1]) { ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, "inflate: bad header"); return APR_EGENERAL ; } - data += 10 ; - len -= 10 ; - } - if (zlib_flags & EXTRA_FIELD) { - unsigned int bytes = (unsigned int)(data[0]); - bytes += ((unsigned int)(data[1])) << 8; - bytes += 2; - if (len < bytes) { - ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, - "inflate: extra field too big (not " - "supported)"); - return APR_EGENERAL; - } - data += bytes; - len -= bytes; - } - if (zlib_flags & ORIG_NAME) { - while (len-- && *data++); - } - if (zlib_flags & COMMENT) { - while (len-- && *data++); - } - if (zlib_flags & HEAD_CRC) { - len -= 2; - data += 2; - } + ctx->zlib_flags = ctx->header[3]; + if ((ctx->zlib_flags & RESERVED)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "inflate: bad flags %02x", + ctx->zlib_flags); + return APR_EGENERAL; + } + } + if (len == rem) { + apr_bucket_delete(e); + continue; + } + data += rem; + len -= rem; + } + + if (ctx->zlib_flags) { + rv = consume_zlib_flags(ctx, &data, &len); + if (rv == APR_SUCCESS) { + ctx->zlib_flags = 0; + } + if (!len) { + apr_bucket_delete(e); + continue; + } } /* pass through zlib inflate. */ @@ -1458,6 +1693,9 @@ static apr_status_t inflate_out_filter(ap_filter_t *f, } if (zRC != Z_OK) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, + "Zlib error %d inflating data (%s)", zRC, + ctx->stream.msg); return APR_EGENERAL; } }