Package: release.debian.org Severity: normal User: release.debian....@packages.debian.org Usertags: pu
Please review apache2_2.2.16-6+squeeze8 for inclusion in squeeze. It fixes a minor security issue and some important bugs: * CVE-2012-2687: mod_negotiation: Escape filenames in variant list to prevent a possible XSS vulnerability for a site where untrusted users can upload files to a location with MultiViews enabled. * Send 408 status instead of 400 if reading of a request fails with a timeout. This allows browsers to retry. Closes: #677086 * mod_cache: Prevent Partial Content responses from being cached and served as normal response. Closes: #671204 * mpm_itk: Fix an issue where users can sometimes get spurious 403s on persistent connections. Closes: #672333 Full debdiff is attached. Cheers, Stefan
diff -u apache2-2.2.16/debian/changelog apache2-2.2.16/debian/changelog --- apache2-2.2.16/debian/changelog +++ apache2-2.2.16/debian/changelog @@ -1,3 +1,17 @@ +apache2 (2.2.16-6+squeeze8) squeeze; urgency=low + + * CVE-2012-2687: mod_negotiation: Escape filenames in variant list to + prevent a possible XSS vulnerability for a site where untrusted users + can upload files to a location with MultiViews enabled. + * Send 408 status instead of 400 if reading of a request fails with a + timeout. This allows browsers to retry. Closes: #677086 + * mod_cache: Prevent Partial Content responses from being cached and served + as normal response. Closes: #671204 + * mpm_itk: Fix an issue where users can sometimes get spurious 403s on + persistent connections. Closes: #672333 + + -- Stefan Fritsch <s...@debian.org> Sun, 09 Sep 2012 23:08:04 +0200 + apache2 (2.2.16-6+squeeze7) squeeze-security; urgency=high * CVE-2012-0216: Remove "Alias /doc /usr/share/doc" from the default virtual diff -u apache2-2.2.16/debian/patches/00list apache2-2.2.16/debian/patches/00list --- apache2-2.2.16/debian/patches/00list +++ apache2-2.2.16/debian/patches/00list @@ -36,6 +36,9 @@ 092_CVE-2011-3607.dpatch 093_CVE-2012-0031.dpatch 094_CVE-2012-0053.dpatch +095_send_408_status.dpatch +096_mod_cache_partial_content-2.2.x.dpatch +097_CVE-2012-2687.dpatch 099_config_guess_sub_update 200_cp_suexec.dpatch 201_build_suexec-custom.dpatch diff -u apache2-2.2.16/debian/mpm-itk/patches/series apache2-2.2.16/debian/mpm-itk/patches/series --- apache2-2.2.16/debian/mpm-itk/patches/series +++ apache2-2.2.16/debian/mpm-itk/patches/series @@ -10,0 +11 @@ +11-fix-htaccess-reads-for-persistent-connections.patch only in patch2: unchanged: --- apache2-2.2.16.orig/debian/patches/097_CVE-2012-2687.dpatch +++ apache2-2.2.16/debian/patches/097_CVE-2012-2687.dpatch @@ -0,0 +1,20 @@ +#! /bin/sh /usr/share/dpatch/dpatch-run +## All lines beginning with `## DP:' are a description of the patch. +## DP: low impact XSS in mod_negotiation +## DP: backport of upstream r1374421 +diff --git a/modules/mappers/mod_negotiation.c b/modules/mappers/mod_negotiation.c +index 25e6034..ef4a72a 100644 +--- a/modules/mappers/mod_negotiation.c ++++ b/modules/mappers/mod_negotiation.c +@@ -2658,9 +2658,9 @@ static char *make_variant_list(request_rec *r, negotiation_state *neg) + * need to change the calculation of max_vlist_array above. + */ + *((const char **) apr_array_push(arr)) = "<li><a href=\""; +- *((const char **) apr_array_push(arr)) = filename; ++ *((const char **) apr_array_push(arr)) = ap_escape_path_segment(r->pool, filename); + *((const char **) apr_array_push(arr)) = "\">"; +- *((const char **) apr_array_push(arr)) = filename; ++ *((const char **) apr_array_push(arr)) = ap_escape_html(r->pool, filename); + *((const char **) apr_array_push(arr)) = "</a> "; + *((const char **) apr_array_push(arr)) = description; + only in patch2: unchanged: --- apache2-2.2.16.orig/debian/patches/095_send_408_status.dpatch +++ apache2-2.2.16/debian/patches/095_send_408_status.dpatch @@ -0,0 +1,170 @@ +#! /bin/sh /usr/share/dpatch/dpatch-run +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: backport of upstream r1100200, debian bug #677086 +# +#commit 590aa99baedb5ae15305e593384f98998ca883fe +#Author: Eric Covener <cove...@apache.org> +#Date: Fri May 6 13:14:27 2011 +0000 +# +# Merge r820760, r919323, r937858, r938265 from trunk: +# +# Reviewed By: sf, trawick, covener +# +# core: Treat timeout reading request as 408 error, not 400. +# Log 408 errors in access log as was done in Apache 1.3.x. +# +# PR: 39785 +# Submitted by: Nobutaka Mantani, Stefan Fritsch +# Reviewed and added to by: Dan Poirier +# +# +# * Only log a 408 if it is no keepalive timeout. +# +# PR: 39785 +# Submitted by: Mark Montague <markmont umich.edu>, rpluem +# Reviewed by: rpluem +# +# +# PR49167, unexpected 413 and double-errordoc during a timeout reading a +# chunk-size. +# +# +# +# Use the more specific 408 (timed out) instead of a generic 400 during a timeout +# reading a chunk-length. +# +# +# +# +# git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.2.x@1100200 13f79535-47bb-0310-9956-ffa450edef68 +# +#diff --git a/CHANGES b/CHANGES +#index 9cc3ee3..1fe363f 100644 +#--- a/CHANGES +#+++ b/CHANGES +#@@ -1,6 +1,19 @@ +# -*- coding: utf-8 -*- +# Changes with Apache 2.2.18 +# +#+ *) Log an error for failures to read a chunk-size, and return 408 instead +#+ 413 when this is due to a read timeout. This change also fixes some cases +#+ of two error documents being sent in the response for the same scenario. +#+ [Eric Covener] PR49167 +#+ +#+ *) core: Only log a 408 if it is no keepalive timeout. PR 39785 +#+ [Ruediger Pluem, Mark Montague <markmont umich.edu>] +#+ +#+ *) core: Treat timeout reading request as 408 error, not 400. +#+ Log 408 errors in access log as was done in Apache 1.3.x. +#+ PR 39785 [Nobutaka Mantani <nobutaka nobutaka.org>, Stefan Fritsch, +#+ Dan Poirier] +#+ +# *) Core HTTP: disable keepalive when the Client has sent +# Expect: 100-continue +# but we respond directly with a non-100 response. Keepalive here led +diff --git a/modules/http/http_filters.c b/modules/http/http_filters.c +index b429989..1aed70b 100644 +--- a/modules/http/http_filters.c ++++ b/modules/http/http_filters.c +@@ -384,8 +384,13 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, + + /* Detect chunksize error (such as overflow) */ + if (rv != APR_SUCCESS || ctx->remaining < 0) { ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, "Error reading first chunk %s ", ++ (ctx->remaining < 0) ? "(overflow)" : ""); + ctx->remaining = 0; /* Reset it in case we have to + * come back here later */ ++ if (APR_STATUS_IS_TIMEUP(rv)) { ++ http_error = HTTP_REQUEST_TIME_OUT; ++ } + return bail_out_on_error(ctx, f, http_error); + } + +@@ -485,10 +490,14 @@ apr_status_t ap_http_filter(ap_filter_t *f, apr_bucket_brigade *b, + + /* Detect chunksize error (such as overflow) */ + if (rv != APR_SUCCESS || ctx->remaining < 0) { ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, rv, f->r, "Error reading chunk %s ", ++ (ctx->remaining < 0) ? "(overflow)" : ""); + ctx->remaining = 0; /* Reset it in case we have to + * come back here later */ +- bail_out_on_error(ctx, f, http_error); +- return rv; ++ if (APR_STATUS_IS_TIMEUP(rv)) { ++ http_error = HTTP_REQUEST_TIME_OUT; ++ } ++ return bail_out_on_error(ctx, f, http_error); + } + + if (!ctx->remaining) { +diff --git a/server/protocol.c b/server/protocol.c +index 9851360..62d8baf 100644 +--- a/server/protocol.c ++++ b/server/protocol.c +@@ -608,6 +608,9 @@ static int read_request_line(request_rec *r, apr_bucket_brigade *bb) + r->proto_num = HTTP_VERSION(1,0); + r->protocol = apr_pstrdup(r->pool, "HTTP/1.0"); + } ++ else if (rv == APR_TIMEUP) { ++ r->status = HTTP_REQUEST_TIME_OUT; ++ } + return 0; + } + } while ((len <= 0) && (++num_blank_lines < max_blank_lines)); +@@ -691,7 +694,12 @@ AP_DECLARE(void) ap_get_mime_headers_core(request_rec *r, apr_bucket_brigade *bb + &len, r, 0, bb); + + if (rv != APR_SUCCESS) { +- r->status = HTTP_BAD_REQUEST; ++ if (rv == APR_TIMEUP) { ++ r->status = HTTP_REQUEST_TIME_OUT; ++ } ++ else { ++ r->status = HTTP_BAD_REQUEST; ++ } + + /* ap_rgetline returns APR_ENOSPC if it fills up the buffer before + * finding the end-of-line. This is only going to happen if it +@@ -877,7 +885,7 @@ request_rec *ap_read_request(conn_rec *conn) + r->read_length = 0; + r->read_body = REQUEST_NO_BODY; + +- r->status = HTTP_REQUEST_TIME_OUT; /* Until we get a request */ ++ r->status = HTTP_OK; /* Until further notice */ + r->the_request = NULL; + + /* Begin by presuming any module can make its own path_info assumptions, +@@ -898,6 +906,14 @@ request_rec *ap_read_request(conn_rec *conn) + apr_brigade_destroy(tmp_bb); + return r; + } ++ else if (r->status == HTTP_REQUEST_TIME_OUT) { ++ ap_update_child_status(conn->sbh, SERVER_BUSY_LOG, r); ++ if (!r->connection->keepalives) { ++ ap_run_log_transaction(r); ++ } ++ apr_brigade_destroy(tmp_bb); ++ return r; ++ } + + apr_brigade_destroy(tmp_bb); + return NULL; +@@ -916,7 +932,7 @@ request_rec *ap_read_request(conn_rec *conn) + + if (!r->assbackwards) { + ap_get_mime_headers_core(r, tmp_bb); +- if (r->status != HTTP_REQUEST_TIME_OUT) { ++ if (r->status != HTTP_OK) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "request failed: error reading the headers"); + ap_send_error_response(r, 0); +@@ -957,8 +973,6 @@ request_rec *ap_read_request(conn_rec *conn) + + apr_brigade_destroy(tmp_bb); + +- r->status = HTTP_OK; /* Until further notice. */ +- + /* update what we think the virtual host is based on the headers we've + * now read. may update status. + */ only in patch2: unchanged: --- apache2-2.2.16.orig/debian/patches/096_mod_cache_partial_content-2.2.x.dpatch +++ apache2-2.2.16/debian/patches/096_mod_cache_partial_content-2.2.x.dpatch @@ -0,0 +1,74 @@ +#! /bin/sh /usr/share/dpatch/dpatch-run +## +## All lines beginning with `## DP:' are a description of the patch. +## DP: Add r1343951 from upstream branch 2.2.x +## DP: Debian bug #671204 + +*) mod_disk_cache, mod_mem_cache: Decline the opportunity to cache if the + response is a 206 Partial Content. This stops a reverse proxied partial + response from becoming cached, and then being served in subsequent + responses. [Graham Leggett] + + +Index: a/modules/cache/mod_cache.c +=================================================================== +--- a/modules/cache/mod_cache.c (revision 1176912) ++++ a/modules/cache/mod_cache.c (working copy) +@@ -473,7 +473,8 @@ + * We include 304 Not Modified here too as this is the origin server + * telling us to serve the cached copy. + */ +- if (exps != NULL || cc_out != NULL) { ++ if ((exps != NULL || cc_out != NULL) ++ && r->status != HTTP_PARTIAL_CONTENT) { + /* We are also allowed to cache any response given that it has a + * valid Expires or Cache Control header. If we find a either of + * those here, we pass request through the rest of the tests. From +@@ -486,6 +487,9 @@ + * include the following: an Expires header (section 14.21); a + * "max-age", "s-maxage", "must-revalidate", "proxy-revalidate", + * "public" or "private" cache-control directive (section 14.9). ++ * ++ * But do NOT store 206 responses in any case since we ++ * don't (yet) cache partial responses. + */ + } + else { +Index: modules/cache/mod_mem_cache.c +=================================================================== +--- a/modules/cache/mod_mem_cache.c (revision 1176912) ++++ a/modules/cache/mod_mem_cache.c (working copy) +@@ -313,6 +313,14 @@ + cache_object_t *obj, *tmp_obj; + mem_cache_object_t *mobj; + ++ /* we don't support caching of range requests (yet) */ ++ if (r->status == HTTP_PARTIAL_CONTENT) { ++ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, ++ "disk_cache: URL %s partial content response not cached", ++ key); ++ return DECLINED; ++ } ++ + if (len == -1) { + /* Caching a streaming response. Assume the response is + * less than or equal to max_streaming_buffer_size. We will +Index: modules/cache/mod_disk_cache.c +=================================================================== +--- a/modules/cache/mod_disk_cache.c (revision 1176912) ++++ a/modules/cache/mod_disk_cache.c (working copy) +@@ -330,6 +330,14 @@ + return DECLINED; + } + ++ /* we don't support caching of range requests (yet) */ ++ if (r->status == HTTP_PARTIAL_CONTENT) { ++ ap_log_error(APLOG_MARK, APLOG_DEBUG, 0, r->server, ++ "disk_cache: URL %s partial content response not cached", ++ key); ++ return DECLINED; ++ } ++ + /* Allocate and initialize cache_object_t and disk_cache_object_t */ + h->cache_obj = obj = apr_pcalloc(r->pool, sizeof(*obj)); + obj->vobj = dobj = apr_pcalloc(r->pool, sizeof(*dobj)); only in patch2: unchanged: --- apache2-2.2.16.orig/debian/mpm-itk/patches/11-fix-htaccess-reads-for-persistent-connections.patch +++ apache2-2.2.16/debian/mpm-itk/patches/11-fix-htaccess-reads-for-persistent-connections.patch @@ -0,0 +1,45 @@ +Fix an issue where users can sometimes get spurious 403s on persistent +connections (the description in the comments explains the logic). +This would particularly hit people with reverse proxies, since these +have a higher tendency of accessing things from different vhosts in +the same connection. + +Index: httpd-2.2.17/server/config.c +=================================================================== +--- httpd-2.2.17.orig/server/config.c ++++ httpd-2.2.17/server/config.c +@@ -1840,6 +1840,34 @@ AP_CORE_DECLARE(int) ap_parse_htaccess(a + else { + if (!APR_STATUS_IS_ENOENT(status) + && !APR_STATUS_IS_ENOTDIR(status)) { ++#ifdef ITK_MPM ++ /* ++ * If we are in a persistent connection, we might end up in a state ++ * where we can no longer read .htaccess files because we have already ++ * setuid(). This can either be because the previous request was for ++ * another vhost (basically the same problem as when setuid() fails in ++ * itk.c), or it can be because a .htaccess file is readable only by ++ * root. ++ * ++ * In any case, we don't want to give out a 403, since the request has ++ * a very real chance of succeeding on a fresh connection (where ++ * presumably uid=0). Thus, we give up serving the request on this ++ * TCP connection, and do a hard close of the socket. As long as we're ++ * in a persistent connection (and there _should_ not be a way this ++ * would happen on the first request in a connection, save for subrequests, ++ * which we special-case), this is allowed, as it is what happens on ++ * a timeout. The browser will simply open a new connection and try ++ * again (there's of course a performance hit, though, both due to ++ * the new connection setup and the fork() of a new server child). ++ */ ++ if (r->main == NULL && getuid() != 0) { ++ ap_log_rerror(APLOG_MARK, APLOG_WARNING, status, r, ++ "Couldn't read %s, closing connection.", ++ filename); ++ ap_lingering_close(r->connection); ++ exit(0); ++ } ++#endif + ap_log_rerror(APLOG_MARK, APLOG_CRIT, status, r, + "%s pcfg_openfile: unable to check htaccess file, " + "ensure it is readable",