1
0
mirror of https://github.com/apache/httpd.git synced 2025-11-09 15:21:02 +03:00

Add some features to ap_expr for use by mod_include:

* a restricted mode that does not allow to bypass request access restrictions
 * new variables DOCUMENT_URI (alias for REQUEST_URI), LAST_MODIFIED
 * -A as an alias for -U
 * an additional data entry in ap_expr_eval_ctx_t for use by the consumer
 * an extensible ap_expr_exec_ctx() API that allows to use that data entry


git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@1128564 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Stefan Fritsch
2011-05-28 07:01:47 +00:00
parent 4895498e38
commit a8c135f27e
5 changed files with 143 additions and 69 deletions

View File

@@ -2,6 +2,13 @@
Changes with Apache 2.3.13 Changes with Apache 2.3.13
*) core: Add some features to ap_expr for use by mod_include: a restricted
mode that does not allow to bypass request access restrictions; new
variables DOCUMENT_URI (alias for REQUEST_URI), LAST_MODIFIED; -A as an
alias for -U; an additional data entry in ap_expr_eval_ctx_t for use by
the consumer; an extensible ap_expr_exec_ctx() API that allows to use that
data entry. [Stefan Fritsch]
*) mod_include: Merge directory configs instead of one SSI* config directive *) mod_include: Merge directory configs instead of one SSI* config directive
causing all other per-directory SSI* config directives to be reset. causing all other per-directory SSI* config directives to be reset.
[Stefan Fritsch] [Stefan Fritsch]

View File

@@ -158,6 +158,8 @@ listfunction ::= listfuncname "<strong>(</strong>" word "<strong>)</strong>"
<td>The scheme part of the request's URI</td></tr> <td>The scheme part of the request's URI</td></tr>
<tr><td><code>REQUEST_URI</code></td> <tr><td><code>REQUEST_URI</code></td>
<td>The URI of the request</td></tr> <td>The URI of the request</td></tr>
<tr><td><code>DOCUMENT_URI</code></td>
<td>Same as REQUEST_URI</td></tr>
<tr><td><code>REQUEST_FILENAME</code></td> <tr><td><code>REQUEST_FILENAME</code></td>
<td>The full local filesystem path to the file or script matching the <td>The full local filesystem path to the file or script matching the
request, if this has already been determined by the server at the request, if this has already been determined by the server at the
@@ -166,6 +168,11 @@ listfunction ::= listfuncname "<strong>(</strong>" word "<strong>)</strong>"
<code>REQUEST_URI</code> </td></tr> <code>REQUEST_URI</code> </td></tr>
<tr><td><code>SCRIPT_FILENAME</code></td> <tr><td><code>SCRIPT_FILENAME</code></td>
<td>Same as <code>REQUEST_FILENAME</code></td></tr> <td>Same as <code>REQUEST_FILENAME</code></td></tr>
<tr><td><code>LAST_MODIFIED</code></td>
<td>The date and time of last modification of the file in the format
<code>20101231235959</code>, if this has already been determined by
the server at the time <code>LAST_MODIFIED</code> is referenced.
</td></tr>
<tr><td><code>SCRIPT_USER</code></td> <tr><td><code>SCRIPT_USER</code></td>
<td>The user name of the owner of the script.</td></tr> <td>The user name of the owner of the script.</td></tr>
<tr><td><code>SCRIPT_GROUP</code></td> <tr><td><code>SCRIPT_GROUP</code></td>
@@ -374,6 +381,8 @@ listfunction ::= listfuncname "<strong>(</strong>" word "<strong>)</strong>"
currently-configured access controls for that path. This uses an currently-configured access controls for that path. This uses an
internal subrequest to do the check, so use it with care - it can internal subrequest to do the check, so use it with care - it can
impact your server's performance!</td></tr> impact your server's performance!</td></tr>
<tr><td><code>-A</code></td>
<td>Alias for <code>-U</code></td></tr>
<tr><td><code>-n</code></td> <tr><td><code>-n</code></td>
<td>True if string is not empty</td></tr> <td>True if string is not empty</td></tr>
<tr><td><code>-z</code></td> <tr><td><code>-z</code></td>

