From f63f26aff6aa747357b84b5bd09c45325fa7f9ba Mon Sep 17 00:00:00 2001
From: Eric Covener <covener@apache.org>
Date: Fri, 5 Jun 2026 10:28:53 +0000
Subject: [PATCH] Merge r1935016 from trunk:

restrict per-dir file funcs centrally

Reviewed By: covener, jorton, jfclere


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/branches/2.4.x@1935017 13f79535-47bb-0310-9956-ffa450edef68

Conflict:no
Reference:https://github.com/apache/httpd/commit/f63f26aff6aa747357b84b5bd09c45325fa7f9ba
---
 include/ap_expr.h               |  2 ++
 modules/mappers/mod_rewrite.c   |  2 --
 modules/metadata/mod_setenvif.c |  5 -----
 modules/proxy/mod_proxy_fcgi.c  |  5 -----
 server/util_expr_eval.c         | 36 ++++++++++++++++++++++-----------
 5 files changed, 26 insertions(+), 24 deletions(-)

diff --git a/include/ap_expr.h b/include/ap_expr.h
index 8e57fcdadcc..16138c9f6cd 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/modules/mappers/mod_rewrite.c b/modules/mappers/mod_rewrite.c
index 9b1c5b4b6ad..f1bc03cb2ee 100644
--- a/modules/mappers/mod_rewrite.c
+++ b/modules/mappers/mod_rewrite.c
@@ -3682,8 +3682,6 @@ static const char *cmd_rewritecond(cmd_parms *cmd, void *in_dconf,
         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 "
diff --git a/modules/metadata/mod_setenvif.c b/modules/metadata/mod_setenvif.c
index b74c9c07dd9..0f44e0d01b3 100644
--- a/modules/metadata/mod_setenvif.c
+++ b/modules/metadata/mod_setenvif.c
@@ -424,11 +424,6 @@ static const char *add_setenvifexpr(cmd_parms *cmd, void *mconfig,
     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.
      * cmd->path == NULL means we're in server-wide context; otherwise,
diff --git a/modules/proxy/mod_proxy_fcgi.c b/modules/proxy/mod_proxy_fcgi.c
index ef090ddc771..82e0c1e4218 100644
--- a/modules/proxy/mod_proxy_fcgi.c
+++ b/modules/proxy/mod_proxy_fcgi.c
@@ -1340,11 +1340,6 @@ static const char *cmd_setenv(cmd_parms *cmd, void *in_dconf,
     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, flags, &err, NULL);
     if (err) {
diff --git a/server/util_expr_eval.c b/server/util_expr_eval.c
index 038d5a06d78..d4cbf92c76e 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)
@@ -1661,11 +1667,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 {
@@ -1695,8 +1705,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 },
@@ -1712,13 +1722,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 },
@@ -1776,8 +1786,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",