[oe] [meta-webserver][PATCH] apache: add fix for CVE-2014-0117 Security Advisory

Kai Kang kai.kang at windriver.com
Wed Oct 29 01:40:08 UTC 2014


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 <xiao.zhang at windriver.com>
Signed-off-by: Kai Kang <kai.kang at windriver.com>
---
 .../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

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 0000000..8585f0b
--- /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 <xiao.zhang at windriver.com>
+---
+ 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 573cd6f..d79d40b 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"
-- 
1.9.1




More information about the Openembedded-devel mailing list