diff options
-rw-r--r-- | meta-webserver/recipes-httpd/apache2/apache2/apache-CVE-2014-0117.patch | 289 | ||||
-rw-r--r-- | meta-webserver/recipes-httpd/apache2/apache2_2.4.10.bb | 4 |
2 files changed, 292 insertions, 1 deletions
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 000000000..8585f0bb3 --- /dev/null +++ b/meta-webserver/recipes-httpd/apache2/apache2/apache-CVE-2014-0117.patch | |||
@@ -0,0 +1,289 @@ | |||
1 | apache: CVE-2014-0117 | ||
2 | |||
3 | The patch comes from upstream: | ||
4 | http://svn.apache.org/viewvc?view=revision&revision=1610674 | ||
5 | |||
6 | SECURITY (CVE-2014-0117): Fix a crash in mod_proxy. In a | ||
7 | reverse proxy configuration, a remote attacker could send a carefully crafted | ||
8 | request which could crash a server process, resulting in denial of service. | ||
9 | |||
10 | Thanks to Marek Kroemeke working with HP's Zero Day Initiative for | ||
11 | reporting this issue. | ||
12 | |||
13 | Upstream-Status: Backport | ||
14 | |||
15 | Submitted by: Edward Lu, breser, covener | ||
16 | Signed-off-by: Zhang Xiao <xiao.zhang@windriver.com> | ||
17 | --- | ||
18 | modules/proxy/mod_proxy_http.c | 8 +++- | ||
19 | include/httpd.h | 17 ++++++++ | ||
20 | modules/proxy/proxy_util.c | 67 ++++++++++++++---------------- | ||
21 | server/util.c | 89 ++++++++++++++++++++++++++++++++++++++++++ | ||
22 | 4 files changed, 143 insertions(+), 38 deletions(-) | ||
23 | |||
24 | diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c | ||
25 | index cffad2e..f11c16f 100644 | ||
26 | --- a/modules/proxy/mod_proxy_http.c | ||
27 | +++ b/modules/proxy/mod_proxy_http.c | ||
28 | @@ -1362,6 +1362,7 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, | ||
29 | */ | ||
30 | if (apr_date_checkmask(buffer, "HTTP/#.# ###*")) { | ||
31 | int major, minor; | ||
32 | + int toclose; | ||
33 | |||
34 | major = buffer[5] - '0'; | ||
35 | minor = buffer[7] - '0'; | ||
36 | @@ -1470,7 +1471,12 @@ apr_status_t ap_proxy_http_process_response(apr_pool_t * p, request_rec *r, | ||
37 | te = apr_table_get(r->headers_out, "Transfer-Encoding"); | ||
38 | |||
39 | /* strip connection listed hop-by-hop headers from response */ | ||
40 | - backend->close = ap_proxy_clear_connection_fn(r, r->headers_out); | ||
41 | + toclose = ap_proxy_clear_connection_fn(r, r->headers_out); | ||
42 | + backend->close = (toclose != 0); | ||
43 | + if (toclose < 0) { | ||
44 | + return ap_proxyerror(r, HTTP_BAD_REQUEST, | ||
45 | + "Malformed connection header"); | ||
46 | + } | ||
47 | |||
48 | if ((buf = apr_table_get(r->headers_out, "Content-Type"))) { | ||
49 | ap_set_content_type(r, apr_pstrdup(p, buf)); | ||
50 | diff --git a/include/httpd.h b/include/httpd.h | ||
51 | index 36cd58d..9a2cf5c 100644 | ||
52 | --- a/include/httpd.h | ||
53 | +++ b/include/httpd.h | ||
54 | @@ -1528,6 +1528,23 @@ AP_DECLARE(int) ap_find_etag_weak(apr_pool_t *p, const char *line, const char *t | ||
55 | AP_DECLARE(int) ap_find_etag_strong(apr_pool_t *p, const char *line, const char *tok); | ||
56 | |||
57 | /** | ||
58 | + * Retrieve an array of tokens in the format "1#token" defined in RFC2616. Only | ||
59 | + * accepts ',' as a delimiter, does not accept quoted strings, and errors on | ||
60 | + * any separator. | ||
61 | + * @param p The pool to allocate from | ||
62 | + * @param tok The line to read tokens from | ||
63 | + * @param tokens Pointer to an array of tokens. If not NULL, must be an array | ||
64 | + * of char*, otherwise it will be allocated on @a p when a token is found | ||
65 | + * @param skip_invalid If true, when an invalid separator is encountered, it | ||
66 | + * will be ignored. | ||
67 | + * @return NULL on success, an error string otherwise. | ||
68 | + * @remark *tokens may be NULL on output if NULL in input and no token is found | ||
69 | + */ | ||
70 | +AP_DECLARE(const char *) ap_parse_token_list_strict(apr_pool_t *p, const char *tok, | ||
71 | + apr_array_header_t **tokens, | ||
72 | + int skip_invalid); | ||
73 | + | ||
74 | +/** | ||
75 | * Retrieve a token, spacing over it and adjusting the pointer to | ||
76 | * the first non-white byte afterwards. Note that these tokens | ||
77 | * are delimited by semis and commas and can also be delimited | ||
78 | diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c | ||
79 | index 67dc939..58daa21 100644 | ||
80 | --- a/modules/proxy/proxy_util.c | ||
81 | +++ b/modules/proxy/proxy_util.c | ||
82 | @@ -2847,68 +2847,59 @@ PROXY_DECLARE(proxy_balancer_shared *) ap_proxy_find_balancershm(ap_slotmem_prov | ||
83 | typedef struct header_connection { | ||
84 | apr_pool_t *pool; | ||
85 | apr_array_header_t *array; | ||
86 | - const char *first; | ||
87 | - unsigned int closed:1; | ||
88 | + const char *error; | ||
89 | + int is_req; | ||
90 | } header_connection; | ||
91 | |||
92 | static int find_conn_headers(void *data, const char *key, const char *val) | ||
93 | { | ||
94 | header_connection *x = data; | ||
95 | - const char *name; | ||
96 | - | ||
97 | - do { | ||
98 | - while (*val == ',' || *val == ';') { | ||
99 | - val++; | ||
100 | - } | ||
101 | - name = ap_get_token(x->pool, &val, 0); | ||
102 | - if (!strcasecmp(name, "close")) { | ||
103 | - x->closed = 1; | ||
104 | - } | ||
105 | - if (!x->first) { | ||
106 | - x->first = name; | ||
107 | - } | ||
108 | - else { | ||
109 | - const char **elt; | ||
110 | - if (!x->array) { | ||
111 | - x->array = apr_array_make(x->pool, 4, sizeof(char *)); | ||
112 | - } | ||
113 | - elt = apr_array_push(x->array); | ||
114 | - *elt = name; | ||
115 | - } | ||
116 | - } while (*val); | ||
117 | |||
118 | - return 1; | ||
119 | + x->error = ap_parse_token_list_strict(x->pool, val, &x->array, !x->is_req); | ||
120 | + return !x->error; | ||
121 | } | ||
122 | |||
123 | /** | ||
124 | * Remove all headers referred to by the Connection header. | ||
125 | + * Returns -1 on error. Otherwise, returns 1 if 'Close' was seen in | ||
126 | + * the Connection header tokens, and 0 if not. | ||
127 | */ | ||
128 | static int ap_proxy_clear_connection(request_rec *r, apr_table_t *headers) | ||
129 | { | ||
130 | - const char **name; | ||
131 | + int closed = 0; | ||
132 | header_connection x; | ||
133 | |||
134 | x.pool = r->pool; | ||
135 | x.array = NULL; | ||
136 | - x.first = NULL; | ||
137 | - x.closed = 0; | ||
138 | + x.error = NULL; | ||
139 | + x.is_req = (headers == r->headers_in); | ||
140 | |||
141 | apr_table_unset(headers, "Proxy-Connection"); | ||
142 | |||
143 | apr_table_do(find_conn_headers, &x, headers, "Connection", NULL); | ||
144 | - if (x.first) { | ||
145 | - /* fast path - no memory allocated for one header */ | ||
146 | - apr_table_unset(headers, "Connection"); | ||
147 | - apr_table_unset(headers, x.first); | ||
148 | + apr_table_unset(headers, "Connection"); | ||
149 | + | ||
150 | + if (x.error) { | ||
151 | + ap_log_rerror(APLOG_MARK, APLOG_NOTICE, 0, r, APLOGNO() | ||
152 | + "Error parsing Connection header: %s", x.error); | ||
153 | + return -1; | ||
154 | } | ||
155 | + | ||
156 | if (x.array) { | ||
157 | - /* two or more headers */ | ||
158 | - while ((name = apr_array_pop(x.array))) { | ||
159 | - apr_table_unset(headers, *name); | ||
160 | + int i; | ||
161 | + for (i = 0; i < x.array->nelts; i++) { | ||
162 | + const char *name = APR_ARRAY_IDX(x.array, i, const char *); | ||
163 | + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO() | ||
164 | + "Removing header '%s' listed in Connection header", | ||
165 | + name); | ||
166 | + if (!strcasecmp(name, "close")) { | ||
167 | + closed = 1; | ||
168 | + } | ||
169 | + apr_table_unset(headers, name); | ||
170 | } | ||
171 | } | ||
172 | |||
173 | - return x.closed; | ||
174 | + return closed; | ||
175 | } | ||
176 | |||
177 | PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p, | ||
178 | @@ -3095,7 +3086,9 @@ PROXY_DECLARE(int) ap_proxy_create_hdrbrgd(apr_pool_t *p, | ||
179 | * apr is compiled with APR_POOL_DEBUG. | ||
180 | */ | ||
181 | headers_in_copy = apr_table_copy(r->pool, r->headers_in); | ||
182 | - ap_proxy_clear_connection(r, headers_in_copy); | ||
183 | + if (ap_proxy_clear_connection(r, headers_in_copy) < 0) { | ||
184 | + return HTTP_BAD_REQUEST; | ||
185 | + } | ||
186 | /* send request headers */ | ||
187 | headers_in_array = apr_table_elts(headers_in_copy); | ||
188 | headers_in = (const apr_table_entry_t *) headers_in_array->elts; | ||
189 | diff --git a/server/util.c b/server/util.c | ||
190 | index e0ba5c2..541c9f0 100644 | ||
191 | --- a/server/util.c | ||
192 | +++ b/server/util.c | ||
193 | @@ -1449,6 +1449,95 @@ AP_DECLARE(int) ap_find_etag_weak(apr_pool_t *p, const char *line, | ||
194 | return find_list_item(p, line, tok, AP_ETAG_WEAK); | ||
195 | } | ||
196 | |||
197 | +/* Grab a list of tokens of the format 1#token (from RFC7230) */ | ||
198 | +AP_DECLARE(const char *) ap_parse_token_list_strict(apr_pool_t *p, | ||
199 | + const char *str_in, | ||
200 | + apr_array_header_t **tokens, | ||
201 | + int skip_invalid) | ||
202 | +{ | ||
203 | + int in_leading_space = 1; | ||
204 | + int in_trailing_space = 0; | ||
205 | + int string_end = 0; | ||
206 | + const char *tok_begin; | ||
207 | + const char *cur; | ||
208 | + | ||
209 | + if (!str_in) { | ||
210 | + return NULL; | ||
211 | + } | ||
212 | + | ||
213 | + tok_begin = cur = str_in; | ||
214 | + | ||
215 | + while (!string_end) { | ||
216 | + const unsigned char c = (unsigned char)*cur; | ||
217 | + | ||
218 | + if (!TEST_CHAR(c, T_HTTP_TOKEN_STOP) && c != '\0') { | ||
219 | + /* Non-separator character; we are finished with leading | ||
220 | + * whitespace. We must never have encountered any trailing | ||
221 | + * whitespace before the delimiter (comma) */ | ||
222 | + in_leading_space = 0; | ||
223 | + if (in_trailing_space) { | ||
224 | + return "Encountered illegal whitespace in token"; | ||
225 | + } | ||
226 | + } | ||
227 | + else if (c == ' ' || c == '\t') { | ||
228 | + /* "Linear whitespace" only includes ASCII CRLF, space, and tab; | ||
229 | + * we can't get a CRLF since headers are split on them already, | ||
230 | + * so only look for a space or a tab */ | ||
231 | + if (in_leading_space) { | ||
232 | + /* We're still in leading whitespace */ | ||
233 | + ++tok_begin; | ||
234 | + } | ||
235 | + else { | ||
236 | + /* We must be in trailing whitespace */ | ||
237 | + ++in_trailing_space; | ||
238 | + } | ||
239 | + } | ||
240 | + else if (c == ',' || c == '\0') { | ||
241 | + if (!in_leading_space) { | ||
242 | + /* If we're out of the leading space, we know we've read some | ||
243 | + * characters of a token */ | ||
244 | + if (*tokens == NULL) { | ||
245 | + *tokens = apr_array_make(p, 4, sizeof(char *)); | ||
246 | + } | ||
247 | + APR_ARRAY_PUSH(*tokens, char *) = | ||
248 | + apr_pstrmemdup((*tokens)->pool, tok_begin, | ||
249 | + (cur - tok_begin) - in_trailing_space); | ||
250 | + } | ||
251 | + /* We're allowed to have null elements, just don't add them to the | ||
252 | + * array */ | ||
253 | + | ||
254 | + tok_begin = cur + 1; | ||
255 | + in_leading_space = 1; | ||
256 | + in_trailing_space = 0; | ||
257 | + string_end = (c == '\0'); | ||
258 | + } | ||
259 | + else { | ||
260 | + /* Encountered illegal separator char */ | ||
261 | + if (skip_invalid) { | ||
262 | + /* Skip to the next separator */ | ||
263 | + const char *temp; | ||
264 | + temp = ap_strchr_c(cur, ','); | ||
265 | + if(!temp) { | ||
266 | + temp = ap_strchr_c(cur, '\0'); | ||
267 | + } | ||
268 | + | ||
269 | + /* Act like we haven't seen a token so we reset */ | ||
270 | + cur = temp - 1; | ||
271 | + in_leading_space = 1; | ||
272 | + in_trailing_space = 0; | ||
273 | + } | ||
274 | + else { | ||
275 | + return apr_psprintf(p, "Encountered illegal separator " | ||
276 | + "'\\x%.2x'", (unsigned int)c); | ||
277 | + } | ||
278 | + } | ||
279 | + | ||
280 | + ++cur; | ||
281 | + } | ||
282 | + | ||
283 | + return NULL; | ||
284 | +} | ||
285 | + | ||
286 | /* Retrieve a token, spacing over it and returning a pointer to | ||
287 | * the first non-white byte afterwards. Note that these tokens | ||
288 | * are delimited by semis and commas; and can also be delimited | ||
289 | -- | ||
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 573cd6fb0..d79d40bd2 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 \ | |||
19 | file://0001-configure-use-pkg-config-for-PCRE-detection.patch \ | 19 | file://0001-configure-use-pkg-config-for-PCRE-detection.patch \ |
20 | file://init \ | 20 | file://init \ |
21 | file://apache2-volatile.conf \ | 21 | file://apache2-volatile.conf \ |
22 | file://apache2.service" | 22 | file://apache2.service \ |
23 | file://apache-CVE-2014-0117.patch \ | ||
24 | " | ||
23 | 25 | ||
24 | LIC_FILES_CHKSUM = "file://LICENSE;md5=dbff5a2b542fa58854455bf1a0b94b83" | 26 | LIC_FILES_CHKSUM = "file://LICENSE;md5=dbff5a2b542fa58854455bf1a0b94b83" |
25 | SRC_URI[md5sum] = "44543dff14a4ebc1e9e2d86780507156" | 27 | SRC_URI[md5sum] = "44543dff14a4ebc1e9e2d86780507156" |