[Date Prev][Date Next] [Thread Prev][Thread Next] [Date Index] [Thread Index]

Please test new apache2 2.2.16-6+squeeze14



Hello,

I have prepared a new upload of apache2 to fix CVE-2014-3581 and
CVE-2013-5704 in squeeze-lts. The debdiff is attached and I have put amd64
package for test online. Grab them with dget
https://people.debian.org/~hertzog/packages/apache2_2.2.16-6+squeeze14_amd64.changes
(contains arch amd64 + arch all packages) and dget
https://people.debian.org/~hertzog/packages/apache2_2.2.16-6+squeeze14_i386.changes
(contains i386 packages only).

Please test the packages and let me know if everything still works for
you, in particular if you use mod_cache and if you can verify that
you can't trigger the crash with a setup like described in
https://issues.apache.org/bugzilla/show_bug.cgi?id=56924


For CVE-2014-3581 I had to backport the patch for 2.4.x since I did not
find any patch for 2.2.x. The problematic code was not present in
modules/cache/cache_util.c but it was duplicated in
modules/cache/mod_{disk,mem}_cache.c.

For CVE-2013-5704, I used the upstream patch for 2.2.x but it did not
apply perfectly as we don't have the latest release in that branch
(only one hunk failed though and it was easy to fix). I dropped the
documentation update[1] from the patch because it applies on XML files while
the released tarball we have contains HTML. I dropped the increase of the
magic minor number[2] as we're way behind and it would be a lie to increase
it to the current value.

Cheers,

[1] https://github.com/apache/httpd/commit/16e241ed9f0482acfda30b115227101744ccbc2c#diff-2
[2] https://github.com/apache/httpd/commit/16e241ed9f0482acfda30b115227101744ccbc2c#diff-4
-- 
Raphaël Hertzog ◈ Debian Developer

Support Debian LTS: http://www.freexian.com/services/debian-lts.html
Learn to master Debian: http://debian-handbook.info/get/
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+squeeze14) squeeze-lts; urgency=high
+
+  * Non-maintainer upload by the LTS Team.
+  * CVE-2014-3581: Fix DoS with empty Content-Type and mod_cache.
+  * CVE-2013-5704: HTTP trailers could be used to replace HTTP headers
+    late during request processing, potentially undoing or otherwise confusing
+    modules that examined or modified request headers earlier.  Adds
+    "MergeTrailers" directive to restore legacy behavior
+
+ -- Raphaël Hertzog <hertzog@debian.org>  Wed, 15 Oct 2014 11:21:37 +0200
+
 apache2 (2.2.16-6+squeeze13) squeeze-lts; urgency=medium
 
   * Non-maintainer upload by the Squeeze 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
