From: Graham Leggett Date: Sat, 1 Sep 2007 21:27:26 +0000 (+0000) Subject: mod_include: Add an "if" directive syntax to test whether an URL X-Git-Tag: 2.3.0~1477 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=05cab47457ba902ea44b6195c00900f569e2e163;p=apache mod_include: Add an "if" directive syntax to test whether an URL is accessible, and if so, conditionally display content. This allows a webmaster to hide a link to a private page when the user has no access to that page. git-svn-id: https://svn.apache.org/repos/asf/httpd/httpd/trunk@571872 13f79535-47bb-0310-9956-ffa450edef68 --- diff --git a/CHANGES b/CHANGES index b6ddfb499b..f419a79c01 100644 --- a/CHANGES +++ b/CHANGES @@ -2,6 +2,11 @@ Changes with Apache 2.3.0 [ When backported to 2.2.x, remove entry from this file ] + *) mod_include: Add an "if" directive syntax to test whether an URL + is accessible, and if so, conditionally display content. This + allows a webmaster to hide a link to a private page when the user + has no access to that page. [Graham Leggett] + *) mod_authnz_ldap, mod_authn_dbd: Tidy up the code to expose authn parameters to the environment. Improve portability to EBCDIC machines by using apr_toupper(). [Martin Kraemer] diff --git a/docs/manual/mod/mod_include.xml b/docs/manual/mod/mod_include.xml index b821b91fd6..cff773dccb 100644 --- a/docs/manual/mod/mod_include.xml +++ b/docs/manual/mod/mod_include.xml @@ -501,6 +501,25 @@
string
true if string is not empty
+
-A string
+

true if the URL represented by the string is accessible by + configuration, false otherwise. This test only has an effect if + SSIEnableAccess is on. This is useful + where content on a page is to be hidden from users who are not + authorized to view the URL, such as a link to that URL. Note + that the URL is only tested for whether access would be granted, + not whether the URL exists.

+ + Example + <!--#if expr="-A /private" -->
+ + Click <a href="/private">here</a> to access private + information.
+
+ <!--#endif --> +
+
+
string1 = string2
string1 == string2
string1 != string2
@@ -744,6 +763,34 @@ displayed + +SSIEnableAccess +Enable the -A flag during conditional flow control processing. +SSIEnableAccess on|off +SSIEnableAccess off +directorylocation + + +

The SSIEnableAccess directive controls whether + the -A test is enabled during conditional flow control processing. + SSIEnableAccess can take on the following values:

