From a4fd0b34103f3fc6365eb154ea5277485ed01a5c Mon Sep 17 00:00:00 2001 From: Kang Kai Date: Wed, 29 Oct 2014 09:40:08 +0800 Subject: apache: add fix for CVE-2014-0117 Security Advisory The patch comes from upstream: http://svn.apache.org/viewvc?view=revision&revision=1610674 SECURITY (CVE-2014-0117): Fix a crash in mod_proxy. In a reverse proxy configuration, a remote attacker could send a carefully crafted request which could crash a server process, resulting in denial of service. Thanks to Marek Kroemeke working with HP's Zero Day Initiative for reporting this issue. Submitted by: Edward Lu, breser, covener Signed-off-by: Zhang Xiao Signed-off-by: Kai Kang --- .../apache2/apache2/apache-CVE-2014-0117.patch | 289 +++++++++++++++++++++ .../recipes-httpd/apache2/apache2_2.4.10.bb | 4 +- 2 files changed, 292 insertions(+), 1 deletion(-) create mode 100644 meta-webserver/recipes-httpd/apache2/apache2/apache-CVE-2014-0117.patch (limited to 'meta-webserver') diff --git a/meta-webserver/recipes-httpd/apache2/apache2/apache-CVE-2014-0117.patch b/meta-webserver/recipes-httpd/apache2/apache2/apache-CVE-2014-0117.patch new file mode 100644 index 0000000000..8585f0bb30 --- /dev/null +++ b/meta-webserver/recipes-httpd/apache2/apache2/apache-CVE-2014-0117.patch @@ -0,0 +1,289 @@ +apache: CVE-2014-0117 + +The patch comes from upstream: +http://svn.apache.org/viewvc?view=revision&revision=1610674 + +SECURITY (CVE-2014-0117): Fix a crash in mod_proxy. In a +reverse proxy configuration, a remote attacker could send a carefully crafted +request which could crash a server process, resulting in denial of service. + +Thanks to Marek Kroemeke working with HP's Zero Day Initiative for +reporting this issue. + +Upstream-Status: Backport + +Submitted by: Edward Lu, breser, covener +Signed-off-by: Zhang Xiao +--- + modules/proxy/mod_proxy_http.c | 8 +++- + include/httpd.h | 17 ++++++++ + modules/proxy/proxy_util.c | 67 ++++++++++++++---------------- + server/util.c | 89 ++++++++++++++++++++++++++++++++++++++++++ + 4 files changed, 143 insertions(+), 38 deletions(-) + +diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c +index cffad2e..f11c16f 100644 +--- a/modules/proxy/mod_proxy_http.c ++++ b/modules/proxy/mod_proxy_http.c +@@ -1362,6 +1362,7 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, + */ + if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) { + int major, minor; ++ int toclose; + + major = buffer[5] - '0'; + minor = buffer[7] - '0'; +@@ -1470,7 +1471,12 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, + te = apr_table_get(r->headers_out, "Transfer-Encoding"); + + /* strip connection listed hop-by-hop headers from response */ +- backend->close = ap_proxy_clear_connection_fn(r, r->headers_out); ++ toclose = ap_proxy_clear_connection_fn(r, r->headers_out); ++ backend->close = (toclose != 0); ++ if (toclose < 0) { ++ return ap_proxyerror(r, HTTP_BAD_REQUEST, ++ "Malformed connection header"); ++ } + + if ((buf = apr_table_get(r->headers_out, "Content-Type"))) { + ap_set_content_type(r, apr_pstrdup(p, buf)); +diff --git a/include/httpd.h b/include/httpd.h +index 36cd58d..9a2cf5c 100644 +--- a/include/httpd.h ++++ b/include/httpd.h +@@ -1528,6 +1528,23 @@ AP_DECLARE(int) ap_find_etag_weak(apr_pool_t *p, const char *line, const char *t + AP_DECLARE(int) ap_find_etag_strong(apr_pool_t *p, const char *line, const char *tok); + + /** ++ * Retrieve an array of tokens in the format "1#token" defined in RFC2616. Only ++ * accepts ',' as a delimiter, does not accept quoted strings, and errors on ++ * any separator. ++ * @param p The pool to allocate from ++ * @param tok The line to read tokens from ++ * @param tokens Pointer to an array of tokens. If not NULL, must be an array ++ * of char*, otherwise it will be allocated on @a p when a token is found ++ * @param skip_invalid If true, when an invalid separator is encountered, it ++ * will be ignored. ++ * @return NULL on success, an error string otherwise. ++ * @remark *tokens may be NULL on output if NULL in input and no token is found ++ */ ++AP_DECLARE(const char *) ap_parse_token_list_strict(apr_pool_t *p, const char *tok, ++ apr_array_header_t **tokens, ++ int skip_invalid); ++ ++/** + * Retrieve a token, spacing over it and adjusting the pointer to + * the first non-white byte afterwards. Note that these tokens + * are delimited by semis and commas and can also be delimited +diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c +index 67dc939..58daa21 100644 +--- a/modules/proxy/proxy_util.c ++++ b/modules/proxy/proxy_util.c +@@ -2847,68 +2847,59 @@ PROXY_DECLARE(proxy_balancer_shared *) ap_proxy_find_balancershm(ap_slotmem_prov + typedef struct header_connection { + apr_pool_t *pool; + apr_array_header_t *array; +- const char *first; +- unsigned int closed:1; ++ const char *error; ++ int is_req; + } header_connection; + + static int find_conn_headers(void *data, const char *key, const char *val) + { + header_connection *x = data; +- const char *name; +- +- do { +- while (*val == ',' || *val == ';') { +- val++; +- } +- name = ap_get_token(x->pool, &val, 0); +- if (!strcasecmp(name, "close")) { +- x->closed = 1; +- } +- if (!x->first) { +- x->first = name; +- } +- else { +- const char **elt; +- if (!x->array) { +- x->array = apr_array_make(x->pool, 4, sizeof(char *)); +- } +- elt = apr_array_push(x->array); +- *elt = name; +- } +- } while (*val); + +- return 1; ++ x->error = ap_parse_token_list_strict(x->pool, val, &x->array, !x->is_req); ++ return !x->error; + } + + /** + * Remove all headers referred to by the Connection header. ++ * Returns -1 on error. Otherwise, returns 1 if 'Close' was seen in ++ * the Connection header tokens, and 0 if not. + */ + static int ap_proxy_clear_connection(request_rec *r, apr_table_t *headers) + { +- const char **name; ++ int closed = 0; + header_connection x; + + x.pool = r->pool; + x.array = NULL; +- x.first = NULL; +- x.closed = 0; ++ x.error = NULL; ++ x.is_req = (headers == r->headers_in); + + apr_table_unset(headers, "Proxy-Connection"); + + apr_table_do(find_conn_headers, &x, headers, "Connection", NULL); +- if (x.first) { +- /* fast path - no memory allocated for one header */ +- apr_table_unset(headers, "Connection"); +- apr_table_unset(headers, x.first); ++ apr_table_unset(headers, "Connection"); ++ ++ if (x.error) { ++ ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO() ++ "Error parsing Connection header: %s", x.error); ++ return -1; + } ++ + if (x.array) { +- /* two or more headers */ +- while ((name = apr_array_pop(x.array))) { +- apr_table_unset(headers, *name); ++ int i; ++ for (i = 0; i < x.array->nelts; i++) { ++ const char *name = APR_ARRAY_IDX(x.array, i, const char *); ++ ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO() ++ "Removing header '%s' listed in Connection header", ++ name); ++ if (!strcasecmp(name, "close")) { ++ closed = 1; ++ } ++ apr_table_unset(headers, name); + } + } + +- return x.closed; ++ return closed; + } + + PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p, +@@ -3095,7 +3086,9 @@ PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p, + * apr is compiled with APR_POOL_DEBUG. + */ + headers_in_copy = apr_table_copy(r->pool, r->headers_in); +- ap_proxy_clear_connection(r, headers_in_copy); ++ if (ap_proxy_clear_connection(r, headers_in_copy) < 0) { ++ return HTTP_BAD_REQUEST; ++ } + /* send request headers */ + headers_in_array = apr_table_elts(headers_in_copy); + headers_in = (const apr_table_entry_t *) headers_in_array->elts; +diff --git a/server/util.c b/server/util.c +index e0ba5c2..541c9f0 100644 +--- a/server/util.c ++++ b/server/util.c +@@ -1449,6 +1449,95 @@ AP_DECLARE(int) ap_find_etag_weak(apr_pool_t *p, const char *line, + return find_list_item(p, line, tok, AP_ETAG_WEAK); + } + ++/* Grab a list of tokens of the format 1#token (from RFC7230) */ ++AP_DECLARE(const char *) ap_parse_token_list_strict(apr_pool_t *p, ++ const char *str_in, ++ apr_array_header_t **tokens, ++ int skip_invalid) ++{ ++ int in_leading_space = 1; ++ int in_trailing_space = 0; ++ int string_end = 0; ++ const char *tok_begin; ++ const char *cur; ++ ++ if (!str_in) { ++ return NULL; ++ } ++ ++ tok_begin = cur = str_in; ++ ++ while (!string_end) { ++ const unsigned char c = (unsigned char)*cur; ++ ++ if (!TEST_CHAR(c, T_HTTP_TOKEN_STOP) && c != '\0') { ++ /* Non-separator character; we are finished with leading ++ * whitespace. We must never have encountered any trailing ++ * whitespace before the delimiter (comma) */ ++ in_leading_space = 0; ++ if (in_trailing_space) { ++ return "Encountered illegal whitespace in token"; ++ } ++ } ++ else if (c == ' ' || c == '\t') { ++ /* "Linear whitespace" only includes ASCII CRLF, space, and tab; ++ * we can't get a CRLF since headers are split on them already, ++ * so only look for a space or a tab */ ++ if (in_leading_space) { ++ /* We're still in leading whitespace */ ++ ++tok_begin; ++ } ++ else { ++ /* We must be in trailing whitespace */ ++ ++in_trailing_space; ++ } ++ } ++ else if (c == ',' || c == '\0') { ++ if (!in_leading_space) { ++ /* If we're out of the leading space, we know we've read some ++ * characters of a token */ ++ if (*tokens == NULL) { ++ *tokens = apr_array_make(p, 4, sizeof(char *)); ++ } ++ APR_ARRAY_PUSH(*tokens, char *) = ++ apr_pstrmemdup((*tokens)->pool, tok_begin, ++ (cur - tok_begin) - in_trailing_space); ++ } ++ /* We're allowed to have null elements, just don't add them to the ++ * array */ ++ ++ tok_begin = cur + 1; ++ in_leading_space = 1; ++ in_trailing_space = 0; ++ string_end = (c == '\0'); ++ } ++ else { ++ /* Encountered illegal separator char */ ++ if (skip_invalid) { ++ /* Skip to the next separator */ ++ const char *temp; ++ temp = ap_strchr_c(cur, ','); ++ if(!temp) { ++ temp = ap_strchr_c(cur, '\0'); ++ } ++ ++ /* Act like we haven't seen a token so we reset */ ++ cur = temp - 1; ++ in_leading_space = 1; ++ in_trailing_space = 0; ++ } ++ else { ++ return apr_psprintf(p, "Encountered illegal separator " ++ "'\\x%.2x'", (unsigned int)c); ++ } ++ } ++ ++ ++cur; ++ } ++ ++ return NULL; ++} ++ + /* Retrieve a token, spacing over it and returning a pointer to + * the first non-white byte afterwards. Note that these tokens + * are delimited by semis and commas; and can also be delimited +-- diff --git a/meta-webserver/recipes-httpd/apache2/apache2_2.4.10.bb b/meta-webserver/recipes-httpd/apache2/apache2_2.4.10.bb index 573cd6fb00..d79d40bd2c 100644 --- a/meta-webserver/recipes-httpd/apache2/apache2_2.4.10.bb +++ b/meta-webserver/recipes-httpd/apache2/apache2_2.4.10.bb @@ -19,7 +19,9 @@ SRC_URI = "http://www.apache.org/dist/httpd/httpd-${PV}.tar.bz2 \ file://0001-configure-use-pkg-config-for-PCRE-detection.patch \ file://init \ file://apache2-volatile.conf \ - file://apache2.service" + file://apache2.service \ + file://apache-CVE-2014-0117.patch \ + " LIC_FILES_CHKSUM = "file://LICENSE;md5=dbff5a2b542fa58854455bf1a0b94b83" SRC_URI[md5sum] = "44543dff14a4ebc1e9e2d86780507156" -- cgit 1.2.3-korg