import UBI httpd-2.4.63-13.el10_2.4
This commit is contained in:
parent
ee8c91751e
commit
192c38ea60
303
httpd-2.4.63-CVE-2024-42516.patch
Normal file
303
httpd-2.4.63-CVE-2024-42516.patch
Normal file
@ -0,0 +1,303 @@
|
||||
From a7a9d814c7c23e990283277230ddd5a9efec27c7 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Covener <covener@apache.org>
|
||||
Date: Mon, 7 Jul 2025 11:59:38 +0000
|
||||
Subject: [PATCH] fix header merging
|
||||
|
||||
Reviewed By: rpluem, jorton, ylavic
|
||||
|
||||
|
||||
|
||||
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1927039 13f79535-47bb-0310-9956-ffa450edef68
|
||||
---
|
||||
modules/http/http_filters.c | 248 +++++++++++++++++++-----------------
|
||||
1 file changed, 128 insertions(+), 120 deletions(-)
|
||||
|
||||
diff --git a/modules/http/http_filters.c b/modules/http/http_filters.c
|
||||
index 60b44d78580..732fb8eb6a7 100644
|
||||
--- a/modules/http/http_filters.c
|
||||
+++ b/modules/http/http_filters.c
|
||||
@@ -1300,107 +1300,10 @@ typedef struct header_filter_ctx {
|
||||
int headers_sent;
|
||||
} header_filter_ctx;
|
||||
|
||||
-AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
|
||||
- apr_bucket_brigade *b)
|
||||
+static void merge_response_headers(request_rec *r, const char **protocol)
|
||||
{
|
||||
- request_rec *r = f->r;
|
||||
- conn_rec *c = r->connection;
|
||||
- const char *clheader;
|
||||
- int header_only = (r->header_only || AP_STATUS_IS_HEADER_ONLY(r->status));
|
||||
- const char *protocol = NULL;
|
||||
- apr_bucket *e;
|
||||
- apr_bucket_brigade *b2;
|
||||
- header_struct h;
|
||||
- header_filter_ctx *ctx = f->ctx;
|
||||
- const char *ctype;
|
||||
- ap_bucket_error *eb = NULL;
|
||||
- apr_status_t rv = APR_SUCCESS;
|
||||
- int recursive_error = 0;
|
||||
-
|
||||
- AP_DEBUG_ASSERT(!r->main);
|
||||
-
|
||||
- if (!ctx) {
|
||||
- ctx = f->ctx = apr_pcalloc(r->pool, sizeof(header_filter_ctx));
|
||||
- }
|
||||
- else if (ctx->headers_sent) {
|
||||
- /* Eat body if response must not have one. */
|
||||
- if (header_only) {
|
||||
- /* Still next filters may be waiting for EOS, so pass it (alone)
|
||||
- * when encountered and be done with this filter.
|
||||
- */
|
||||
- e = APR_BRIGADE_LAST(b);
|
||||
- if (e != APR_BRIGADE_SENTINEL(b) && APR_BUCKET_IS_EOS(e)) {
|
||||
- APR_BUCKET_REMOVE(e);
|
||||
- apr_brigade_cleanup(b);
|
||||
- APR_BRIGADE_INSERT_HEAD(b, e);
|
||||
- ap_remove_output_filter(f);
|
||||
- rv = ap_pass_brigade(f->next, b);
|
||||
- }
|
||||
- apr_brigade_cleanup(b);
|
||||
- return rv;
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- for (e = APR_BRIGADE_FIRST(b);
|
||||
- e != APR_BRIGADE_SENTINEL(b);
|
||||
- e = APR_BUCKET_NEXT(e))
|
||||
- {
|
||||
- if (AP_BUCKET_IS_ERROR(e) && !eb) {
|
||||
- eb = e->data;
|
||||
- continue;
|
||||
- }
|
||||
- /*
|
||||
- * If we see an EOC bucket it is a signal that we should get out
|
||||
- * of the way doing nothing.
|
||||
- */
|
||||
- if (AP_BUCKET_IS_EOC(e)) {
|
||||
- ap_remove_output_filter(f);
|
||||
- return ap_pass_brigade(f->next, b);
|
||||
- }
|
||||
- }
|
||||
-
|
||||
- if (!ctx->headers_sent && !check_headers(r)) {
|
||||
- /* We may come back here from ap_die() below,
|
||||
- * so clear anything from this response.
|
||||
- */
|
||||
- apr_table_clear(r->headers_out);
|
||||
- apr_table_clear(r->err_headers_out);
|
||||
- r->content_type = r->content_encoding = NULL;
|
||||
- r->content_languages = NULL;
|
||||
- r->clength = r->chunked = 0;
|
||||
- apr_brigade_cleanup(b);
|
||||
-
|
||||
- /* Don't recall ap_die() if we come back here (from its own internal
|
||||
- * redirect or error response), otherwise we can end up in infinite
|
||||
- * recursion; better fall through with 500, minimal headers and an
|
||||
- * empty body (EOS only).
|
||||
- */
|
||||
- if (!check_headers_recursion(r)) {
|
||||
- ap_die(HTTP_INTERNAL_SERVER_ERROR, r);
|
||||
- return AP_FILTER_ERROR;
|
||||
- }
|
||||
- r->status = HTTP_INTERNAL_SERVER_ERROR;
|
||||
- e = ap_bucket_eoc_create(c->bucket_alloc);
|
||||
- APR_BRIGADE_INSERT_TAIL(b, e);
|
||||
- e = apr_bucket_eos_create(c->bucket_alloc);
|
||||
- APR_BRIGADE_INSERT_TAIL(b, e);
|
||||
- ap_set_content_length(r, 0);
|
||||
- recursive_error = 1;
|
||||
- }
|
||||
- else if (eb) {
|
||||
- int status;
|
||||
- status = eb->status;
|
||||
- apr_brigade_cleanup(b);
|
||||
- ap_die(status, r);
|
||||
- return AP_FILTER_ERROR;
|
||||
- }
|
||||
-
|
||||
- if (r->assbackwards) {
|
||||
- r->sent_bodyct = 1;
|
||||
- ap_remove_output_filter(f);
|
||||
- rv = ap_pass_brigade(f->next, b);
|
||||
- goto out;
|
||||
- }
|
||||
+ const char *ctype = NULL;
|
||||
+ const char *clheader = NULL;
|
||||
|
||||
/*
|
||||
* Now that we are ready to send a response, we need to combine the two
|
||||
@@ -1430,6 +1333,9 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
|
||||
fixup_vary(r);
|
||||
}
|
||||
|
||||
+ /* determine the protocol and whether we should use keepalives. */
|
||||
+ basic_http_header_check(r, protocol);
|
||||
+ ap_set_keepalive(r);
|
||||
|
||||
/*
|
||||
* Control cachability for non-cacheable responses if not already set by
|
||||
@@ -1449,10 +1355,6 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
|
||||
apr_table_unset(r->headers_out, "ETag");
|
||||
}
|
||||
|
||||
- /* determine the protocol and whether we should use keepalives. */
|
||||
- basic_http_header_check(r, &protocol);
|
||||
- ap_set_keepalive(r);
|
||||
-
|
||||
/* 204/304 responses don't have content related headers */
|
||||
if (AP_STATUS_IS_HEADER_ONLY(r->status)) {
|
||||
apr_table_unset(r->headers_out, "Transfer-Encoding");
|
||||
@@ -1513,30 +1415,136 @@ AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
|
||||
&& !strcmp(clheader, "0")) {
|
||||
apr_table_unset(r->headers_out, "Content-Length");
|
||||
}
|
||||
+}
|
||||
|
||||
- b2 = apr_brigade_create(r->pool, c->bucket_alloc);
|
||||
- basic_http_header(r, b2, protocol);
|
||||
-
|
||||
- h.pool = r->pool;
|
||||
- h.bb = b2;
|
||||
+AP_CORE_DECLARE_NONSTD(apr_status_t) ap_http_header_filter(ap_filter_t *f,
|
||||
+ apr_bucket_brigade *b)
|
||||
+{
|
||||
+ request_rec *r = f->r;
|
||||
+ conn_rec *c = r->connection;
|
||||
+ int header_only = (r->header_only || AP_STATUS_IS_HEADER_ONLY(r->status));
|
||||
+ apr_bucket *e;
|
||||
+ apr_bucket_brigade *b2;
|
||||
+ header_struct h;
|
||||
+ header_filter_ctx *ctx = f->ctx;
|
||||
+ ap_bucket_error *eb = NULL;
|
||||
+ apr_status_t rv = APR_SUCCESS;
|
||||
+ int recursive_error = 0;
|
||||
+ const char *protocol;
|
||||
|
||||
- send_all_header_fields(&h, r);
|
||||
+ AP_DEBUG_ASSERT(!r->main);
|
||||
|
||||
- terminate_header(b2);
|
||||
+ if (!ctx) {
|
||||
+ ctx = f->ctx = apr_pcalloc(r->pool, sizeof(header_filter_ctx));
|
||||
+ }
|
||||
+ else if (ctx->headers_sent) {
|
||||
+ /* Eat body if response must not have one. */
|
||||
+ if (header_only) {
|
||||
+ /* Still next filters may be waiting for EOS, so pass it (alone)
|
||||
+ * when encountered and be done with this filter.
|
||||
+ */
|
||||
+ e = APR_BRIGADE_LAST(b);
|
||||
+ if (e != APR_BRIGADE_SENTINEL(b) && APR_BUCKET_IS_EOS(e)) {
|
||||
+ APR_BUCKET_REMOVE(e);
|
||||
+ apr_brigade_cleanup(b);
|
||||
+ APR_BRIGADE_INSERT_HEAD(b, e);
|
||||
+ ap_remove_output_filter(f);
|
||||
+ rv = ap_pass_brigade(f->next, b);
|
||||
+ }
|
||||
+ apr_brigade_cleanup(b);
|
||||
+ return rv;
|
||||
+ }
|
||||
+ }
|
||||
|
||||
- if (header_only) {
|
||||
- e = APR_BRIGADE_LAST(b);
|
||||
- if (e != APR_BRIGADE_SENTINEL(b) && APR_BUCKET_IS_EOS(e)) {
|
||||
- APR_BUCKET_REMOVE(e);
|
||||
- APR_BRIGADE_INSERT_TAIL(b2, e);
|
||||
+ for (e = APR_BRIGADE_FIRST(b);
|
||||
+ e != APR_BRIGADE_SENTINEL(b);
|
||||
+ e = APR_BUCKET_NEXT(e))
|
||||
+ {
|
||||
+ if (AP_BUCKET_IS_ERROR(e) && !eb) {
|
||||
+ eb = e->data;
|
||||
+ continue;
|
||||
+ }
|
||||
+ /*
|
||||
+ * If we see an EOC bucket it is a signal that we should get out
|
||||
+ * of the way doing nothing.
|
||||
+ */
|
||||
+ if (AP_BUCKET_IS_EOC(e)) {
|
||||
ap_remove_output_filter(f);
|
||||
+ return ap_pass_brigade(f->next, b);
|
||||
+ }
|
||||
+ }
|
||||
+
|
||||
+ if (!ctx->headers_sent) {
|
||||
+ merge_response_headers(r, &protocol);
|
||||
+ if (!check_headers(r)) {
|
||||
+ /* We may come back here from ap_die() below,
|
||||
+ * so clear anything from this response.
|
||||
+ */
|
||||
+ apr_table_clear(r->headers_out);
|
||||
+ apr_table_clear(r->err_headers_out);
|
||||
+ r->content_type = r->content_encoding = NULL;
|
||||
+ r->content_languages = NULL;
|
||||
+ r->clength = r->chunked = 0;
|
||||
+ apr_brigade_cleanup(b);
|
||||
+
|
||||
+ /* Don't recall ap_die() if we come back here (from its own internal
|
||||
+ * redirect or error response), otherwise we can end up in infinite
|
||||
+ * recursion; better fall through with 500, minimal headers and an
|
||||
+ * empty body (EOS only).
|
||||
+ */
|
||||
+ if (!check_headers_recursion(r)) {
|
||||
+ ap_die(HTTP_INTERNAL_SERVER_ERROR, r);
|
||||
+ return AP_FILTER_ERROR;
|
||||
+ }
|
||||
+ r->status = HTTP_INTERNAL_SERVER_ERROR;
|
||||
+ e = ap_bucket_eoc_create(c->bucket_alloc);
|
||||
+ APR_BRIGADE_INSERT_TAIL(b, e);
|
||||
+ e = apr_bucket_eos_create(c->bucket_alloc);
|
||||
+ APR_BRIGADE_INSERT_TAIL(b, e);
|
||||
+ ap_set_content_length(r, 0);
|
||||
+ recursive_error = 1;
|
||||
+ }
|
||||
+ else if (eb) {
|
||||
+ int status;
|
||||
+ status = eb->status;
|
||||
+ apr_brigade_cleanup(b);
|
||||
+ ap_die(status, r);
|
||||
+ return AP_FILTER_ERROR;
|
||||
}
|
||||
- apr_brigade_cleanup(b);
|
||||
}
|
||||
|
||||
- rv = ap_pass_brigade(f->next, b2);
|
||||
- apr_brigade_cleanup(b2);
|
||||
- ctx->headers_sent = 1;
|
||||
+ if (r->assbackwards) {
|
||||
+ r->sent_bodyct = 1;
|
||||
+ ap_remove_output_filter(f);
|
||||
+ rv = ap_pass_brigade(f->next, b);
|
||||
+ goto out;
|
||||
+ }
|
||||
+
|
||||
+ if (!ctx->headers_sent) {
|
||||
+ b2 = apr_brigade_create(r->pool, c->bucket_alloc);
|
||||
+ basic_http_header(r, b2, protocol);
|
||||
+
|
||||
+ h.pool = r->pool;
|
||||
+ h.bb = b2;
|
||||
+
|
||||
+ send_all_header_fields(&h, r);
|
||||
+
|
||||
+ terminate_header(b2);
|
||||
+
|
||||
+ if (header_only) {
|
||||
+ e = APR_BRIGADE_LAST(b);
|
||||
+ if (e != APR_BRIGADE_SENTINEL(b) && APR_BUCKET_IS_EOS(e)) {
|
||||
+ APR_BUCKET_REMOVE(e);
|
||||
+ APR_BRIGADE_INSERT_TAIL(b2, e);
|
||||
+ ap_remove_output_filter(f);
|
||||
+ }
|
||||
+ apr_brigade_cleanup(b);
|
||||
+ }
|
||||
+
|
||||
+ rv = ap_pass_brigade(f->next, b2);
|
||||
+ apr_brigade_cleanup(b2);
|
||||
+ ctx->headers_sent = 1;
|
||||
+ }
|
||||
|
||||
if (rv != APR_SUCCESS || header_only) {
|
||||
goto out;
|
||||
80
httpd-2.4.63-CVE-2026-24072.patch
Normal file
80
httpd-2.4.63-CVE-2026-24072.patch
Normal file
@ -0,0 +1,80 @@
|
||||
diff --git a/modules/mappers/mod_rewrite.c b/modules/mappers/mod_rewrite.c
|
||||
index 93430e5..c718f6f 100644
|
||||
--- a/modules/mappers/mod_rewrite.c
|
||||
+++ b/modules/mappers/mod_rewrite.c
|
||||
@@ -3673,12 +3673,17 @@ static const char *cmd_rewritecond(cmd_parms *cmd, void *in_dconf,
|
||||
newcond->regexp = regexp;
|
||||
}
|
||||
else if (newcond->ptype == CONDPAT_AP_EXPR) {
|
||||
+ int in_htaccess = cmd->pool == cmd->temp_pool;
|
||||
unsigned int flags = newcond->flags & CONDFLAG_NOVARY ?
|
||||
AP_EXPR_FLAG_DONT_VARY : 0;
|
||||
+ /* Use restricted ap_expr() parser in htaccess context. */
|
||||
+ if (in_htaccess) flags |= AP_EXPR_FLAG_RESTRICTED;
|
||||
newcond->expr = ap_expr_parse_cmd(cmd, a2, flags, &err, NULL);
|
||||
if (err)
|
||||
return apr_psprintf(cmd->pool, "RewriteCond: cannot compile "
|
||||
- "expression \"%s\": %s", a2, err);
|
||||
+ "expression%s \"%s\" %s",
|
||||
+ in_htaccess ? " in htaccess context" : "",
|
||||
+ a2, err);
|
||||
}
|
||||
|
||||
return NULL;
|
||||
diff --git a/modules/metadata/mod_setenvif.c b/modules/metadata/mod_setenvif.c
|
||||
index 23d60cd..b74c9c0 100644
|
||||
--- a/modules/metadata/mod_setenvif.c
|
||||
+++ b/modules/metadata/mod_setenvif.c
|
||||
@@ -422,6 +422,12 @@ static const char *add_setenvifexpr(cmd_parms *cmd, void *mconfig,
|
||||
sei_cfg_rec *sconf;
|
||||
sei_entry *new;
|
||||
const char *err;
|
||||
+ unsigned int flags = 0;
|
||||
+
|
||||
+ /* Use restricted ap_expr() parser in htaccess context. */
|
||||
+ if (cmd->pool == cmd->temp_pool) {
|
||||
+ flags |= AP_EXPR_FLAG_RESTRICTED;
|
||||
+ }
|
||||
|
||||
/*
|
||||
* Determine from our context into which record to put the entry.
|
||||
@@ -445,7 +451,7 @@ static const char *add_setenvifexpr(cmd_parms *cmd, void *mconfig,
|
||||
new->regex = NULL;
|
||||
new->pattern = NULL;
|
||||
new->preg = NULL;
|
||||
- new->expr = ap_expr_parse_cmd(cmd, expr, 0, &err, NULL);
|
||||
+ new->expr = ap_expr_parse_cmd(cmd, expr, flags, &err, NULL);
|
||||
if (err)
|
||||
return apr_psprintf(cmd->pool, "Could not parse expression \"%s\": %s",
|
||||
expr, err);
|
||||
diff --git a/modules/proxy/mod_proxy_fcgi.c b/modules/proxy/mod_proxy_fcgi.c
|
||||
index 128cf1e..ef090dd 100644
|
||||
--- a/modules/proxy/mod_proxy_fcgi.c
|
||||
+++ b/modules/proxy/mod_proxy_fcgi.c
|
||||
@@ -1338,9 +1338,15 @@ static const char *cmd_setenv(cmd_parms *cmd, void *in_dconf,
|
||||
const char *err;
|
||||
sei_entry *new;
|
||||
const char *envvar = arg2;
|
||||
+ unsigned int flags = 0;
|
||||
+
|
||||
+ /* Use restricted ap_expr() parser in htaccess context. */
|
||||
+ if (cmd->pool == cmd->temp_pool) {
|
||||
+ flags |= AP_EXPR_FLAG_RESTRICTED;
|
||||
+ }
|
||||
|
||||
new = apr_array_push(dconf->env_fixups);
|
||||
- new->cond = ap_expr_parse_cmd(cmd, arg1, 0, &err, NULL);
|
||||
+ new->cond = ap_expr_parse_cmd(cmd, arg1, flags, &err, NULL);
|
||||
if (err) {
|
||||
return apr_psprintf(cmd->pool, "Could not parse expression \"%s\": %s",
|
||||
arg1, err);
|
||||
@@ -1367,7 +1373,8 @@ static const char *cmd_setenv(cmd_parms *cmd, void *in_dconf,
|
||||
arg3 = "";
|
||||
}
|
||||
|
||||
- new->subst = ap_expr_parse_cmd(cmd, arg3, AP_EXPR_FLAG_STRING_RESULT, &err, NULL);
|
||||
+ flags |= AP_EXPR_FLAG_STRING_RESULT;
|
||||
+ new->subst = ap_expr_parse_cmd(cmd, arg3, flags, &err, NULL);
|
||||
if (err) {
|
||||
return apr_psprintf(cmd->pool, "Could not parse expression \"%s\": %s",
|
||||
arg3, err);
|
||||
38
httpd-2.4.63-CVE-2026-29169.patch
Normal file
38
httpd-2.4.63-CVE-2026-29169.patch
Normal file
@ -0,0 +1,38 @@
|
||||
From 225dc070adba11040b774cf641e1d8bc79941643 Mon Sep 17 00:00:00 2001
|
||||
From: Eric Covener <covener@apache.org>
|
||||
Date: Sun, 26 Apr 2026 16:04:35 +0000
|
||||
Subject: [PATCH] Merge r1933353 from trunk:
|
||||
|
||||
mod_dav_lock: use the right dav_lock_discovery
|
||||
|
||||
|
||||
|
||||
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1933354 13f79535-47bb-0310-9956-ffa450edef68
|
||||
---
|
||||
modules/dav/lock/locks.c | 12 ++++++------
|
||||
1 file changed, 6 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/modules/dav/lock/locks.c b/modules/dav/lock/locks.c
|
||||
index 0f072ec0647..d357e4572d1 100644
|
||||
--- a/modules/dav/lock/locks.c
|
||||
+++ b/modules/dav/lock/locks.c
|
||||
@@ -1190,13 +1190,13 @@ static dav_error * dav_generic_refresh_locks(dav_lockdb *lockdb,
|
||||
}
|
||||
if (dav_generic_do_refresh(dp_scan, ltl, new_time)) {
|
||||
/* the lock was refreshed. return the lock. */
|
||||
- newlock = dav_generic_alloc_lock(lockdb, ip->key, dp->locktoken);
|
||||
+ newlock = dav_generic_alloc_lock(lockdb, ip->key, dp_scan->locktoken);
|
||||
newlock->is_locknull = !resource->exists;
|
||||
- newlock->scope = dp->f.scope;
|
||||
- newlock->type = dp->f.type;
|
||||
- newlock->depth = dp->f.depth;
|
||||
- newlock->timeout = dp->f.timeout;
|
||||
- newlock->owner = dp->owner;
|
||||
+ newlock->scope = dp_scan->f.scope;
|
||||
+ newlock->type = dp_scan->f.type;
|
||||
+ newlock->depth = dp_scan->f.depth;
|
||||
+ newlock->timeout = dp_scan->f.timeout;
|
||||
+ newlock->owner = dp_scan->owner;
|
||||
newlock->auth_user = dp_scan->auth_user;
|
||||
|
||||
newlock->next = *locks;
|
||||
349
httpd-2.4.63-CVE-2026-33006.patch
Normal file
349
httpd-2.4.63-CVE-2026-33006.patch
Normal file
@ -0,0 +1,349 @@
|
||||
diff --git a/include/httpd.h b/include/httpd.h
|
||||
index 6c7484f..5a3cce4 100644
|
||||
--- a/include/httpd.h
|
||||
+++ b/include/httpd.h
|
||||
@@ -2125,6 +2125,54 @@ AP_DECLARE(int) ap_ind(const char *str, char c); /* Sigh... */
|
||||
*/
|
||||
AP_DECLARE(int) ap_rind(const char *str, char c);
|
||||
|
||||
+/**
|
||||
+ * Check whether two buffers of equal size have the same content, using a
|
||||
+ * constant time algorithm (branch-less with regard to the content of the
|
||||
+ * buffers and an execution time solely dependent on the number of bytes
|
||||
+ * compared, not the bytes themselves).
|
||||
+ *
|
||||
+ * @param buf1 first buffer to compare
|
||||
+ * @param buf2 second buffer to compare
|
||||
+ * @param n number of bytes to compare
|
||||
+ * @return 1 if equal, 0 otherwise
|
||||
+ */
|
||||
+AP_DECLARE(int) ap_memeq_timingsafe(const void *buf1, const void *buf2,
|
||||
+ apr_size_t n);
|
||||
+
|
||||
+/**
|
||||
+ * Check whether two NUL-terminated strings have the same content, using a
|
||||
+ * constant time algorithm (branch-less with regard to the content of the
|
||||
+ * secret string and an execution time solely dependent on the length of
|
||||
+ * the non-secret string). The secret string of the two should be set in
|
||||
+ * the first parameter \c sec1 to avoid leaking its length.
|
||||
+ *
|
||||
+ * @param sec1 first string to compare (the secret one)
|
||||
+ * @param str2 second string to compare
|
||||
+ * @return 1 if equal, 0 otherwise
|
||||
+ * @remark The function will compare as much characters as there are in
|
||||
+ * \c str2, so the length of \c str2 might leak through side channel,
|
||||
+ * while the length of \c sec1 does not.
|
||||
+ */
|
||||
+AP_DECLARE(int) ap_streq_timingsafe(const char *sec1, const char *str2);
|
||||
+
|
||||
+/**
|
||||
+ * Check whether two NUL-terminated strings have the same content, up to \c n
|
||||
+ * characters, using a constant time algorithm (branch-less with regard to the
|
||||
+ * content of the secret string and an execution time solely dependent on the
|
||||
+ * length of the non-secret string or \c n). The secret string of the two
|
||||
+ * should be set in the first parameter \c sec1 to avoid leaking its length.
|
||||
+ *
|
||||
+ * @param sec1 secret string to compare
|
||||
+ * @param str2 string to compare with
|
||||
+ * @param n max number of characters to compare
|
||||
+ * @return 1 if equal, 0 otherwise
|
||||
+ * @remark The function will compare as much characters as there are in
|
||||
+ * \c str2 if it's less than \c n, so the length of \c str2 might
|
||||
+ * leak through side channel, while the length of \c sec1 does not.
|
||||
+ */
|
||||
+AP_DECLARE(int) ap_strneq_timingsafe(const char *sec1, const char *str2,
|
||||
+ apr_size_t n);
|
||||
+
|
||||
/**
|
||||
* Given a string, replace any bare " with \\" .
|
||||
* @param p The pool to allocate memory from
|
||||
diff --git a/modules/aaa/mod_auth_digest.c b/modules/aaa/mod_auth_digest.c
|
||||
index 791cec2..f10af0d 100644
|
||||
--- a/modules/aaa/mod_auth_digest.c
|
||||
+++ b/modules/aaa/mod_auth_digest.c
|
||||
@@ -100,10 +100,17 @@ typedef struct digest_config_struct {
|
||||
#define DFLT_NONCE_LIFE apr_time_from_sec(300)
|
||||
#define NEXTNONCE_DELTA apr_time_from_sec(30)
|
||||
|
||||
-
|
||||
+/* The server nonce has fixed length and is the concatenation of:
|
||||
+ * base64(apr_time_t timestamp) + hex(SHA1(realm+time[+opaque])) */
|
||||
#define NONCE_TIME_LEN (((sizeof(apr_time_t)+2)/3)*4)
|
||||
#define NONCE_HASH_LEN (2*APR_SHA1_DIGESTSIZE)
|
||||
#define NONCE_LEN (int )(NONCE_TIME_LEN + NONCE_HASH_LEN)
|
||||
+/* Evaluates to true if nonce string is valid. Since the time part of
|
||||
+ * the nonce is a base64 encoding of an apr_time_t (8 bytes), it
|
||||
+ * must end with a '='. */
|
||||
+#define VALID_NONCE(n_) ((n_) && strlen((n_)) == NONCE_LEN && (n_)[NONCE_TIME_LEN - 1] == '=')
|
||||
+
|
||||
+#define MD5_DIGEST_LEN (2*APR_MD5_DIGESTSIZE) /* ignoring trailing \0 */
|
||||
|
||||
#define SECRET_LEN 20
|
||||
#define RETAINED_DATA_ID "mod_auth_digest"
|
||||
@@ -1017,8 +1024,9 @@ static int get_digest_rec(request_rec *r, digest_header_rec *resp)
|
||||
resp->nonce_count = apr_pstrdup(r->pool, value);
|
||||
}
|
||||
|
||||
- if (!resp->username || !resp->realm || !resp->nonce || !resp->uri
|
||||
- || !resp->digest
|
||||
+ if (!resp->username || !resp->realm || !resp->uri
|
||||
+ || !VALID_NONCE(resp->nonce)
|
||||
+ || !resp->digest || strlen(resp->digest) != MD5_DIGEST_LEN
|
||||
|| (resp->message_qop && (!resp->cnonce || !resp->nonce_count))) {
|
||||
resp->auth_hdr_sts = INVALID;
|
||||
return !OK;
|
||||
@@ -1070,14 +1078,9 @@ static int parse_hdr_and_update_nc(request_rec *r)
|
||||
}
|
||||
|
||||
|
||||
-/*
|
||||
- * Nonce generation code
|
||||
- */
|
||||
-
|
||||
-/* The hash part of the nonce is a SHA-1 hash of the time, realm, server host
|
||||
- * and port, opaque, and our secret.
|
||||
- */
|
||||
-static void gen_nonce_hash(char *hash, const char *timestr, const char *opaque,
|
||||
+/* Writes the hash part of the server nonce to hash, which must be of
|
||||
+ * minimum size (NONCE_HASH_LEN+1). */
|
||||
+static void gen_nonce_hash(char hash[NONCE_HASH_LEN+1], const char *timestr, const char *opaque,
|
||||
const server_rec *server,
|
||||
const digest_config_rec *conf)
|
||||
{
|
||||
@@ -1426,19 +1429,6 @@ static int check_nonce(request_rec *r, digest_header_rec *resp,
|
||||
time_rec nonce_time;
|
||||
char tmp, hash[NONCE_HASH_LEN+1];
|
||||
|
||||
- /* Since the time part of the nonce is a base64 encoding of an
|
||||
- * apr_time_t (8 bytes), it should end with a '=', fail early otherwise.
|
||||
- */
|
||||
- if (strlen(resp->nonce) != NONCE_LEN
|
||||
- || resp->nonce[NONCE_TIME_LEN - 1] != '=') {
|
||||
- ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01775)
|
||||
- "invalid nonce '%s' received - length is not %d "
|
||||
- "or time encoding is incorrect",
|
||||
- resp->nonce, NONCE_LEN);
|
||||
- note_digest_auth_failure(r, conf, resp, 1);
|
||||
- return HTTP_UNAUTHORIZED;
|
||||
- }
|
||||
-
|
||||
tmp = resp->nonce[NONCE_TIME_LEN];
|
||||
resp->nonce[NONCE_TIME_LEN] = '\0';
|
||||
apr_base64_decode_binary(nonce_time.arr, resp->nonce);
|
||||
@@ -1446,7 +1436,7 @@ static int check_nonce(request_rec *r, digest_header_rec *resp,
|
||||
resp->nonce[NONCE_TIME_LEN] = tmp;
|
||||
resp->nonce_time = nonce_time.time;
|
||||
|
||||
- if (strcmp(hash, resp->nonce+NONCE_TIME_LEN)) {
|
||||
+ if (!ap_memeq_timingsafe(hash, resp->nonce+NONCE_TIME_LEN, NONCE_HASH_LEN)) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01776)
|
||||
"invalid nonce %s received - hash is not %s",
|
||||
resp->nonce, hash);
|
||||
@@ -1796,7 +1786,7 @@ static int authenticate_digest_user(request_rec *r)
|
||||
|
||||
if (resp->message_qop == NULL) {
|
||||
/* old (rfc-2069) style digest */
|
||||
- if (strcmp(resp->digest, old_digest(r, resp))) {
|
||||
+ if (!ap_memeq_timingsafe(old_digest(r, resp), resp->digest, MD5_DIGEST_LEN)) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01792)
|
||||
"user %s: password mismatch: %s", r->user,
|
||||
r->uri);
|
||||
@@ -1831,7 +1821,7 @@ static int authenticate_digest_user(request_rec *r)
|
||||
/* we failed to allocate a client struct */
|
||||
return HTTP_INTERNAL_SERVER_ERROR;
|
||||
}
|
||||
- if (strcmp(resp->digest, exp_digest)) {
|
||||
+ if (!ap_memeq_timingsafe(exp_digest, resp->digest, MD5_DIGEST_LEN)) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, APLOGNO(01794)
|
||||
"user %s: password mismatch: %s", r->user,
|
||||
r->uri);
|
||||
diff --git a/modules/session/mod_session_crypto.c b/modules/session/mod_session_crypto.c
|
||||
index fe39f2c..b33847b 100644
|
||||
--- a/modules/session/mod_session_crypto.c
|
||||
+++ b/modules/session/mod_session_crypto.c
|
||||
@@ -69,8 +69,6 @@ typedef struct {
|
||||
#define AP_SIPHASH_KSIZE APR_SIPHASH_KSIZE
|
||||
#define ap_siphash24_auth apr_siphash24_auth
|
||||
|
||||
-#define ap_crypto_equals apr_crypto_equals
|
||||
-
|
||||
#else
|
||||
|
||||
#define AP_SIPHASH_DSIZE 8
|
||||
@@ -165,21 +163,6 @@ static void ap_siphash24_auth(unsigned char out[AP_SIPHASH_DSIZE],
|
||||
U64TO8_LE(out, h);
|
||||
}
|
||||
|
||||
-static int ap_crypto_equals(const void *buf1, const void *buf2,
|
||||
- apr_size_t size)
|
||||
-{
|
||||
- const unsigned char *p1 = buf1;
|
||||
- const unsigned char *p2 = buf2;
|
||||
- unsigned char diff = 0;
|
||||
- apr_size_t i;
|
||||
-
|
||||
- for (i = 0; i < size; ++i) {
|
||||
- diff |= p1[i] ^ p2[i];
|
||||
- }
|
||||
-
|
||||
- return 1 & ((diff - 1) >> 8);
|
||||
-}
|
||||
-
|
||||
#endif
|
||||
|
||||
static void compute_auth(const void *src, apr_size_t len,
|
||||
@@ -404,7 +387,7 @@ static apr_status_t decrypt_string(request_rec * r, const apr_crypto_t *f,
|
||||
* the MAC and comparing it (timing safe) with the one in the payload.
|
||||
*/
|
||||
compute_auth(slider, len, passphrase, passlen, auth);
|
||||
- if (!ap_crypto_equals(auth, decoded, AP_SIPHASH_DSIZE)) {
|
||||
+ if (!ap_memeq_timingsafe(auth, decoded, AP_SIPHASH_DSIZE)) {
|
||||
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, res, r, APLOGNO(10006)
|
||||
"auth does not match, skipping");
|
||||
continue;
|
||||
diff --git a/server/util.c b/server/util.c
|
||||
index 11d0e40..0cdf543 100644
|
||||
--- a/server/util.c
|
||||
+++ b/server/util.c
|
||||
@@ -3777,3 +3777,141 @@ AP_DECLARE(const char *)ap_dir_fnmatch(ap_dir_match_t *w, const char *path,
|
||||
|
||||
return NULL;
|
||||
}
|
||||
+
|
||||
+
|
||||
+#if APR_VERSION_AT_LEAST(1,8,0)
|
||||
+AP_DECLARE(int) ap_memeq_timingsafe(const void *buf1, const void *buf2,
|
||||
+ apr_size_t n)
|
||||
+{
|
||||
+ return apr_memeq_timingsafe(buf1, buf2, n);
|
||||
+}
|
||||
+
|
||||
+AP_DECLARE(int) ap_streq_timingsafe(const char *sec1, const char *str2)
|
||||
+{
|
||||
+ return apr_streq_timingsafe(sec1, str2);
|
||||
+}
|
||||
+
|
||||
+AP_DECLARE(int) ap_strneq_timingsafe(const char *sec1, const char *str2,
|
||||
+ apr_size_t n)
|
||||
+{
|
||||
+ return apr_strneq_timingsafe(sec1, str2, n);
|
||||
+}
|
||||
+
|
||||
+#else /* !APR_VERSION_AT_LEAST(1,8,0) */
|
||||
+
|
||||
+/* A volatile variable which is always zero but allows to block the compiler
|
||||
+ * from optimizing or eliding code using it. Volatile forces the compiler to
|
||||
+ * emit a memory load for which no value can be assumed, so for instance an
|
||||
+ * add/sub/xor/or with "optblocker" is a noop that will hide the result to
|
||||
+ * the optimizer.
|
||||
+ */
|
||||
+static volatile const apr_uint32_t optblocker;
|
||||
+
|
||||
+/* Return whether x is not zero, with no branching controlled by x.
|
||||
+ *
|
||||
+ * Taken from the cryptoint library (public domain) by D. J. Bernstein,
|
||||
+ * which provides timing attacks safe integer operations/primitives.
|
||||
+ * Code:
|
||||
+ * https://lib.mceliece.org/libmceliece-20250507/cryptoint/crypto_uint32.h
|
||||
+ * Paper:
|
||||
+ * https://cr.yp.to/papers/cryptoint-20250424.pdf
|
||||
+ */
|
||||
+#if __has_attribute(always_inline)
|
||||
+__attribute__((always_inline))
|
||||
+#endif
|
||||
+static APR_INLINE int test_nonzero_timingsafe(apr_uint32_t x)
|
||||
+{
|
||||
+ x |= -x; /* sets the most significant bit unless x == 0 */
|
||||
+
|
||||
+ /* shift bit 31 (MSB) to bit 0 */
|
||||
+ x >>= 32-6; /* keep 6 bits */
|
||||
+ x += optblocker; /* lose the optimizer */
|
||||
+ x >>= 5; /* keep the (original) MSB only */
|
||||
+
|
||||
+ /* x is now 0 or 1 */
|
||||
+ return x & INT_MAX;
|
||||
+}
|
||||
+
|
||||
+AP_DECLARE(int) ap_memeq_timingsafe(const void *buf1, const void *buf2,
|
||||
+ apr_size_t n)
|
||||
+{
|
||||
+ apr_uint32_t diff = 0;
|
||||
+ volatile apr_size_t count = n; /* prevent loop unrolling */
|
||||
+ apr_size_t i = 0;
|
||||
+
|
||||
+ for (; i < count; ++i) {
|
||||
+ const unsigned char c1 = ((volatile const unsigned char *)buf1)[i];
|
||||
+ const unsigned char c2 = ((volatile const unsigned char *)buf2)[i];
|
||||
+
|
||||
+ diff |= c1 ^ c2; /* sets diff to non-zero whenever c1 != c2 */
|
||||
+ }
|
||||
+
|
||||
+ /* (diff == 0) <=> (diff != 0) ^ 1 */
|
||||
+ return test_nonzero_timingsafe(diff) ^ 1;
|
||||
+}
|
||||
+
|
||||
+AP_DECLARE(int) ap_streq_timingsafe(const char *sec1, const char *str2)
|
||||
+{
|
||||
+ apr_uint32_t diff = 0;
|
||||
+ apr_size_t i1 = 0, i2 = 0;
|
||||
+
|
||||
+ for (;; ++i2) {
|
||||
+ const unsigned char c1 = ((volatile const unsigned char *)sec1)[i1];
|
||||
+ const unsigned char c2 = ((volatile const unsigned char *)str2)[i2];
|
||||
+
|
||||
+ diff |= c1 ^ c2; /* sets diff to non-zero whenever c1 != c2 */
|
||||
+
|
||||
+ /* Not a shortest/longest match because an attacker would usually know
|
||||
+ * one of the strings and could then determine the length of the other.
|
||||
+ * So assume only sec1 and its length are secret and stop the loop at
|
||||
+ * the end of str2. If sec1 is shorter than str2 the loop will continue
|
||||
+ * by comparing the rest of str2 with the trailing NUL byte of sec1.
|
||||
+ * In any case since the diff above is computed up to and including a
|
||||
+ * NUL byte, only the same content and length will raise match.
|
||||
+ */
|
||||
+ if (!c2) {
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /* Don't go above sec1's NUL byte */
|
||||
+ i1 += test_nonzero_timingsafe(c1);
|
||||
+ }
|
||||
+
|
||||
+ /* (diff == 0) <=> (diff != 0) ^ 1 */
|
||||
+ return test_nonzero_timingsafe(diff) ^ 1;
|
||||
+}
|
||||
+
|
||||
+AP_DECLARE(int) ap_strneq_timingsafe(const char *sec1, const char *str2,
|
||||
+ apr_size_t n)
|
||||
+{
|
||||
+ apr_uint32_t diff = 0;
|
||||
+ volatile apr_size_t count = n; /* prevent loop unrolling */
|
||||
+ apr_size_t i1 = 0, i2 = 0;
|
||||
+
|
||||
+ for (; i2 < count; ++i2) {
|
||||
+ const unsigned char c1 = ((volatile const unsigned char *)sec1)[i1];
|
||||
+ const unsigned char c2 = ((volatile const unsigned char *)str2)[i2];
|
||||
+
|
||||
+ diff |= c1 ^ c2; /* sets diff to non-zero whenever c1 != c2 */
|
||||
+
|
||||
+ /* Not a shortest/longest match because an attacker would usually know
|
||||
+ * one of the strings and could then determine the length of the other.
|
||||
+ * So assume only sec1 and its length are secret and stop the loop at
|
||||
+ * the end of str2. If sec1 is shorter than str2 the loop will continue
|
||||
+ * by comparing the rest of str2 with the trailing NUL byte of sec1.
|
||||
+ * In any case since the diff above is computed up to and including a
|
||||
+ * NUL byte, only the same content and length will raise match.
|
||||
+ */
|
||||
+ if (!c2) {
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ /* Don't go above sec1's NUL byte */
|
||||
+ i1 += test_nonzero_timingsafe(c1);
|
||||
+ }
|
||||
+
|
||||
+ /* (diff == 0) <=> (diff != 0) ^ 1 */
|
||||
+ return test_nonzero_timingsafe(diff) ^ 1;
|
||||
+}
|
||||
+
|
||||
+#endif /* !APR_VERSION_AT_LEAST(1,8,0) */
|
||||
529
httpd-2.4.63-CVE-2026-34355.patch
Normal file
529
httpd-2.4.63-CVE-2026-34355.patch
Normal file
@ -0,0 +1,529 @@
|
||||
diff --git a/modules/filters/mod_proxy_html.c b/modules/filters/mod_proxy_html.c
|
||||
index 4205a61..27614fe 100644
|
||||
--- a/modules/filters/mod_proxy_html.c
|
||||
+++ b/modules/filters/mod_proxy_html.c
|
||||
@@ -63,6 +63,7 @@
|
||||
#include "mod_xml2enc.h"
|
||||
#include "http_request.h"
|
||||
#include "ap_expr.h"
|
||||
+#include "util_varbuf.h"
|
||||
|
||||
/* globals set once at startup */
|
||||
static ap_rxplus_t *old_expr;
|
||||
@@ -122,9 +123,7 @@ typedef struct {
|
||||
proxy_html_conf *cfg;
|
||||
htmlParserCtxtPtr parser;
|
||||
apr_bucket_brigade *bb;
|
||||
- char *buf;
|
||||
- size_t offset;
|
||||
- size_t avail;
|
||||
+ struct ap_varbuf vb;
|
||||
const char *encoding;
|
||||
urlmap *map;
|
||||
char rbuf[4];
|
||||
@@ -132,6 +131,7 @@ typedef struct {
|
||||
apr_size_t rmin;
|
||||
} saxctxt;
|
||||
|
||||
+#define DEFAULT_BUFSZ (8192)
|
||||
|
||||
#define NORM_LC 0x1
|
||||
#define NORM_MSSLASH 0x2
|
||||
@@ -155,17 +155,17 @@ static const char *const xhtml_etag = " />";
|
||||
static const char *const DEFAULT_DOCTYPE = "";
|
||||
#define DEFAULT_ETAG html_etag
|
||||
|
||||
-static void normalise(unsigned int flags, char *str)
|
||||
+static void normalise(unsigned int flags, struct ap_varbuf *vb)
|
||||
{
|
||||
- char *p;
|
||||
+ apr_size_t n;
|
||||
if (flags & NORM_LC)
|
||||
- for (p = str; *p; ++p)
|
||||
- if (isupper(*p))
|
||||
- *p = tolower(*p);
|
||||
+ for (n = 0; n < vb->strlen; ++n)
|
||||
+ vb->buf[n] = apr_tolower(vb->buf[n]);
|
||||
|
||||
if (flags & NORM_MSSLASH)
|
||||
- for (p = ap_strchr(str, '\\'); p; p = ap_strchr(p+1, '\\'))
|
||||
- *p = '/';
|
||||
+ for (n = 0; n < vb->strlen; ++n)
|
||||
+ if (vb->buf[n] == '\\')
|
||||
+ vb->buf[n] = '/';
|
||||
|
||||
}
|
||||
#define consume_buffer(ctx,inbuf,bytes,flag) \
|
||||
@@ -194,120 +194,97 @@ static void pcharacters(void *ctxt, const xmlChar *uchars, int length)
|
||||
FLUSH;
|
||||
}
|
||||
|
||||
-static void preserve(saxctxt *ctx, const size_t len)
|
||||
-{
|
||||
- char *newbuf;
|
||||
- if (len <= (ctx->avail - ctx->offset))
|
||||
- return;
|
||||
- else while (len > (ctx->avail - ctx->offset))
|
||||
- ctx->avail += ctx->cfg->bufsz;
|
||||
-
|
||||
- newbuf = realloc(ctx->buf, ctx->avail);
|
||||
- if (newbuf != ctx->buf) {
|
||||
- if (ctx->buf)
|
||||
- apr_pool_cleanup_kill(ctx->f->r->pool, ctx->buf,
|
||||
- (int(*)(void*))free);
|
||||
- apr_pool_cleanup_register(ctx->f->r->pool, newbuf,
|
||||
- (int(*)(void*))free, apr_pool_cleanup_null);
|
||||
- ctx->buf = newbuf;
|
||||
- }
|
||||
-}
|
||||
-
|
||||
-static void pappend(saxctxt *ctx, const char *buf, const size_t len)
|
||||
-{
|
||||
- preserve(ctx, len);
|
||||
- memcpy(ctx->buf+ctx->offset, buf, len);
|
||||
- ctx->offset += len;
|
||||
-}
|
||||
-
|
||||
static void dump_content(saxctxt *ctx)
|
||||
{
|
||||
urlmap *m;
|
||||
char *found;
|
||||
size_t s_from, s_to;
|
||||
size_t match;
|
||||
- char c = 0;
|
||||
int nmatch;
|
||||
ap_regmatch_t pmatch[10];
|
||||
- char *subs;
|
||||
size_t len, offs;
|
||||
urlmap *themap = ctx->map;
|
||||
+ struct ap_varbuf temp;
|
||||
#ifndef GO_FASTER
|
||||
int verbose = APLOGrtrace1(ctx->f->r);
|
||||
#endif
|
||||
|
||||
- pappend(ctx, &c, 1); /* append null byte */
|
||||
- /* parse the text for URLs */
|
||||
+ /* Ensure buffer is null-terminated and strlen is set */
|
||||
+ if (ctx->vb.strlen == AP_VARBUF_UNKNOWN) {
|
||||
+ ctx->vb.strlen = strlen(ctx->vb.buf);
|
||||
+ }
|
||||
+
|
||||
+ ap_varbuf_init(ctx->f->r->pool, &temp, 0);
|
||||
+
|
||||
+ /* parse the text for URLs */
|
||||
for (m = themap; m; m = m->next) {
|
||||
if (!(m->flags & M_CDATA))
|
||||
continue;
|
||||
if (m->flags & M_REGEX) {
|
||||
+ temp.strlen = 0;
|
||||
nmatch = 10;
|
||||
offs = 0;
|
||||
- while (!ap_regexec(m->from.r, ctx->buf+offs, nmatch, pmatch, 0)) {
|
||||
+ while (!ap_regexec(m->from.r, ctx->vb.buf+offs, nmatch, pmatch, 0)) {
|
||||
match = pmatch[0].rm_so;
|
||||
s_from = pmatch[0].rm_eo - match;
|
||||
- subs = ap_pregsub(ctx->f->r->pool, m->to, ctx->buf+offs,
|
||||
- nmatch, pmatch);
|
||||
- s_to = strlen(subs);
|
||||
- len = strlen(ctx->buf);
|
||||
- offs += match;
|
||||
VERBOSEB(
|
||||
const char *f = apr_pstrndup(ctx->f->r->pool,
|
||||
- ctx->buf + offs, s_from);
|
||||
+ ctx->vb.buf + offs + match, s_from);
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, ctx->f->r,
|
||||
- "C/RX: match at %s, substituting %s", f, subs);
|
||||
+ "C/RX: match at %s, substituting with pattern %s",
|
||||
+ f, m->to);
|
||||
)
|
||||
- if (s_to > s_from) {
|
||||
- preserve(ctx, s_to - s_from);
|
||||
- memmove(ctx->buf+offs+s_to, ctx->buf+offs+s_from,
|
||||
- len + 1 - s_from - offs);
|
||||
- memcpy(ctx->buf+offs, subs, s_to);
|
||||
- }
|
||||
- else {
|
||||
- memcpy(ctx->buf + offs, subs, s_to);
|
||||
- memmove(ctx->buf+offs+s_to, ctx->buf+offs+s_from,
|
||||
- len + 1 - s_from - offs);
|
||||
- }
|
||||
- offs += s_to;
|
||||
+ /* Copy text before the match */
|
||||
+ ap_varbuf_strmemcat(&temp, ctx->vb.buf+offs, match);
|
||||
+ /* Append the substitution */
|
||||
+ ap_varbuf_regsub(&temp, m->to, ctx->vb.buf+offs, nmatch, pmatch, 0);
|
||||
+ /* Continue past the match */
|
||||
+ offs += pmatch[0].rm_eo;
|
||||
}
|
||||
+ /* Copy any remaining text */
|
||||
+ ap_varbuf_strcat(&temp, ctx->vb.buf+offs);
|
||||
+ /* Replace the original buffer with the result */
|
||||
+ ctx->vb.strlen = 0;
|
||||
+ ap_varbuf_strmemcat(&ctx->vb, temp.buf, temp.strlen);
|
||||
}
|
||||
else {
|
||||
s_from = strlen(m->from.c);
|
||||
s_to = strlen(m->to);
|
||||
- for (found = strstr(ctx->buf, m->from.c); found;
|
||||
- found = strstr(ctx->buf+match+s_to, m->from.c)) {
|
||||
- match = found - ctx->buf;
|
||||
+ for (found = strstr(ctx->vb.buf, m->from.c); found;
|
||||
+ found = strstr(ctx->vb.buf+match+s_to, m->from.c)) {
|
||||
+ match = found - ctx->vb.buf;
|
||||
if ((m->flags & M_ATSTART) && (match != 0))
|
||||
break;
|
||||
- len = strlen(ctx->buf);
|
||||
+ len = ctx->vb.strlen;
|
||||
if ((m->flags & M_ATEND) && (match < (len - s_from)))
|
||||
continue;
|
||||
VERBOSE(ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0, ctx->f->r,
|
||||
"C: matched %s, substituting %s",
|
||||
m->from.c, m->to));
|
||||
if (s_to > s_from) {
|
||||
- preserve(ctx, s_to - s_from);
|
||||
- memmove(ctx->buf+match+s_to, ctx->buf+match+s_from,
|
||||
- len + 1 - s_from - match);
|
||||
- memcpy(ctx->buf+match, m->to, s_to);
|
||||
+ ap_varbuf_grow(&ctx->vb, len + s_to - s_from);
|
||||
+ memmove(ctx->vb.buf+match+s_to, ctx->vb.buf+match+s_from,
|
||||
+ len + 1 - s_from - match);
|
||||
+ memcpy(ctx->vb.buf+match, m->to, s_to);
|
||||
}
|
||||
else {
|
||||
- memcpy(ctx->buf+match, m->to, s_to);
|
||||
- memmove(ctx->buf+match+s_to, ctx->buf+match+s_from,
|
||||
+ memcpy(ctx->vb.buf+match, m->to, s_to);
|
||||
+ memmove(ctx->vb.buf+match+s_to, ctx->vb.buf+match+s_from,
|
||||
len + 1 - s_from - match);
|
||||
}
|
||||
+ ctx->vb.strlen = len - s_from + s_to;
|
||||
}
|
||||
}
|
||||
}
|
||||
- AP_fwrite(ctx, ctx->buf, strlen(ctx->buf), 1);
|
||||
+ AP_fwrite(ctx, ctx->vb.buf, ctx->vb.strlen, 1);
|
||||
+ ap_varbuf_free(&temp);
|
||||
}
|
||||
static void pcdata(void *ctxt, const xmlChar *uchars, int length)
|
||||
{
|
||||
const char *chars = (const char*) uchars;
|
||||
saxctxt *ctx = (saxctxt*) ctxt;
|
||||
if (ctx->cfg->extfix) {
|
||||
- pappend(ctx, chars, length);
|
||||
+ ap_varbuf_strmemcat(&ctx->vb, chars, length);
|
||||
}
|
||||
else {
|
||||
/* not sure if this should force-flush
|
||||
@@ -324,9 +301,9 @@ static void pcomment(void *ctxt, const xmlChar *uchars)
|
||||
return;
|
||||
|
||||
if (ctx->cfg->extfix) {
|
||||
- pappend(ctx, "<!--", 4);
|
||||
- pappend(ctx, chars, strlen(chars));
|
||||
- pappend(ctx, "-->", 3);
|
||||
+ ap_varbuf_strcat(&ctx->vb, "<!--");
|
||||
+ ap_varbuf_strcat(&ctx->vb, chars);
|
||||
+ ap_varbuf_strcat(&ctx->vb, "-->");
|
||||
}
|
||||
else {
|
||||
ap_fputs(ctx->f->next, ctx->bb, "<!--");
|
||||
@@ -356,9 +333,9 @@ static void pendElement(void *ctxt, const xmlChar *uname)
|
||||
/* TODO - implement HTML "allowed here" using the stack */
|
||||
/* nah. Keeping the stack is too much overhead */
|
||||
|
||||
- if (ctx->offset > 0) {
|
||||
+ if (ctx->vb.strlen > 0) {
|
||||
dump_content(ctx);
|
||||
- ctx->offset = 0; /* having dumped it, we can re-use the memory */
|
||||
+ ctx->vb.strlen = 0; /* having dumped it, we can re-use the memory */
|
||||
}
|
||||
if (!desc || !desc->empty) {
|
||||
ap_fprintf(ctx->f->next, ctx->bb, "</%s>", name);
|
||||
@@ -371,7 +348,6 @@ static void pstartElement(void *ctxt, const xmlChar *uname,
|
||||
int required_attrs;
|
||||
int num_match;
|
||||
size_t offs, len;
|
||||
- char *subs;
|
||||
rewrite_t is_uri;
|
||||
const char** a;
|
||||
urlmap *m;
|
||||
@@ -389,6 +365,7 @@ static void pstartElement(void *ctxt, const xmlChar *uname,
|
||||
const char** attrs = (const char**) uattrs;
|
||||
const htmlElemDesc* desc = htmlTagLookup(uname);
|
||||
urlmap *themap = ctx->map;
|
||||
+ struct ap_varbuf temp;
|
||||
#ifdef HAVE_STACK
|
||||
const void** descp;
|
||||
#endif
|
||||
@@ -418,6 +395,8 @@ static void pstartElement(void *ctxt, const xmlChar *uname,
|
||||
/* TODO - implement HTML "allowed here" */
|
||||
#endif
|
||||
|
||||
+ ap_varbuf_init(ctx->f->r->pool, &temp, 0);
|
||||
+
|
||||
ap_fputc(ctx->f->next, ctx->bb, '<');
|
||||
ap_fputs(ctx->f->next, ctx->bb, name);
|
||||
|
||||
@@ -448,9 +427,10 @@ static void pstartElement(void *ctxt, const xmlChar *uname,
|
||||
break;
|
||||
}
|
||||
}
|
||||
- ctx->offset = 0;
|
||||
+ ctx->vb.strlen = 0;
|
||||
+ ctx->vb.buf[0] = '\0';
|
||||
if (a[1]) {
|
||||
- pappend(ctx, a[1], strlen(a[1])+1);
|
||||
+ ap_varbuf_strcat(&ctx->vb, a[1]);
|
||||
is_uri = ATTR_IGNORE;
|
||||
if (linkattrs) {
|
||||
tattr *attrs = (tattr*) linkattrs->elts;
|
||||
@@ -479,59 +459,53 @@ static void pstartElement(void *ctxt, const xmlChar *uname,
|
||||
continue;
|
||||
if (m->flags & M_REGEX) {
|
||||
nmatch = 10;
|
||||
- if (!ap_regexec(m->from.r, ctx->buf, nmatch,
|
||||
+ if (!ap_regexec(m->from.r, ctx->vb.buf, nmatch,
|
||||
pmatch, 0)) {
|
||||
++num_match;
|
||||
- offs = match = pmatch[0].rm_so;
|
||||
+ match = pmatch[0].rm_so;
|
||||
s_from = pmatch[0].rm_eo - match;
|
||||
- subs = ap_pregsub(ctx->f->r->pool, m->to,
|
||||
- ctx->buf, nmatch, pmatch);
|
||||
VERBOSE({
|
||||
const char *f;
|
||||
f = apr_pstrndup(ctx->f->r->pool,
|
||||
- ctx->buf + offs, s_from);
|
||||
+ ctx->vb.buf + match, s_from);
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0,
|
||||
ctx->f->r,
|
||||
- "H/RX: match at %s, substituting %s",
|
||||
- f, subs);
|
||||
+ "H/RX: match at %s, substituting with pattern %s",
|
||||
+ f, m->to);
|
||||
})
|
||||
- s_to = strlen(subs);
|
||||
- len = strlen(ctx->buf);
|
||||
- if (s_to > s_from) {
|
||||
- preserve(ctx, s_to - s_from);
|
||||
- memmove(ctx->buf+offs+s_to,
|
||||
- ctx->buf+offs+s_from,
|
||||
- len + 1 - s_from - offs);
|
||||
- memcpy(ctx->buf+offs, subs, s_to);
|
||||
- }
|
||||
- else {
|
||||
- memcpy(ctx->buf + offs, subs, s_to);
|
||||
- memmove(ctx->buf+offs+s_to,
|
||||
- ctx->buf+offs+s_from,
|
||||
- len + 1 - s_from - offs);
|
||||
- }
|
||||
+ temp.strlen = 0;
|
||||
+ /* Copy text before match */
|
||||
+ ap_varbuf_strmemcat(&temp, ctx->vb.buf, match);
|
||||
+ /* Append substitution */
|
||||
+ ap_varbuf_regsub(&temp, m->to, ctx->vb.buf, nmatch, pmatch, 0);
|
||||
+ /* Copy text after match */
|
||||
+ ap_varbuf_strcat(&temp, ctx->vb.buf + pmatch[0].rm_eo);
|
||||
+ /* Replace buffer */
|
||||
+ ctx->vb.strlen = 0;
|
||||
+ ap_varbuf_strmemcat(&ctx->vb, temp.buf, temp.strlen);
|
||||
}
|
||||
} else {
|
||||
s_from = strlen(m->from.c);
|
||||
- if (!strncasecmp(ctx->buf, m->from.c, s_from)) {
|
||||
+ if (!strncasecmp(ctx->vb.buf, m->from.c, s_from)) {
|
||||
++num_match;
|
||||
s_to = strlen(m->to);
|
||||
- len = strlen(ctx->buf);
|
||||
+ len = ctx->vb.strlen;
|
||||
VERBOSE(ap_log_rerror(APLOG_MARK, APLOG_TRACE3,
|
||||
0, ctx->f->r,
|
||||
"H: matched %s, substituting %s",
|
||||
m->from.c, m->to));
|
||||
if (s_to > s_from) {
|
||||
- preserve(ctx, s_to - s_from);
|
||||
- memmove(ctx->buf+s_to, ctx->buf+s_from,
|
||||
+ ap_varbuf_grow(&ctx->vb, len + s_to - s_from);
|
||||
+ memmove(ctx->vb.buf+s_to, ctx->vb.buf+s_from,
|
||||
len + 1 - s_from);
|
||||
- memcpy(ctx->buf, m->to, s_to);
|
||||
+ memcpy(ctx->vb.buf, m->to, s_to);
|
||||
}
|
||||
else { /* it fits in the existing space */
|
||||
- memcpy(ctx->buf, m->to, s_to);
|
||||
- memmove(ctx->buf+s_to, ctx->buf+s_from,
|
||||
+ memcpy(ctx->vb.buf, m->to, s_to);
|
||||
+ memmove(ctx->vb.buf+s_to, ctx->vb.buf+s_from,
|
||||
len + 1 - s_from);
|
||||
}
|
||||
+ ctx->vb.strlen = len - s_from + s_to;
|
||||
break;
|
||||
}
|
||||
}
|
||||
@@ -546,79 +520,73 @@ static void pstartElement(void *ctxt, const xmlChar *uname,
|
||||
if (!(m->flags & M_EVENTS))
|
||||
continue;
|
||||
if (m->flags & M_REGEX) {
|
||||
+ temp.strlen = 0;
|
||||
nmatch = 10;
|
||||
offs = 0;
|
||||
- while (!ap_regexec(m->from.r, ctx->buf+offs,
|
||||
+ while (!ap_regexec(m->from.r, ctx->vb.buf+offs,
|
||||
nmatch, pmatch, 0)) {
|
||||
match = pmatch[0].rm_so;
|
||||
s_from = pmatch[0].rm_eo - match;
|
||||
- subs = ap_pregsub(ctx->f->r->pool, m->to, ctx->buf+offs,
|
||||
- nmatch, pmatch);
|
||||
VERBOSE({
|
||||
const char *f;
|
||||
f = apr_pstrndup(ctx->f->r->pool,
|
||||
- ctx->buf + offs, s_from);
|
||||
+ ctx->vb.buf + offs + match, s_from);
|
||||
ap_log_rerror(APLOG_MARK, APLOG_TRACE3, 0,
|
||||
ctx->f->r,
|
||||
- "E/RX: match at %s, substituting %s",
|
||||
- f, subs);
|
||||
+ "E/RX: match at %s, substituting with pattern %s",
|
||||
+ f, m->to);
|
||||
})
|
||||
- s_to = strlen(subs);
|
||||
- offs += match;
|
||||
- len = strlen(ctx->buf);
|
||||
- if (s_to > s_from) {
|
||||
- preserve(ctx, s_to - s_from);
|
||||
- memmove(ctx->buf+offs+s_to,
|
||||
- ctx->buf+offs+s_from,
|
||||
- len + 1 - s_from - offs);
|
||||
- memcpy(ctx->buf+offs, subs, s_to);
|
||||
- }
|
||||
- else {
|
||||
- memcpy(ctx->buf + offs, subs, s_to);
|
||||
- memmove(ctx->buf+offs+s_to,
|
||||
- ctx->buf+offs+s_from,
|
||||
- len + 1 - s_from - offs);
|
||||
- }
|
||||
- offs += s_to;
|
||||
+ /* Copy text before match */
|
||||
+ ap_varbuf_strmemcat(&temp, ctx->vb.buf+offs, match);
|
||||
+ /* Append substitution */
|
||||
+ ap_varbuf_regsub(&temp, m->to, ctx->vb.buf+offs, nmatch, pmatch, 0);
|
||||
+ /* Continue past the match */
|
||||
+ offs += pmatch[0].rm_eo;
|
||||
++num_match;
|
||||
}
|
||||
+ /* Copy any remaining text */
|
||||
+ ap_varbuf_strcat(&temp, ctx->vb.buf+offs);
|
||||
+ /* Replace buffer */
|
||||
+ ctx->vb.strlen = 0;
|
||||
+ ap_varbuf_strmemcat(&ctx->vb, temp.buf, temp.strlen);
|
||||
}
|
||||
else {
|
||||
- found = strstr(ctx->buf, m->from.c);
|
||||
- if ((m->flags & M_ATSTART) && (found != ctx->buf))
|
||||
+ found = strstr(ctx->vb.buf, m->from.c);
|
||||
+ if ((m->flags & M_ATSTART) && (found != ctx->vb.buf))
|
||||
continue;
|
||||
while (found) {
|
||||
s_from = strlen(m->from.c);
|
||||
s_to = strlen(m->to);
|
||||
- match = found - ctx->buf;
|
||||
+ match = found - ctx->vb.buf;
|
||||
if ((s_from < strlen(found))
|
||||
&& (m->flags & M_ATEND)) {
|
||||
- found = strstr(ctx->buf+match+s_from,
|
||||
+ found = strstr(ctx->vb.buf+match+s_from,
|
||||
m->from.c);
|
||||
continue;
|
||||
}
|
||||
else {
|
||||
- found = strstr(ctx->buf+match+s_to,
|
||||
+ found = strstr(ctx->vb.buf+match+s_to,
|
||||
m->from.c);
|
||||
}
|
||||
VERBOSE(ap_log_rerror(APLOG_MARK, APLOG_TRACE3,
|
||||
0, ctx->f->r,
|
||||
"E: matched %s, substituting %s",
|
||||
m->from.c, m->to));
|
||||
- len = strlen(ctx->buf);
|
||||
+ len = ctx->vb.strlen;
|
||||
if (s_to > s_from) {
|
||||
- preserve(ctx, s_to - s_from);
|
||||
- memmove(ctx->buf+match+s_to,
|
||||
- ctx->buf+match+s_from,
|
||||
+ ap_varbuf_grow(&ctx->vb, len + s_to - s_from);
|
||||
+ memmove(ctx->vb.buf+match+s_to,
|
||||
+ ctx->vb.buf+match+s_from,
|
||||
len + 1 - s_from - match);
|
||||
- memcpy(ctx->buf+match, m->to, s_to);
|
||||
+ memcpy(ctx->vb.buf+match, m->to, s_to);
|
||||
}
|
||||
else {
|
||||
- memcpy(ctx->buf+match, m->to, s_to);
|
||||
- memmove(ctx->buf+match+s_to,
|
||||
- ctx->buf+match+s_from,
|
||||
+ memcpy(ctx->vb.buf+match, m->to, s_to);
|
||||
+ memmove(ctx->vb.buf+match+s_to,
|
||||
+ ctx->vb.buf+match+s_from,
|
||||
len + 1 - s_from - match);
|
||||
}
|
||||
+ ctx->vb.strlen = len - s_from + s_to;
|
||||
++num_match;
|
||||
}
|
||||
}
|
||||
@@ -635,18 +603,18 @@ static void pstartElement(void *ctxt, const xmlChar *uname,
|
||||
else {
|
||||
|
||||
if (ctx->cfg->flags != 0)
|
||||
- normalise(ctx->cfg->flags, ctx->buf);
|
||||
+ normalise(ctx->cfg->flags, &ctx->vb);
|
||||
|
||||
/* write the attribute, using pcharacters to html-escape
|
||||
anything that needs it in the value.
|
||||
*/
|
||||
ap_fputstrs(ctx->f->next, ctx->bb, " ", a[0], "=\"", NULL);
|
||||
- pcharacters(ctx, (const xmlChar*)ctx->buf, strlen(ctx->buf));
|
||||
+ pcharacters(ctx, (const xmlChar*)ctx->vb.buf, ctx->vb.strlen);
|
||||
ap_fputc(ctx->f->next, ctx->bb, '"');
|
||||
}
|
||||
}
|
||||
}
|
||||
- ctx->offset = 0;
|
||||
+ ctx->vb.strlen = 0;
|
||||
if (desc && desc->empty)
|
||||
ap_fputs(ctx->f->next, ctx->bb, ctx->cfg->etag);
|
||||
else
|
||||
@@ -658,6 +626,7 @@ static void pstartElement(void *ctxt, const xmlChar *uname,
|
||||
"HTML element %s is missing %d required attributes",
|
||||
name, required_attrs);
|
||||
}
|
||||
+ ap_varbuf_free(&temp);
|
||||
}
|
||||
|
||||
static meta *metafix(request_rec *r, const char *buf, apr_size_t len)
|
||||
@@ -826,6 +795,8 @@ static saxctxt *check_filter_init (ap_filter_t *f)
|
||||
proxy_html_conf *cfg;
|
||||
const char *force;
|
||||
const char *errmsg = NULL;
|
||||
+ apr_size_t bufsz;
|
||||
+
|
||||
cfg = ap_get_module_config(f->r->per_dir_config, &proxy_html_module);
|
||||
force = apr_table_get(f->r->subprocess_env, "PROXY_HTML_FORCE");
|
||||
|
||||
@@ -859,6 +830,8 @@ static saxctxt *check_filter_init (ap_filter_t *f)
|
||||
fctx->bb = apr_brigade_create(f->r->pool,
|
||||
f->r->connection->bucket_alloc);
|
||||
fctx->cfg = cfg;
|
||||
+ bufsz = (cfg->bufsz <= 0 || cfg->bufsz > (128 * 1024)) ? DEFAULT_BUFSZ : cfg->bufsz;
|
||||
+ ap_varbuf_init(f->r->pool, &fctx->vb, bufsz);
|
||||
apr_table_unset(f->r->headers_out, "Content-Length");
|
||||
|
||||
if (cfg->interp)
|
||||
@@ -1028,7 +1001,7 @@ static void *proxy_html_config(apr_pool_t *pool, char *x)
|
||||
proxy_html_conf *ret = apr_pcalloc(pool, sizeof(proxy_html_conf));
|
||||
ret->doctype = DEFAULT_DOCTYPE;
|
||||
ret->etag = DEFAULT_ETAG;
|
||||
- ret->bufsz = 8192;
|
||||
+ ret->bufsz = DEFAULT_BUFSZ;
|
||||
/* ret->interp = 1; */
|
||||
/* don't initialise links and events until they get set/used */
|
||||
return ret;
|
||||
30
httpd-2.4.63-CVE-2026-34356.patch
Normal file
30
httpd-2.4.63-CVE-2026-34356.patch
Normal file
@ -0,0 +1,30 @@
|
||||
diff --git a/modules/proxy/proxy_util.c b/modules/proxy/proxy_util.c
|
||||
index 00fedb1..81b7de2 100644
|
||||
--- a/modules/proxy/proxy_util.c
|
||||
+++ b/modules/proxy/proxy_util.c
|
||||
@@ -1036,6 +1036,12 @@ PROXY_DECLARE(const char *) ap_proxy_cookie_reverse_map(request_rec *r,
|
||||
poffs = pathp - tmpstr_orig;
|
||||
l1 = strlen(pathp);
|
||||
pathe = str + poffs + l1;
|
||||
+ /*
|
||||
+ * RFC 6265 § 5.3 7): Only the last path= should be meaningful
|
||||
+ * so reset anything previously found.
|
||||
+ */
|
||||
+ newpath = NULL;
|
||||
+ pdiff = 0;
|
||||
if (conf->interpolate_env == 1) {
|
||||
ent = (struct proxy_alias *)rconf->cookie_paths->elts;
|
||||
}
|
||||
@@ -1056,6 +1062,12 @@ PROXY_DECLARE(const char *) ap_proxy_cookie_reverse_map(request_rec *r,
|
||||
doffs = domainp - tmpstr_orig;
|
||||
l1 = strlen(domainp);
|
||||
domaine = str + doffs + l1;
|
||||
+ /*
|
||||
+ * RFC 6265 § 5.3 4): Only the last domain= should be meaningful
|
||||
+ * so reset anything previously found.
|
||||
+ */
|
||||
+ newdomain = NULL;
|
||||
+ ddiff = 0;
|
||||
if (conf->interpolate_env == 1) {
|
||||
ent = (struct proxy_alias *)rconf->cookie_domains->elts;
|
||||
}
|
||||
54
httpd-2.4.63-CVE-2026-42535.patch
Normal file
54
httpd-2.4.63-CVE-2026-42535.patch
Normal file
@ -0,0 +1,54 @@
|
||||
diff --git a/modules/dav/fs/repos.c b/modules/dav/fs/repos.c
|
||||
index 64bc894..fbab779 100644
|
||||
--- a/modules/dav/fs/repos.c
|
||||
+++ b/modules/dav/fs/repos.c
|
||||
@@ -22,6 +22,7 @@
|
||||
#include "apr_file_io.h"
|
||||
#include "apr_strings.h"
|
||||
#include "apr_buckets.h"
|
||||
+#include "apr_lib.h"
|
||||
|
||||
#if APR_HAVE_UNISTD_H
|
||||
#include <unistd.h> /* for getpid() */
|
||||
@@ -673,8 +674,8 @@ static dav_error * dav_fs_get_resource(
|
||||
{
|
||||
dav_resource_private *ctx;
|
||||
dav_resource *resource;
|
||||
- char *s;
|
||||
- char *filename;
|
||||
+ char *s, *parent;
|
||||
+ const char *filename, *dirname;
|
||||
apr_size_t len;
|
||||
|
||||
/* ### optimize this into a single allocation! */
|
||||
@@ -708,6 +709,30 @@ static dav_error * dav_fs_get_resource(
|
||||
if (len > 1 && s[len - 1] == '/') {
|
||||
s[len - 1] = '\0';
|
||||
}
|
||||
+
|
||||
+ /* Deny any access to, or within, the state directory. */
|
||||
+ filename = apr_filepath_name_get(s);
|
||||
+ parent = ap_make_dirstr_parent(r->pool, s);
|
||||
+ /* Strip the trailing slash and extract the leaf directory name. */
|
||||
+ len = strlen(parent);
|
||||
+ if (len > 1 && parent[len - 1] == '/') {
|
||||
+ parent[len - 1] = '\0';
|
||||
+ }
|
||||
+ dirname = apr_filepath_name_get(parent);
|
||||
+#ifdef CASE_BLIND_FILESYSTEM
|
||||
+ if (ap_cstr_casecmp(filename, DAV_FS_STATE_DIR) == 0
|
||||
+ || ap_cstr_casecmp(dirname, DAV_FS_STATE_DIR) == 0) {
|
||||
+#else
|
||||
+ if (strcmp(filename, DAV_FS_STATE_DIR) == 0
|
||||
+ || strcmp(dirname, DAV_FS_STATE_DIR) == 0) {
|
||||
+#endif
|
||||
+ ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r,
|
||||
+ "access to " DAV_FS_STATE_DIR " state directory "
|
||||
+ "denied for %s", r->filename);
|
||||
+ return dav_new_error(r->pool, HTTP_FORBIDDEN, 0, 0,
|
||||
+ "Access to the state directory denied.");
|
||||
+ }
|
||||
+
|
||||
ctx->pathname = s;
|
||||
|
||||
/* Create resource descriptor */
|
||||
12
httpd-2.4.63-CVE-2026-42536.patch
Normal file
12
httpd-2.4.63-CVE-2026-42536.patch
Normal file
@ -0,0 +1,12 @@
|
||||
diff --git a/modules/filters/mod_xml2enc.c b/modules/filters/mod_xml2enc.c
|
||||
index eb05c18..20862bb 100644
|
||||
--- a/modules/filters/mod_xml2enc.c
|
||||
+++ b/modules/filters/mod_xml2enc.c
|
||||
@@ -164,6 +164,7 @@ static void fix_skipto(request_rec* r, xml2ctx* ctx)
|
||||
apr_bucket_delete(b);
|
||||
}
|
||||
ctx->bytes -= (p-ctx->buf);
|
||||
+ ctx->bblen -= (p-ctx->buf);
|
||||
ctx->buf = p ;
|
||||
found = 1;
|
||||
ap_log_rerror(APLOG_MARK, APLOG_DEBUG, 0, r, APLOGNO(01428)
|
||||
16
httpd-2.4.63-CVE-2026-43951.patch
Normal file
16
httpd-2.4.63-CVE-2026-43951.patch
Normal file
@ -0,0 +1,16 @@
|
||||
diff --git a/modules/http/http_filters.c b/modules/http/http_filters.c
|
||||
index 732fb8e..ef24122 100644
|
||||
--- a/modules/http/http_filters.c
|
||||
+++ b/modules/http/http_filters.c
|
||||
@@ -1381,10 +1381,10 @@ static void merge_response_headers(request_rec *r, const char **protocol)
|
||||
if (!apr_is_empty_array(r->content_languages)) {
|
||||
int i;
|
||||
char *token;
|
||||
- char **languages = (char **)(r->content_languages->elts);
|
||||
const char *field = apr_table_get(r->headers_out, "Content-Language");
|
||||
|
||||
while (field && (token = ap_get_list_item(r->pool, &field)) != NULL) {
|
||||
+ char **languages = (char **)(r->content_languages->elts);
|
||||
for (i = 0; i < r->content_languages->nelts; ++i) {
|
||||
if (!ap_cstr_casecmp(token, languages[i]))
|
||||
break;
|
||||
92
httpd-2.4.63-CVE-2026-44119.patch
Normal file
92
httpd-2.4.63-CVE-2026-44119.patch
Normal file
@ -0,0 +1,92 @@
|
||||
diff --git a/include/ap_expr.h b/include/ap_expr.h
|
||||
index 8e57fcd..16138c9 100644
|
||||
--- a/include/ap_expr.h
|
||||
+++ b/include/ap_expr.h
|
||||
@@ -66,6 +66,8 @@ typedef struct {
|
||||
#define AP_EXPR_FLAG_RESTRICTED 4
|
||||
/** Expression evaluates to a string, not to a bool */
|
||||
#define AP_EXPR_FLAG_STRING_RESULT 8
|
||||
+/** Don't allow functions/vars that expose content from the filesystem. */
|
||||
+#define AP_EXPR_FLAG_RESTRICTED_FILE_FUNC 16
|
||||
|
||||
|
||||
/**
|
||||
diff --git a/server/util_expr_eval.c b/server/util_expr_eval.c
|
||||
index db4be95..8ff5518 100644
|
||||
--- a/server/util_expr_eval.c
|
||||
+++ b/server/util_expr_eval.c
|
||||
@@ -437,6 +437,12 @@ AP_DECLARE(ap_expr_info_t*) ap_expr_parse_cmd_mi(const cmd_parms *cmd,
|
||||
info->line_number = cmd->directive->line_num;
|
||||
info->flags = flags;
|
||||
info->module_index = module_index;
|
||||
+
|
||||
+ /* Use restricted-contents ap_expr() parser in htaccess context. */
|
||||
+ if (cmd->pool == cmd->temp_pool) {
|
||||
+ info->flags |= AP_EXPR_FLAG_RESTRICTED_FILE_FUNC;
|
||||
+ }
|
||||
+
|
||||
*err = ap_expr_parse(cmd->pool, cmd->temp_pool, info, expr, lookup_fn);
|
||||
|
||||
if (*err)
|
||||
@@ -1638,11 +1644,15 @@ static int op_strcmatch(ap_expr_eval_ctx_t *ctx, const void *data,
|
||||
return (APR_SUCCESS == apr_fnmatch(arg2, arg1, APR_FNM_CASE_BLIND));
|
||||
}
|
||||
|
||||
+#define RESTRICTED_FILE_TEST 0x01
|
||||
+#define RESTRICTED_FILE_FUNC 0x02
|
||||
+#define RESTRICTED_ALL (RESTRICTED_FILE_TEST | RESTRICTED_FILE_FUNC)
|
||||
+
|
||||
struct expr_provider_single {
|
||||
const void *func;
|
||||
const char *name;
|
||||
ap_expr_lookup_fn_t *arg_parsing_func;
|
||||
- int restricted;
|
||||
+ unsigned int restricted;
|
||||
};
|
||||
|
||||
struct expr_provider_multi {
|
||||
@@ -1672,8 +1682,8 @@ static const struct expr_provider_single string_func_providers[] = {
|
||||
{ toupper_func, "toupper", NULL, 0 },
|
||||
{ escape_func, "escape", NULL, 0 },
|
||||
{ unescape_func, "unescape", NULL, 0 },
|
||||
- { file_func, "file", NULL, 1 },
|
||||
- { filesize_func, "filesize", NULL, 1 },
|
||||
+ { file_func, "file", NULL, RESTRICTED_FILE_FUNC },
|
||||
+ { filesize_func, "filesize", NULL, RESTRICTED_FILE_FUNC },
|
||||
{ base64_func, "base64", NULL, 0 },
|
||||
{ unbase64_func, "unbase64", NULL, 0 },
|
||||
{ sha1_func, "sha1", NULL, 0 },
|
||||
@@ -1689,13 +1699,13 @@ static const struct expr_provider_single unary_op_providers[] = {
|
||||
{ op_nz, "z", NULL, 0 },
|
||||
{ op_R, "R", subnet_parse_arg, 0 },
|
||||
{ op_T, "T", NULL, 0 },
|
||||
- { op_file_min, "d", NULL, 1 },
|
||||
- { op_file_min, "e", NULL, 1 },
|
||||
- { op_file_min, "f", NULL, 1 },
|
||||
- { op_file_min, "s", NULL, 1 },
|
||||
- { op_file_link, "L", NULL, 1 },
|
||||
- { op_file_link, "h", NULL, 1 },
|
||||
- { op_file_xbit, "x", NULL, 1 },
|
||||
+ { op_file_min, "d", NULL, RESTRICTED_FILE_TEST },
|
||||
+ { op_file_min, "e", NULL, RESTRICTED_FILE_TEST },
|
||||
+ { op_file_min, "f", NULL, RESTRICTED_FILE_TEST },
|
||||
+ { op_file_min, "s", NULL, RESTRICTED_FILE_TEST },
|
||||
+ { op_file_link, "L", NULL, RESTRICTED_FILE_TEST },
|
||||
+ { op_file_link, "h", NULL, RESTRICTED_FILE_TEST },
|
||||
+ { op_file_xbit, "x", NULL, RESTRICTED_FILE_TEST },
|
||||
{ op_file_subr, "F", NULL, 0 },
|
||||
{ op_url_subr, "U", NULL, 0 },
|
||||
{ op_url_subr, "A", NULL, 0 },
|
||||
@@ -1753,8 +1763,10 @@ static int core_expr_lookup(ap_expr_lookup_parms *parms)
|
||||
else
|
||||
match = !ap_cstr_casecmp(prov->name, parms->name);
|
||||
if (match) {
|
||||
- if ((parms->flags & AP_EXPR_FLAG_RESTRICTED)
|
||||
- && prov->restricted) {
|
||||
+ if (((parms->flags & AP_EXPR_FLAG_RESTRICTED)
|
||||
+ && (prov->restricted & RESTRICTED_ALL))
|
||||
+ || ((parms->flags & AP_EXPR_FLAG_RESTRICTED_FILE_FUNC)
|
||||
+ && (prov->restricted & RESTRICTED_FILE_FUNC))) {
|
||||
*parms->err =
|
||||
apr_psprintf(parms->ptemp,
|
||||
"%s%s not available in restricted context",
|
||||
13
httpd-2.4.63-CVE-2026-44185.patch
Normal file
13
httpd-2.4.63-CVE-2026-44185.patch
Normal file
@ -0,0 +1,13 @@
|
||||
diff --git a/modules/ssl/ssl_util_ocsp.c b/modules/ssl/ssl_util_ocsp.c
|
||||
index a202a72..9dd12cf 100644
|
||||
--- a/modules/ssl/ssl_util_ocsp.c
|
||||
+++ b/modules/ssl/ssl_util_ocsp.c
|
||||
@@ -133,7 +133,7 @@ static apr_socket_t *send_request(BIO *request, const apr_uri_t *uri,
|
||||
apr_size_t wlen = remain;
|
||||
|
||||
rv = apr_socket_send(sd, wbuf, &wlen);
|
||||
- wbuf += remain;
|
||||
+ wbuf += wlen;
|
||||
remain -= wlen;
|
||||
} while (rv == APR_SUCCESS && remain > 0);
|
||||
|
||||
18
httpd-2.4.63-CVE-2026-44186.patch
Normal file
18
httpd-2.4.63-CVE-2026-44186.patch
Normal file
@ -0,0 +1,18 @@
|
||||
diff --git a/modules/proxy/mod_proxy_ftp.c b/modules/proxy/mod_proxy_ftp.c
|
||||
index 5175e45..5b97c3d 100644
|
||||
--- a/modules/proxy/mod_proxy_ftp.c
|
||||
+++ b/modules/proxy/mod_proxy_ftp.c
|
||||
@@ -1204,10 +1204,12 @@ static int proxy_ftp_handler(request_rec *r, proxy_worker *worker,
|
||||
time_t secs;
|
||||
|
||||
/* Look for a number, preceded by whitespace */
|
||||
- while (*secs_str)
|
||||
+ while (*secs_str) {
|
||||
if ((secs_str==ftpmessage || apr_isspace(secs_str[-1])) &&
|
||||
apr_isdigit(secs_str[0]))
|
||||
break;
|
||||
+ secs_str++;
|
||||
+ }
|
||||
if (*secs_str != '\0') {
|
||||
secs = atol(secs_str);
|
||||
apr_table_addn(r->headers_out, "Retry-After",
|
||||
107
httpd-2.4.63-CVE-2026-44631.patch
Normal file
107
httpd-2.4.63-CVE-2026-44631.patch
Normal file
@ -0,0 +1,107 @@
|
||||
From 7d9f3cfb10b0fe70df7358d26d7b1f374ea1a0cb Mon Sep 17 00:00:00 2001
|
||||
From: Eric Covener <covener@apache.org>
|
||||
Date: Fri, 5 Jun 2026 10:26:23 +0000
|
||||
Subject: [PATCH] Merge r1935014 from trunk:
|
||||
|
||||
ap_regname: restrict to reasonable captures
|
||||
|
||||
Reviewed By: covener, gbechis, jfclere
|
||||
|
||||
|
||||
|
||||
git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1935015 13f79535-47bb-0310-9956-ffa450edef68
|
||||
---
|
||||
include/ap_regex.h | 2 ++
|
||||
modules/proxy/mod_proxy.c | 4 +++-
|
||||
server/core.c | 12 +++++++++---
|
||||
server/util_pcre.c | 11 ++++++++++-
|
||||
4 files changed, 24 insertions(+), 5 deletions(-)
|
||||
|
||||
diff --git a/include/ap_regex.h b/include/ap_regex.h
|
||||
index 50d5abaa0fd..d6afbb17d76 100644
|
||||
--- a/include/ap_regex.h
|
||||
+++ b/include/ap_regex.h
|
||||
@@ -204,6 +204,8 @@ AP_DECLARE(apr_size_t) ap_regerror(int errcode, const ap_regex_t *preg,
|
||||
* @param prefix An optional prefix to add to the returned names. AP_REG_MATCH
|
||||
* is the recommended prefix.
|
||||
* @param upper If non zero, uppercase the names
|
||||
+ * @return number of regex backrefernces returned, -1 for error
|
||||
+ * for successful match, AP_REG_NOMATCH otherwise
|
||||
*/
|
||||
AP_DECLARE(int) ap_regname(const ap_regex_t *preg,
|
||||
apr_array_header_t *names, const char *prefix,
|
||||
diff --git a/modules/proxy/mod_proxy.c b/modules/proxy/mod_proxy.c
|
||||
index 4047d58f2aa..521e8f21837 100644
|
||||
--- a/modules/proxy/mod_proxy.c
|
||||
+++ b/modules/proxy/mod_proxy.c
|
||||
@@ -2886,7 +2886,9 @@ static const char *proxysection(cmd_parms *cmd, void *mconfig, const char *arg)
|
||||
|
||||
if (r) {
|
||||
conf->refs = apr_array_make(cmd->pool, 8, sizeof(char *));
|
||||
- ap_regname(r, conf->refs, AP_REG_MATCH, 1);
|
||||
+ if (ap_regname(r, conf->refs, AP_REG_MATCH, 1) < 0) {
|
||||
+ return "Error processing regex captures";
|
||||
+ }
|
||||
}
|
||||
|
||||
ap_add_per_proxy_conf(cmd->server, new_dir_conf);
|
||||
diff --git a/server/core.c b/server/core.c
|
||||
index 925bb23018d..df270b019e4 100644
|
||||
--- a/server/core.c
|
||||
+++ b/server/core.c
|
||||
@@ -2526,7 +2526,9 @@ static const char *dirsection(cmd_parms *cmd, void *mconfig, const char *arg)
|
||||
|
||||
if (r) {
|
||||
conf->refs = apr_array_make(cmd->pool, 8, sizeof(char *));
|
||||
- ap_regname(r, conf->refs, AP_REG_MATCH, 1);
|
||||
+ if (ap_regname(r, conf->refs, AP_REG_MATCH, 1) < 0) {
|
||||
+ return "Error processing regex captures";
|
||||
+ }
|
||||
}
|
||||
|
||||
/* Make this explicit - the "/" root has 0 elements, that is, we
|
||||
@@ -2607,7 +2609,9 @@ static const char *urlsection(cmd_parms *cmd, void *mconfig, const char *arg)
|
||||
|
||||
if (r) {
|
||||
conf->refs = apr_array_make(cmd->pool, 8, sizeof(char *));
|
||||
- ap_regname(r, conf->refs, AP_REG_MATCH, 1);
|
||||
+ if (ap_regname(r, conf->refs, AP_REG_MATCH, 1) < 0) {
|
||||
+ return "Error processing regex captures";
|
||||
+ }
|
||||
}
|
||||
|
||||
ap_add_per_url_conf(cmd->server, new_url_conf);
|
||||
@@ -2694,7 +2698,9 @@ static const char *filesection(cmd_parms *cmd, void *mconfig, const char *arg)
|
||||
|
||||
if (r) {
|
||||
conf->refs = apr_array_make(cmd->pool, 8, sizeof(char *));
|
||||
- ap_regname(r, conf->refs, AP_REG_MATCH, 1);
|
||||
+ if (ap_regname(r, conf->refs, AP_REG_MATCH, 1) < 0) {
|
||||
+ return "Error processing regex captures";
|
||||
+ }
|
||||
}
|
||||
|
||||
ap_add_file_conf(cmd->pool, (core_dir_config *)mconfig, new_file_conf);
|
||||
diff --git a/server/util_pcre.c b/server/util_pcre.c
|
||||
index 0a9dc50112d..46e82112a58 100644
|
||||
--- a/server/util_pcre.c
|
||||
+++ b/server/util_pcre.c
|
||||
@@ -521,7 +521,16 @@ AP_DECLARE(int) ap_regname(const ap_regex_t *preg,
|
||||
|
||||
for (i = 0; i < namecount; i++) {
|
||||
const char *offset = nametable + i * nameentrysize;
|
||||
- int capture = ((offset[0] << 8) + offset[1]);
|
||||
+ int capture = (((unsigned char)offset[0] << 8) + (unsigned char)offset[1]);
|
||||
+
|
||||
+ /* Sanity check: reject unreasonably large capture group numbers.
|
||||
+ * PCRE allows up to 65535 groups, but such large numbers would
|
||||
+ * cause excessive memory allocation. Limit to a reasonable maximum.
|
||||
+ */
|
||||
+ if (capture > 1024) {
|
||||
+ return -1;
|
||||
+ }
|
||||
+
|
||||
while (names->nelts <= capture) {
|
||||
apr_array_push(names);
|
||||
}
|
||||
|
||||
41
httpd.spec
41
httpd.spec
@ -25,7 +25,7 @@
|
||||
Summary: Apache HTTP Server
|
||||
Name: httpd
|
||||
Version: 2.4.63
|
||||
Release: 13%{?dist}.1
|
||||
Release: 13%{?dist}.4
|
||||
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
|
||||
@ -144,6 +144,27 @@ Patch209: httpd-2.4.63-CVE-2026-33857.patch
|
||||
Patch210: httpd-2.4.63-CVE-2026-34032.patch
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=2464940
|
||||
Patch211: httpd-2.4.63-CVE-2026-34059.patch
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=2486395
|
||||
Patch212: httpd-2.4.63-CVE-2026-34356.patch
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=2486411
|
||||
Patch213: httpd-2.4.63-CVE-2026-42536.patch
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=2486414
|
||||
Patch214: httpd-2.4.63-CVE-2026-34355.patch
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=2486397
|
||||
Patch215: httpd-2.4.63-CVE-2026-44185.patch
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=2486399
|
||||
Patch216: httpd-2.4.63-CVE-2026-44631.patch
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=2374549
|
||||
Patch217: httpd-2.4.63-CVE-2024-42516.patch
|
||||
# https://bugzilla.redhat.com/show_bug.cgi?id=2465296
|
||||
Patch218: httpd-2.4.63-CVE-2026-29169.patch
|
||||
Patch219: httpd-2.4.63-CVE-2026-43951.patch
|
||||
Patch220: httpd-2.4.63-CVE-2026-44119.patch
|
||||
Patch221: httpd-2.4.63-CVE-2026-42535.patch
|
||||
Patch222: httpd-2.4.63-CVE-2026-24072.patch
|
||||
Patch223: httpd-2.4.63-CVE-2026-33006.patch
|
||||
Patch224: httpd-2.4.63-CVE-2026-44186.patch
|
||||
|
||||
|
||||
# Apache-2.0: everything
|
||||
# BSD-3-Clause: util_pcre.c, ap_regex.h
|
||||
@ -865,6 +886,24 @@ exit $rv
|
||||
%{_rpmconfigdir}/macros.d/macros.httpd
|
||||
|
||||
%changelog
|
||||
* Tue Jun 23 2026 Luboš Uhliarik <luhliari@redhat.com> - 2.4.63-13.4
|
||||
- Resolves: RHEL-186221 - httpd: Apache HTTP Server: Heap-based Buffer Overflow
|
||||
via malicious backend servers (CVE-2026-34356)
|
||||
- Resolves: RHEL-186195 - httpd: Apache HTTP Server: Heap-based Buffer Overflow
|
||||
via untrusted content in mod_xml2enc (CVE-2026-42536)
|
||||
- Resolves: RHEL-186182 - httpd: Apache HTTP Server: Buffer overflow in
|
||||
mod_proxy_html allows security bypass (CVE-2026-34355)
|
||||
- Resolves: RHEL-186158 - httpd: Apache HTTP Server: Buffer Over-read via
|
||||
outbound OCSP requests to attacker-controlled server (CVE-2026-44185)
|
||||
- Resolves: RHEL-184305 - httpd: Apache HTTP Server: Denial of Service via
|
||||
crafted regular expressions (CVE-2026-44631)
|
||||
- Resolves : RHEL-182581 - httpd: incomplete fix for
|
||||
CVE-2023-38709 (CVE-2024-42516)
|
||||
- Resolves: RHEL-175621 - httpd: NULL pointer dereference via specially crafted
|
||||
request (CVE-2026-29169)
|
||||
- Also addresses CVE-2026-44119, CVE-2026-44186, CVE-2026-42535,
|
||||
CVE-2026-24072, CVE-2026-33006, CVE-2026-43951
|
||||
|
||||
* Mon May 11 2026 Luboš Uhliarik <luhliari@redhat.com> - 2.4.63-13.1
|
||||
- Resolves: RHEL-173549 - httpd: Apache HTTP Server mod_proxy_ajp: Arbitrary
|
||||
code execution via heap-based buffer overflow (CVE-2026-28780)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user