[apache2] 01/01: CVE-2013-5704: Fix handling of chunk trailers
Stefan Fritsch
sf at moszumanska.debian.org
Sun Nov 9 13:51:59 UTC 2014
This is an automated email from the git hooks/post-receive script.
sf pushed a commit to branch wheezy
in repository apache2.
commit 7c7e357f628efcecb31b3480925fee93e5a8c575
Author: Stefan Fritsch <sf at sfritsch.de>
Date: Sun Nov 9 14:44:13 2014 +0100
CVE-2013-5704: Fix handling of chunk trailers
---
debian/changelog | 12 +
debian/patches/CVE-2013-5704_trailers.patch | 383 ++++++++++++++++++++++++++++
debian/patches/series | 1 +
3 files changed, 396 insertions(+)
diff --git a/debian/changelog b/debian/changelog
index 6258086..d5f2a9d 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,15 @@
+apache2 (2.2.22-13+deb7u4) UNRELEASED; urgency=high
+
+ * CVE-2013-5704: Fix handling of chunk trailers. A remote attacker could
+ use this flaw to bypass intended mod_headers restrictions, allowing
+ them to send requests to applications that include headers that should
+ have been removed by mod_headers.
+ The new behavior is to not merge trailers into the headers autmatically.
+ A new directive "MergeTrailers" is introduced to restore the old
+ behavior.
+
+ -- Stefan Fritsch <sf at debian.org> Sun, 09 Nov 2014 14:38:26 +0100
+
apache2 (2.2.22-13+deb7u3) wheezy-security; urgency=high
* CVE-2014-0226: Fix a race condition in scoreboard handling,
diff --git a/debian/patches/CVE-2013-5704_trailers.patch b/debian/patches/CVE-2013-5704_trailers.patch
new file mode 100644
index 0000000..ca6320d
--- /dev/null
+++ b/debian/patches/CVE-2013-5704_trailers.patch
@@ -0,0 +1,383 @@
+# http://svn,apache.org/r1619489
+#
+# *) SECURITY: CVE-2013-5704 (cve.mitre.org)
+# core: 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. [Edward Lu, Yann Ylavic, Joe Orton, Eric Covener]
+#
+Index: apache2/modules/loggers/mod_log_config.c
+===================================================================
+--- apache2.orig/modules/loggers/mod_log_config.c
++++ apache2/modules/loggers/mod_log_config.c
+@@ -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));
+@@ -813,7 +824,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);
+@@ -883,7 +894,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];
+
+@@ -1389,7 +1409,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)
+ {
+@@ -1558,6 +1578,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);
+ }
+
+ /* reset to default conditions */
+Index: apache2/modules/http/http_request.c
+===================================================================
+--- apache2.orig/modules/http/http_request.c
++++ apache2/modules/http/http_request.c
+@@ -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);
+
+@@ -495,6 +497,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);
+
+Index: apache2/modules/http/http_filters.c
+===================================================================
+--- apache2.orig/modules/http/http_filters.c
++++ apache2/modules/http/http_filters.c
+@@ -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);
+@@ -395,13 +442,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);
+ }
+ }
+ }
+@@ -501,13 +543,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;
+Index: apache2/modules/proxy/proxy_util.c
+===================================================================
+--- apache2.orig/modules/proxy/proxy_util.c
++++ apache2/modules/proxy/proxy_util.c
+@@ -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);
+
+Index: apache2/modules/proxy/mod_proxy_http.c
+===================================================================
+--- apache2.orig/modules/proxy/mod_proxy_http.c
++++ apache2/modules/proxy/mod_proxy_http.c
+@@ -1229,6 +1229,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;
+
+ /*
+@@ -1355,6 +1356,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,
+@@ -1808,6 +1817,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
+Index: apache2/include/httpd.h
+===================================================================
+--- apache2.orig/include/httpd.h
++++ apache2/include/httpd.h
+@@ -1006,6 +1006,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;
+ };
+
+ /**
+Index: apache2/include/http_core.h
+===================================================================
+--- apache2.orig/include/http_core.h
++++ apache2/include/http_core.h
+@@ -613,6 +613,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;
+
+Index: apache2/server/protocol.c
+===================================================================
+--- apache2.orig/server/protocol.c
++++ apache2/server/protocol.c
+@@ -708,6 +708,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;
+ }
+
+@@ -887,9 +889,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);
+@@ -1117,7 +1121,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
+@@ -1129,6 +1134,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;
+Index: apache2/server/core.c
+===================================================================
+--- apache2.orig/server/core.c
++++ apache2/server/core.c
+@@ -542,6 +542,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;
+ }
+
+@@ -3295,6 +3299,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
+ */
+@@ -3534,6 +3548,8 @@
+ AP_INIT_FLAG("Suexec", unixd_set_suexec, NULL, RSRC_CONF,
+ "Enable or disable suEXEC support"),
+ #endif
++AP_INIT_FLAG("MergeTrailers", set_merge_trailers, NULL, RSRC_CONF,
++ "merge request trailers into request headers or not"),
+ { NULL }
+ };
+
diff --git a/debian/patches/series b/debian/patches/series
index 3d1d61f..da1c001 100644
--- a/debian/patches/series
+++ b/debian/patches/series
@@ -45,3 +45,4 @@ mod_proxy-crash-PR_50335.patch
CVE-2014-0226_scoreboard.patch
CVE-2014-0231_mod_cgid-DoS.patch
CVE-2014-0118_mod_deflate-DoS.patch
+CVE-2013-5704_trailers.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