@@ -55,0 +56,2 @@
+CVE-2014-3581.dpatch
+CVE-2013-5704.dpatch
only in patch2:
unchanged:
--- apache2-2.2.16.orig/debian/patches/CVE-2014-3581.dpatch
+++ apache2-2.2.16/debian/patches/CVE-2014-3581.dpatch
@@ -0,0 +1,35 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+# Description: fix DoS in mod_cache when empty Content-Type is supplied
+# Origin: backport, http://svn.apache.org/viewvc?view=revision&revision=1624234
+
+@DPATCH@
+--- a/modules/cache/mod_disk_cache.c.orig	2014-10-15 11:30:14.824162491 +0200
++++ b/modules/cache/mod_disk_cache.c	2014-10-15 11:34:49.348705466 +0200
+@@ -931,8 +931,10 @@
+ 
+         if (!apr_table_get(headers_out, "Content-Type")
+             && r->content_type) {
+-            apr_table_setn(headers_out, "Content-Type",
+-                           ap_make_content_type(r, r->content_type));
++            const char *ctype = ap_make_content_type(r, r->content_type);
++            if (ctype) {
++                apr_table_setn(headers_out, "Content-Type", ctype);
++            }
+         }
+ 
+         if (!apr_table_get(headers_out, "Content-Encoding")
+--- a/modules/cache/mod_mem_cache.c.orig	2014-10-15 11:30:19.032171032 +0200
++++ b/modules/cache/mod_mem_cache.c	2014-10-15 11:33:08.468515018 +0200
+@@ -632,8 +632,10 @@
+     /* If not set in headers_out, set Content-Type */
+     if (!apr_table_get(headers_out, "Content-Type")
+         && r->content_type) {
+-        apr_table_setn(headers_out, "Content-Type",
+-                       ap_make_content_type(r, r->content_type));
++        const char *ctype = ap_make_content_type(r, r->content_type);
++        if (ctype) {
++            apr_table_setn(headers_out, "Content-Type", ctype);
++        }
+     }
+ 
+     if (!apr_table_get(headers_out, "Content-Encoding")
only in patch2:
unchanged:
--- apache2-2.2.16.orig/debian/patches/CVE-2013-5704.dpatch
+++ apache2-2.2.16/debian/patches/CVE-2013-5704.dpatch
@@ -0,0 +1,372 @@
+#! /bin/sh /usr/share/dpatch/dpatch-run
+## CVE-2013-5704.dpatch by Raphaël Hertzog <hertzog@debian.org>
+##
+## DP: Fix CVE-2013-5704
+## DP: Origin: backport, https://github.com/apache/httpd/commit/16e241ed9f0482acfda30b115227101744ccbc2c
+
+@DPATCH@
+diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' apache2-2.2.16~/include/http_core.h apache2-2.2.16/include/http_core.h
+--- apache2-2.2.16~/include/http_core.h	2009-05-28 10:04:03.000000000 +0200
++++ apache2-2.2.16/include/http_core.h	2014-10-15 11:52:06.102564667 +0200
+@@ -603,6 +603,10 @@
+ #define AP_TRACE_ENABLE    1
+ #define AP_TRACE_EXTENDED  2
+     int trace_enable;
++#define AP_MERGE_TRAILERS_UNSET    0
++#define AP_MERGE_TRAILERS_ENABLE   1
++#define AP_MERGE_TRAILERS_DISABLE  2
++    int merge_trailers;
+ 
+ } core_server_config;
+ 
+diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' apache2-2.2.16~/include/httpd.h apache2-2.2.16/include/httpd.h
+--- apache2-2.2.16~/include/httpd.h	2010-07-21 20:25:49.000000000 +0200
++++ apache2-2.2.16/include/httpd.h	2014-10-15 11:52:06.102564667 +0200
+@@ -1004,6 +1004,11 @@
+  * record to improve 64bit alignment the next time we need to break
+  * binary compatibility for some other reason.
+  */
++
++    /** MIME trailer environment from the request */
++    apr_table_t *trailers_in;
++    /** MIME trailer environment from the response */
++    apr_table_t *trailers_out;
+ };
+ 
+ /**
+diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' apache2-2.2.16~/modules/http/http_filters.c apache2-2.2.16/modules/http/http_filters.c
+--- apache2-2.2.16~/modules/http/http_filters.c	2014-10-15 11:46:37.000000000 +0200
++++ apache2-2.2.16/modules/http/http_filters.c	2014-10-15 11:52:06.102564667 +0200
+@@ -206,6 +206,49 @@
+ }
+ 
+ 
++static apr_status_t read_chunked_trailers(http_ctx_t *ctx, ap_filter_t *f,
++                                          apr_bucket_brigade *b, int merge)
++{
++    int rv;
++    apr_bucket *e;
++    request_rec *r = f->r;
++    apr_table_t *saved_headers_in = r->headers_in;
++    int saved_status = r->status;
++
++    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) {
++        r->status = saved_status;
++        e = apr_bucket_eos_create(f->c->bucket_alloc);
++        APR_BRIGADE_INSERT_TAIL(b, e);
++        ctx->eos_sent = 1;
++        rv = APR_SUCCESS;
++    }
++    else {
++        const char *error_notes = apr_table_get(r->notes,
++                                                "error-notes");
++        ap_log_rerror(APLOG_MARK, APLOG_INFO, 0, r, 
++                      "Error while reading HTTP trailer: %i%s%s",
++                      r->status, error_notes ? ": " : "",
++                      error_notes ? error_notes : "");
++        rv = APR_EINVAL;
++    }
++
++    if(!merge) {
++        r->headers_in = saved_headers_in;
++    }
++    else {
++        r->headers_in = apr_table_overlay(r->pool, saved_headers_in,
++                r->trailers_in);
++    }
++
++    return rv;
++}
++
+ /* This is the HTTP_INPUT filter for HTTP requests and responses from
+  * proxied servers (mod_proxy).  It handles chunked and content-length
+  * bodies.  This can only be inserted/used after the headers
+@@ -215,6 +258,7 @@
+                             ap_input_mode_t mode, apr_read_type_e block,
+                             apr_off_t readbytes)
+ {
++    core_server_config *conf;
+     apr_bucket *e;
+     http_ctx_t *ctx = f->ctx;
+     apr_status_t rv;
+@@ -222,6 +266,9 @@
+     int http_error = HTTP_REQUEST_ENTITY_TOO_LARGE;
+     apr_bucket_brigade *bb;
+ 
++    conf = (core_server_config *)
++        ap_get_module_config(f->r->server->module_config, &core_module);
++
+     /* just get out of the way of things we don't want. */
+     if (mode != AP_MODE_READBYTES && mode != AP_MODE_GETLINE) {
+         return ap_get_brigade(f->next, b, mode, block, readbytes);
+@@ -391,13 +438,8 @@
+             }
+ 
+             if (!ctx->remaining) {
+-                /* Handle trailers by calling ap_get_mime_headers again! */
+-                ctx->state = BODY_NONE;
+-                ap_get_mime_headers(f->r);
+-                e = apr_bucket_eos_create(f->c->bucket_alloc);
+-                APR_BRIGADE_INSERT_TAIL(b, e);
+-                ctx->eos_sent = 1;
+-                return APR_SUCCESS;
++                return read_chunked_trailers(ctx, f, b,
++                        conf->merge_trailers == AP_MERGE_TRAILERS_ENABLE);
+             }
+         }
+     }
+@@ -497,13 +539,8 @@
+                 }
+ 
+                 if (!ctx->remaining) {
+-                    /* Handle trailers by calling ap_get_mime_headers again! */
+-                    ctx->state = BODY_NONE;
+-                    ap_get_mime_headers(f->r);
+-                    e = apr_bucket_eos_create(f->c->bucket_alloc);
+-                    APR_BRIGADE_INSERT_TAIL(b, e);
+-                    ctx->eos_sent = 1;
+-                    return APR_SUCCESS;
++                    return read_chunked_trailers(ctx, f, b,
++                            conf->merge_trailers == AP_MERGE_TRAILERS_ENABLE);
+                 }
+             }
+             break;
+diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' apache2-2.2.16~/modules/http/http_request.c apache2-2.2.16/modules/http/http_request.c
+--- apache2-2.2.16~/modules/http/http_request.c	2010-01-27 01:45:57.000000000 +0100
++++ apache2-2.2.16/modules/http/http_request.c	2014-10-15 11:52:06.102564667 +0200
+@@ -384,8 +384,10 @@
+     new->main            = r->main;
+ 
+     new->headers_in      = r->headers_in;
++    new->trailers_in     = r->trailers_in;
+     new->headers_out     = apr_table_make(r->pool, 12);
+     new->err_headers_out = r->err_headers_out;
++    new->trailers_out    = apr_table_make(r->pool, 5);
+     new->subprocess_env  = rename_original_env(r->pool, r->subprocess_env);
+     new->notes           = apr_table_make(r->pool, 5);
+ 
+@@ -460,6 +462,8 @@
+                                        r->headers_out);
+     r->err_headers_out = apr_table_overlay(r->pool, rr->err_headers_out,
+                                            r->err_headers_out);
++    r->trailers_out = apr_table_overlay(r->pool, rr->trailers_out,
++                                           r->trailers_out);
+     r->subprocess_env = apr_table_overlay(r->pool, rr->subprocess_env,
+                                           r->subprocess_env);
+ 
+diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' apache2-2.2.16~/modules/loggers/mod_log_config.c apache2-2.2.16/modules/loggers/mod_log_config.c
+--- apache2-2.2.16~/modules/loggers/mod_log_config.c	2010-02-27 19:43:16.000000000 +0100
++++ apache2-2.2.16/modules/loggers/mod_log_config.c	2014-10-15 11:52:06.102564667 +0200
+@@ -412,6 +412,12 @@
+     return ap_escape_logitem(r->pool, apr_table_get(r->headers_in, a));
+ }
+ 
++static const char *log_trailer_in(request_rec *r, char *a)
++{
++    return ap_escape_logitem(r->pool, apr_table_get(r->trailers_in, a));
++}
++
++
+ static APR_INLINE char *find_multiple_headers(apr_pool_t *pool,
+                                               const apr_table_t *table,
+                                               const char *key)
+@@ -495,6 +501,11 @@
+     return ap_escape_logitem(r->pool, cp);
+ }
+ 
++static const char *log_trailer_out(request_rec *r, char *a)
++{
++    return ap_escape_logitem(r->pool, apr_table_get(r->trailers_out, a));
++}
++
+ static const char *log_note(request_rec *r, char *a)
+ {
+     return ap_escape_logitem(r->pool, apr_table_get(r->notes, a));
+@@ -781,7 +792,7 @@
+ static char *parse_log_item(apr_pool_t *p, log_format_item *it, const char **sa)
+ {
+     const char *s = *sa;
+-    ap_log_handler *handler;
++    ap_log_handler *handler = NULL;
+ 
+     if (*s != '%') {
+         return parse_log_misc_string(p, it, sa);
+@@ -851,7 +862,16 @@
+             break;
+ 
+         default:
+-            handler = (ap_log_handler *)apr_hash_get(log_hash, s++, 1);
++            /* check for '^' + two character format first */
++            if (*s == '^' && *(s+1) && *(s+2)) { 
++                handler = (ap_log_handler *)apr_hash_get(log_hash, s, 3); 
++                if (handler) { 
++                   s += 3;
++                }
++            }
++            if (!handler) {  
++                handler = (ap_log_handler *)apr_hash_get(log_hash, s++, 1);  
++            }
+             if (!handler) {
+                 char dummy[2];
+ 
+@@ -1353,7 +1373,7 @@
+     log_struct->func = handler;
+     log_struct->want_orig_default = def;
+ 
+-    apr_hash_set(log_hash, tag, 1, (const void *)log_struct);
++    apr_hash_set(log_hash, tag, strlen(tag), (const void *)log_struct);
+ }
+ static ap_log_writer_init* ap_log_set_writer_init(ap_log_writer_init *handle)
+ {
+@@ -1522,6 +1542,9 @@
+         log_pfn_register(p, "U", log_request_uri, 1);
+         log_pfn_register(p, "s", log_status, 1);
+         log_pfn_register(p, "R", log_handler, 1);
++
++        log_pfn_register(p, "^ti", log_trailer_in, 0);
++        log_pfn_register(p, "^to", log_trailer_out, 0);
+     }
+ 
+     return OK;
+diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' apache2-2.2.16~/modules/proxy/mod_proxy_http.c apache2-2.2.16/modules/proxy/mod_proxy_http.c
+--- apache2-2.2.16~/modules/proxy/mod_proxy_http.c	2010-06-11 11:10:29.000000000 +0200
++++ apache2-2.2.16/modules/proxy/mod_proxy_http.c	2014-10-15 11:52:06.102564667 +0200
+@@ -1219,6 +1219,7 @@
+     psc = (proxy_server_conf *) ap_get_module_config(sconf, &proxy_module);
+ 
+     r->headers_out = apr_table_make(r->pool, 20);
++    r->trailers_out = apr_table_make(r->pool, 5);
+     *pread_len = 0;
+ 
+     /*
+@@ -1345,6 +1346,14 @@
+ #define AP_MAX_INTERIM_RESPONSES 10
+ #endif
+ 
++static int add_trailers(void *data, const char *key, const char *val)
++{
++    if (val) {
++        apr_table_add((apr_table_t*)data, key, val);
++    }
++    return 1;
++}
++
+ static
+ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r,
+                                             proxy_conn_rec *backend,
+@@ -1799,6 +1808,12 @@
+                     /* next time try a non-blocking read */
+                     mode = APR_NONBLOCK_READ;
+ 
++                    if (!apr_is_empty_table(rp->trailers_in)) {
++                        apr_table_do(add_trailers, r->trailers_out,
++                                rp->trailers_in, NULL);
++                        apr_table_clear(rp->trailers_in);
++                    }
++
+                     apr_brigade_length(bb, 0, &readbytes);
+                     backend->worker->s->read += readbytes;
+ #if DEBUGGING
+diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' apache2-2.2.16~/modules/proxy/proxy_util.c apache2-2.2.16/modules/proxy/proxy_util.c
+--- apache2-2.2.16~/modules/proxy/proxy_util.c	2010-02-15 20:54:41.000000000 +0100
++++ apache2-2.2.16/modules/proxy/proxy_util.c	2014-10-15 11:52:06.102564667 +0200
+@@ -350,8 +350,11 @@
+     rp->status          = HTTP_OK;
+ 
+     rp->headers_in      = apr_table_make(r->pool, 50);
++    rp->trailers_in     = apr_table_make(r->pool, 5);
++
+     rp->subprocess_env  = apr_table_make(r->pool, 50);
+     rp->headers_out     = apr_table_make(r->pool, 12);
++    rp->trailers_out    = apr_table_make(r->pool, 5);
+     rp->err_headers_out = apr_table_make(r->pool, 5);
+     rp->notes           = apr_table_make(r->pool, 5);
+ 
+diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' apache2-2.2.16~/server/core.c apache2-2.2.16/server/core.c
+--- apache2-2.2.16~/server/core.c	2014-10-15 11:46:37.694023832 +0200
++++ apache2-2.2.16/server/core.c	2014-10-15 11:55:19.102728068 +0200
+@@ -530,6 +530,10 @@
+                          ? virt->trace_enable
+                          : base->trace_enable;
+ 
++    conf->merge_trailers = (virt->merge_trailers != AP_MERGE_TRAILERS_UNSET)
++                           ? virt->merge_trailers
++                           : base->merge_trailers;
++
+     return conf;
+ }
+ 
+@@ -3246,6 +3250,16 @@
+     return NULL;
+ }
+ 
++static const char *set_merge_trailers(cmd_parms *cmd, void *dummy, int arg)
++{
++    core_server_config *conf = ap_get_module_config(cmd->server->module_config,
++                                                    &core_module);
++    conf->merge_trailers = (arg ? AP_MERGE_TRAILERS_ENABLE :
++            AP_MERGE_TRAILERS_DISABLE);
++
++    return NULL;
++}
++
+ /* Note --- ErrorDocument will now work from .htaccess files.
+  * The AllowOverride of Fileinfo allows webmasters to turn it off
+  */
+@@ -3478,6 +3492,8 @@
+ #endif
+ AP_INIT_TAKE1("TraceEnable", set_trace_enable, NULL, RSRC_CONF,
+               "'on' (default), 'off' or 'extended' to trace request body content"),
++AP_INIT_FLAG("MergeTrailers", set_merge_trailers, NULL, RSRC_CONF,
++              "merge request trailers into request headers or not"),
+ { NULL }
+ };
+ 
+diff -urNad '--exclude=CVS' '--exclude=.svn' '--exclude=.git' '--exclude=.arch' '--exclude=.hg' '--exclude=_darcs' '--exclude=.bzr' apache2-2.2.16~/server/protocol.c apache2-2.2.16/server/protocol.c
+--- apache2-2.2.16~/server/protocol.c	2014-10-15 11:46:37.000000000 +0200
++++ apache2-2.2.16/server/protocol.c	2014-10-15 11:52:06.102564667 +0200
+@@ -709,6 +709,8 @@
+                 r->status = HTTP_REQUEST_TIME_OUT;
+             }
+             else {
++                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rv, r, 
++                              "Failed to read request header line %s", field);
+                 r->status = HTTP_BAD_REQUEST;
+             }
+ 
+@@ -888,9 +890,11 @@
+     r->allowed_methods = ap_make_method_list(p, 2);
+ 
+     r->headers_in      = apr_table_make(r->pool, 25);
++    r->trailers_in     = apr_table_make(r->pool, 5);
+     r->subprocess_env  = apr_table_make(r->pool, 25);
+     r->headers_out     = apr_table_make(r->pool, 12);
+     r->err_headers_out = apr_table_make(r->pool, 5);
++    r->trailers_out    = apr_table_make(r->pool, 5);
+     r->notes           = apr_table_make(r->pool, 5);
+ 
+     r->request_config  = ap_create_request_config(r->pool);
+@@ -1126,7 +1130,8 @@
+ 
+     rnew->status          = HTTP_OK;
+ 
+-    rnew->headers_in = apr_table_copy(rnew->pool, r->headers_in);
++    rnew->headers_in      = apr_table_copy(rnew->pool, r->headers_in);
++    rnew->trailers_in     = apr_table_copy(rnew->pool, r->trailers_in);
+ 
+     /* did the original request have a body?  (e.g. POST w/SSI tags)
+      * if so, make sure the subrequest doesn't inherit body headers
+@@ -1138,6 +1143,7 @@
+     rnew->subprocess_env  = apr_table_copy(rnew->pool, r->subprocess_env);
+     rnew->headers_out     = apr_table_make(rnew->pool, 5);
+     rnew->err_headers_out = apr_table_make(rnew->pool, 5);
++    rnew->trailers_out    = apr_table_make(rnew->pool, 5);
+     rnew->notes           = apr_table_make(rnew->pool, 5);
+ 
+     rnew->expecting_100   = r->expecting_100;

Reply to: