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
*) 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
causing all other per-directory SSI* config directives to be reset.
[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>
<tr><td><code>REQUEST_URI</code></td>
<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>
<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
@ -166,6 +168,11 @@ listfunction ::= listfuncname "<strong>(</strong>" word "<strong>)</strong>"
<code>REQUEST_URI</code> </td></tr>
<tr><td><code>SCRIPT_FILENAME</code></td>
<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>
<td>The user name of the owner of the script.</td></tr>
<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
internal subrequest to do the check, so use it with care - it can
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>
<td>True if string is not empty</td></tr>
<tr><td><code>-z</code></td>

View File

@ -59,6 +59,11 @@ typedef struct {
#define AP_EXPR_FLAGS_SSL_EXPR_COMPAT 1
/** Don't add siginificant request headers to the Vary response header */
#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.
*/
const char **vary_this;
/** Arbitrary context data provided by the caller for custom functions */
void *data;
} 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

View File

@ -323,6 +323,8 @@
* 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.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" */
@ -330,7 +332,7 @@
#ifndef MODULE_MAGIC_NUMBER_MAJOR
#define MODULE_MAGIC_NUMBER_MAJOR 20110329
#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

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_lookup_parms parms;
parms.type = type;
parms.flags = 0;
parms.flags = ctx->flags;
parms.pool = ctx->pool;
parms.ptemp = ctx->ptemp;
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);
}
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,
apr_size_t nmatch, ap_regmatch_t *pmatch,
const char **source, const char **err)
{
ap_expr_eval_ctx_t ctx;
int rc;
int dont_vary = (info->flags & AP_EXPR_FLAGS_DONT_VARY);
const char *tmp_source = NULL, *vary_this = NULL;
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_source = source;
ctx.vary_this = dont_vary ? NULL : &vary_this;
ctx.data = NULL;
if (!pmatch) {
ctx.re_nmatch = 10;
ctx.re_pmatch = tmp_pmatch;
ctx.re_source = &tmp_source;
}
else {
AP_DEBUG_ASSERT(source != NULL);
AP_DEBUG_ASSERT(nmatch > 0);
}
*err = NULL;
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;
}
return ap_expr_exec_ctx(&ctx);
}
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;
const char *name = (const char *)data;
if (apr_stat(&sb, arg, APR_FINFO_MIN, ctx->p) != APR_SUCCESS)
return 0;
return FALSE;
switch (name[0]) {
case 'd':
return (sb.filetype == APR_DIR);
case 'e':
return 1;
return TRUE;
case 'f':
return (sb.filetype == APR_REG);
case 's':
@ -924,7 +939,7 @@ static int op_file_min(ap_expr_eval_ctx_t *ctx, const void *data, const char *ar
default:
ap_assert(0);
}
return 0;
return FALSE;
}
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 (apr_stat(&sb, arg, APR_FINFO_MIN | APR_FINFO_LINK, ctx->p) == APR_SUCCESS
&& sb.filetype == APR_LNK) {
return 1;
return TRUE;
}
#endif
return 0;
return FALSE;
}
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;
if (apr_stat(&sb, arg, APR_FINFO_PROT| APR_FINFO_LINK, ctx->p) == APR_SUCCESS
&& (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)
{
int rc = 0;
int rc = FALSE;
request_rec *rsub, *r = ctx->r;
if (!r)
return 0;
return FALSE;
/* avoid some infinite recursions */
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);
if (rsub->status < 400) {
rc = 1;
rc = TRUE;
}
ap_log_rerror(LOG_MARK(ctx->info), APLOG_TRACE5, 0, r,
"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)
{
int rc = 0;
int rc = FALSE;
apr_finfo_t sb;
request_rec *rsub, *r = ctx->r;
if (!r)
return 0;
return FALSE;
rsub = ap_sub_req_lookup_file(arg, r, NULL);
if (rsub->status < 300 &&
/* double-check that file exists since default result is 200 */
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,
"Subrequest for -F %s at %s:%d gave status: %d",
@ -1064,6 +1079,8 @@ static const char *request_var_names[] = {
"REQUEST_LOG_ID", /* 20 */
"SCRIPT_USER", /* 21 */
"SCRIPT_GROUP", /* 22 */
"DOCUMENT_URI", /* 23 */
"LAST_MODIFIED", /* 24 */
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);
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:
ap_assert(0);
return NULL;
@ -1326,6 +1354,7 @@ struct expr_provider_single {
const void *func;
const char *name;
ap_expr_lookup_fn_t *arg_parsing_func;
int restricted;
};
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[] = {
{ osenv_func, "osenv", NULL },
{ env_func, "env", NULL },
{ req_table_func, "resp", NULL },
{ req_table_func, "req", NULL },
{ osenv_func, "osenv", NULL, 0 },
{ env_func, "env", NULL, 0 },
{ req_table_func, "resp", NULL, 0 },
{ req_table_func, "req", NULL, 0 },
/* 'http' as alias for 'req' for compatibility with ssl_expr */
{ req_table_func, "http", NULL },
{ req_table_func, "note", NULL },
{ req_table_func, "reqenv", NULL },
{ tolower_func, "tolower", NULL },
{ toupper_func, "toupper", NULL },
{ escape_func, "escape", NULL },
{ unescape_func, "unescape", NULL },
{ file_func, "file", NULL },
{ filesize_func, "filesize", NULL },
{ req_table_func, "http", NULL, 0 },
{ req_table_func, "note", NULL, 0 },
{ req_table_func, "reqenv", NULL, 0 },
{ tolower_func, "tolower", NULL, 0 },
{ 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 },
{ NULL, NULL, NULL}
};
/* XXX: base64 encode/decode ? */
static const struct expr_provider_single unary_op_providers[] = {
{ op_nz, "n", NULL },
{ op_nz, "z", NULL },
{ op_R, "R", subnet_parse_arg },
{ op_T, "T", NULL },
{ op_file_min, "d", NULL },
{ op_file_min, "e", NULL },
{ op_file_min, "f", NULL },
{ op_file_min, "s", NULL },
{ op_file_link, "L", NULL },
{ op_file_link, "h", NULL },
{ op_file_xbit, "x", NULL },
{ op_file_subr, "F", NULL },
{ op_url_subr, "U", NULL },
{ op_nz, "n", NULL, 0 },
{ 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_subr, "F", NULL, 0 },
{ op_url_subr, "U", NULL, 0 },
{ op_url_subr, "A", NULL, 0 },
{ NULL, NULL, NULL }
};
static const struct expr_provider_single binary_op_providers[] = {
{ op_ipmatch, "ipmatch", subnet_parse_arg },
{ op_fnmatch, "fnmatch", NULL },
{ op_strmatch, "strmatch", NULL },
{ op_strcmatch, "strcmatch", NULL },
{ op_ipmatch, "ipmatch", subnet_parse_arg, 0 },
{ op_fnmatch, "fnmatch", NULL, 0 },
{ op_strmatch, "strmatch", NULL, 0 },
{ op_strcmatch, "strcmatch", NULL, 0 },
{ NULL, NULL, NULL }
};
@ -1423,6 +1453,15 @@ static int core_expr_lookup(ap_expr_lookup_parms *parms)
}
while (prov->func) {
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;
if (prov->arg_parsing_func) {
return prov->arg_parsing_func(parms);