[apache2] 01/01: Fix CVE-2015-3183

Stefan Fritsch sf at moszumanska.debian.org
Sat Aug 1 20:02:49 UTC 2015


This is an automated email from the git hooks/post-receive script.

sf pushed a commit to branch wheezy
in repository apache2.

commit ab30708f0f991c6d0ef8a8816d8b415ebdf7a43c
Author: Stefan Fritsch <sf at sfritsch.de>
Date:   Sat Aug 1 21:57:31 2015 +0200

    Fix CVE-2015-3183
    
    request smuggling via chunked transfer encoding
---
 debian/changelog                   |   2 +
 debian/patches/CVE-2015-3183.patch | 801 +++++++++++++++++++++++++++++++++++++
 debian/patches/series              |   1 +
 3 files changed, 804 insertions(+)

diff --git a/debian/changelog b/debian/changelog
index 0daf30e..e96b61c 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,5 +1,7 @@
 apache2 (2.2.22-13+deb7u5) UNRELEASED; urgency=medium
 
+  * CVE-2015-3183: Fix request smuggling via chunked transfer encoding.
+    Backported by Marc Deslauriers.
   * Don't limit default DH parameters to 1024 bits. Closes: #780398
     This may cause problems with some Java based clients. A work-around is to
     configure these client not to use DHE key exchange but use ECDHE or RSA
diff --git a/debian/patches/CVE-2015-3183.patch b/debian/patches/CVE-2015-3183.patch
new file mode 100644
index 0000000..286c858
--- /dev/null
+++ b/debian/patches/CVE-2015-3183.patch
@@ -0,0 +1,801 @@
+Description: fix request smuggling via chunked transfer encoding
+Origin: backport, http://svn.apache.org/viewvc?view=revision&revision=1687338
+Origin: backport, http://svn.apache.org/viewvc?view=revision&revision=1687339
+Origin: backport, http://svn.apache.org/viewvc?view=revision&revision=1688936
+Origin: backport, http://svn.apache.org/viewvc?view=revision&revision=1689522
+
+Index: apache2-2.2.22/modules/http/http_filters.c
+===================================================================
+--- apache2-2.2.22.orig/modules/http/http_filters.c	2015-07-24 09:33:00.000000000 -0400
++++ apache2-2.2.22/modules/http/http_filters.c	2015-07-24 13:06:08.276786883 -0400
+@@ -56,27 +56,33 @@
+ #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 chunk_bws;
++    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_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 after data, expect LF */
++        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 +98,162 @@
+     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];
++
++        ap_xlate_proto_from_ascii(&c, 1);
+ 
+-        if (APR_BUCKET_IS_METADATA(e)) {
++        /* 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;
++            ctx->chunk_bws = 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 (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) {
++            /*
++             * ';', CR or LF expected.
++             */
++            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->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);
++            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;
++            }
+         }
+-        /* Line is complete. So reset ctx->pos for next round. */
+-        ctx->pos = ctx->chunk_ln;
+-        return APR_SUCCESS;
++        else {
++            /* Should not happen */
++            return APR_EGENERAL;
++        }
++
++        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 +267,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) {
+@@ -262,9 +310,9 @@
+     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;
+ 
+     conf = (core_server_config *)
+         ap_get_module_config(f->r->server->module_config, &core_module);
+@@ -278,7 +326,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 +355,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 +379,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 +421,8 @@
+             if (!ap_is_HTTP_SUCCESS(f->r->status)) {
+                 ctx->state = BODY_NONE;
+                 ctx->eos_sent = 1;
+-            } else {
++            }
++            else {
+                 char *tmp;
+                 int len;
+ 
+@@ -396,276 +444,199 @@
+                 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;
++    }
++
++    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_CR:
++        case BODY_CHUNK_LF:
++        case BODY_CHUNK_END:
++        case BODY_CHUNK_END_LF: {
+ 
+-            rv = ap_get_brigade(f->next, bb, AP_MODE_GETLINE,
+-                                block, 0);
++            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;
+-        }
+-    }
+ 
+-    /* 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;
++            break;
+         }
+-        AP_DEBUG_ASSERT(readbytes > 0);
+-    }
++        case BODY_NONE:
++        case BODY_LENGTH:
++        case BODY_CHUNK_DATA: {
+ 
+-    rv = ap_get_brigade(f->next, b, mode, block, readbytes);
++            /* 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) {
++                apr_off_t totalread;
++
++                rv = ap_get_brigade(f->next, b, mode, block, readbytes);
++
++                /* 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;
+-    }
++                if (rv == APR_EOF && ctx->state != BODY_NONE
++                        && ctx->remaining > 0) {
++                    return APR_INCOMPLETE;
++                }
+ 
+-    /* How many bytes did we just read? */
+-    apr_brigade_length(b, 0, &totalread);
++                if (rv != APR_SUCCESS) {
++                    return rv;
++                }
+ 
+-    /* If this happens, we have a bucket of unknown length.  Die because
+-     * it means our assumptions have changed. */
+-    AP_DEBUG_ASSERT(totalread >= 0);
+-
+-    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);
++
++                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;
++                    }
++                }
+ 
+-    /* 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);
+-        }
+-    }
++                /* 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);
++                    }
++                }
++            }
+ 
+-    return APR_SUCCESS;
+-}
++            /* 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;
++            }
+ 
+-/**
+- * 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;
++            break;
++        }
++        case BODY_CHUNK_TRAILER: {
+ 
+-    ap_xlate_proto_from_ascii(b, strlen(b));
++            rv = ap_get_brigade(f->next, b, mode, block, readbytes);
+ 
+-    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;
+-    }
++            /* for timeout */
++            if (block == APR_NONBLOCK_READ
++                    && ((rv == APR_SUCCESS && APR_BRIGADE_EMPTY(b))
++                            || (APR_STATUS_IS_EAGAIN(rv)))) {
++                return APR_EAGAIN;
++            }
+ 
+-    while (apr_isxdigit(*b) && (chunkbits > 0)) {
+-        int xvalue = 0;
++            if (rv != APR_SUCCESS) {
++                return rv;
++            }
+ 
+-        if (*b >= '0' && *b <= '9') {
+-            xvalue = *b - '0';
++            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 {
diff --git a/debian/patches/series b/debian/patches/series
index e4ce26c..21e54f2 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -49,3 +49,4 @@ CVE-2013-5704_trailers.patch
 SNI_case_insensitve.diff
 mod_ssl_SSL_CLIENT_S_DN_UID.diff
 DH-SSLCertificateFile.patch
+CVE-2015-3183.patch

-- 
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-apache/apache2.git



More information about the Pkg-apache-commits mailing list