parent
2bebdf5bc0
commit
8f268ad56c
@ -0,0 +1,303 @@
|
||||
From 554554b0ebb14d6578adb70a389c57a0d5f18a3b Mon Sep 17 00:00:00 2001
|
||||
From: Eric Covener <covener@apache.org>
|
||||
Date: Mon, 24 Jun 2024 17:54:34 +0000
|
||||
Subject: [PATCH] Merge r1918551 from trunk:
|
||||
|
||||
add ap_set_content_type_ex to differentiate
|
||||
|
||||
trusted sources
|
||||
|
||||
|
||||
|
||||
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1918560 13f79535-47bb-0310-9956-ffa450edef68
|
||||
---
|
||||
include/http_protocol.h | 11 +++++++++++
|
||||
include/httpd.h | 7 +++++++
|
||||
modules/http/http_protocol.c | 6 ++++++
|
||||
modules/http/mod_mime.c | 20 ++++++++++----------
|
||||
modules/mappers/mod_actions.c | 6 ++++--
|
||||
modules/mappers/mod_negotiation.c | 8 ++++----
|
||||
modules/mappers/mod_rewrite.c | 2 +-
|
||||
modules/metadata/mod_headers.c | 6 +++---
|
||||
modules/metadata/mod_mime_magic.c | 4 ++--
|
||||
server/config.c | 2 +-
|
||||
server/core.c | 2 +-
|
||||
11 files changed, 50 insertions(+), 24 deletions(-)
|
||||
|
||||
diff --git a/include/http_protocol.h b/include/http_protocol.h
|
||||
index 94c481e5f43..f2c99c9e86e 100644
|
||||
--- a/include/http_protocol.h
|
||||
+++ b/include/http_protocol.h
|
||||
@@ -438,6 +438,17 @@ AP_DECLARE(void) ap_clear_method_list(ap_method_list_t *l);
|
||||
*/
|
||||
AP_DECLARE(void) ap_set_content_type(request_rec *r, const char *ct);
|
||||
|
||||
+/**
|
||||
+ * Set the content type for this request (r->content_type).
|
||||
+ * @param r The current request
|
||||
+ * @param ct The new content type
|
||||
+ * @param trusted If non-zero, The content-type should come from a
|
||||
+ * trusted source such as server configuration rather
|
||||
+ * than application output.
|
||||
+ * for the AddOutputFilterByType directive to work correctly.
|
||||
+ */
|
||||
+AP_DECLARE(void) ap_set_content_type_ex(request_rec *r, const char *ct, int trusted);
|
||||
+
|
||||
/**
|
||||
* Set the Accept-Ranges header for this response
|
||||
* @param r The current request
|
||||
diff --git a/include/httpd.h b/include/httpd.h
|
||||
index 826c46ef591..766df2bde00 100644
|
||||
--- a/include/httpd.h
|
||||
+++ b/include/httpd.h
|
||||
@@ -667,6 +667,7 @@ typedef apr_uint64_t ap_request_bnotes_t;
|
||||
*
|
||||
*/
|
||||
#define AP_REQUEST_STRONG_ETAG 1 >> 0
|
||||
+#define AP_REQUEST_TRUSTED_CT 1 << 1
|
||||
|
||||
/**
|
||||
* This is a convenience macro to ease with getting specific request
|
||||
@@ -689,6 +690,12 @@ typedef apr_uint64_t ap_request_bnotes_t;
|
||||
AP_REQUEST_GET_BNOTE((r), AP_REQUEST_STRONG_ETAG)
|
||||
/** @} */
|
||||
|
||||
+/**
|
||||
+ * Returns true if the content-type field is from a trusted source
|
||||
+ */
|
||||
+#define AP_REQUEST_IS_TRUSTED_CT(r) \
|
||||
+ (!!AP_REQUEST_GET_BNOTE((r), AP_REQUEST_TRUSTED_CT))
|
||||
+/** @} */
|
||||
|
||||
/**
|
||||
* @defgroup module_magic Module Magic mime types
|
||||
diff --git a/modules/http/http_protocol.c b/modules/http/http_protocol.c
|
||||
index d031f245188..c31e8737337 100644
|
||||
--- a/modules/http/http_protocol.c
|
||||
+++ b/modules/http/http_protocol.c
|
||||
@@ -1097,8 +1097,14 @@ AP_DECLARE(void) ap_set_content_type(request_rec *r, const char *ct)
|
||||
}
|
||||
else if (!r->content_type || strcmp(r->content_type, ct)) {
|
||||
r->content_type = ct;
|
||||
+ AP_REQUEST_SET_BNOTE(r, AP_REQUEST_TRUSTED_CT, 0);
|
||||
}
|
||||
}
|
||||
+AP_DECLARE(void) ap_set_content_type_ex(request_rec *r, const char *ct, int trusted)
|
||||
+{
|
||||
+ ap_set_content_type(r, ct);
|
||||
+ AP_REQUEST_SET_BNOTE(r, AP_REQUEST_TRUSTED_CT, trusted ? AP_REQUEST_TRUSTED_CT : 0);
|
||||
+}
|
||||
|
||||
AP_DECLARE(void) ap_set_accept_ranges(request_rec *r)
|
||||
{
|
||||
diff --git a/modules/http/mod_mime.c b/modules/http/mod_mime.c
|
||||
index 700f824f32a..51095a0e74c 100644
|
||||
--- a/modules/http/mod_mime.c
|
||||
+++ b/modules/http/mod_mime.c
|
||||
@@ -759,7 +759,7 @@ static int find_ct(request_rec *r)
|
||||
int found_metadata = 0;
|
||||
|
||||
if (r->finfo.filetype == APR_DIR) {
|
||||
- ap_set_content_type(r, DIR_MAGIC_TYPE);
|
||||
+ ap_set_content_type_ex(r, DIR_MAGIC_TYPE, 1);
|
||||
return OK;
|
||||
}
|
||||
|
||||
@@ -850,7 +850,7 @@ static int find_ct(request_rec *r)
|
||||
if (exinfo == NULL || !exinfo->forced_type) {
|
||||
if ((type = apr_hash_get(mime_type_extensions, ext,
|
||||
APR_HASH_KEY_STRING)) != NULL) {
|
||||
- ap_set_content_type(r, (char*) type);
|
||||
+ ap_set_content_type_ex(r, (char*) type, 1);
|
||||
found = 1;
|
||||
}
|
||||
}
|
||||
@@ -859,7 +859,7 @@ static int find_ct(request_rec *r)
|
||||
|
||||
/* empty string is treated as special case for RemoveType */
|
||||
if (exinfo->forced_type && *exinfo->forced_type) {
|
||||
- ap_set_content_type(r, exinfo->forced_type);
|
||||
+ ap_set_content_type_ex(r, exinfo->forced_type, 1);
|
||||
found = 1;
|
||||
}
|
||||
|
||||
@@ -964,33 +964,33 @@ static int find_ct(request_rec *r)
|
||||
memcpy(tmp, ctp->subtype, ctp->subtype_len);
|
||||
tmp += ctp->subtype_len;
|
||||
*tmp = 0;
|
||||
- ap_set_content_type(r, base_content_type);
|
||||
+ ap_set_content_type_ex(r, base_content_type, AP_REQUEST_IS_TRUSTED_CT(r));
|
||||
while (pp != NULL) {
|
||||
if (charset && !strcmp(pp->attr, "charset")) {
|
||||
if (!override) {
|
||||
- ap_set_content_type(r,
|
||||
+ ap_set_content_type_ex(r,
|
||||
apr_pstrcat(r->pool,
|
||||
r->content_type,
|
||||
"; charset=",
|
||||
charset,
|
||||
- NULL));
|
||||
+ NULL), AP_REQUEST_IS_TRUSTED_CT(r));
|
||||
override = 1;
|
||||
}
|
||||
}
|
||||
else {
|
||||
- ap_set_content_type(r,
|
||||
+ ap_set_content_type_ex(r,
|
||||
apr_pstrcat(r->pool,
|
||||
r->content_type,
|
||||
"; ", pp->attr,
|
||||
"=", pp->val,
|
||||
- NULL));
|
||||
+ NULL), AP_REQUEST_IS_TRUSTED_CT(r));
|
||||
}
|
||||
pp = pp->next;
|
||||
}
|
||||
if (charset && !override) {
|
||||
- ap_set_content_type(r, apr_pstrcat(r->pool, r->content_type,
|
||||
+ ap_set_content_type_ex(r, apr_pstrcat(r->pool, r->content_type,
|
||||
"; charset=", charset,
|
||||
- NULL));
|
||||
+ NULL), AP_REQUEST_IS_TRUSTED_CT(r));
|
||||
}
|
||||
}
|
||||
}
|
||||
diff --git a/modules/mappers/mod_actions.c b/modules/mappers/mod_actions.c
|
||||
index ac9c3b7428f..5e398b53d9e 100644
|
||||
--- a/modules/mappers/mod_actions.c
|
||||
+++ b/modules/mappers/mod_actions.c
|
||||
@@ -182,8 +182,10 @@ static int action_handler(request_rec *r)
|
||||
return DECLINED;
|
||||
|
||||
/* Second, check for actions (which override the method scripts) */
|
||||
- action = r->handler ? r->handler :
|
||||
- ap_field_noparam(r->pool, r->content_type);
|
||||
+ action = r->handler;
|
||||
+ if (!action && AP_REQUEST_IS_TRUSTED_CT(r)) {
|
||||
+ action = ap_field_noparam(r->pool, r->content_type);
|
||||
+ }
|
||||
|
||||
if (action && (t = apr_table_get(conf->action_types, action))) {
|
||||
int virtual = (*t++ == '0' ? 0 : 1);
|
||||
diff --git a/modules/mappers/mod_negotiation.c b/modules/mappers/mod_negotiation.c
|
||||
index c056b284550..a528f814397 100644
|
||||
--- a/modules/mappers/mod_negotiation.c
|
||||
+++ b/modules/mappers/mod_negotiation.c
|
||||
@@ -1167,7 +1167,7 @@ static int read_types_multi(negotiation_state *neg)
|
||||
* might be doing.
|
||||
*/
|
||||
if (sub_req->handler && !sub_req->content_type) {
|
||||
- ap_set_content_type(sub_req, CGI_MAGIC_TYPE);
|
||||
+ ap_set_content_type_ex(sub_req, CGI_MAGIC_TYPE, 1);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -3003,14 +3003,14 @@ static int handle_map_file(request_rec *r)
|
||||
/* set MIME type and charset as negotiated */
|
||||
if (best->mime_type && *best->mime_type) {
|
||||
if (best->content_charset && *best->content_charset) {
|
||||
- ap_set_content_type(r, apr_pstrcat(r->pool,
|
||||
+ ap_set_content_type_ex(r, apr_pstrcat(r->pool,
|
||||
best->mime_type,
|
||||
"; charset=",
|
||||
best->content_charset,
|
||||
- NULL));
|
||||
+ NULL), 1);
|
||||
}
|
||||
else {
|
||||
- ap_set_content_type(r, apr_pstrdup(r->pool, best->mime_type));
|
||||
+ ap_set_content_type_ex(r, apr_pstrdup(r->pool, best->mime_type), 1);
|
||||
}
|
||||
}
|
||||
|
||||
diff --git a/modules/mappers/mod_rewrite.c b/modules/mappers/mod_rewrite.c
|
||||
index bbcc11b3c52..df6f16b83f0 100644
|
||||
--- a/modules/mappers/mod_rewrite.c
|
||||
+++ b/modules/mappers/mod_rewrite.c
|
||||
@@ -5333,7 +5333,7 @@ static int hook_mimetype(request_rec *r)
|
||||
rewritelog((r, 1, NULL, "force filename %s to have MIME-type '%s'",
|
||||
r->filename, t));
|
||||
|
||||
- ap_set_content_type(r, t);
|
||||
+ ap_set_content_type_ex(r, t, 1);
|
||||
}
|
||||
|
||||
/* handler */
|
||||
diff --git a/modules/metadata/mod_headers.c b/modules/metadata/mod_headers.c
|
||||
index ef812cd3edc..4838bd6cd0d 100644
|
||||
--- a/modules/metadata/mod_headers.c
|
||||
+++ b/modules/metadata/mod_headers.c
|
||||
@@ -783,14 +783,14 @@ static int do_headers_fixup(request_rec *r, apr_table_t *headers,
|
||||
break;
|
||||
case hdr_set:
|
||||
if (!ap_cstr_casecmp(hdr->header, "Content-Type")) {
|
||||
- ap_set_content_type(r, process_tags(hdr, r));
|
||||
+ ap_set_content_type_ex(r, process_tags(hdr, r), 1);
|
||||
}
|
||||
apr_table_setn(headers, hdr->header, process_tags(hdr, r));
|
||||
break;
|
||||
case hdr_setifempty:
|
||||
if (NULL == apr_table_get(headers, hdr->header)) {
|
||||
if (!ap_cstr_casecmp(hdr->header, "Content-Type")) {
|
||||
- ap_set_content_type(r, process_tags(hdr, r));
|
||||
+ ap_set_content_type_ex(r, process_tags(hdr, r), 1);
|
||||
}
|
||||
apr_table_setn(headers, hdr->header, process_tags(hdr, r));
|
||||
}
|
||||
@@ -809,7 +809,7 @@ static int do_headers_fixup(request_rec *r, apr_table_t *headers,
|
||||
const char *repl = process_regexp(hdr, r->content_type, r);
|
||||
if (repl == NULL)
|
||||
return 0;
|
||||
- ap_set_content_type(r, repl);
|
||||
+ ap_set_content_type_ex(r, repl, 1);
|
||||
}
|
||||
if (apr_table_get(headers, hdr->header)) {
|
||||
edit_do ed;
|
||||
diff --git a/modules/metadata/mod_mime_magic.c b/modules/metadata/mod_mime_magic.c
|
||||
index 7dac4fdbd3d..1c96db4cd7a 100644
|
||||
--- a/modules/metadata/mod_mime_magic.c
|
||||
+++ b/modules/metadata/mod_mime_magic.c
|
||||
@@ -788,7 +788,7 @@ static int magic_rsl_to_request(request_rec *r)
|
||||
/* XXX: this could be done at config time I'm sure... but I'm
|
||||
* confused by all this magic_rsl stuff. -djg */
|
||||
ap_content_type_tolower(tmp);
|
||||
- ap_set_content_type(r, tmp);
|
||||
+ ap_set_content_type_ex(r, tmp, 1);
|
||||
|
||||
if (state == rsl_encoding) {
|
||||
tmp = rsl_strdup(r, encoding_frag,
|
||||
@@ -2326,7 +2326,7 @@ static int revision_suffix(request_rec *r)
|
||||
|
||||
/* extract content type/encoding/language from sub-request */
|
||||
if (sub->content_type) {
|
||||
- ap_set_content_type(r, apr_pstrdup(r->pool, sub->content_type));
|
||||
+ ap_set_content_type_ex(r, apr_pstrdup(r->pool, sub->content_type), 1);
|
||||
#if MIME_MAGIC_DEBUG
|
||||
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01557)
|
||||
MODNAME ": subrequest %s got %s",
|
||||
diff --git a/server/config.c b/server/config.c
|
||||
index 3d11ff58a44..635b65def1d 100644
|
||||
--- a/server/config.c
|
||||
+++ b/server/config.c
|
||||
@@ -418,7 +418,7 @@ AP_CORE_DECLARE(int) ap_invoke_handler(request_rec *r)
|
||||
}
|
||||
|
||||
if (!r->handler) {
|
||||
- if (r->content_type) {
|
||||
+ if (r->content_type && AP_REQUEST_IS_TRUSTED_CT(r)) {
|
||||
handler = r->content_type;
|
||||
if ((p=ap_strchr_c(handler, ';')) != NULL) {
|
||||
char *new_handler = (char *)apr_pmemdup(r->pool, handler,
|
||||
diff --git a/server/core.c b/server/core.c
|
||||
index f511bba4897..843b97320f8 100644
|
||||
--- a/server/core.c
|
||||
+++ b/server/core.c
|
||||
@@ -4835,7 +4835,7 @@ static int core_override_type(request_rec *r)
|
||||
/* Check for overrides with ForceType / SetHandler
|
||||
*/
|
||||
if (conf->mime_type && strcmp(conf->mime_type, "none"))
|
||||
- ap_set_content_type(r, (char*) conf->mime_type);
|
||||
+ ap_set_content_type_ex(r, (char*) conf->mime_type, 1);
|
||||
|
||||
if (conf->expr_handler) {
|
||||
const char *err;
|
@ -0,0 +1,269 @@
|
||||
|
||||
-- Contains also regression fix (CVE-2024-40725) - https://svn.apache.org/viewvc?view=revision&revision=1919249
|
||||
|
||||
diff --git a/modules/cluster/mod_heartmonitor.c b/modules/cluster/mod_heartmonitor.c
|
||||
index 53b6504..68db585 100644
|
||||
--- a/modules/cluster/mod_heartmonitor.c
|
||||
+++ b/modules/cluster/mod_heartmonitor.c
|
||||
@@ -782,7 +782,7 @@ static int hm_handler(request_rec *r)
|
||||
hmserver.seen = apr_time_now();
|
||||
hm_update_stat(ctx, &hmserver, r->pool);
|
||||
|
||||
- ap_set_content_type(r, "text/plain");
|
||||
+ ap_set_content_type_ex(r, "text/plain", 1);
|
||||
ap_set_content_length(r, 2);
|
||||
ap_rputs("OK", r);
|
||||
ap_rflush(r);
|
||||
diff --git a/modules/dav/main/mod_dav.c b/modules/dav/main/mod_dav.c
|
||||
index dea3f18..7a3eed7 100644
|
||||
--- a/modules/dav/main/mod_dav.c
|
||||
+++ b/modules/dav/main/mod_dav.c
|
||||
@@ -355,7 +355,7 @@ static int dav_error_response(request_rec *r, int status, const char *body)
|
||||
r->status = status;
|
||||
r->status_line = ap_get_status_line(status);
|
||||
|
||||
- ap_set_content_type(r, "text/html; charset=ISO-8859-1");
|
||||
+ ap_set_content_type_ex(r, "text/html; charset=ISO-8859-1", 1);
|
||||
|
||||
/* begin the response now... */
|
||||
ap_rvputs(r,
|
||||
@@ -386,7 +386,7 @@ static int dav_error_response_tag(request_rec *r,
|
||||
{
|
||||
r->status = err->status;
|
||||
|
||||
- ap_set_content_type(r, DAV_XML_CONTENT_TYPE);
|
||||
+ ap_set_content_type_ex(r, DAV_XML_CONTENT_TYPE, 1);
|
||||
|
||||
ap_rputs(DAV_XML_HEADER DEBUG_CR
|
||||
"<D:error xmlns:D=\"DAV:\"", r);
|
||||
@@ -544,7 +544,7 @@ DAV_DECLARE(void) dav_begin_multistatus(apr_bucket_brigade *bb,
|
||||
{
|
||||
/* Set the correct status and Content-Type */
|
||||
r->status = status;
|
||||
- ap_set_content_type(r, DAV_XML_CONTENT_TYPE);
|
||||
+ ap_set_content_type_ex(r, DAV_XML_CONTENT_TYPE, 1);
|
||||
|
||||
/* Send the headers and actual multistatus response now... */
|
||||
ap_fputs(r->output_filters, bb, DAV_XML_HEADER DEBUG_CR
|
||||
@@ -2016,7 +2016,7 @@ static int dav_method_options(request_rec *r)
|
||||
|
||||
/* send the options response */
|
||||
r->status = HTTP_OK;
|
||||
- ap_set_content_type(r, DAV_XML_CONTENT_TYPE);
|
||||
+ ap_set_content_type_ex(r, DAV_XML_CONTENT_TYPE, 1);
|
||||
|
||||
/* send the headers and response body */
|
||||
ap_rputs(DAV_XML_HEADER DEBUG_CR
|
||||
@@ -3328,7 +3328,7 @@ static int dav_method_lock(request_rec *r)
|
||||
(*locks_hooks->close_lockdb)(lockdb);
|
||||
|
||||
r->status = HTTP_OK;
|
||||
- ap_set_content_type(r, DAV_XML_CONTENT_TYPE);
|
||||
+ ap_set_content_type_ex(r, DAV_XML_CONTENT_TYPE, 1);
|
||||
|
||||
ap_rputs(DAV_XML_HEADER DEBUG_CR "<D:prop xmlns:D=\"DAV:\">" DEBUG_CR, r);
|
||||
if (lock == NULL)
|
||||
diff --git a/modules/examples/mod_example_hooks.c b/modules/examples/mod_example_hooks.c
|
||||
index f7ef5a5..d937906 100644
|
||||
--- a/modules/examples/mod_example_hooks.c
|
||||
+++ b/modules/examples/mod_example_hooks.c
|
||||
@@ -993,7 +993,7 @@ static int x_handler(request_rec *r)
|
||||
* Set the Content-type header. Note that we do not actually have to send
|
||||
* the headers: this is done by the http core.
|
||||
*/
|
||||
- ap_set_content_type(r, "text/html");
|
||||
+ ap_set_content_type_ex(r, "text/html", 1);
|
||||
/*
|
||||
* If we're only supposed to send header information (HEAD request), we're
|
||||
* already there.
|
||||
diff --git a/modules/filters/mod_data.c b/modules/filters/mod_data.c
|
||||
index ddadd1b..4e6e636 100644
|
||||
--- a/modules/filters/mod_data.c
|
||||
+++ b/modules/filters/mod_data.c
|
||||
@@ -117,7 +117,7 @@ static apr_status_t data_out_filter(ap_filter_t *f, apr_bucket_brigade *bb)
|
||||
}
|
||||
}
|
||||
|
||||
- ap_set_content_type(r, "text/plain");
|
||||
+ ap_set_content_type_ex(r, "text/plain", 1);
|
||||
|
||||
}
|
||||
|
||||
diff --git a/modules/filters/mod_include.c b/modules/filters/mod_include.c
|
||||
index 584d8fb..2c0cc67 100644
|
||||
--- a/modules/filters/mod_include.c
|
||||
+++ b/modules/filters/mod_include.c
|
||||
@@ -3972,7 +3972,7 @@ static int include_fixup(request_rec *r)
|
||||
if (r->handler && (strcmp(r->handler, "server-parsed") == 0))
|
||||
{
|
||||
if (!r->content_type || !*r->content_type) {
|
||||
- ap_set_content_type(r, "text/html");
|
||||
+ ap_set_content_type_ex(r, "text/html", 1);
|
||||
}
|
||||
r->handler = "default-handler";
|
||||
}
|
||||
diff --git a/modules/filters/mod_proxy_html.c b/modules/filters/mod_proxy_html.c
|
||||
index 7783da1..4205a61 100644
|
||||
--- a/modules/filters/mod_proxy_html.c
|
||||
+++ b/modules/filters/mod_proxy_html.c
|
||||
@@ -952,7 +952,7 @@ static apr_status_t proxy_html_filter(ap_filter_t *f, apr_bucket_brigade *bb)
|
||||
ap_log_rerror(APLOG_MARK, APLOG_WARNING, 0, f->r, APLOGNO(01422)
|
||||
"No i18n support found. Install mod_xml2enc if required");
|
||||
enc = XML_CHAR_ENCODING_NONE;
|
||||
- ap_set_content_type(f->r, "text/html;charset=utf-8");
|
||||
+ ap_set_content_type_ex(f->r, "text/html;charset=utf-8", 1);
|
||||
}
|
||||
else {
|
||||
/* if we wanted a non-default charset_out, insert the
|
||||
@@ -968,7 +968,7 @@ static apr_status_t proxy_html_filter(ap_filter_t *f, apr_bucket_brigade *bb)
|
||||
cenc, NULL));
|
||||
}
|
||||
else /* Normal case, everything worked, utf-8 output */
|
||||
- ap_set_content_type(f->r, "text/html;charset=utf-8");
|
||||
+ ap_set_content_type_ex(f->r, "text/html;charset=utf-8", 1);
|
||||
}
|
||||
|
||||
ap_fputs(f->next, ctxt->bb, ctxt->cfg->doctype);
|
||||
diff --git a/modules/generators/mod_cgi.c b/modules/generators/mod_cgi.c
|
||||
index ecca381..b486dfe 100644
|
||||
--- a/modules/generators/mod_cgi.c
|
||||
+++ b/modules/generators/mod_cgi.c
|
||||
@@ -795,7 +795,7 @@ static apr_status_t include_cgi(include_ctx_t *ctx, ap_filter_t *f,
|
||||
/* Force sub_req to be treated as a CGI request, even if ordinary
|
||||
* typing rules would have called it something else.
|
||||
*/
|
||||
- ap_set_content_type(rr, CGI_MAGIC_TYPE);
|
||||
+ ap_set_content_type_ex(rr, CGI_MAGIC_TYPE, 1);
|
||||
|
||||
/* Run it. */
|
||||
rr_status = ap_run_sub_req(rr);
|
||||
diff --git a/modules/generators/mod_cgid.c b/modules/generators/mod_cgid.c
|
||||
index dd91b99..ce744bc 100644
|
||||
--- a/modules/generators/mod_cgid.c
|
||||
+++ b/modules/generators/mod_cgid.c
|
||||
@@ -1802,7 +1802,7 @@ static apr_status_t include_cgi(include_ctx_t *ctx, ap_filter_t *f,
|
||||
/* Force sub_req to be treated as a CGI request, even if ordinary
|
||||
* typing rules would have called it something else.
|
||||
*/
|
||||
- ap_set_content_type(rr, CGI_MAGIC_TYPE);
|
||||
+ ap_set_content_type_ex(rr, CGI_MAGIC_TYPE, 1);
|
||||
|
||||
/* Run it. */
|
||||
rr_status = ap_run_sub_req(rr);
|
||||
diff --git a/modules/generators/mod_info.c b/modules/generators/mod_info.c
|
||||
index 1662242..a94e4e4 100644
|
||||
--- a/modules/generators/mod_info.c
|
||||
+++ b/modules/generators/mod_info.c
|
||||
@@ -784,7 +784,7 @@ static int display_info(request_rec * r)
|
||||
return DECLINED;
|
||||
}
|
||||
|
||||
- ap_set_content_type(r, "text/html; charset=ISO-8859-1");
|
||||
+ ap_set_content_type_ex(r, "text/html; charset=ISO-8859-1", 1);
|
||||
|
||||
ap_rputs(DOCTYPE_XHTML_1_0T
|
||||
"<html xmlns=\"http://www.w3.org/1999/xhtml\">\n"
|
||||
diff --git a/modules/generators/mod_status.c b/modules/generators/mod_status.c
|
||||
index 5bada07..2cb38c7 100644
|
||||
--- a/modules/generators/mod_status.c
|
||||
+++ b/modules/generators/mod_status.c
|
||||
@@ -273,7 +273,7 @@ static int status_handler(request_rec *r)
|
||||
if (r->method_number != M_GET)
|
||||
return DECLINED;
|
||||
|
||||
- ap_set_content_type(r, "text/html; charset=ISO-8859-1");
|
||||
+ ap_set_content_type_ex(r, "text/html; charset=ISO-8859-1", 1);
|
||||
|
||||
/*
|
||||
* Simple table-driven form data set parser that lets you alter the header
|
||||
@@ -301,7 +301,7 @@ static int status_handler(request_rec *r)
|
||||
no_table_report = 1;
|
||||
break;
|
||||
case STAT_OPT_AUTO:
|
||||
- ap_set_content_type(r, "text/plain; charset=ISO-8859-1");
|
||||
+ ap_set_content_type_ex(r, "text/plain; charset=ISO-8859-1", 1);
|
||||
short_report = 1;
|
||||
break;
|
||||
}
|
||||
diff --git a/modules/http/http_filters.c b/modules/http/http_filters.c
|
||||
index 1a8df34..99ccbbb 100644
|
||||
--- a/modules/http/http_filters.c
|
||||
+++ b/modules/http/http_filters.c
|
||||
@@ -1249,7 +1249,7 @@ AP_DECLARE_NONSTD(int) ap_send_http_trace(request_rec *r)
|
||||
}
|
||||
}
|
||||
|
||||
- ap_set_content_type(r, "message/http");
|
||||
+ ap_set_content_type_ex(r, "message/http", 1);
|
||||
|
||||
/* Now we recreate the request, and echo it back */
|
||||
|
||||
diff --git a/modules/http/http_protocol.c b/modules/http/http_protocol.c
|
||||
index c31e873..3bc666e 100644
|
||||
--- a/modules/http/http_protocol.c
|
||||
+++ b/modules/http/http_protocol.c
|
||||
@@ -1443,10 +1443,10 @@ AP_DECLARE(void) ap_send_error_response(request_rec *r, int recursive_error)
|
||||
request_conf->suppress_charset = 1; /* avoid adding default
|
||||
* charset later
|
||||
*/
|
||||
- ap_set_content_type(r, "text/html");
|
||||
+ ap_set_content_type_ex(r, "text/html", 1);
|
||||
}
|
||||
else {
|
||||
- ap_set_content_type(r, "text/html; charset=iso-8859-1");
|
||||
+ ap_set_content_type_ex(r, "text/html; charset=iso-8859-1", 1);
|
||||
}
|
||||
|
||||
if ((status == HTTP_METHOD_NOT_ALLOWED)
|
||||
diff --git a/modules/http/http_request.c b/modules/http/http_request.c
|
||||
index d59cfe2..7e9477b 100644
|
||||
--- a/modules/http/http_request.c
|
||||
+++ b/modules/http/http_request.c
|
||||
@@ -708,7 +708,7 @@ AP_DECLARE(void) ap_internal_fast_redirect(request_rec *rr, request_rec *r)
|
||||
r->args = rr->args;
|
||||
r->finfo = rr->finfo;
|
||||
r->handler = rr->handler;
|
||||
- ap_set_content_type(r, rr->content_type);
|
||||
+ ap_set_content_type_ex(r, rr->content_type, AP_REQUEST_IS_TRUSTED_CT(rr));
|
||||
r->content_encoding = rr->content_encoding;
|
||||
r->content_languages = rr->content_languages;
|
||||
r->per_dir_config = rr->per_dir_config;
|
||||
diff --git a/modules/ldap/util_ldap.c b/modules/ldap/util_ldap.c
|
||||
index 39b1890..5e35852 100644
|
||||
--- a/modules/ldap/util_ldap.c
|
||||
+++ b/modules/ldap/util_ldap.c
|
||||
@@ -139,7 +139,7 @@ static int util_ldap_handler(request_rec *r)
|
||||
st = (util_ldap_state_t *) ap_get_module_config(r->server->module_config,
|
||||
&ldap_module);
|
||||
|
||||
- ap_set_content_type(r, "text/html; charset=ISO-8859-1");
|
||||
+ ap_set_content_type_ex(r, "text/html; charset=ISO-8859-1", 1);
|
||||
|
||||
if (r->header_only)
|
||||
return OK;
|
||||
diff --git a/modules/mappers/mod_imagemap.c b/modules/mappers/mod_imagemap.c
|
||||
index 206c0b6..b2dca7e 100644
|
||||
--- a/modules/mappers/mod_imagemap.c
|
||||
+++ b/modules/mappers/mod_imagemap.c
|
||||
@@ -475,7 +475,7 @@ static int imap_reply(request_rec *r, const char *redirect)
|
||||
|
||||
static void menu_header(request_rec *r, char *menu)
|
||||
{
|
||||
- ap_set_content_type(r, "text/html; charset=ISO-8859-1");
|
||||
+ ap_set_content_type_ex(r, "text/html; charset=ISO-8859-1", 1);
|
||||
|
||||
ap_rvputs(r, DOCTYPE_HTML_3_2, "<html><head>\n<title>Menu for ",
|
||||
ap_escape_html(r->pool, r->uri),
|
||||
diff --git a/modules/proxy/mod_proxy_balancer.c b/modules/proxy/mod_proxy_balancer.c
|
||||
index b8b452d..3c0f5a8 100644
|
||||
--- a/modules/proxy/mod_proxy_balancer.c
|
||||
+++ b/modules/proxy/mod_proxy_balancer.c
|
||||
@@ -1471,7 +1471,7 @@ static void balancer_display_page(request_rec *r, proxy_server_conf *conf,
|
||||
|
||||
if (usexml) {
|
||||
char date[APR_RFC822_DATE_LEN];
|
||||
- ap_set_content_type(r, "text/xml");
|
||||
+ ap_set_content_type_ex(r, "text/xml", 1);
|
||||
ap_rputs("<?xml version='1.0' encoding='UTF-8' ?>\n", r);
|
||||
ap_rputs("<httpd:manager xmlns:httpd='http://httpd.apache.org'>\n", r);
|
||||
ap_rputs(" <httpd:balancers>\n", r);
|
@ -0,0 +1,242 @@
|
||||
From a0a68b99d131741c1867cff321424892838fc4b3 Mon Sep 17 00:00:00 2001
|
||||
From: Yann Ylavic <ylavic@apache.org>
|
||||
Date: Sat, 27 Jul 2024 13:35:53 +0000
|
||||
Subject: [PATCH] mod_rewrite: Better question mark tracking to avoid
|
||||
UnsafeAllow3F. PR 69197.
|
||||
|
||||
Track in do_expand() whether a '?' in the uri-path comes from a literal in
|
||||
the substitution string or from an expansion (variable, lookup, ...).
|
||||
In the former case it's safe to assume that it's the query-string separator
|
||||
but for the other case it's not (could be a decoded %3f from r->uri).
|
||||
|
||||
This allows to avoid [UnsafeAllow3F] for most cases.
|
||||
|
||||
|
||||
Merges r1919325 from trunk
|
||||
Reviewed by: ylavic, covener, jorton
|
||||
Github: closes #462
|
||||
|
||||
|
||||
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1919545 13f79535-47bb-0310-9956-ffa450edef68
|
||||
---
|
||||
modules/mappers/mod_rewrite.c | 107 ++++++++++++++++++++++++++++------
|
||||
1 file changed, 89 insertions(+), 18 deletions(-)
|
||||
|
||||
|
||||
diff --git a/modules/mappers/mod_rewrite.c b/modules/mappers/mod_rewrite.c
|
||||
index 226602f..a56a85c 100644
|
||||
--- a/modules/mappers/mod_rewrite.c
|
||||
+++ b/modules/mappers/mod_rewrite.c
|
||||
@@ -2365,9 +2365,16 @@ static APR_INLINE char *find_char_in_curlies(char *s, int c)
|
||||
* of an earlier expansion to include expansion specifiers that
|
||||
* are interpreted by a later expansion, producing results that
|
||||
* were not intended by the administrator.
|
||||
+ *
|
||||
+ * unsafe_qmark if not NULL will be set to 1 or 0 if a question mark
|
||||
+ * is found respectively in a literal or in a lookup/expansion (whether
|
||||
+ * it's the first or last qmark depends on [QSL]). Should be initialized
|
||||
+ * to -1 and remains so if no qmark is found.
|
||||
*/
|
||||
-static char *do_expand(char *input, rewrite_ctx *ctx, rewriterule_entry *entry)
|
||||
+static char *do_expand(char *input, rewrite_ctx *ctx, rewriterule_entry *entry,
|
||||
+ int *unsafe_qmark)
|
||||
{
|
||||
+#define EXPAND_SPECIALS "\\$%"
|
||||
result_list *result, *current;
|
||||
result_list sresult[SMALL_EXPANSION];
|
||||
unsigned spc = 0;
|
||||
@@ -2375,8 +2382,29 @@ static char *do_expand(char *input, rewrite_ctx *ctx, rewriterule_entry *entry)
|
||||
char *p, *c;
|
||||
apr_pool_t *pool = ctx->r->pool;
|
||||
|
||||
- span = strcspn(input, "\\$%");
|
||||
inputlen = strlen(input);
|
||||
+ if (!unsafe_qmark) {
|
||||
+ span = strcspn(input, EXPAND_SPECIALS);
|
||||
+ }
|
||||
+ else {
|
||||
+ span = strcspn(input, EXPAND_SPECIALS "?");
|
||||
+ if (input[span] == '?') {
|
||||
+ /* this qmark is not from an expansion thus safe */
|
||||
+ *unsafe_qmark = 0;
|
||||
+
|
||||
+ /* keep tracking only if interested in the last qmark */
|
||||
+ if (entry && (entry->flags & RULEFLAG_QSLAST)) {
|
||||
+ do {
|
||||
+ span++;
|
||||
+ span += strcspn(input + span, EXPAND_SPECIALS "?");
|
||||
+ } while (input[span] == '?');
|
||||
+ }
|
||||
+ else {
|
||||
+ unsafe_qmark = NULL;
|
||||
+ span += strcspn(input + span, EXPAND_SPECIALS);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
|
||||
/* fast exit */
|
||||
if (inputlen == span) {
|
||||
@@ -2394,6 +2422,8 @@ static char *do_expand(char *input, rewrite_ctx *ctx, rewriterule_entry *entry)
|
||||
|
||||
/* loop for specials */
|
||||
do {
|
||||
+ int expanded = 0;
|
||||
+
|
||||
/* prepare next entry */
|
||||
if (current->len) {
|
||||
current->next = (spc < SMALL_EXPANSION)
|
||||
@@ -2439,6 +2469,8 @@ static char *do_expand(char *input, rewrite_ctx *ctx, rewriterule_entry *entry)
|
||||
current->len = span;
|
||||
current->string = p;
|
||||
outlen += span;
|
||||
+
|
||||
+ expanded = 1;
|
||||
p = endp + 1;
|
||||
}
|
||||
|
||||
@@ -2478,19 +2510,18 @@ static char *do_expand(char *input, rewrite_ctx *ctx, rewriterule_entry *entry)
|
||||
}
|
||||
|
||||
/* reuse of key variable as result */
|
||||
- key = lookup_map(ctx->r, map, do_expand(key, ctx, entry));
|
||||
-
|
||||
+ key = lookup_map(ctx->r, map, do_expand(key, ctx, entry, NULL));
|
||||
if (!key && dflt && *dflt) {
|
||||
- key = do_expand(dflt, ctx, entry);
|
||||
+ key = do_expand(dflt, ctx, entry, NULL);
|
||||
}
|
||||
-
|
||||
- if (key) {
|
||||
+ if (key && *key) {
|
||||
span = strlen(key);
|
||||
current->len = span;
|
||||
current->string = key;
|
||||
outlen += span;
|
||||
}
|
||||
|
||||
+ expanded = 1;
|
||||
p = endp + 1;
|
||||
}
|
||||
}
|
||||
@@ -2520,8 +2551,9 @@ static char *do_expand(char *input, rewrite_ctx *ctx, rewriterule_entry *entry)
|
||||
current->len = span;
|
||||
current->string = bri->source + bri->regmatch[n].rm_so;
|
||||
}
|
||||
-
|
||||
outlen += span;
|
||||
+
|
||||
+ expanded = 1;
|
||||
}
|
||||
|
||||
p += 2;
|
||||
@@ -2534,8 +2566,41 @@ static char *do_expand(char *input, rewrite_ctx *ctx, rewriterule_entry *entry)
|
||||
++outlen;
|
||||
}
|
||||
|
||||
+ if (unsafe_qmark && expanded && current->len
|
||||
+ && memchr(current->string, '?', current->len)) {
|
||||
+ /* this qmark is from an expansion thus unsafe */
|
||||
+ *unsafe_qmark = 1;
|
||||
+
|
||||
+ /* keep tracking only if interested in the last qmark */
|
||||
+ if (!entry || !(entry->flags & RULEFLAG_QSLAST)) {
|
||||
+ unsafe_qmark = NULL;
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
/* check the remainder */
|
||||
- if (*p && (span = strcspn(p, "\\$%")) > 0) {
|
||||
+ if (!unsafe_qmark) {
|
||||
+ span = strcspn(p, EXPAND_SPECIALS);
|
||||
+ }
|
||||
+ else {
|
||||
+ span = strcspn(p, EXPAND_SPECIALS "?");
|
||||
+ if (p[span] == '?') {
|
||||
+ /* this qmark is not from an expansion thus safe */
|
||||
+ *unsafe_qmark = 0;
|
||||
+
|
||||
+ /* keep tracking only if interested in the last qmark */
|
||||
+ if (entry && (entry->flags & RULEFLAG_QSLAST)) {
|
||||
+ do {
|
||||
+ span++;
|
||||
+ span += strcspn(p + span, EXPAND_SPECIALS "?");
|
||||
+ } while (p[span] == '?');
|
||||
+ }
|
||||
+ else {
|
||||
+ unsafe_qmark = NULL;
|
||||
+ span += strcspn(p + span, EXPAND_SPECIALS);
|
||||
+ }
|
||||
+ }
|
||||
+ }
|
||||
+ if (span > 0) {
|
||||
if (current->len) {
|
||||
current->next = (spc < SMALL_EXPANSION)
|
||||
? &(sresult[spc++])
|
||||
@@ -2580,7 +2645,7 @@ static void do_expand_env(data_item *env, rewrite_ctx *ctx)
|
||||
char *name, *val;
|
||||
|
||||
while (env) {
|
||||
- name = do_expand(env->data, ctx, NULL);
|
||||
+ name = do_expand(env->data, ctx, NULL, NULL);
|
||||
if (*name == '!') {
|
||||
name++;
|
||||
apr_table_unset(ctx->r->subprocess_env, name);
|
||||
@@ -2714,7 +2779,7 @@ static void add_cookie(request_rec *r, char *s)
|
||||
static void do_expand_cookie(data_item *cookie, rewrite_ctx *ctx)
|
||||
{
|
||||
while (cookie) {
|
||||
- add_cookie(ctx->r, do_expand(cookie->data, ctx, NULL));
|
||||
+ add_cookie(ctx->r, do_expand(cookie->data, ctx, NULL, NULL));
|
||||
cookie = cookie->next;
|
||||
}
|
||||
|
||||
@@ -3992,7 +4057,7 @@ static int apply_rewrite_cond(rewritecond_entry *p, rewrite_ctx *ctx)
|
||||
int basis;
|
||||
|
||||
if (p->ptype != CONDPAT_AP_EXPR)
|
||||
- input = do_expand(p->input, ctx, NULL);
|
||||
+ input = do_expand(p->input, ctx, NULL, NULL);
|
||||
|
||||
switch (p->ptype) {
|
||||
case CONDPAT_FILE_EXISTS:
|
||||
@@ -4156,7 +4221,7 @@ static APR_INLINE void force_type_handler(rewriterule_entry *p,
|
||||
char *expanded;
|
||||
|
||||
if (p->forced_mimetype) {
|
||||
- expanded = do_expand(p->forced_mimetype, ctx, p);
|
||||
+ expanded = do_expand(p->forced_mimetype, ctx, p, NULL);
|
||||
|
||||
if (*expanded) {
|
||||
ap_str_tolower(expanded);
|
||||
@@ -4170,7 +4235,7 @@ static APR_INLINE void force_type_handler(rewriterule_entry *p,
|
||||
}
|
||||
|
||||
if (p->forced_handler) {
|
||||
- expanded = do_expand(p->forced_handler, ctx, p);
|
||||
+ expanded = do_expand(p->forced_handler, ctx, p, NULL);
|
||||
|
||||
if (*expanded) {
|
||||
ap_str_tolower(expanded);
|
||||
@@ -4307,12 +4372,18 @@ static rule_return_type apply_rewrite_rule(rewriterule_entry *p,
|
||||
|
||||
/* expand the result */
|
||||
if (!(p->flags & RULEFLAG_NOSUB)) {
|
||||
- newuri = do_expand(p->output, ctx, p);
|
||||
+ int unsafe_qmark = -1;
|
||||
+
|
||||
+ if (p->flags & RULEFLAG_UNSAFE_ALLOW3F) {
|
||||
+ newuri = do_expand(p->output, ctx, p, NULL);
|
||||
+ }
|
||||
+ else {
|
||||
+ newuri = do_expand(p->output, ctx, p, &unsafe_qmark);
|
||||
+ }
|
||||
rewritelog((r, 2, ctx->perdir, "rewrite '%s' -> '%s'", ctx->uri,
|
||||
newuri));
|
||||
- if (!(p->flags & RULEFLAG_UNSAFE_ALLOW3F) &&
|
||||
- ap_strcasestr(r->unparsed_uri, "%3f") &&
|
||||
- ap_strchr_c(newuri, '?')) {
|
||||
+
|
||||
+ if (unsafe_qmark > 0) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO()
|
||||
"Unsafe URL with %%3f URL rewritten without "
|
||||
"UnsafeAllow3F");
|
Loading…
Reference in new issue