From 6dc5080f08abcf54b9e3b640ef1640fc96c84fcf Mon Sep 17 00:00:00 2001 From: wenjie2025 Date: Wed, 28 Jan 2026 23:24:59 +0800 Subject: [PATCH] Update code from upstream --- httpd-2.4.62-r1919325.patch | 242 +++++++++++++++++++++++++++++ httpd-upstream-e9082a8a.patch | 277 ++++++++++++++++++++++++++++++++++ httpd.spec | 8 +- 3 files changed, 526 insertions(+), 1 deletion(-) create mode 100644 httpd-2.4.62-r1919325.patch create mode 100644 httpd-upstream-e9082a8a.patch diff --git a/httpd-2.4.62-r1919325.patch b/httpd-2.4.62-r1919325.patch new file mode 100644 index 0000000..1b344e5 --- /dev/null +++ b/httpd-2.4.62-r1919325.patch @@ -0,0 +1,242 @@ +From a0a68b99d131741c1867cff321424892838fc4b3 Mon Sep 17 00:00:00 2001 +From: Yann Ylavic +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 f1c22e3235b..53fb1e91ffb 100644 +--- a/modules/mappers/mod_rewrite.c ++++ b/modules/mappers/mod_rewrite.c +@@ -2376,9 +2376,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; +@@ -2386,8 +2393,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) { +@@ -2405,6 +2433,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) +@@ -2450,6 +2480,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; + } + +@@ -2489,19 +2521,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; + } + } +@@ -2531,8 +2562,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; +@@ -2545,8 +2577,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++]) +@@ -2591,7 +2656,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); +@@ -2725,7 +2790,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; + } + +@@ -4014,7 +4079,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: +@@ -4178,7 +4243,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); +@@ -4192,7 +4257,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); +@@ -4329,12 +4394,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(10508) + "Unsafe URL with %%3f URL rewritten without " + "UnsafeAllow3F"); + diff --git a/httpd-upstream-e9082a8a.patch b/httpd-upstream-e9082a8a.patch new file mode 100644 index 0000000..67ae84a --- /dev/null +++ b/httpd-upstream-e9082a8a.patch @@ -0,0 +1,277 @@ +--- a/httpd-2.4.62-r1919325.patch ++++ b/httpd-2.4.62-r1919325.patch +@@ -0,0 +1,242 @@ ++From a0a68b99d131741c1867cff321424892838fc4b3 Mon Sep 17 00:00:00 2001 ++From: Yann Ylavic ++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 f1c22e3235b..53fb1e91ffb 100644 ++--- a/modules/mappers/mod_rewrite.c +++++ b/modules/mappers/mod_rewrite.c ++@@ -2376,9 +2376,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; ++@@ -2386,8 +2393,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) { ++@@ -2405,6 +2433,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) ++@@ -2450,6 +2480,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; ++ } ++ ++@@ -2489,19 +2521,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; ++ } ++ } ++@@ -2531,8 +2562,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; ++@@ -2545,8 +2577,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++]) ++@@ -2591,7 +2656,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); ++@@ -2725,7 +2790,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; ++ } ++ ++@@ -4014,7 +4079,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: ++@@ -4178,7 +4243,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); ++@@ -4192,7 +4257,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); ++@@ -4329,12 +4394,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(10508) ++ "Unsafe URL with %%3f URL rewritten without " ++ "UnsafeAllow3F"); ++ + +--- a/httpd.spec ++++ b/httpd.spec +@@ -25,7 +25,7 @@ + Summary: Apache HTTP Server + Name: httpd + Version: 2.4.62 +-Release: 1%{?dist} ++Release: 2%{?dist} + URL: https://httpd.apache.org/ + Source0: https://www.apache.org/dist/httpd/httpd-%{version}.tar.bz2 + Source1: https://www.apache.org/dist/httpd/httpd-%{version}.tar.bz2.asc +@@ -101,6 +101,8 @@ Patch33: httpd-2.4.54-selinux.patch + # https://bugzilla.redhat.com/show_bug.cgi?id=1397243 + Patch60: httpd-2.4.43-enable-sslv3.patch + Patch61: httpd-2.4.59-no-engine.patch ++# https://bz.apache.org/bugzilla/show_bug.cgi?id=69197 ++Patch62: httpd-2.4.62-r1919325.patch + + # Security fixes + # Patch200: ... +@@ -824,6 +826,9 @@ exit $rv + %{_rpmconfigdir}/macros.d/macros.httpd + + %changelog ++* Sat Aug 03 2024 Luboš Uhliarik - 2.4.62-2 ++- Resolves: RHEL-52722 - Regression introduced by CVE-2024-38474 fix ++ + * Fri Jul 19 2024 Luboš Uhliarik - 2.4.62-1 + - new version 2.4.62 + - Resolves: RHEL-33446 + diff --git a/httpd.spec b/httpd.spec index d5b0f5a..cc0c102 100644 --- a/httpd.spec +++ b/httpd.spec @@ -1,4 +1,4 @@ -%define anolis_release 1 +%define anolis_release 2 %define contentdir %{_datadir}/%{name} %define docroot /var/www %define suexec_caller apache @@ -94,6 +94,9 @@ Patch1000: 1000-httpd-anolis-rebrand.patch #https://github.com/apache/httpd/commit/9494aa8d52e3c263bc0413b77ac8a73b0d524388 #Patch1003: fix-cve-2024-39573.patch +# Resolves: RHEL-52722 - Regression introduced by CVE-2024-38474 fix +Patch0023: httpd-2.4.62-r1919325.patch + BuildRequires: gcc > 12.0 BuildRequires: autoconf BuildRequires: pkgconfig @@ -765,6 +768,9 @@ exit $rv %changelog +* Wed Jan 28 2026 wenjie2025 - 2.4.62-2 +- Add httpd-2.4.62-r1919325.patch to address regression introduced by CVE-2024-38474 fix (RHEL-52722) + * Mon Nov 25 2024 yangxinyu - 2.4.62-1 - new version 2.4.62 -- Gitee