El 16/07/15 a las 19:43, Ben Hutchings escribió:
> PS: A member of the LTS team might start working on this update at
> any point in time. You can verify whether someone is registered
> on this update in this file:
> https://anonscm.debian.org/viewvc/secure-testing/data/dla-needed.txt?view=markup
Hi,
Attached you can find the debdiff of the apache2 package I'm preparing
for squeeze. Reviews are warmly welcome. Also, a test package is
available at the repository:
deb https://people.debian.org/~santiago/debian santiago-squeeze-lts/
Cheers,
Santiago
diff -u apache2-2.2.16/debian/changelog apache2-2.2.16/debian/changelog
--- apache2-2.2.16/debian/changelog
+++ apache2-2.2.16/debian/changelog
@@ -1,3 +1,14 @@
+apache2 (2.2.16-6+squeeze15~1) santiago-squeeze-lts; urgency=high
+
+ * Non-maintainer upload by the Squeeze LTS Team.
+ * CVE-2015-3183: Fix chunk header parsing defect.
+ Remove apr_brigade_flatten(), buffering and duplicated code from the
+ HTTP_IN filter, parse chunks in a single pass with zero copy. Limit
+ accepted chunk-size to 2^63-1 and be strict about chunk-ext
+ authorized characters.
+
+ -- Santiago Ruano Rincón <santiagorr@riseup.net> Sat, 18 Jul 2015 15:54:48 +0200
+
apache2 (2.2.16-6+squeeze14) squeeze-lts; urgency=high
* Non-maintainer upload by the LTS Team.
diff -u apache2-2.2.16/debian/patches/00list apache2-2.2.16/debian/patches/00list
--- apache2-2.2.16/debian/patches/00list
+++ apache2-2.2.16/debian/patches/00list
@@ -57,0 +58,4 @@
+CVE-2015-3183-r1687338.dpatch
+CVE-2015-3183-r1687339.dpatch
+CVE-2015-3183-r1688936.dpatch
+CVE-2015-3183-r1689522.dpatch
only in patch2:
unchanged:
--- apache2-2.2.16.orig/debian/patches/CVE-2015-3183-r1687338.dpatch
+++ apache2-2.2.16/debian/patches/CVE-2015-3183-r1687338.dpatch
@@ -0,0 +1,773 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## CVE-2015-3183-r1687338.dpatch by <santiagorr@riseup.net>
+##
+## DP: Fix CVE-2015-3183 (1/4 patches) as 2015-07-18
+## DP: Origin: upstream, svn diff --git --patch-compatible -c 1687338 http://svn.apache.org/repos/asf/httpd/httpd/branches/2.2.x
+
+@DPATCH@
+diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' apache2~/modules/http/http_filters.c apache2/modules/http/http_filters.c
+--- apache2~/modules/http/http_filters.c 2015-07-18 15:07:51.000000000 +0200
++++ apache2/modules/http/http_filters.c 2015-07-18 15:11:57.038506035 +0200
+@@ -56,27 +56,31 @@
+ #include <unistd.h>
+ #endif
+
+-#define INVALID_CHAR -2
+-
+-static long get_chunk_size(char *);
+-
+-typedef struct http_filter_ctx {
++typedef struct http_filter_ctx
++{
+ apr_off_t remaining;
+ apr_off_t limit;
+ apr_off_t limit_used;
+- enum {
+- BODY_NONE,
+- BODY_LENGTH,
+- BODY_CHUNK,
+- BODY_CHUNK_PART
++ apr_int32_t chunk_used;
++ apr_int32_t chunkbits;
++ enum
++ {
++ BODY_NONE, /* streamed data */
++ BODY_LENGTH, /* data constrained by content length */
++ BODY_CHUNK, /* chunk expected */
++ BODY_CHUNK_PART, /* chunk digits */
++ BODY_CHUNK_EXT, /* chunk extension */
++ BODY_CHUNK_LF, /* got CR, expect LF after digits/extension */
++ BODY_CHUNK_DATA, /* data constrained by chunked encoding */
++ BODY_CHUNK_END, /* chunked data terminating CRLF */
++ BODY_CHUNK_END_LF, /* got CR, expect LF after data */
++ BODY_CHUNK_TRAILER /* trailers */
+ } state;
+- int eos_sent;
+- char chunk_ln[32];
+- char *pos;
+- apr_off_t linesize;
++ unsigned int eos_sent :1;
+ apr_bucket_brigade *bb;
+ } http_ctx_t;
+
++/* bail out if some error in the HTTP input filter happens */
+ static apr_status_t bail_out_on_error(http_ctx_t *ctx,
+ ap_filter_t *f,
+ int http_error)
+@@ -92,119 +96,147 @@
+ e = apr_bucket_eos_create(f->c->bucket_alloc);
+ APR_BRIGADE_INSERT_TAIL(bb, e);
+ ctx->eos_sent = 1;
++ /* If chunked encoding / content-length are corrupt, we may treat parts
++ * of this request's body as the next one's headers.
++ * To be safe, disable keep-alive.
++ */
++ f->r->connection->keepalive = AP_CONN_CLOSE;
+ return ap_pass_brigade(f->r->output_filters, bb);
+ }
+
+-static apr_status_t get_remaining_chunk_line(http_ctx_t *ctx,
+- apr_bucket_brigade *b,
+- int linelimit)
++/**
++ * Parse a chunk line with optional extension, detect overflow.
++ * There are two error cases:
++ * 1) If the conversion would require too many bits, APR_EGENERAL is returned.
++ * 2) If the conversion used the correct number of bits, but an overflow
++ * caused only the sign bit to flip, then APR_ENOSPC is returned.
++ * In general, any negative number can be considered an overflow error.
++ */
++static apr_status_t parse_chunk_size(http_ctx_t *ctx, const char *buffer,
++ apr_size_t len, int linelimit)
+ {
+- apr_status_t rv;
+- apr_off_t brigade_length;
+- apr_bucket *e;
+- const char *lineend;
+- apr_size_t len;
++ apr_size_t i = 0;
+
+- /*
+- * As the brigade b should have been requested in mode AP_MODE_GETLINE
+- * all buckets in this brigade are already some type of memory
+- * buckets (due to the needed scanning for LF in mode AP_MODE_GETLINE)
+- * or META buckets.
+- */
+- rv = apr_brigade_length(b, 0, &brigade_length);
+- if (rv != APR_SUCCESS) {
+- return rv;
+- }
+- /* Sanity check. Should never happen. See above. */
+- if (brigade_length == -1) {
+- return APR_EGENERAL;
+- }
+- if (!brigade_length) {
+- return APR_EAGAIN;
+- }
+- ctx->linesize += brigade_length;
+- if (ctx->linesize > linelimit) {
+- return APR_ENOSPC;
+- }
+- /*
+- * As all buckets are already some type of memory buckets or META buckets
+- * (see above), we only need to check the last byte in the last data bucket.
+- */
+- for (e = APR_BRIGADE_LAST(b);
+- e != APR_BRIGADE_SENTINEL(b);
+- e = APR_BUCKET_PREV(e)) {
++ while (i < len) {
++ char c = buffer[i];
+
+- if (APR_BUCKET_IS_METADATA(e)) {
++ ap_xlate_proto_from_ascii(&c, 1);
++
++ /* handle CRLF after the chunk */
++ if (ctx->state == BODY_CHUNK_END
++ || ctx->state == BODY_CHUNK_END_LF) {
++ if (c == LF) {
++ ctx->state = BODY_CHUNK;
++ }
++ else if (c == CR && ctx->state == BODY_CHUNK_END) {
++ ctx->state = BODY_CHUNK_END_LF;
++ }
++ else {
++ /*
++ * LF expected.
++ */
++ return APR_EINVAL;
++ }
++ i++;
+ continue;
+ }
+- rv = apr_bucket_read(e, &lineend, &len, APR_BLOCK_READ);
+- if (rv != APR_SUCCESS) {
+- return rv;
++
++ /* handle start of the chunk */
++ if (ctx->state == BODY_CHUNK) {
++ if (!apr_isxdigit(c)) {
++ /*
++ * Detect invalid character at beginning. This also works for
++ * empty chunk size lines.
++ */
++ return APR_EINVAL;
++ }
++ else {
++ ctx->state = BODY_CHUNK_PART;
++ }
++ ctx->remaining = 0;
++ ctx->chunkbits = sizeof(apr_off_t) * 8;
++ ctx->chunk_used = 0;
+ }
+- if (len > 0) {
+- break; /* we got the data we want */
++
++ if (c == LF) {
++ if (ctx->remaining) {
++ ctx->state = BODY_CHUNK_DATA;
++ }
++ else {
++ ctx->state = BODY_CHUNK_TRAILER;
++ }
+ }
+- /* If we got a zero-length data bucket, we try the next one */
+- }
+- /* We had no data in this brigade */
+- if (!len || e == APR_BRIGADE_SENTINEL(b)) {
+- return APR_EAGAIN;
+- }
+- if (lineend[len - 1] != APR_ASCII_LF) {
+- return APR_EAGAIN;
+- }
+- /* Line is complete. So reset ctx->linesize for next round. */
+- ctx->linesize = 0;
+- return APR_SUCCESS;
+-}
++ else if (ctx->state == BODY_CHUNK_LF) {
++ /*
++ * LF expected.
++ */
++ return APR_EINVAL;
++ }
++ else if (c == CR) {
++ ctx->state = BODY_CHUNK_LF;
++ }
++ else if (c == ';') {
++ ctx->state = BODY_CHUNK_EXT;
++ }
++ else if (ctx->state == BODY_CHUNK_EXT) {
++ /*
++ * Control chars (but tabs) are invalid.
++ */
++ if (c != '\t' && apr_iscntrl(c)) {
++ return APR_EINVAL;
++ }
++ }
++ else if (ctx->state == BODY_CHUNK_PART) {
++ int xvalue;
+
+-static apr_status_t get_chunk_line(http_ctx_t *ctx, apr_bucket_brigade *b,
+- int linelimit)
+-{
+- apr_size_t len;
+- int tmp_len;
+- apr_status_t rv;
++ /* ignore leading zeros */
++ if (!ctx->remaining && c == '0') {
++ i++;
++ continue;
++ }
+
+- tmp_len = sizeof(ctx->chunk_ln) - (ctx->pos - ctx->chunk_ln) - 1;
+- /* Saveguard ourselves against underflows */
+- if (tmp_len < 0) {
+- len = 0;
+- }
+- else {
+- len = (apr_size_t) tmp_len;
+- }
+- /*
+- * Check if there is space left in ctx->chunk_ln. If not, then either
+- * the chunk size is insane or we have chunk-extensions. Ignore both
+- * by discarding the remaining part of the line via
+- * get_remaining_chunk_line. Only bail out if the line is too long.
+- */
+- if (len > 0) {
+- rv = apr_brigade_flatten(b, ctx->pos, &len);
+- if (rv != APR_SUCCESS) {
+- return rv;
++ ctx->chunkbits -= 4;
++ if (ctx->chunkbits < 0) {
++ /* overflow */
++ return APR_ENOSPC;
++ }
++
++ if (c >= '0' && c <= '9') {
++ xvalue = c - '0';
++ }
++ else if (c >= 'A' && c <= 'F') {
++ xvalue = c - 'A' + 0xa;
++ }
++ else if (c >= 'a' && c <= 'f') {
++ xvalue = c - 'a' + 0xa;
++ }
++ else {
++ /* bogus character */
++ return APR_EINVAL;
++ }
++
++ ctx->remaining = (ctx->remaining << 4) | xvalue;
++ if (ctx->remaining < 0) {
++ /* overflow */
++ return APR_ENOSPC;
++ }
+ }
+- ctx->pos += len;
+- ctx->linesize += len;
+- *(ctx->pos) = '\0';
+- /*
+- * Check if we really got a full line. If yes the
+- * last char in the just read buffer must be LF.
+- * If not advance the buffer and return APR_EAGAIN.
+- * We do not start processing until we have the
+- * full line.
+- */
+- if (ctx->pos[-1] != APR_ASCII_LF) {
+- /* Check if the remaining data in the brigade has the LF */
+- return get_remaining_chunk_line(ctx, b, linelimit);
++ else {
++ /* Should not happen */
++ return APR_EGENERAL;
+ }
+- /* Line is complete. So reset ctx->pos for next round. */
+- ctx->pos = ctx->chunk_ln;
+- return APR_SUCCESS;
++
++ i++;
++ }
++
++ /* sanity check */
++ ctx->chunk_used += len;
++ if (ctx->chunk_used < 0 || ctx->chunk_used > linelimit) {
++ return APR_ENOSPC;
+ }
+- return get_remaining_chunk_line(ctx, b, linelimit);
+-}
+
++ return APR_SUCCESS;
++}
+
+ static apr_status_t read_chunked_trailers(http_ctx_t *ctx, ap_filter_t *f,
+ apr_bucket_brigade *b, int merge)
+@@ -218,7 +250,6 @@
+ r->status = HTTP_OK;
+ r->headers_in = r->trailers_in;
+ apr_table_clear(r->headers_in);
+- ctx->state = BODY_NONE;
+ ap_get_mime_headers(r);
+
+ if(r->status == HTTP_OK) {
+@@ -265,6 +296,7 @@
+ apr_off_t totalread;
+ int http_error = HTTP_REQUEST_ENTITY_TOO_LARGE;
+ apr_bucket_brigade *bb;
++ int again;
+
+ conf = (core_server_config *)
+ ap_get_module_config(f->r->server->module_config, &core_module);
+@@ -278,7 +310,6 @@
+ const char *tenc, *lenp;
+ f->ctx = ctx = apr_pcalloc(f->r->pool, sizeof(*ctx));
+ ctx->state = BODY_NONE;
+- ctx->pos = ctx->chunk_ln;
+ ctx->bb = apr_brigade_create(f->r->pool, f->c->bucket_alloc);
+ bb = ctx->bb;
+
+@@ -308,7 +339,7 @@
+ */
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,
+ "Unknown Transfer-Encoding: %s", tenc);
+- return bail_out_on_error(ctx, f, HTTP_NOT_IMPLEMENTED);
++ return bail_out_on_error(ctx, f, HTTP_BAD_REQUEST);
+ }
+ else {
+ ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, f->r,
+@@ -332,7 +363,7 @@
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,
+ "Invalid Content-Length");
+
+- return bail_out_on_error(ctx, f, HTTP_REQUEST_ENTITY_TOO_LARGE);
++ return bail_out_on_error(ctx, f, HTTP_BAD_REQUEST);
+ }
+
+ /* If we have a limit in effect and we know the C-L ahead of
+@@ -374,7 +405,8 @@
+ if (!ap_is_HTTP_SUCCESS(f->r->status)) {
+ ctx->state = BODY_NONE;
+ ctx->eos_sent = 1;
+- } else {
++ }
++ else {
+ char *tmp;
+ int len;
+
+@@ -392,276 +424,194 @@
+ ap_pass_brigade(f->c->output_filters, bb);
+ }
+ }
++ }
+
+- /* We can't read the chunk until after sending 100 if required. */
+- if (ctx->state == BODY_CHUNK) {
+- apr_brigade_cleanup(bb);
++ /* sanity check in case we're read twice */
++ if (ctx->eos_sent) {
++ e = apr_bucket_eos_create(f->c->bucket_alloc);
++ APR_BRIGADE_INSERT_TAIL(b, e);
++ return APR_SUCCESS;
++ }
+
+- rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE,
+- block, 0);
++ do {
++ apr_brigade_cleanup(b);
++ again = 0; /* until further notice */
++
++ /* read and handle the brigade */
++ switch (ctx->state) {
++ case BODY_CHUNK:
++ case BODY_CHUNK_PART:
++ case BODY_CHUNK_EXT:
++ case BODY_CHUNK_LF:
++ case BODY_CHUNK_END:
++ case BODY_CHUNK_END_LF: {
++
++ rv = ap_get_brigade(f->next, b, AP_MODE_GETLINE, block, 0);
+
+ /* for timeout */
+- if (block == APR_NONBLOCK_READ &&
+- ( (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb)) ||
+- (APR_STATUS_IS_EAGAIN(rv)) )) {
+- ctx->state = BODY_CHUNK_PART;
++ if (block == APR_NONBLOCK_READ
++ && ((rv == APR_SUCCESS && APR_BRIGADE_EMPTY(b))
++ || (APR_STATUS_IS_EAGAIN(rv)))) {
+ return APR_EAGAIN;
+ }
+
+- if (rv == APR_SUCCESS) {
+- rv = get_chunk_line(ctx, bb, f->r->server->limit_req_line);
+- if (APR_STATUS_IS_EAGAIN(rv)) {
+- apr_brigade_cleanup(bb);
+- ctx->state = BODY_CHUNK_PART;
+- return rv;
+- }
+- if (rv == APR_SUCCESS) {
+- ctx->remaining = get_chunk_size(ctx->chunk_ln);
+- if (ctx->remaining == INVALID_CHAR) {
+- rv = APR_EGENERAL;
+- http_error = HTTP_SERVICE_UNAVAILABLE;
+- }
+- }
++ if (rv == APR_EOF) {
++ return APR_INCOMPLETE;
+ }
+- apr_brigade_cleanup(bb);
+
+- /* Detect chunksize error (such as overflow) */
+- if (rv != APR_SUCCESS || ctx->remaining < 0) {
+- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, "Error reading first chunk %s ",
+- (ctx->remaining < 0) ? "(overflow)" : "");
+- ctx->remaining = 0; /* Reset it in case we have to
+- * come back here later */
+- if (APR_STATUS_IS_TIMEUP(rv)) {
+- http_error = HTTP_REQUEST_TIME_OUT;
+- }
+- return bail_out_on_error(ctx, f, http_error);
++ if (rv != APR_SUCCESS) {
++ return rv;
+ }
+
+- if (!ctx->remaining) {
+- return read_chunked_trailers(ctx, f, b,
+- conf->merge_trailers == AP_MERGE_TRAILERS_ENABLE);
+- }
+- }
+- }
+- else {
+- bb = ctx->bb;
+- }
++ e = APR_BRIGADE_FIRST(b);
++ while (e != APR_BRIGADE_SENTINEL(b)) {
++ const char *buffer;
++ apr_size_t len;
+
+- if (ctx->eos_sent) {
+- e = apr_bucket_eos_create(f->c->bucket_alloc);
+- APR_BRIGADE_INSERT_TAIL(b, e);
+- return APR_SUCCESS;
+- }
++ if (!APR_BUCKET_IS_METADATA(e)) {
++ int parsing = 0;
+
+- if (!ctx->remaining) {
+- switch (ctx->state) {
+- case BODY_NONE:
+- break;
+- case BODY_LENGTH:
+- e = apr_bucket_eos_create(f->c->bucket_alloc);
+- APR_BRIGADE_INSERT_TAIL(b, e);
+- ctx->eos_sent = 1;
+- return APR_SUCCESS;
+- case BODY_CHUNK:
+- case BODY_CHUNK_PART:
+- {
+- apr_brigade_cleanup(bb);
++ rv = apr_bucket_read(e, &buffer, &len, APR_BLOCK_READ);
+
+- /* We need to read the CRLF after the chunk. */
+- if (ctx->state == BODY_CHUNK) {
+- rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE,
+- block, 0);
+- if (block == APR_NONBLOCK_READ &&
+- ( (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb)) ||
+- (APR_STATUS_IS_EAGAIN(rv)) )) {
+- return APR_EAGAIN;
++ if (rv == APR_SUCCESS) {
++ parsing = 1;
++ rv = parse_chunk_size(ctx, buffer, len,
++ f->r->server->limit_req_fieldsize);
+ }
+- /* If we get an error, then leave */
+ if (rv != APR_SUCCESS) {
+- return rv;
+- }
+- /*
+- * We really don't care whats on this line. If it is RFC
+- * compliant it should be only \r\n. If there is more
+- * before we just ignore it as long as we do not get over
+- * the limit for request lines.
+- */
+- rv = get_remaining_chunk_line(ctx, bb,
+- f->r->server->limit_req_line);
+- apr_brigade_cleanup(bb);
+- if (APR_STATUS_IS_EAGAIN(rv)) {
+- return rv;
+- }
+- } else {
+- rv = APR_SUCCESS;
+- }
+-
+- if (rv == APR_SUCCESS) {
+- /* Read the real chunk line. */
+- rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE,
+- block, 0);
+- /* Test timeout */
+- if (block == APR_NONBLOCK_READ &&
+- ( (rv == APR_SUCCESS && APR_BRIGADE_EMPTY(bb)) ||
+- (APR_STATUS_IS_EAGAIN(rv)) )) {
+- ctx->state = BODY_CHUNK_PART;
+- return APR_EAGAIN;
+- }
+- ctx->state = BODY_CHUNK;
+- if (rv == APR_SUCCESS) {
+- rv = get_chunk_line(ctx, bb, f->r->server->limit_req_line);
+- if (APR_STATUS_IS_EAGAIN(rv)) {
+- ctx->state = BODY_CHUNK_PART;
+- apr_brigade_cleanup(bb);
+- return rv;
+- }
+- if (rv == APR_SUCCESS) {
+- ctx->remaining = get_chunk_size(ctx->chunk_ln);
+- if (ctx->remaining == INVALID_CHAR) {
+- rv = APR_EGENERAL;
+- http_error = HTTP_SERVICE_UNAVAILABLE;
++ ap_log_rerror(APLOG_MARK, APLOG_INFO, rv, f->r,
++ "Error reading/parsing chunk %s ",
++ (APR_ENOSPC == rv) ? "(overflow)" : "");
++ if (parsing) {
++ if (rv != APR_ENOSPC) {
++ http_error = HTTP_BAD_REQUEST;
+ }
++ return bail_out_on_error(ctx, f, http_error);
+ }
++ return rv;
+ }
+- apr_brigade_cleanup(bb);
+ }
+
+- /* Detect chunksize error (such as overflow) */
+- if (rv != APR_SUCCESS || ctx->remaining < 0) {
+- ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, "Error reading chunk %s ",
+- (ctx->remaining < 0) ? "(overflow)" : "");
+- ctx->remaining = 0; /* Reset it in case we have to
+- * come back here later */
+- if (APR_STATUS_IS_TIMEUP(rv)) {
+- http_error = HTTP_REQUEST_TIME_OUT;
+- }
+- return bail_out_on_error(ctx, f, http_error);
+- }
++ apr_bucket_delete(e);
++ e = APR_BRIGADE_FIRST(b);
++ }
++ again = 1; /* come around again */
+
+- if (!ctx->remaining) {
+- return read_chunked_trailers(ctx, f, b,
++ if (ctx->state == BODY_CHUNK_TRAILER) {
++ /* Treat UNSET as DISABLE - trailers aren't merged by default */
++ return read_chunked_trailers(ctx, f, b,
+ conf->merge_trailers == AP_MERGE_TRAILERS_ENABLE);
+- }
+ }
++
+ break;
+ }
+- }
++ case BODY_NONE:
++ case BODY_LENGTH:
++ case BODY_CHUNK_DATA: {
+
+- /* Ensure that the caller can not go over our boundary point. */
+- if (ctx->state == BODY_LENGTH || ctx->state == BODY_CHUNK) {
+- if (ctx->remaining < readbytes) {
+- readbytes = ctx->remaining;
+- }
+- AP_DEBUG_ASSERT(readbytes > 0);
+- }
++ /* Ensure that the caller can not go over our boundary point. */
++ if (ctx->state != BODY_NONE && ctx->remaining < readbytes) {
++ readbytes = ctx->remaining;
++ }
++ if (readbytes > 0) {
+
+- rv = ap_get_brigade(f->next, b, mode, block, readbytes);
++ rv = ap_get_brigade(f->next, b, mode, block, readbytes);
+
+- if (rv != APR_SUCCESS) {
+- return rv;
+- }
++ /* for timeout */
++ if (block == APR_NONBLOCK_READ
++ && ((rv == APR_SUCCESS && APR_BRIGADE_EMPTY(b))
++ || (APR_STATUS_IS_EAGAIN(rv)))) {
++ return APR_EAGAIN;
++ }
+
+- /* How many bytes did we just read? */
+- apr_brigade_length(b, 0, &totalread);
++ if (rv == APR_EOF && ctx->state != BODY_NONE
++ && ctx->remaining > 0) {
++ return APR_INCOMPLETE;
++ }
+
+- /* If this happens, we have a bucket of unknown length. Die because
+- * it means our assumptions have changed. */
+- AP_DEBUG_ASSERT(totalread >= 0);
++ if (rv != APR_SUCCESS) {
++ return rv;
++ }
+
+- if (ctx->state != BODY_NONE) {
+- ctx->remaining -= totalread;
+- if (ctx->remaining > 0) {
+- e = APR_BRIGADE_LAST(b);
+- if (APR_BUCKET_IS_EOS(e))
+- return APR_EOF;
+- }
+- }
++ /* How many bytes did we just read? */
++ apr_brigade_length(b, 0, &totalread);
+
+- /* If we have no more bytes remaining on a C-L request,
+- * save the callter a roundtrip to discover EOS.
+- */
+- if (ctx->state == BODY_LENGTH && ctx->remaining == 0) {
+- e = apr_bucket_eos_create(f->c->bucket_alloc);
+- APR_BRIGADE_INSERT_TAIL(b, e);
+- }
++ /* If this happens, we have a bucket of unknown length. Die because
++ * it means our assumptions have changed. */
++ AP_DEBUG_ASSERT(totalread >= 0);
+
+- /* We have a limit in effect. */
+- if (ctx->limit) {
+- /* FIXME: Note that we might get slightly confused on chunked inputs
+- * as we'd need to compensate for the chunk lengths which may not
+- * really count. This seems to be up for interpretation. */
+- ctx->limit_used += totalread;
+- if (ctx->limit < ctx->limit_used) {
+- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, f->r,
+- "Read content-length of %" APR_OFF_T_FMT
+- " is larger than the configured limit"
+- " of %" APR_OFF_T_FMT, ctx->limit_used, ctx->limit);
+- apr_brigade_cleanup(bb);
+- e = ap_bucket_error_create(HTTP_REQUEST_ENTITY_TOO_LARGE, NULL,
+- f->r->pool,
+- f->c->bucket_alloc);
+- APR_BRIGADE_INSERT_TAIL(bb, e);
+- e = apr_bucket_eos_create(f->c->bucket_alloc);
+- APR_BRIGADE_INSERT_TAIL(bb, e);
+- ctx->eos_sent = 1;
+- return ap_pass_brigade(f->r->output_filters, bb);
+- }
+- }
++ if (ctx->state != BODY_NONE) {
++ ctx->remaining -= totalread;
++ if (ctx->remaining > 0) {
++ e = APR_BRIGADE_LAST(b);
++ if (APR_BUCKET_IS_EOS(e)) {
++ apr_bucket_delete(e);
++ return APR_INCOMPLETE;
++ }
++ }
++ else if (ctx->state == BODY_CHUNK_DATA) {
++ /* next chunk please */
++ ctx->state = BODY_CHUNK_END;
++ ctx->chunk_used = 0;
++ }
++ }
+
+- return APR_SUCCESS;
+-}
++ }
+
+-/**
+- * Parse a chunk extension, detect overflow.
+- * There are two error cases:
+- * 1) If the conversion would require too many bits, a -1 is returned.
+- * 2) If the conversion used the correct number of bits, but an overflow
+- * caused only the sign bit to flip, then that negative number is
+- * returned.
+- * In general, any negative number can be considered an overflow error.
+- */
+-static long get_chunk_size(char *b)
+-{
+- long chunksize = 0;
+- size_t chunkbits = sizeof(long) * 8;
++ /* If we have no more bytes remaining on a C-L request,
++ * save the caller a round trip to discover EOS.
++ */
++ if (ctx->state == BODY_LENGTH && ctx->remaining == 0) {
++ e = apr_bucket_eos_create(f->c->bucket_alloc);
++ APR_BRIGADE_INSERT_TAIL(b, e);
++ ctx->eos_sent = 1;
++ }
+
+- ap_xlate_proto_from_ascii(b, strlen(b));
++ /* We have a limit in effect. */
++ if (ctx->limit) {
++ /* FIXME: Note that we might get slightly confused on chunked inputs
++ * as we'd need to compensate for the chunk lengths which may not
++ * really count. This seems to be up for interpretation. */
++ ctx->limit_used += totalread;
++ if (ctx->limit < ctx->limit_used) {
++ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r,
++ "Read content-length of %" APR_OFF_T_FMT
++ " is larger than the configured limit"
++ " of %" APR_OFF_T_FMT, ctx->limit_used, ctx->limit);
++ return bail_out_on_error(ctx, f, HTTP_REQUEST_ENTITY_TOO_LARGE);
++ }
++ }
+
+- if (!apr_isxdigit(*b)) {
+- /*
+- * Detect invalid character at beginning. This also works for empty
+- * chunk size lines.
+- */
+- return INVALID_CHAR;
+- }
+- /* Skip leading zeros */
+- while (*b == '0') {
+- ++b;
+- }
++ break;
++ }
++ case BODY_CHUNK_TRAILER: {
+
+- while (apr_isxdigit(*b) && (chunkbits > 0)) {
+- int xvalue = 0;
++ rv = ap_get_brigade(f->next, b, mode, block, readbytes);
+
+- if (*b >= '0' && *b <= '9') {
+- xvalue = *b - '0';
++ /* for timeout */
++ if (block == APR_NONBLOCK_READ
++ && ((rv == APR_SUCCESS && APR_BRIGADE_EMPTY(b))
++ || (APR_STATUS_IS_EAGAIN(rv)))) {
++ return APR_EAGAIN;
++ }
++
++ if (rv != APR_SUCCESS) {
++ return rv;
++ }
++
++ break;
+ }
+- else if (*b >= 'A' && *b <= 'F') {
+- xvalue = *b - 'A' + 0xa;
++ default: {
++ /* Should not happen */
++ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r,
++ "Unexpected body state (%i)", (int)ctx->state);
++ return APR_EGENERAL;
+ }
+- else if (*b >= 'a' && *b <= 'f') {
+- xvalue = *b - 'a' + 0xa;
+ }
+
+- chunksize = (chunksize << 4) | xvalue;
+- chunkbits -= 4;
+- ++b;
+- }
+- if (apr_isxdigit(*b) && (chunkbits <= 0)) {
+- /* overflow */
+- return -1;
+- }
++ } while (again);
+
+- return chunksize;
++ return APR_SUCCESS;
+ }
+
+ typedef struct header_struct {
only in patch2:
unchanged:
--- apache2-2.2.16.orig/debian/patches/CVE-2015-3183-r1689522.dpatch
+++ apache2-2.2.16/debian/patches/CVE-2015-3183-r1689522.dpatch
@@ -0,0 +1,47 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## CVE-2015-3183-r1689522.dpatch by <santiagorr@riseup.net>
+##
+## DP: Fix CVE-2015-3183 (4/4 patches) as 2015-07-18
+## DP: Origin: upstream, svn diff --git --patch-compatible -c 1689522 http://svn.apache.org/repos/asf/httpd/httpd/branches/2.2.x
+
+@DPATCH@
+diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' apache2~/modules/http/http_filters.c apache2/modules/http/http_filters.c
+--- apache2~/modules/http/http_filters.c 2015-07-18 15:52:18.000000000 +0200
++++ apache2/modules/http/http_filters.c 2015-07-18 15:52:19.000000000 +0200
+@@ -62,6 +62,7 @@
+ apr_off_t limit;
+ apr_off_t limit_used;
+ apr_int32_t chunk_used;
++ apr_int32_t chunk_bws;
+ apr_int32_t chunkbits;
+ enum
+ {
+@@ -157,6 +158,7 @@
+ ctx->remaining = 0;
+ ctx->chunkbits = sizeof(apr_off_t) * 8;
+ ctx->chunk_used = 0;
++ ctx->chunk_bws = 0;
+ }
+
+ if (c == LF) {
+@@ -188,7 +190,12 @@
+ }
+ }
+ else if (c == ' ' || c == '\t') {
++ /* Be lenient up to 10 BWS (term from rfc7230 - 3.2.3).
++ */
+ ctx->state = BODY_CHUNK_CR;
++ if (++ctx->chunk_bws > 10) {
++ return APR_EINVAL;
++ }
+ }
+ else if (ctx->state == BODY_CHUNK_CR) {
+ /*
+@@ -451,6 +458,7 @@
+ case BODY_CHUNK:
+ case BODY_CHUNK_PART:
+ case BODY_CHUNK_EXT:
++ case BODY_CHUNK_CR:
+ case BODY_CHUNK_LF:
+ case BODY_CHUNK_END:
+ case BODY_CHUNK_END_LF: {
only in patch2:
unchanged:
--- apache2-2.2.16.orig/debian/patches/CVE-2015-3183-r1687339.dpatch
+++ apache2-2.2.16/debian/patches/CVE-2015-3183-r1687339.dpatch
@@ -0,0 +1,42 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## CVE-2015-3183-r1687339.dpatch by <santiagorr@riseup.net>
+##
+## All lines beginning with `## DP:' are a description of the patch.
+## DP: Fix CVE-2015-3183 (2/4 patches) as 2015-07-18
+## DP: Origin: upstream, svn diff --git --patch-compatible -c 1687339 http://svn.apache.org/repos/asf/httpd/httpd/branches/2.2.x
+
+
+@DPATCH@
+diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' apache2~/modules/http/http_filters.c apache2/modules/http/http_filters.c
+--- apache2~/modules/http/http_filters.c 2015-07-18 15:51:34.000000000 +0200
++++ apache2/modules/http/http_filters.c 2015-07-18 15:51:34.000000000 +0200
+@@ -70,10 +70,11 @@
+ BODY_CHUNK, /* chunk expected */
+ BODY_CHUNK_PART, /* chunk digits */
+ BODY_CHUNK_EXT, /* chunk extension */
+- BODY_CHUNK_LF, /* got CR, expect LF after digits/extension */
++ BODY_CHUNK_CR, /* got space(s) after digits, expect [CR]LF or ext */
++ BODY_CHUNK_LF, /* got CR after digits or ext, expect LF */
+ BODY_CHUNK_DATA, /* data constrained by chunked encoding */
+ BODY_CHUNK_END, /* chunked data terminating CRLF */
+- BODY_CHUNK_END_LF, /* got CR, expect LF after data */
++ BODY_CHUNK_END_LF, /* got CR after data, expect LF */
+ BODY_CHUNK_TRAILER /* trailers */
+ } state;
+ unsigned int eos_sent :1;
+@@ -186,6 +187,15 @@
+ return APR_EINVAL;
+ }
+ }
++ else if (c == ' ' || c == '\t') {
++ ctx->state = BODY_CHUNK_CR;
++ }
++ else if (ctx->state == BODY_CHUNK_CR) {
++ /*
++ * ';', CR or LF expected.
++ */
++ return APR_EINVAL;
++ }
+ else if (ctx->state == BODY_CHUNK_PART) {
+ int xvalue;
+
only in patch2:
unchanged:
--- apache2-2.2.16.orig/debian/patches/CVE-2015-3183-r1688936.dpatch
+++ apache2-2.2.16/debian/patches/CVE-2015-3183-r1688936.dpatch
@@ -0,0 +1,73 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## CVE-2015-3183-r1688936.dpatch by <santiagorr@riseup.net>
+##
+## DP: Fix CVE-2015-3183 (3/4 patches) as 2015-07-18
+## DP: Origin: upstream, svn diff --git --patch-compatible -c 1688936 http://svn.apache.org/repos/asf/httpd/httpd/branches/2.2.x
+
+@DPATCH@
+diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' apache2~/modules/http/http_filters.c apache2/modules/http/http_filters.c
+--- apache2~/modules/http/http_filters.c 2015-07-18 15:52:04.000000000 +0200
++++ apache2/modules/http/http_filters.c 2015-07-18 15:52:04.000000000 +0200
+@@ -303,7 +303,6 @@
+ apr_bucket *e;
+ http_ctx_t *ctx = f->ctx;
+ apr_status_t rv;
+- apr_off_t totalread;
+ int http_error = HTTP_REQUEST_ENTITY_TOO_LARGE;
+ apr_bucket_brigade *bb;
+ int again;
+@@ -524,6 +523,7 @@
+ readbytes = ctx->remaining;
+ }
+ if (readbytes > 0) {
++ apr_off_t totalread;
+
+ rv = ap_get_brigade(f->next, b, mode, block, readbytes);
+
+@@ -566,6 +566,24 @@
+ }
+ }
+
++ /* We have a limit in effect. */
++ if (ctx->limit) {
++ /* FIXME: Note that we might get slightly confused on
++ * chunked inputs as we'd need to compensate for the chunk
++ * lengths which may not really count. This seems to be up
++ * for interpretation.
++ */
++ ctx->limit_used += totalread;
++ if (ctx->limit < ctx->limit_used) {
++ ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r,
++ "Read content length of "
++ "%" APR_OFF_T_FMT " is larger than the "
++ "configured limit of %" APR_OFF_T_FMT,
++ ctx->limit_used, ctx->limit);
++ return bail_out_on_error(ctx, f,
++ HTTP_REQUEST_ENTITY_TOO_LARGE);
++ }
++ }
+ }
+
+ /* If we have no more bytes remaining on a C-L request,
+@@ -577,21 +595,6 @@
+ ctx->eos_sent = 1;
+ }
+
+- /* We have a limit in effect. */
+- if (ctx->limit) {
+- /* FIXME: Note that we might get slightly confused on chunked inputs
+- * as we'd need to compensate for the chunk lengths which may not
+- * really count. This seems to be up for interpretation. */
+- ctx->limit_used += totalread;
+- if (ctx->limit < ctx->limit_used) {
+- ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, f->r,
+- "Read content-length of %" APR_OFF_T_FMT
+- " is larger than the configured limit"
+- " of %" APR_OFF_T_FMT, ctx->limit_used, ctx->limit);
+- return bail_out_on_error(ctx, f, HTTP_REQUEST_ENTITY_TOO_LARGE);
+- }
+- }
+-
+ break;
+ }
+ case BODY_CHUNK_TRAILER: {
Attachment:
signature.asc
Description: Digital signature