-- backported bnotes needed by this CVE fix from https://svn.apache.org/viewvc?view=revision&revision=1887384 -- modified it a little bit so it actually doesn't store bnotes into req structure to not brake compatibility -- but it stores AP_REQUEST_TRUSTED_CT info into r->notes diff --git a/include/http_protocol.h b/include/http_protocol.h index 8ed77ac..81949ca 100644 --- a/include/http_protocol.h +++ b/include/http_protocol.h @@ -402,6 +402,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 99f7f04..d8746c1 100644 --- a/include/httpd.h +++ b/include/httpd.h @@ -645,6 +645,7 @@ struct ap_method_list_t { /** the array used for extension methods */ apr_array_header_t *method_list; }; +/** @} */ /** * @defgroup module_magic Module Magic mime types @@ -715,6 +716,31 @@ struct ap_method_list_t { /** @} // values_request_rec_used_path_info */ +#define AP_REQUEST_TRUSTED_CT "rh-bnotes-trusted-ct" + +/** + * This is a convenience macro to ease with getting specific request + * binary notes. + */ +#define AP_REQUEST_GET_BNOTE(r, mask) (apr_table_get(r->notes, mask) ? 1 : 0) + +/** + * This is a convenience macro to ease with setting specific request + * binary notes. + */ +#define AP_REQUEST_SET_BNOTE(r, mask, val) do { \ + if (val) \ + apr_table_setn(r->notes, mask, "1"); \ + else \ + apr_table_unset(r->notes, mask); \ +} while (0) + +/** + * 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)) +/** @} */ /* * Things which may vary per file-lookup WITHIN a request --- diff --git a/modules/http/http_protocol.c b/modules/http/http_protocol.c index df862e0..d003aea 100644 --- a/modules/http/http_protocol.c +++ b/modules/http/http_protocol.c @@ -1077,8 +1077,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 28c53be..4cd1c48 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; } @@ -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)); } } } diff --git a/modules/mappers/mod_actions.c b/modules/mappers/mod_actions.c index ac9c3b7..5e398b5 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 da68353..e91ba5f 100644 --- a/modules/mappers/mod_negotiation.c +++ b/modules/mappers/mod_negotiation.c @@ -1169,7 +1169,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); } /* @@ -3008,14 +3008,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 a25c7ae..2b70d66 100644 --- a/modules/mappers/mod_rewrite.c +++ b/modules/mappers/mod_rewrite.c @@ -5477,7 +5477,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 1ea970d..55ab281 100644 --- a/modules/metadata/mod_headers.c +++ b/modules/metadata/mod_headers.c @@ -792,14 +792,14 @@ static int do_headers_fixup(request_rec *r, apr_table_t *headers, 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 *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 c12529a..fe30f27 100644 --- a/modules/metadata/mod_mime_magic.c +++ b/modules/metadata/mod_mime_magic.c @@ -789,7 +789,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 f815b22..fc4d76b 100644 --- a/server/config.c +++ b/server/config.c @@ -419,7 +419,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 8575aae..82706cf 100644 --- a/server/core.c +++ b/server/core.c @@ -4722,7 +4722,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;