View File

@@ -59,6 +59,11 @@ typedef struct {
#define AP_EXPR_FLAGS_SSL_EXPR_COMPAT 1 #define AP_EXPR_FLAGS_SSL_EXPR_COMPAT 1
/** Don't add siginificant request headers to the Vary response header */ /** Don't add siginificant request headers to the Vary response header */
#define AP_EXPR_FLAGS_DONT_VARY 2 #define AP_EXPR_FLAGS_DONT_VARY 2
/** Don't allow functions/vars that bypass the current request's access
* restrictions or would otherwise leak confidential information.
* Used by e.g. mod_include.
*/
#define AP_EXPR_FLAGS_RESTRICTED 4
/** /**
@@ -119,8 +124,20 @@ typedef struct {
* interested in this information. * interested in this information.
*/ */
const char **vary_this; const char **vary_this;
/** Arbitrary context data provided by the caller for custom functions */
void *data;
} ap_expr_eval_ctx_t; } ap_expr_eval_ctx_t;
/**
* Evaluate a parse tree, full featured version
* @param ctx The evaluation context with all data filled in
* @return > 0 if expression evaluates to true, == 0 if false, < 0 on error
* @note *ctx->err will be set to NULL on success, or to an error message on
* error
* @note request headers used during evaluation will be added to the Vary:
* response header if ctx->vary_this is set.
*/
AP_DECLARE(int) ap_expr_exec_ctx(ap_expr_eval_ctx_t *ctx);
/** /**
* The parser can be extended with variable lookup, functions, and * The parser can be extended with variable lookup, functions, and

View File

@@ -323,6 +323,8 @@
* 20110329.3 (2.3.12-dev) Add format field to ap_errorlog_info. * 20110329.3 (2.3.12-dev) Add format field to ap_errorlog_info.
* 20110329.4 (2.3.13-dev) bgrowth and max_balancers to proxy_server_conf. * 20110329.4 (2.3.13-dev) bgrowth and max_balancers to proxy_server_conf.
* 20110329.5 (2.3.13-dev) Add ap_regexec_len() * 20110329.5 (2.3.13-dev) Add ap_regexec_len()
* 20110329.6 (2.3.13-dev) Add AP_EXPR_FLAGS_RESTRICTED, ap_expr_eval_ctx_t->data,
* ap_expr_exec_ctx()
*/ */
#define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */ #define MODULE_MAGIC_COOKIE 0x41503234UL /* "AP24" */
@@ -330,7 +332,7 @@
#ifndef MODULE_MAGIC_NUMBER_MAJOR #ifndef MODULE_MAGIC_NUMBER_MAJOR
#define MODULE_MAGIC_NUMBER_MAJOR 20110329 #define MODULE_MAGIC_NUMBER_MAJOR 20110329
#endif #endif
#define MODULE_MAGIC_NUMBER_MINOR 5 /* 0...n */ #define MODULE_MAGIC_NUMBER_MINOR 6 /* 0...n */
/** /**
* Determine if the server's current MODULE_MAGIC_NUMBER is at least a * Determine if the server's current MODULE_MAGIC_NUMBER is at least a

View File

@@ -384,7 +384,7 @@ static ap_expr_t *ap_expr_info_make(int type, const char *name,
ap_expr_t *info = apr_palloc(ctx->pool, sizeof(ap_expr_t)); ap_expr_t *info = apr_palloc(ctx->pool, sizeof(ap_expr_t));
ap_expr_lookup_parms parms; ap_expr_lookup_parms parms;
parms.type = type; parms.type = type;
parms.flags = 0; parms.flags = ctx->flags;
parms.pool = ctx->pool; parms.pool = ctx->pool;
parms.ptemp = ctx->ptemp; parms.ptemp = ctx->ptemp;
parms.name = name; parms.name = name;
@@ -691,12 +691,47 @@ AP_DECLARE(int) ap_expr_exec(request_rec *r, const ap_expr_info_t *info,
return ap_expr_exec_re(r, info, 0, NULL, NULL, err); return ap_expr_exec_re(r, info, 0, NULL, NULL, err);
} }
AP_DECLARE(int) ap_expr_exec_ctx(ap_expr_eval_ctx_t *ctx)
{
int rc;
AP_DEBUG_ASSERT(ctx->p != NULL);
/* XXX: allow r, c == NULL */
AP_DEBUG_ASSERT(ctx->r != NULL);
AP_DEBUG_ASSERT(ctx->c != NULL);
AP_DEBUG_ASSERT(ctx->s != NULL);
AP_DEBUG_ASSERT(ctx->err != NULL);
AP_DEBUG_ASSERT(ctx->info != NULL);
if (ctx->re_pmatch) {
AP_DEBUG_ASSERT(ctx->re_source != NULL);
AP_DEBUG_ASSERT(ctx->re_nmatch > 0);
}
*ctx->err = NULL;
rc = ap_expr_eval(ctx, ctx->info->root_node);
if (*ctx->err != NULL) {
ap_log_rerror(LOG_MARK(ctx->info), APLOG_ERR, 0, ctx->r,
"Evaluation of expression from %s:%d failed: %s",
ctx->info->filename, ctx->info->line_number, *ctx->err);
return -1;
} else {
rc = rc ? 1 : 0;
ap_log_rerror(LOG_MARK(ctx->info), APLOG_TRACE4, 0, ctx->r,
"Evaluation of expression from %s:%d gave: %d",
ctx->info->filename, ctx->info->line_number, rc);
if (ctx->vary_this)
apr_table_merge(ctx->r->headers_out, "Vary", *ctx->vary_this);
return rc;
}
}
AP_DECLARE(int) ap_expr_exec_re(request_rec *r, const ap_expr_info_t *info, AP_DECLARE(int) ap_expr_exec_re(request_rec *r, const ap_expr_info_t *info,
apr_size_t nmatch, ap_regmatch_t *pmatch, apr_size_t nmatch, ap_regmatch_t *pmatch,
const char **source, const char **err) const char **source, const char **err)
{ {
ap_expr_eval_ctx_t ctx; ap_expr_eval_ctx_t ctx;
int rc;
int dont_vary = (info->flags & AP_EXPR_FLAGS_DONT_VARY); int dont_vary = (info->flags & AP_EXPR_FLAGS_DONT_VARY);
const char *tmp_source = NULL, *vary_this = NULL; const char *tmp_source = NULL, *vary_this = NULL;
ap_regmatch_t tmp_pmatch[10]; ap_regmatch_t tmp_pmatch[10];
@@ -711,35 +746,15 @@ AP_DECLARE(int) ap_expr_exec_re(request_rec *r, const ap_expr_info_t *info,
ctx.re_pmatch = pmatch; ctx.re_pmatch = pmatch;
ctx.re_source = source; ctx.re_source = source;
ctx.vary_this = dont_vary ? NULL : &vary_this; ctx.vary_this = dont_vary ? NULL : &vary_this;
ctx.data = NULL;
if (!pmatch) { if (!pmatch) {
ctx.re_nmatch = 10; ctx.re_nmatch = 10;
ctx.re_pmatch = tmp_pmatch; ctx.re_pmatch = tmp_pmatch;
ctx.re_source = &tmp_source; ctx.re_source = &tmp_source;
} }
else {
AP_DEBUG_ASSERT(source != NULL);
AP_DEBUG_ASSERT(nmatch > 0);
}
*err = NULL; return ap_expr_exec_ctx(&ctx);
rc = ap_expr_eval(&ctx, info->root_node);
if (*err != NULL) {
ap_log_rerror(LOG_MARK(info), APLOG_ERR, 0, r,
"Evaluation of expression from %s:%d failed: %s",
info->filename, info->line_number, *err);
return -1;
} else {
rc = rc ? 1 : 0;
ap_log_rerror(LOG_MARK(info), APLOG_TRACE4, 0, r,
"Evaluation of expression from %s:%d gave: %d",
info->filename, info->line_number, rc);
if (vary_this)
apr_table_merge(r->headers_out, "Vary", vary_this);
return rc;
}
} }
static void add_vary(ap_expr_eval_ctx_t *ctx, const char *name) static void add_vary(ap_expr_eval_ctx_t *ctx, const char *name)
@@ -911,12 +926,12 @@ static int op_file_min(ap_expr_eval_ctx_t *ctx, const void *data, const char *ar
apr_finfo_t sb; apr_finfo_t sb;
const char *name = (const char *)data; const char *name = (const char *)data;
if (apr_stat(&sb, arg, APR_FINFO_MIN, ctx->p) != APR_SUCCESS) if (apr_stat(&sb, arg, APR_FINFO_MIN, ctx->p) != APR_SUCCESS)
return 0; return FALSE;
switch (name[0]) { switch (name[0]) {
case 'd': case 'd':
return (sb.filetype == APR_DIR); return (sb.filetype == APR_DIR);
case 'e': case 'e':
return 1; return TRUE;
case 'f': case 'f':
return (sb.filetype == APR_REG); return (sb.filetype == APR_REG);
case 's': case 's':
@@ -924,7 +939,7 @@ static int op_file_min(ap_expr_eval_ctx_t *ctx, const void *data, const char *ar
default: default:
ap_assert(0); ap_assert(0);
} }
return 0; return FALSE;
} }
static int op_file_link(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg) static int op_file_link(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
@@ -933,10 +948,10 @@ static int op_file_link(ap_expr_eval_ctx_t *ctx, const void *data, const char *a
#if !defined(OS2) #if !defined(OS2)
if (apr_stat(&sb, arg, APR_FINFO_MIN | APR_FINFO_LINK, ctx->p) == APR_SUCCESS if (apr_stat(&sb, arg, APR_FINFO_MIN | APR_FINFO_LINK, ctx->p) == APR_SUCCESS
&& sb.filetype == APR_LNK) { && sb.filetype == APR_LNK) {
return 1; return TRUE;
} }
#endif #endif
return 0; return FALSE;
} }
static int op_file_xbit(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg) static int op_file_xbit(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
@@ -944,24 +959,24 @@ static int op_file_xbit(ap_expr_eval_ctx_t *ctx, const void *data, const char *a
apr_finfo_t sb; apr_finfo_t sb;
if (apr_stat(&sb, arg, APR_FINFO_PROT| APR_FINFO_LINK, ctx->p) == APR_SUCCESS if (apr_stat(&sb, arg, APR_FINFO_PROT| APR_FINFO_LINK, ctx->p) == APR_SUCCESS
&& (sb.protection & (APR_UEXECUTE | APR_GEXECUTE | APR_WEXECUTE))) { && (sb.protection & (APR_UEXECUTE | APR_GEXECUTE | APR_WEXECUTE))) {
return 1; return TRUE;
} }
return 0; return FALSE;
} }
static int op_url_subr(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg) static int op_url_subr(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
{ {
int rc = 0; int rc = FALSE;
request_rec *rsub, *r = ctx->r; request_rec *rsub, *r = ctx->r;
if (!r) if (!r)
return 0; return FALSE;
/* avoid some infinite recursions */ /* avoid some infinite recursions */
if (r->main && r->main->uri && r->uri && strcmp(r->main->uri, r->uri) == 0) if (r->main && r->main->uri && r->uri && strcmp(r->main->uri, r->uri) == 0)
return 0; return FALSE;
rsub = ap_sub_req_lookup_uri(arg, r, NULL); rsub = ap_sub_req_lookup_uri(arg, r, NULL);
if (rsub->status < 400) { if (rsub->status < 400) {
rc = 1; rc = TRUE;
} }
ap_log_rerror(LOG_MARK(ctx->info), APLOG_TRACE5, 0, r, ap_log_rerror(LOG_MARK(ctx->info), APLOG_TRACE5, 0, r,
"Subrequest for -U %s at %s:%d gave status: %d", "Subrequest for -U %s at %s:%d gave status: %d",
@@ -973,16 +988,16 @@ static int op_url_subr(ap_expr_eval_ctx_t *ctx, const void *data, const char *ar
static int op_file_subr(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg) static int op_file_subr(ap_expr_eval_ctx_t *ctx, const void *data, const char *arg)
{ {
int rc = 0; int rc = FALSE;
apr_finfo_t sb; apr_finfo_t sb;
request_rec *rsub, *r = ctx->r; request_rec *rsub, *r = ctx->r;
if (!r) if (!r)
return 0; return FALSE;
rsub = ap_sub_req_lookup_file(arg, r, NULL); rsub = ap_sub_req_lookup_file(arg, r, NULL);
if (rsub->status < 300 && if (rsub->status < 300 &&
/* double-check that file exists since default result is 200 */ /* double-check that file exists since default result is 200 */
apr_stat(&sb, rsub->filename, APR_FINFO_MIN, ctx->p) == APR_SUCCESS) { apr_stat(&sb, rsub->filename, APR_FINFO_MIN, ctx->p) == APR_SUCCESS) {
rc = 1; rc = TRUE;
} }
ap_log_rerror(LOG_MARK(ctx->info), APLOG_TRACE5, 0, r, ap_log_rerror(LOG_MARK(ctx->info), APLOG_TRACE5, 0, r,
"Subrequest for -F %s at %s:%d gave status: %d", "Subrequest for -F %s at %s:%d gave status: %d",
@@ -1064,6 +1079,8 @@ static const char *request_var_names[] = {
"REQUEST_LOG_ID", /* 20 */ "REQUEST_LOG_ID", /* 20 */
"SCRIPT_USER", /* 21 */ "SCRIPT_USER", /* 21 */
"SCRIPT_GROUP", /* 22 */ "SCRIPT_GROUP", /* 22 */
"DOCUMENT_URI", /* 23 */
"LAST_MODIFIED", /* 24 */
NULL NULL
}; };
@@ -1132,6 +1149,17 @@ static const char *request_var_fn(ap_expr_eval_ctx_t *ctx, const void *data)
apr_gid_name_get(&result, r->finfo.group, ctx->p); apr_gid_name_get(&result, r->finfo.group, ctx->p);
return result; return result;
} }
case 23:
return r->uri;
case 24:
{
apr_time_exp_t tm;
apr_time_exp_lt(&tm, r->mtime);
return apr_psprintf(ctx->p, "%02d%02d%02d%02d%02d%02d%02d",
(tm.tm_year / 100) + 19, (tm.tm_year % 100),
tm.tm_mon+1, tm.tm_mday, tm.tm_hour, tm.tm_min,
tm.tm_sec);
}
default: default:
ap_assert(0); ap_assert(0);
return NULL; return NULL;
@@ -1326,6 +1354,7 @@ struct expr_provider_single {
const void *func; const void *func;
const char *name; const char *name;
ap_expr_lookup_fn_t *arg_parsing_func; ap_expr_lookup_fn_t *arg_parsing_func;
int restricted;
}; };
struct expr_provider_multi { struct expr_provider_multi {
@@ -1342,46 +1371,47 @@ static const struct expr_provider_multi var_providers[] = {
}; };
static const struct expr_provider_single string_func_providers[] = { static const struct expr_provider_single string_func_providers[] = {
{ osenv_func, "osenv", NULL }, { osenv_func, "osenv", NULL, 0 },
{ env_func, "env", NULL }, { env_func, "env", NULL, 0 },
{ req_table_func, "resp", NULL }, { req_table_func, "resp", NULL, 0 },
{ req_table_func, "req", NULL }, { req_table_func, "req", NULL, 0 },
/* 'http' as alias for 'req' for compatibility with ssl_expr */ /* 'http' as alias for 'req' for compatibility with ssl_expr */
{ req_table_func, "http", NULL }, { req_table_func, "http", NULL, 0 },
{ req_table_func, "note", NULL }, { req_table_func, "note", NULL, 0 },
{ req_table_func, "reqenv", NULL }, { req_table_func, "reqenv", NULL, 0 },
{ tolower_func, "tolower", NULL }, { tolower_func, "tolower", NULL, 0 },
{ toupper_func, "toupper", NULL }, { toupper_func, "toupper", NULL, 0 },
{ escape_func, "escape", NULL }, { escape_func, "escape", NULL, 0 },
{ unescape_func, "unescape", NULL }, { unescape_func, "unescape", NULL, 0 },
{ file_func, "file", NULL }, { file_func, "file", NULL, 1 },
{ filesize_func, "filesize", NULL }, { filesize_func, "filesize", NULL, 1 },
{ NULL, NULL, NULL} { NULL, NULL, NULL}
}; };
/* XXX: base64 encode/decode ? */ /* XXX: base64 encode/decode ? */
static const struct expr_provider_single unary_op_providers[] = { static const struct expr_provider_single unary_op_providers[] = {
{ op_nz, "n", NULL }, { op_nz, "n", NULL, 0 },
{ op_nz, "z", NULL }, { op_nz, "z", NULL, 0 },
{ op_R, "R", subnet_parse_arg }, { op_R, "R", subnet_parse_arg, 0 },
{ op_T, "T", NULL }, { op_T, "T", NULL, 0 },
{ op_file_min, "d", NULL }, { op_file_min, "d", NULL, 1 },
{ op_file_min, "e", NULL }, { op_file_min, "e", NULL, 1 },
{ op_file_min, "f", NULL }, { op_file_min, "f", NULL, 1 },
{ op_file_min, "s", NULL }, { op_file_min, "s", NULL, 1 },
{ op_file_link, "L", NULL }, { op_file_link, "L", NULL, 1 },
{ op_file_link, "h", NULL }, { op_file_link, "h", NULL, 1 },
{ op_file_xbit, "x", NULL }, { op_file_xbit, "x", NULL, 1 },
{ op_file_subr, "F", NULL }, { op_file_subr, "F", NULL, 0 },
{ op_url_subr, "U", NULL }, { op_url_subr, "U", NULL, 0 },
{ op_url_subr, "A", NULL, 0 },
{ NULL, NULL, NULL } { NULL, NULL, NULL }
}; };
static const struct expr_provider_single binary_op_providers[] = { static const struct expr_provider_single binary_op_providers[] = {
{ op_ipmatch, "ipmatch", subnet_parse_arg }, { op_ipmatch, "ipmatch", subnet_parse_arg, 0 },
{ op_fnmatch, "fnmatch", NULL }, { op_fnmatch, "fnmatch", NULL, 0 },
{ op_strmatch, "strmatch", NULL }, { op_strmatch, "strmatch", NULL, 0 },
{ op_strcmatch, "strcmatch", NULL }, { op_strcmatch, "strcmatch", NULL, 0 },
{ NULL, NULL, NULL } { NULL, NULL, NULL }
}; };
@@ -1423,6 +1453,15 @@ static int core_expr_lookup(ap_expr_lookup_parms *parms)
} }
while (prov->func) { while (prov->func) {
if (strcasecmp(prov->name, parms->name) == 0) { if (strcasecmp(prov->name, parms->name) == 0) {
if ((parms->flags & AP_EXPR_FLAGS_RESTRICTED)
&& prov->restricted) {
*parms->err =
apr_psprintf(parms->ptemp,
"%s%s not available in restricted context",
(parms->type == AP_EXPR_FUNC_STRING) ? "" : "-",
prov->name);
return !OK;
}
*parms->func = prov->func; *parms->func = prov->func;
if (prov->arg_parsing_func) { if (prov->arg_parsing_func) {
return prov->arg_parsing_func(parms); return prov->arg_parsing_func(parms);