Sign Up
Log In
Log In
or
Sign Up
Places
All Projects
Status Monitor
Collapse sidebar
SUSE:SLE-12-SP2:GA
apache2.2876
httpd-2.4.x-mod_headers_chunked_requests.patch
Overview
Repositories
Revisions
Requests
Users
Attributes
Meta
File httpd-2.4.x-mod_headers_chunked_requests.patch of Package apache2.2876
From 6688f9d102ad29d6bb4167d690ee495d709e47b6 Mon Sep 17 00:00:00 2001 From: "William A. Rowe Jr" <wrowe@apache.org> Date: Fri, 22 Aug 2014 18:18:08 +0000 Subject: [PATCH] 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. Submitted by: Edward Lu, Yann Ylavic, Joe Orton, Eric Covener Backports: r1610814 Reviewed by: covener, wrowe, ylavic git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1619884 13f79535-47bb-0310-9956-ffa450edef68 Index: httpd-2.4.10/include/http_core.h =================================================================== --- httpd-2.4.10.orig/include/http_core.h +++ httpd-2.4.10/include/http_core.h @@ -667,6 +667,10 @@ typedef struct { #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; #define AP_HTTP_CL_HEAD_ZERO_UNSET 0 #define AP_HTTP_CL_HEAD_ZERO_ENABLE 1 Index: httpd-2.4.10/include/httpd.h =================================================================== --- httpd-2.4.10.orig/include/httpd.h +++ httpd-2.4.10/include/httpd.h @@ -1035,6 +1035,11 @@ struct request_rec { */ apr_sockaddr_t *useragent_addr; char *useragent_ip; + + /** MIME trailer environment from the request */ + apr_table_t *trailers_in; + /** MIME trailer environment from the response */ + apr_table_t *trailers_out; }; /** Index: httpd-2.4.10/modules/http/http_filters.c =================================================================== --- httpd-2.4.10.orig/modules/http/http_filters.c +++ httpd-2.4.10/modules/http/http_filters.c @@ -231,6 +231,49 @@ static apr_status_t get_chunk_line(http_ } +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 @@ -240,6 +283,7 @@ apr_status_t ap_http_filter(ap_filter_t 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; @@ -247,6 +291,9 @@ apr_status_t ap_http_filter(ap_filter_t 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); @@ -425,13 +472,8 @@ apr_status_t ap_http_filter(ap_filter_t } 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); } } } @@ -534,13 +576,8 @@ apr_status_t ap_http_filter(ap_filter_t } 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: httpd-2.4.10/modules/http/http_request.c =================================================================== --- httpd-2.4.10.orig/modules/http/http_request.c +++ httpd-2.4.10/modules/http/http_request.c @@ -463,6 +463,7 @@ static request_rec *internal_internal_re 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); if (ap_is_HTTP_REDIRECT(new->status)) { const char *location = apr_table_get(r->headers_out, "Location"); @@ -470,6 +471,7 @@ static request_rec *internal_internal_re apr_table_setn(new->headers_out, "Location", location); } 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); @@ -583,6 +585,8 @@ AP_DECLARE(void) ap_internal_fast_redire 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: httpd-2.4.10/modules/loggers/mod_log_config.c =================================================================== --- httpd-2.4.10.orig/modules/loggers/mod_log_config.c +++ httpd-2.4.10/modules/loggers/mod_log_config.c @@ -431,6 +431,12 @@ static const char *log_header_in(request 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) @@ -514,6 +520,11 @@ static const char *log_header_out(reques 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)); @@ -916,7 +927,7 @@ static char *parse_log_misc_string(apr_p 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); @@ -986,7 +997,16 @@ static char *parse_log_item(apr_pool_t * 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]; @@ -1516,7 +1536,7 @@ static void ap_register_log_handler(apr_ 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) { @@ -1694,6 +1714,9 @@ static int log_pre_config(apr_pool_t *p, 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: httpd-2.4.10/modules/proxy/mod_proxy_http.c =================================================================== --- httpd-2.4.10.orig/modules/proxy/mod_proxy_http.c +++ httpd-2.4.10/modules/proxy/mod_proxy_http.c @@ -1011,8 +1011,11 @@ static request_rec *make_fake_req(conn_r rp->status = HTTP_OK; rp->headers_in = apr_table_make(pool, 50); + rp->trailers_in = apr_table_make(pool, 5); + rp->subprocess_env = apr_table_make(pool, 50); rp->headers_out = apr_table_make(pool, 12); + rp->trailers_out = apr_table_make(pool, 5); rp->err_headers_out = apr_table_make(pool, 5); rp->notes = apr_table_make(pool, 5); @@ -1093,6 +1096,7 @@ static void ap_proxy_read_headers(reques 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; /* @@ -1223,6 +1227,14 @@ apr_status_t ap_proxygetline(apr_bucket_ #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_ptr, @@ -1735,6 +1747,12 @@ apr_status_t ap_proxy_http_process_respo /* next time try a non-blocking read */ mode = APR_NONBLOCK_READ; + if (!apr_is_empty_table(backend->r->trailers_in)) { + apr_table_do(add_trailers, r->trailers_out, + backend->r->trailers_in, NULL); + apr_table_clear(backend->r->trailers_in); + } + apr_brigade_length(bb, 0, &readbytes); backend->worker->s->read += readbytes; #if DEBUGGING Index: httpd-2.4.10/server/core.c =================================================================== --- httpd-2.4.10.orig/server/core.c +++ httpd-2.4.10/server/core.c @@ -526,6 +526,10 @@ static void *merge_core_server_configs(a if (virt->error_log_req) conf->error_log_req = virt->error_log_req; + conf->merge_trailers = (virt->merge_trailers != AP_MERGE_TRAILERS_UNSET) + ? virt->merge_trailers + : base->merge_trailers; + return conf; } @@ -3914,6 +3918,16 @@ AP_DECLARE(void) ap_register_errorlog_ha } +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 */ @@ -4165,6 +4179,8 @@ AP_INIT_FLAG("HttpContentLengthHeadZero" "whether to permit Content-Length of 0 responses to HEAD requests"), AP_INIT_FLAG("HttpExpectStrict", set_expect_strict, NULL, OR_OPTIONS, "whether to return a 417 if a client doesn't send 100-Continue"), +AP_INIT_FLAG("MergeTrailers", set_merge_trailers, NULL, RSRC_CONF, + "merge request trailers into request headers or not"), { NULL } }; @@ -4247,7 +4263,6 @@ static int core_map_to_storage(request_r static int do_nothing(request_rec *r) { return OK; } - static int core_override_type(request_rec *r) { core_dir_config *conf = Index: httpd-2.4.10/server/protocol.c =================================================================== --- httpd-2.4.10.orig/server/protocol.c +++ httpd-2.4.10/server/protocol.c @@ -718,6 +718,8 @@ AP_DECLARE(void) ap_get_mime_headers_cor 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; } @@ -917,9 +919,11 @@ request_rec *ap_read_request(conn_rec *c 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); @@ -1194,6 +1198,7 @@ AP_DECLARE(void) ap_set_sub_req_protocol rnew->status = HTTP_OK; 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 @@ -1205,6 +1210,7 @@ AP_DECLARE(void) ap_set_sub_req_protocol 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;
Locations
Projects
Search
Status Monitor
Help
OpenBuildService.org
Documentation
API Documentation
Code of Conduct
Contact
Support
@OBShq
Terms
openSUSE Build Service is sponsored by
The Open Build Service is an
openSUSE project
.
Sign Up
Log In
Places
Places
All Projects
Status Monitor