diff --git a/fix-cve-2024-38472.patch b/fix-cve-2024-38472.patch new file mode 100644 index 0000000000000000000000000000000000000000..0b7217644885e395f4b6011887bca5ec26ff6327 --- /dev/null +++ b/fix-cve-2024-38472.patch @@ -0,0 +1,175 @@ +From 7407cc4cf2041229f1642f66855d270dd268be75 Mon Sep 17 00:00:00 2001 +From: Yang_X_Y +Date: Fri, 2 Aug 2024 10:47:20 +0800 +Subject: [PATCH] fix-cve-2024-38472 + +--- + include/http_core.h | 3 ++ + include/httpd.h | 10 +++++ + server/core.c | 101 ++++++++++++++++++++++++++++++++++++++++++++ + 3 files changed, 114 insertions(+) + +diff --git a/include/http_core.h b/include/http_core.h +index 948034f..9fb9f51 100644 +--- a/include/http_core.h ++++ b/include/http_core.h +@@ -756,6 +756,9 @@ typedef struct { + apr_size_t flush_max_threshold; + apr_int32_t flush_max_pipelined; + unsigned int strict_host_check; ++#ifdef WIN32 ++ apr_array_header_t *unc_list; ++#endif + } core_server_config; + + /* for AddOutputFiltersByType in core.c */ +diff --git a/include/httpd.h b/include/httpd.h +index 799cf97..1081846 100644 +--- a/include/httpd.h ++++ b/include/httpd.h +@@ -2656,6 +2656,16 @@ AP_DECLARE(const char *)ap_dir_fnmatch(ap_dir_match_t *w, const char *path, + */ + AP_DECLARE(int) ap_is_chunked(apr_pool_t *p, const char *line); + ++AP_DECLARE(apr_status_t) ap_filepath_merge(char **newpath, ++ const char *rootpath, ++ const char *addpath, ++ apr_int32_t flags, ++ apr_pool_t *p); ++ ++#ifdef WIN32 ++#define apr_filepath_merge ap_filepath_merge ++#endif ++ + #ifdef __cplusplus + } + #endif +diff --git a/server/core.c b/server/core.c +index f98ad9d..baadf95 100644 +--- a/server/core.c ++++ b/server/core.c +@@ -4453,6 +4453,25 @@ static const char *set_merge_trailers(cmd_parms *cmd, void *dummy, int arg) + return NULL; + } + ++#ifdef WIN32 ++static const char *set_unc_list(cmd_parms *cmd, void *d_, int argc, char *const argv[]) ++{ ++ core_server_config *sconf = ap_get_core_module_config(cmd->server->module_config); ++ int i; ++ const char *err; ++ ++ if ((err = ap_check_cmd_context(cmd, NOT_IN_VIRTUALHOST)) != NULL) ++ return err; ++ ++ sconf->unc_list = apr_array_make(cmd->pool, argc, sizeof(char *)); ++ ++ for (i = 0; i < argc; i++) { ++ *(char **)apr_array_push(sconf->unc_list) = apr_pstrdup(cmd->pool, argv[i]); ++ } ++ ++ return NULL; ++} ++#endif + /* Note --- ErrorDocument will now work from .htaccess files. + * The AllowOverride of Fileinfo allows webmasters to turn it off + */ +@@ -4554,6 +4573,10 @@ AP_INIT_TAKE1("FlushMaxThreshold", set_flush_max_threshold, NULL, RSRC_CONF, + AP_INIT_TAKE1("FlushMaxPipelined", set_flush_max_pipelined, NULL, RSRC_CONF, + "Maximum number of pipelined responses (pending) above which they are " + "flushed to the network"), ++#ifdef WIN32 ++AP_INIT_TAKE_ARGV("UNCList", set_unc_list, NULL, RSRC_CONF, ++ "Controls what UNC hosts may be looked up"), ++#endif + + /* Old server config file commands */ + +@@ -5722,6 +5745,84 @@ AP_CORE_DECLARE(apr_status_t) ap_get_pollfd_from_conn(conn_rec *c, + return ap_run_get_pollfd_from_conn(c, pfd, ptimeout); + } + ++#ifdef WIN32 ++static apr_status_t check_unc(const char *path, apr_pool_t *p) ++{ ++ int i; ++ char *s, *teststring; ++ apr_status_t rv = APR_EACCES; ++ core_server_config *sconf = NULL; ++ ++ if (!ap_server_conf) { ++ return APR_SUCCESS; /* this early, if we have a UNC, it's specified by an admin */ ++ } ++ ++ if (!path || (path != ap_strstr_c(path, "\\\\") && ++ path != ap_strstr_c(path, "//"))) { ++ return APR_SUCCESS; /* not a UNC */ ++ } ++ ++ sconf = ap_get_core_module_config(ap_server_conf->module_config); ++ s = teststring = apr_pstrdup(p, path); ++ *s++ = '/'; ++ *s++ = '/'; ++ ++ ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, ap_server_conf, ++ "ap_filepath_merge: check converted path %s allowed %d", ++ teststring, ++ sconf->unc_list ? sconf->unc_list->nelts : 0); ++ ++ for (i = 0; sconf->unc_list && i < sconf->unc_list->nelts; i++) { ++ char *configured_unc = ((char **)sconf->unc_list->elts)[i]; ++ apr_uri_t uri; ++ if (APR_SUCCESS == apr_uri_parse(p, teststring, &uri) && ++ (uri.hostinfo == NULL || ++ !ap_cstr_casecmp(uri.hostinfo, configured_unc))) { ++ rv = APR_SUCCESS; ++ ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, ap_server_conf, ++ "ap_filepath_merge: match %s %s", ++ uri.hostinfo, configured_unc); ++ break; ++ } ++ else { ++ ap_log_error(APLOG_MARK, APLOG_TRACE4, 0, ap_server_conf, ++ "ap_filepath_merge: no match %s %s", uri.hostinfo, ++ configured_unc); ++ } ++ } ++ if (rv != APR_SUCCESS) { ++ ap_log_error(APLOG_MARK, APLOG_ERR, rv, ap_server_conf, APLOGNO(10504) ++ "ap_filepath_merge: UNC path %s not allowed by UNCList", teststring); ++ } ++ ++ return rv; ++} ++#endif ++ ++AP_DECLARE(apr_status_t) ap_filepath_merge(char **newpath, ++ const char *rootpath, ++ const char *addpath, ++ apr_int32_t flags, ++ apr_pool_t *p) ++{ ++#ifdef WIN32 ++ apr_status_t rv; ++ ++ if (APR_SUCCESS != (rv = check_unc(rootpath, p))) { ++ return rv; ++ } ++ if (APR_SUCCESS != (rv = check_unc(addpath, p))) { ++ return rv; ++ } ++#undef apr_filepath_merge ++#endif ++ return apr_filepath_merge(newpath, rootpath, addpath, flags, p); ++#ifdef WIN32 ++#define apr_filepath_merge ap_filepath_merge ++#endif ++} ++ ++ + static void register_hooks(apr_pool_t *p) + { + errorlog_hash = apr_hash_make(p); +-- +2.33.0 + diff --git a/fix-cve-2024-38475.patch b/fix-cve-2024-38475.patch new file mode 100644 index 0000000000000000000000000000000000000000..e5b6b1ea30ffc16cafdf1b181aca43a68e26953b --- /dev/null +++ b/fix-cve-2024-38475.patch @@ -0,0 +1,353 @@ +From f3ca642e89c355015e21275fd431ec6ab23d3cc3 Mon Sep 17 00:00:00 2001 +From: Yang_X_Y +Date: Fri, 2 Aug 2024 10:21:45 +0800 +Subject: [PATCH] fix-cve-2024-38475 + +--- + modules/mappers/mod_rewrite.c | 128 +++++++++++++++++++++++++++++----- + 1 file changed, 109 insertions(+), 19 deletions(-) + +diff --git a/modules/mappers/mod_rewrite.c b/modules/mappers/mod_rewrite.c +index bbcc11b..9c9076e 100644 +--- a/modules/mappers/mod_rewrite.c ++++ b/modules/mappers/mod_rewrite.c +@@ -177,6 +177,8 @@ static const char* really_last_key = "rewrite_really_last"; + #define RULEFLAG_QSLAST (1<<19) + #define RULEFLAG_QSNONE (1<<20) /* programattic only */ + #define RULEFLAG_ESCAPECTLS (1<<21) ++#define RULEFLAG_UNSAFE_PREFIX_STAT (1<<22) ++#define RULEFLAG_UNSAFE_ALLOW3F (1<<23) + + /* return code of the rewrite rule + * the result may be escaped - or not +@@ -184,7 +186,7 @@ static const char* really_last_key = "rewrite_really_last"; + #define ACTION_NORMAL (1<<0) + #define ACTION_NOESCAPE (1<<1) + #define ACTION_STATUS (1<<2) +- ++#define ACTION_STATUS_SET (1<<3) + + #define MAPTYPE_TXT (1<<0) + #define MAPTYPE_DBM (1<<1) +@@ -208,6 +210,7 @@ static const char* really_last_key = "rewrite_really_last"; + #define OPTION_IGNORE_INHERIT (1<<8) + #define OPTION_IGNORE_CONTEXT_INFO (1<<9) + #define OPTION_LEGACY_PREFIX_DOCROOT (1<<10) ++#define OPTION_UNSAFE_PREFIX_STAT (1<<12) + + #ifndef RAND_MAX + #define RAND_MAX 32767 +@@ -301,6 +304,14 @@ typedef enum { + CONDPAT_AP_EXPR + } pattern_type; + ++typedef enum { ++ RULE_RC_NOMATCH = 0, /* the rule didn't match */ ++ RULE_RC_MATCH = 1, /* a matching rule w/ substitution */ ++ RULE_RC_NOSUB = 2, /* a matching rule w/ no substitution */ ++ RULE_RC_STATUS_SET = 3 /* a matching rule that has set an HTTP error ++ to be returned in r->status */ ++} rule_return_type; ++ + typedef struct { + char *input; /* Input string of RewriteCond */ + char *pattern; /* the RegExp pattern string */ +@@ -927,10 +938,16 @@ static void fully_qualify_uri(request_rec *r) + return; + } + ++static int startsWith(request_rec *r, const char *haystack, const char *needle) { ++ int rc = (ap_strstr_c(haystack, needle) == haystack); ++ rewritelog((r, 5, NULL, "prefix_stat startsWith(%s, %s) %d", haystack, needle, rc)); ++ return rc; ++} ++ + /* +- * stat() only the first segment of a path ++ * stat() only the first segment of a path, and only if it matches the output of the last matching rule + */ +-static int prefix_stat(const char *path, apr_pool_t *pool) ++static int prefix_stat(request_rec *r, const char *path, apr_pool_t *pool, rewriterule_entry *lastsub) + { + const char *curpath = path; + const char *root; +@@ -964,10 +981,36 @@ static int prefix_stat(const char *path, apr_pool_t *pool) + apr_finfo_t sb; + + if (apr_stat(&sb, statpath, APR_FINFO_MIN, pool) == APR_SUCCESS) { +- return 1; ++ if (!lastsub) { ++ rewritelog((r, 3, NULL, "prefix_stat no lastsub subst prefix %s", statpath)); ++ return 1; ++ } ++ ++ rewritelog((r, 3, NULL, "prefix_stat compare statpath %s and lastsub output %s STATOK %d ", ++ statpath, lastsub->output, lastsub->flags & RULEFLAG_UNSAFE_PREFIX_STAT)); ++ if (lastsub->flags & RULEFLAG_UNSAFE_PREFIX_STAT) { ++ return 1; ++ } ++ else { ++ const char *docroot = ap_document_root(r); ++ const char *context_docroot = ap_context_document_root(r); ++ /* ++ * As an example, path (r->filename) is /var/foo/bar/baz.html ++ * even if the flag is not set, we can accept a rule that ++ * began with a literal /var (stapath), or if the entire path ++ * starts with the docroot or context document root ++ */ ++ if (startsWith(r, lastsub->output, statpath) || ++ startsWith(r, path, docroot) || ++ ((docroot != context_docroot) && ++ startsWith(r, path, context_docroot))) { ++ return 1; ++ } ++ } + } + } + ++ /* prefix will be added */ + return 0; + } + +@@ -3072,6 +3115,9 @@ static const char *cmd_rewriteoptions(cmd_parms *cmd, + else if (!strcasecmp(w, "legacyprefixdocroot")) { + options |= OPTION_LEGACY_PREFIX_DOCROOT; + } ++ else if (!strcasecmp(w, "UnsafePrefixStat")) { ++ options |= OPTION_UNSAFE_PREFIX_STAT; ++ } + else { + return apr_pstrcat(cmd->pool, "RewriteOptions: unknown option '", + w, "'", NULL); +@@ -3780,6 +3826,19 @@ static const char *cmd_rewriterule_setflag(apr_pool_t *p, void *_cfg, + ++error; + } + break; ++ ++ case 'u': ++ case 'U': ++ if (!strcasecmp(key, "nsafePrefixStat")){ ++ cfg->flags |= (RULEFLAG_UNSAFE_PREFIX_STAT); ++ } ++ else if(!strcasecmp(key, "nsafeAllow3F")) { ++ cfg->flags |= RULEFLAG_UNSAFE_ALLOW3F; ++ } ++ else { ++ ++error; ++ } ++ break; + default: + ++error; + break; +@@ -4138,7 +4197,8 @@ static APR_INLINE void force_type_handler(rewriterule_entry *p, + /* + * Apply a single RewriteRule + */ +-static int apply_rewrite_rule(rewriterule_entry *p, rewrite_ctx *ctx) ++static rule_return_type apply_rewrite_rule(rewriterule_entry *p, ++ rewrite_ctx *ctx) + { + ap_regmatch_t regmatch[AP_MAX_REG_MATCH]; + apr_array_header_t *rewriteconds; +@@ -4189,7 +4249,7 @@ static int apply_rewrite_rule(rewriterule_entry *p, rewrite_ctx *ctx) + rc = !ap_regexec(p->regexp, ctx->uri, AP_MAX_REG_MATCH, regmatch, 0); + if (! (( rc && !(p->flags & RULEFLAG_NOTMATCH)) || + (!rc && (p->flags & RULEFLAG_NOTMATCH)) ) ) { +- return 0; ++ return RULE_RC_NOMATCH; + } + + /* It matched, wow! Now it's time to prepare the context structure for +@@ -4240,7 +4300,7 @@ static int apply_rewrite_rule(rewriterule_entry *p, rewrite_ctx *ctx) + } + } + else if (!rc) { +- return 0; ++ return RULE_RC_NOMATCH; + } + + /* If some HTTP header was involved in the condition, remember it +@@ -4260,6 +4320,15 @@ static int apply_rewrite_rule(rewriterule_entry *p, rewrite_ctx *ctx) + newuri = do_expand(p->output, ctx, p); + 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, '?')) { ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO() ++ "Unsafe URL with %%3f URL rewritten without " ++ "UnsafeAllow3F"); ++ r->status = HTTP_FORBIDDEN; ++ return RULE_RC_STATUS_SET; ++ } + } + + /* expand [E=var:val] and [CO=] */ +@@ -4277,7 +4346,7 @@ static int apply_rewrite_rule(rewriterule_entry *p, rewrite_ctx *ctx) + r->status = p->forced_responsecode; + } + +- return 2; ++ return RULE_RC_NOSUB; + } + + /* Now adjust API's knowledge about r->filename and r->args */ +@@ -4329,7 +4398,7 @@ static int apply_rewrite_rule(rewriterule_entry *p, rewrite_ctx *ctx) + r->filename)); + + r->filename = apr_pstrcat(r->pool, "proxy:", r->filename, NULL); +- return 1; ++ return RULE_RC_MATCH; + } + + /* If this rule is explicitly forced for HTTP redirection +@@ -4344,7 +4413,7 @@ static int apply_rewrite_rule(rewriterule_entry *p, rewrite_ctx *ctx) + r->filename)); + + r->status = p->forced_responsecode; +- return 1; ++ return RULE_RC_MATCH; + } + + /* Special Rewriting Feature: Self-Reduction +@@ -4366,7 +4435,7 @@ static int apply_rewrite_rule(rewriterule_entry *p, rewrite_ctx *ctx) + "with %s", p->forced_responsecode, r->filename)); + + r->status = p->forced_responsecode; +- return 1; ++ return RULE_RC_MATCH; + } + + /* Finally remember the forced mime-type */ +@@ -4375,7 +4444,7 @@ static int apply_rewrite_rule(rewriterule_entry *p, rewrite_ctx *ctx) + /* Puuhhhhhhhh... WHAT COMPLICATED STUFF ;_) + * But now we're done for this particular rule. + */ +- return 1; ++ return RULE_RC_MATCH; + } + + /* +@@ -4383,13 +4452,13 @@ static int apply_rewrite_rule(rewriterule_entry *p, rewrite_ctx *ctx) + * i.e. a list of rewrite rules + */ + static int apply_rewrite_list(request_rec *r, apr_array_header_t *rewriterules, +- char *perdir) ++ char *perdir, rewriterule_entry **lastsub) + { + rewriterule_entry *entries; + rewriterule_entry *p; + int i; + int changed; +- int rc; ++ rule_return_type rc; + int s; + rewrite_ctx *ctx; + int round = 1; +@@ -4397,6 +4466,7 @@ static int apply_rewrite_list(request_rec *r, apr_array_header_t *rewriterules, + ctx = apr_palloc(r->pool, sizeof(*ctx)); + ctx->perdir = perdir; + ctx->r = r; ++ *lastsub = NULL; + + /* + * Iterate over all existing rules +@@ -4424,7 +4494,12 @@ static int apply_rewrite_list(request_rec *r, apr_array_header_t *rewriterules, + ctx->vary = NULL; + rc = apply_rewrite_rule(p, ctx); + +- if (rc) { ++ if (rc != RULE_RC_NOMATCH) { ++ ++ if (!(p->flags & RULEFLAG_NOSUB)) { ++ rewritelog((r, 2, perdir, "setting lastsub to rule with output %s", p->output)); ++ *lastsub = p; ++ } + + /* Catch looping rules with pathinfo growing unbounded */ + if ( strlen( r->filename ) > 2*r->server->limit_req_line ) { +@@ -4451,10 +4526,15 @@ static int apply_rewrite_list(request_rec *r, apr_array_header_t *rewriterules, + return ACTION_STATUS; + } + ++ /* Error while evaluating rule, r->status set */ ++ if (RULE_RC_STATUS_SET == rc) { ++ return ACTION_STATUS_SET; ++ } ++ + /* + * Indicate a change if this was not a match-only rule. + */ +- if (rc != 2) { ++ if (rc != RULE_RC_NOSUB) { + changed = ((p->flags & RULEFLAG_NOESCAPE) + ? ACTION_NOESCAPE : ACTION_NORMAL); + } +@@ -4643,6 +4723,7 @@ static int hook_uri2file(request_rec *r) + int rulestatus; + void *skipdata; + const char *oargs; ++ rewriterule_entry *lastsub = NULL; + + /* + * retrieve the config structures +@@ -4754,7 +4835,7 @@ static int hook_uri2file(request_rec *r) + /* + * now apply the rules ... + */ +- rulestatus = apply_rewrite_list(r, conf->rewriterules, NULL); ++ rulestatus = apply_rewrite_list(r, conf->rewriterules, NULL, &lastsub); + apr_table_setn(r->notes, "mod_rewrite_rewritten", + apr_psprintf(r->pool,"%d",rulestatus)); + } +@@ -4792,6 +4873,9 @@ static int hook_uri2file(request_rec *r) + r->status = HTTP_OK; + return n; + } ++ else if (ACTION_STATUS_SET == rulestatus) { ++ return r->status; ++ } + + if (to_proxyreq) { + /* it should be go on as an internal proxy request */ +@@ -4936,7 +5020,9 @@ static int hook_uri2file(request_rec *r) + uri_reduced = apr_table_get(r->notes, "mod_rewrite_uri_reduced"); + } + +- if (!prefix_stat(r->filename, r->pool) || uri_reduced != NULL) { ++ if (!prefix_stat(r, r->filename, r->pool, ++ conf->options & OPTION_UNSAFE_PREFIX_STAT ? NULL : lastsub) ++ || uri_reduced != NULL) { + int res; + char *tmp = r->uri; + +@@ -4981,6 +5067,7 @@ static int hook_fixup(request_rec *r) + char *ofilename, *oargs; + int is_proxyreq; + void *skipdata; ++ rewriterule_entry *lastsub; + + dconf = (rewrite_perdir_conf *)ap_get_module_config(r->per_dir_config, + &rewrite_module); +@@ -5065,7 +5152,7 @@ static int hook_fixup(request_rec *r) + /* + * now apply the rules ... + */ +- rulestatus = apply_rewrite_list(r, dconf->rewriterules, dconf->directory); ++ rulestatus = apply_rewrite_list(r, dconf->rewriterules, dconf->directory, &lastsub); + if (rulestatus) { + unsigned skip_absolute = is_absolute_uri(r->filename, NULL); + int to_proxyreq = 0; +@@ -5094,6 +5181,9 @@ static int hook_fixup(request_rec *r) + r->status = HTTP_OK; + return n; + } ++ else if (ACTION_STATUS_SET == rulestatus) { ++ return r->status; ++ } + + if (to_proxyreq) { + /* it should go on as an internal proxy request */ +-- +2.33.0 + diff --git a/httpd.spec b/httpd.spec index 3ff73b70b050a421dcc4319a27cabed8d1835ce2..0d990016fa7f444aea501544ec9ab41a5b7a8e02 100644 --- a/httpd.spec +++ b/httpd.spec @@ -1,4 +1,4 @@ -%define anolis_release 2 +%define anolis_release 3 %define contentdir %{_datadir}/%{name} %define docroot /var/www %define suexec_caller apache @@ -87,6 +87,11 @@ Patch0022: httpd-2.4.51-r1894152.patch # modify for anolis Patch1000: 1000-httpd-anolis-rebrand.patch +#https://github.com/apache/httpd/commit/1feb5e04a4f7b5f3f13cd40f9635144319dcf24a +Patch1001: fix-cve-2024-38475.patch +#https://github.com/apache/httpd/commit/12542a80324b69ad6a1a489e1b697398551a5fe0 +Patch1002: fix-cve-2024-38472.patch + BuildRequires: gcc > 12.0 BuildRequires: autoconf BuildRequires: pkgconfig @@ -758,6 +763,9 @@ exit $rv %changelog +* Fri Aug 02 2024 yangxinyu - 2.4.58-3 +- fix-cve-2024-38475/38472 + * Thu Dec 21 2023 mgb01105731 - 2.4.58-2 - rebuild