From: Yann Ylavic Date: Thu, 27 Feb 2014 17:35:58 +0000 (+0000) Subject: Commit 2 on 6 to fix reentrance (incomplete Zlib header or validation bytes) in mod_d... X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=c2b96229d1888983b5bb37ffc2eff529503f21ca;p=thirdparty%2Fapache%2Fhttpd.git Commit 2 on 6 to fix reentrance (incomplete Zlib header or validation bytes) in mod_deflate's output and input filters. PR 46146 (patches from duplicated PR 55666) Handle Zlib header buffering in the inflate input filter : - loop until all the header is received, - handle non blocking reads returning empty brigade, - fix a double ap_get_brigade() when an EOS brigade is encountered while reading the header, - in that case and no data was received so far, don't return an error but SUCCESS with the EOS, otherwise fail, - don't remove the Content-Length and Content-MD5 headers until some data is read. Still does not handle Zlib flags for now, next commits. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1572663 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/modules/filters/mod_deflate.c b/modules/filters/mod_deflate.c index 0e64c09de31..ef11bd0da96 100644 --- a/modules/filters/mod_deflate.c +++ b/modules/filters/mod_deflate.c @@ -999,72 +999,98 @@ static apr_status_t deflate_in_filter(ap_filter_t *f, c = ap_get_module_config(r->server->module_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); - /* zero length body? step aside */ - bkt = APR_BRIGADE_FIRST(ctx->bb); - if (APR_BUCKET_IS_EOS(bkt)) { - ap_remove_input_filter(f); - return ap_get_brigade(f->next, bb, mode, block, readbytes); - } + /* 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; + } - apr_table_unset(r->headers_in, "Content-Length"); - apr_table_unset(r->headers_in, "Content-MD5"); + /* 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, APLOGNO() + "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; + } - len = 10; - rv = apr_brigade_flatten(ctx->bb, deflate_hdr, &len); - if (rv != APR_SUCCESS) { - return rv; - } + 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_WARNING, 0, r, APLOGNO(01387) "Zlib: Invalid header"); + if (ctx->header[0] != deflate_magic[0] || + ctx->header[1] != deflate_magic[1]) { + ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01387) + "Zlib: Invalid header"); return APR_EGENERAL; } /* We can't handle flags for now. */ - if (deflate_hdr[3] != 0) { + if (ctx->header[3] != 0) { ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, r, APLOGNO(01388) - "Zlib: Unsupported flags %02x", (int)deflate_hdr[3]); + "Zlib: Unsupported flags %02x", (int)ctx->header[3]); return APR_EGENERAL; }