+ +
+ +
off
+
<!--#if expr="-A /foo"--> will be interpreted as a series + of string and regular expression tokens, the -A has no special + meaning.
+ +
on
+
<!--#if expr="-A /foo"--> will evaluate to false if the + URL /foo is inaccessible by configuration, or true otherwise.
+ +
+ +
+
+ XBitHack Parse SSI directives in files with the execute bit @@ -785,7 +832,7 @@ set - + diff --git a/modules/filters/mod_include.c b/modules/filters/mod_include.c index 3d5c466778..11357b760e 100644 --- a/modules/filters/mod_include.c +++ b/modules/filters/mod_include.c @@ -80,7 +80,8 @@ typedef enum { TOKEN_GE, TOKEN_LE, TOKEN_GT, - TOKEN_LT + TOKEN_LT, + TOKEN_ACCESS } token_type_t; typedef struct { @@ -114,6 +115,7 @@ typedef struct { const char *default_time_fmt; const char *undefined_echo; xbithack_t xbithack; + const int accessenable; } include_dir_config; typedef struct { @@ -941,7 +943,7 @@ static APR_INLINE int re_check(include_ctx_t *ctx, const char *string, return rc; } -static int get_ptoken(apr_pool_t *pool, const char **parse, token_t *token) +static int get_ptoken(include_ctx_t *ctx, const char **parse, token_t *token, token_t *previous) { const char *p; apr_size_t shift; @@ -990,6 +992,10 @@ static int get_ptoken(apr_pool_t *pool, const char **parse, token_t *token) unmatched = '\''; break; case '/': + /* if last token was ACCESS, this token is STRING */ + if (previous != NULL && TOKEN_ACCESS == previous->type) { + break; + } TYPE_TOKEN(token, TOKEN_RE); unmatched = '/'; break; @@ -1023,6 +1029,13 @@ static int get_ptoken(apr_pool_t *pool, const char **parse, token_t *token) } TYPE_TOKEN(token, TOKEN_LT); return 0; + case '-': + if (**parse == 'A' && (ctx->accessenable)) { + TYPE_TOKEN(token, TOKEN_ACCESS); + ++*parse; + return 0; + } + break; } /* It's a string or regex token @@ -1079,11 +1092,11 @@ static int get_ptoken(apr_pool_t *pool, const char **parse, token_t *token) } if (unmatched) { - token->value = apr_pstrdup(pool, ""); + token->value = apr_pstrdup(ctx->dpool, ""); } else { apr_size_t len = p - token->value - shift; - char *c = apr_palloc(pool, len + 1); + char *c = apr_palloc(ctx->dpool, len + 1); p = token->value; token->value = c; @@ -1111,6 +1124,7 @@ static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error) { parse_node_t *new, *root = NULL, *current = NULL; request_rec *r = ctx->intern->r; + request_rec *rr = NULL; const char *error = "Invalid expression \"%s\" in file %s"; const char *parse = expr; int was_unmatched = 0; @@ -1130,7 +1144,8 @@ static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error) */ CREATE_NODE(ctx, new); - was_unmatched = get_ptoken(ctx->dpool, &parse, &new->token); + was_unmatched = get_ptoken(ctx, &parse, &new->token, + (current != NULL ? ¤t->token : NULL)); if (!parse) { break; } @@ -1142,6 +1157,7 @@ static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error) switch (new->token.type) { case TOKEN_STRING: case TOKEN_NOT: + case TOKEN_ACCESS: case TOKEN_LBRACE: root = current = new; continue; @@ -1276,6 +1292,7 @@ static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error) break; case TOKEN_NOT: + case TOKEN_ACCESS: case TOKEN_LBRACE: switch (current->token.type) { case TOKEN_STRING: @@ -1462,6 +1479,34 @@ static int parse_expr(include_ctx_t *ctx, const char *expr, int *was_error) } break; + case TOKEN_ACCESS: + if (current->left || !current->right || + (current->right->token.type != TOKEN_STRING && + current->right->token.type != TOKEN_RE)) { + ap_log_rerror(APLOG_MARK, APLOG_ERR, 0, r, + "Invalid expression \"%s\" in file %s: Token '-A' must be followed by a URI string.", + expr, r->filename); + *was_error = 1; + return 0; + } + current->right->token.value = + ap_ssi_parse_string(ctx, current->right->token.value, NULL, 0, + SSI_EXPAND_DROP_NAME); + rr = ap_sub_req_lookup_uri(current->right->token.value, r, NULL); + /* 400 and higher are considered access denied */ + if (rr->status < HTTP_BAD_REQUEST) { + current->value = 1; + } + else { + current->value = 0; + ap_log_rerror(APLOG_MARK, APLOG_DEBUG, rr->status, r, + "mod_include: The tested " + "subrequest -A \"%s\" returned an error code.", + current->right->token.value); + } + ap_destroy_sub_req(rr); + break; + case TOKEN_RE: if (!error) { error = "No operator before regex in expr \"%s\" in file %s"; @@ -3526,6 +3571,7 @@ static apr_status_t includes_filter(ap_filter_t *f, apr_bucket_brigade *b) if (ap_allow_options(r) & OPT_INCNOEXEC) { ctx->flags |= SSI_FLAG_NO_EXEC; } + ctx->accessenable = conf->accessenable; ctx->if_nesting_level = 0; intern->re = NULL; @@ -3808,6 +3854,9 @@ static const command_rec includes_cmds[] = "SSI End String Tag"), AP_INIT_TAKE1("SSIUndefinedEcho", set_undefined_echo, NULL, OR_ALL, "String to be displayed if an echoed variable is undefined"), + AP_INIT_FLAG("SSIAccessEnable", ap_set_flag_slot, + (void *)APR_OFFSETOF(include_dir_config, accessenable), + OR_LIMIT, "Whether testing access is enabled. Limited to 'on' or 'off'"), {NULL} }; diff --git a/modules/filters/mod_include.h b/modules/filters/mod_include.h index fd5621c0b5..d357a49995 100644 --- a/modules/filters/mod_include.h +++ b/modules/filters/mod_include.h @@ -96,6 +96,9 @@ typedef struct { /* pointer to internal (non-public) data, don't touch */ struct ssi_internal_ctx *intern; + + /* is using the access tests allowed? */ + int accessenable; } include_ctx_t; typedef apr_status_t (include_handler_fn_t)(include_ctx_t *, ap_filter_t *,