Backport of:

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(-)

Index: apache2-2.4.29/include/http_protocol.h
===================================================================
--- apache2-2.4.29.orig/include/http_protocol.h
+++ apache2-2.4.29/include/http_protocol.h
@@ -403,6 +403,17 @@ AP_DECLARE(void) ap_clear_method_list(ap
 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
  */
Index: apache2-2.4.29/include/httpd.h
===================================================================
--- apache2-2.4.29.orig/include/httpd.h
+++ apache2-2.4.29/include/httpd.h
@@ -666,6 +666,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
@@ -688,6 +689,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
Index: apache2-2.4.29/modules/http/http_protocol.c
===================================================================
--- apache2-2.4.29.orig/modules/http/http_protocol.c
+++ apache2-2.4.29/modules/http/http_protocol.c
@@ -1077,8 +1077,14 @@ AP_DECLARE(void) ap_set_content_type(req
     }
     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)
 {
Index: apache2-2.4.29/modules/http/mod_mime.c
===================================================================
--- apache2-2.4.29.orig/modules/http/mod_mime.c
+++ apache2-2.4.29/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;
     }
 
@@ -837,7 +837,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;
             }
         }
@@ -846,7 +846,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;
             }
 
@@ -951,33 +951,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));
             }
         }
     }
Index: apache2-2.4.29/modules/mappers/mod_actions.c
===================================================================
--- apache2-2.4.29.orig/modules/mappers/mod_actions.c
+++ apache2-2.4.29/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);
Index: apache2-2.4.29/modules/mappers/mod_negotiation.c
===================================================================
--- apache2-2.4.29.orig/modules/mappers/mod_negotiation.c
+++ apache2-2.4.29/modules/mappers/mod_negotiation.c
@@ -1169,7 +1169,7 @@ static int read_types_multi(negotiation_
          * 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);
         }
 
         /*
@@ -3008,14 +3008,14 @@ static int handle_map_file(request_rec *
         /* 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);
             }
         }
 
Index: apache2-2.4.29/modules/mappers/mod_rewrite.c
===================================================================
--- apache2-2.4.29.orig/modules/mappers/mod_rewrite.c
+++ apache2-2.4.29/modules/mappers/mod_rewrite.c
@@ -5344,7 +5344,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 */
Index: apache2-2.4.29/modules/metadata/mod_headers.c
===================================================================
--- apache2-2.4.29.orig/modules/metadata/mod_headers.c
+++ apache2-2.4.29/modules/metadata/mod_headers.c
@@ -792,14 +792,14 @@ static int do_headers_fixup(request_rec
             break;
         case hdr_set:
             if (!strcasecmp(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 (!strcasecmp(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));
             }
@@ -818,7 +818,7 @@ static int do_headers_fixup(request_rec
                 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;
Index: apache2-2.4.29/modules/metadata/mod_mime_magic.c
===================================================================
--- apache2-2.4.29.orig/modules/metadata/mod_mime_magic.c
+++ apache2-2.4.29/modules/metadata/mod_mime_magic.c
@@ -789,7 +789,7 @@ static int magic_rsl_to_request(request_
     /* 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 *
 
     /* 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",
Index: apache2-2.4.29/server/config.c
===================================================================
--- apache2-2.4.29.orig/server/config.c
+++ apache2-2.4.29/server/config.c
@@ -409,7 +409,7 @@ AP_CORE_DECLARE(int) ap_invoke_handler(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,
Index: apache2-2.4.29/server/core.c
===================================================================
--- apache2-2.4.29.orig/server/core.c
+++ apache2-2.4.29/server/core.c
@@ -4675,7 +4675,7 @@ static int core_override_type(request_re
     /* 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;
