diff --git a/.gitignore b/.gitignore index 260a2d6..9969f1d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1 @@ SOURCES/httpd-2.4.6.tar.bz2 -SOURCES/centos-noindex.tar.gz diff --git a/.httpd.metadata b/.httpd.metadata index 17ede1b..d335a99 100644 --- a/.httpd.metadata +++ b/.httpd.metadata @@ -1,2 +1 @@ 16d8ec72535ded65d035122b0d944b0e64eaa2a2 SOURCES/httpd-2.4.6.tar.bz2 -6ce5ab3c765b9efeceb2e636e32373bc6e6ed489 SOURCES/centos-noindex.tar.gz diff --git a/SOURCES/httpd-2.4.6-CVE-2023-25690.patch b/SOURCES/httpd-2.4.6-CVE-2023-25690.patch new file mode 100644 index 0000000..db8498b --- /dev/null +++ b/SOURCES/httpd-2.4.6-CVE-2023-25690.patch @@ -0,0 +1,232 @@ +diff --git a/modules/mappers/mod_rewrite.c b/modules/mappers/mod_rewrite.c +index b89d3e4..19f70d1 100644 +--- a/modules/mappers/mod_rewrite.c ++++ b/modules/mappers/mod_rewrite.c +@@ -166,6 +166,7 @@ static const char* really_last_key = "rewrite_really_last"; + #define RULEFLAG_DISCARDPATHINFO 1<<15 + #define RULEFLAG_QSDISCARD 1<<16 + #define RULEFLAG_END 1<<17 ++#define RULEFLAG_QSNONE (1<<20) /* programattic only */ + + /* return code of the rewrite rule + * the result may be escaped - or not +@@ -725,10 +726,18 @@ static char *escape_absolute_uri(apr_pool_t *p, char *uri, unsigned scheme) + * split out a QUERY_STRING part from + * the current URI string + */ +-static void splitout_queryargs(request_rec *r, int qsappend, int qsdiscard) ++static void splitout_queryargs(request_rec *r, int flags) + { + char *q; + int split; ++ int qsappend = flags & RULEFLAG_QSAPPEND; ++ int qsdiscard = flags & RULEFLAG_QSDISCARD; ++ ++ if (flags & RULEFLAG_QSNONE) { ++ rewritelog((r, 2, NULL, "discarding query string, no parse from substitution")); ++ r->args = NULL; ++ return; ++ } + + /* don't touch, unless it's a scheme for which a query string makes sense. + * See RFC 1738 and RFC 2368. +@@ -2661,7 +2670,7 @@ static apr_status_t rewritelock_remove(void *data) + * XXX: what an inclined parser. Seems we have to leave it so + * for backwards compat. *sigh* + */ +-static int parseargline(char *str, char **a1, char **a2, char **a3) ++static int parseargline(char *str, char **a1, char **a2, char **a2_end, char **a3) + { + char quote; + +@@ -2712,8 +2721,10 @@ static int parseargline(char *str, char **a1, char **a2, char **a3) + + if (!*str) { + *a3 = NULL; /* 3rd argument is optional */ ++ *a2_end = str; + return 0; + } ++ *a2_end = str; + *str++ = '\0'; + + while (apr_isspace(*str)) { +@@ -3230,6 +3241,7 @@ static const char *cmd_rewritecond(cmd_parms *cmd, void *in_dconf, + ap_regex_t *regexp; + char *a1; + char *a2; ++ char *a2_end; + char *a3; + const char *err; + +@@ -3248,7 +3260,7 @@ static const char *cmd_rewritecond(cmd_parms *cmd, void *in_dconf, + * of the argument line. So we can use a1 .. a3 without + * copying them again. + */ +- if (parseargline(str, &a1, &a2, &a3)) { ++ if (parseargline(str, &a1, &a2, &a2_end, &a3)) { + return apr_pstrcat(cmd->pool, "RewriteCond: bad argument line '", str, + "'", NULL); + } +@@ -3645,6 +3657,7 @@ static const char *cmd_rewriterule(cmd_parms *cmd, void *in_dconf, + ap_regex_t *regexp; + char *a1; + char *a2; ++ char *a2_end; + char *a3; + const char *err; + +@@ -3659,7 +3672,7 @@ static const char *cmd_rewriterule(cmd_parms *cmd, void *in_dconf, + } + + /* parse the argument line ourself */ +- if (parseargline(str, &a1, &a2, &a3)) { ++ if (parseargline(str, &a1, &a2, &a2_end, &a3)) { + return apr_pstrcat(cmd->pool, "RewriteRule: bad argument line '", str, + "'", NULL); + } +@@ -3705,6 +3718,16 @@ static const char *cmd_rewriterule(cmd_parms *cmd, void *in_dconf, + newrule->flags |= RULEFLAG_NOSUB; + } + ++ if (*(a2_end-1) == '?') { ++ /* a literal ? at the end of the unsubstituted rewrite rule */ ++ newrule->flags |= RULEFLAG_QSNONE; ++ } ++ else if (newrule->flags & RULEFLAG_QSDISCARD) { ++ if (NULL == ap_strchr(newrule->output, '?')) { ++ newrule->flags |= RULEFLAG_QSNONE; ++ } ++ } ++ + /* now, if the server or per-dir config holds an + * array of RewriteCond entries, we take it for us + * and clear the array +@@ -4110,7 +4133,7 @@ static int apply_rewrite_rule(rewriterule_entry *p, rewrite_ctx *ctx) + r->path_info = NULL; + } + +- splitout_queryargs(r, p->flags & RULEFLAG_QSAPPEND, p->flags & RULEFLAG_QSDISCARD); ++ splitout_queryargs(r, p->flags); + + /* Add the previously stripped per-directory location prefix, unless + * (1) it's an absolute URL path and +@@ -4565,6 +4588,17 @@ static int hook_uri2file(request_rec *r) + unsigned skip; + apr_size_t flen; + ++ if (r->args && *(ap_scan_vchar_obstext(r->args))) { ++ /* ++ * We have a raw control character or a ' ' in r->args. ++ * Correct encoding was missed. ++ */ ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10410) ++ "Rewritten query string contains control " ++ "characters or spaces"); ++ return HTTP_FORBIDDEN; ++ } ++ + if (ACTION_STATUS == rulestatus) { + int n = r->status; + +@@ -4833,6 +4867,17 @@ static int hook_fixup(request_rec *r) + if (rulestatus) { + unsigned skip; + ++ if (r->args && *(ap_scan_vchar_obstext(r->args))) { ++ /* ++ * We have a raw control character or a ' ' in r->args. ++ * Correct encoding was missed. ++ */ ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10411) ++ "Rewritten query string contains control " ++ "characters or spaces"); ++ return HTTP_FORBIDDEN; ++ } ++ + if (ACTION_STATUS == rulestatus) { + int n = r->status; + +diff --git a/modules/proxy/mod_proxy_ajp.c b/modules/proxy/mod_proxy_ajp.c +index 9b69a2e..a52d777 100644 +--- a/modules/proxy/mod_proxy_ajp.c ++++ b/modules/proxy/mod_proxy_ajp.c +@@ -69,6 +69,16 @@ static int proxy_ajp_canon(request_rec *r, char *url) + path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, + r->proxyreq); + search = r->args; ++ if (search && *(ap_scan_vchar_obstext(search))) { ++ /* ++ * We have a raw control character or a ' ' in r->args. ++ * Correct encoding was missed. ++ */ ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10406) ++ "To be forwarded query string contains control " ++ "characters or spaces"); ++ return HTTP_FORBIDDEN; ++ } + } + if (path == NULL) + return HTTP_BAD_REQUEST; +diff --git a/modules/proxy/mod_proxy_balancer.c b/modules/proxy/mod_proxy_balancer.c +index 4d9d2af..fa385c3 100644 +--- a/modules/proxy/mod_proxy_balancer.c ++++ b/modules/proxy/mod_proxy_balancer.c +@@ -94,6 +94,16 @@ static int proxy_balancer_canon(request_rec *r, char *url) + path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, + r->proxyreq); + search = r->args; ++ if (search && *(ap_scan_vchar_obstext(search))) { ++ /* ++ * We have a raw control character or a ' ' in r->args. ++ * Correct encoding was missed. ++ */ ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10407) ++ "To be forwarded query string contains control " ++ "characters or spaces"); ++ return HTTP_FORBIDDEN; ++ } + } + if (path == NULL) + return HTTP_BAD_REQUEST; +diff --git a/modules/proxy/mod_proxy_http.c b/modules/proxy/mod_proxy_http.c +index 6767c89..1a10d99 100644 +--- a/modules/proxy/mod_proxy_http.c ++++ b/modules/proxy/mod_proxy_http.c +@@ -87,6 +87,16 @@ static int proxy_http_canon(request_rec *r, char *url) + path = ap_proxy_canonenc(r->pool, url, strlen(url), + enc_path, 0, r->proxyreq); + search = r->args; ++ if (search && *(ap_scan_vchar_obstext(search))) { ++ /* ++ * We have a raw control character or a ' ' in r->args. ++ * Correct encoding was missed. ++ */ ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10408) ++ "To be forwarded query string contains control " ++ "characters or spaces"); ++ return HTTP_FORBIDDEN; ++ } + } + break; + case PROXYREQ_PROXY: +diff --git a/modules/proxy/mod_proxy_wstunnel.c b/modules/proxy/mod_proxy_wstunnel.c +index eb34eee..438d035 100644 +--- a/modules/proxy/mod_proxy_wstunnel.c ++++ b/modules/proxy/mod_proxy_wstunnel.c +@@ -73,6 +73,16 @@ static int proxy_wstunnel_canon(request_rec *r, char *url) + path = ap_proxy_canonenc(r->pool, url, strlen(url), enc_path, 0, + r->proxyreq); + search = r->args; ++ if (search && *(ap_scan_vchar_obstext(search))) { ++ /* ++ * We have a raw control character or a ' ' in r->args. ++ * Correct encoding was missed. ++ */ ++ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(10409) ++ "To be forwarded query string contains control " ++ "characters or spaces"); ++ return HTTP_FORBIDDEN; ++ } + } + if (path == NULL) + return HTTP_BAD_REQUEST; diff --git a/SOURCES/welcome.conf b/SOURCES/welcome.conf index c1b6c11..5d1e452 100644 --- a/SOURCES/welcome.conf +++ b/SOURCES/welcome.conf @@ -16,7 +16,3 @@ Alias /.noindex.html /usr/share/httpd/noindex/index.html -Alias /noindex/css/bootstrap.min.css /usr/share/httpd/noindex/css/bootstrap.min.css -Alias /noindex/css/open-sans.css /usr/share/httpd/noindex/css/open-sans.css -Alias /images/apache_pb.gif /usr/share/httpd/noindex/images/apache_pb.gif -Alias /images/poweredby.png /usr/share/httpd/noindex/images/poweredby.png diff --git a/SPECS/httpd.spec b/SPECS/httpd.spec index 83df334..4dfdd9c 100644 --- a/SPECS/httpd.spec +++ b/SPECS/httpd.spec @@ -4,7 +4,7 @@ %define mmn 20120211 %define oldmmnisa %{mmn}-%{__isa_name}-%{__isa_bits} %define mmnisa %{mmn}%{__isa_name}%{__isa_bits} -%define vstring CentOS +%define vstring %(source /etc/os-release; echo ${REDHAT_SUPPORT_PRODUCT}) # Drop automatic provides for module DSOs %{?filter_setup: @@ -15,10 +15,10 @@ Summary: Apache HTTP Server Name: httpd Version: 2.4.6 -Release: 98%{?dist}.6 +Release: 98%{?dist}.7 URL: http://httpd.apache.org/ Source0: http://www.apache.org/dist/httpd/httpd-%{version}.tar.bz2 -Source1: centos-noindex.tar.gz +Source1: index.html Source2: httpd.logrotate Source3: httpd.sysconf Source4: httpd-ssl-pass-dialog @@ -249,6 +249,7 @@ Patch243: httpd-2.4.6-CVE-2021-34798.patch Patch244: httpd-2.4.6-CVE-2021-39275.patch Patch245: httpd-2.4.6-CVE-2021-26691.patch Patch246: httpd-2.4.6-CVE-2022-22720.patch +Patch247: httpd-2.4.6-CVE-2023-25690.patch License: ASL 2.0 Group: System Environment/Daemons @@ -518,6 +519,7 @@ rm modules/ssl/ssl_engine_dh.c %patch244 -p1 -b .cve39275 %patch245 -p1 -b .cve26691 %patch246 -p1 -b .cve22720 +%patch247 -p1 -b .cve25690 # need to be applied in the end since security patches # are changing the code that present in this patch @@ -675,10 +677,8 @@ EOF # Handle contentdir mkdir $RPM_BUILD_ROOT%{contentdir}/noindex -tar xzf $RPM_SOURCE_DIR/centos-noindex.tar.gz \ - -C $RPM_BUILD_ROOT%{contentdir}/noindex/ \ - --strip-components=1 - +install -m 644 -p $RPM_SOURCE_DIR/index.html \ + $RPM_BUILD_ROOT%{contentdir}/noindex/index.html rm -rf %{contentdir}/htdocs # remove manual sources @@ -701,7 +701,7 @@ rm -v $RPM_BUILD_ROOT%{docroot}/html/*.html \ $RPM_BUILD_ROOT%{docroot}/cgi-bin/* # Symlink for the powered-by-$DISTRO image: -ln -s ../noindex/images/poweredby.png \ +ln -s ../../pixmaps/poweredby.png \ $RPM_BUILD_ROOT%{contentdir}/icons/poweredby.png # symlinks for /etc/httpd @@ -887,7 +887,7 @@ rm -rf $RPM_BUILD_ROOT %{contentdir}/error/README %{contentdir}/error/*.var %{contentdir}/error/include/*.html -%{contentdir}/noindex/* +%{contentdir}/noindex/index.html %dir %{docroot} %dir %{docroot}/cgi-bin @@ -953,11 +953,9 @@ rm -rf $RPM_BUILD_ROOT %{_sysconfdir}/rpm/macros.httpd %changelog -* Tue Jan 24 2023 CentOS Sources - 2.4.6-98.el7.centos.6 -- Remove index.html, add centos-noindex.tar.gz -- change vstring -- change symlink for poweredby.png -- update welcome.conf with proper aliases +* Tue Mar 21 2023 Luboš Uhliarik - 2.4.6-97.7 +- Resolves: #2177742 - CVE-2023-25690 httpd: HTTP request splitting with + mod_rewrite and mod_proxy * Wed Dec 07 2022 Luboš Uhliarik - 2.4.6-97.6 - Resolves: #2101997 - HEAD request with a 404 and custom ErrorPage causes