<dt><code><var>string</var></code></dt>
<dd>true if <var>string</var> is not empty</dd>
+ <dt><code><var>-A string</var></code></dt>
+ <dd><p>true if the URL represented by the string is accessible by
+ configuration, false otherwise. This test only has an effect if
+ <directive>SSIEnableAccess</directive> 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.</p>
+
+ <example><title>Example</title>
+ <!--#if expr="-A /private" --><br />
+ <indent>
+ Click <a href="/private">here</a> to access private
+ information.<br />
+ </indent>
+ <!--#endif -->
+ </example>
+ </dd>
+
<dt><code><var>string1</var> = <var>string2</var><br />
<var>string1</var> == <var>string2</var><br />
<var>string1</var> != <var>string2</var></code></dt>
</usage>
</directivesynopsis>
+<directivesynopsis>
+<name>SSIEnableAccess</name>
+<description>Enable the -A flag during conditional flow control processing.</description>
+<syntax>SSIEnableAccess on|off</syntax>
+<default>SSIEnableAccess off</default>
+<contextlist><context>directory</context><context>location</context></contextlist>
+
+<usage>
+ <p>The <directive>SSIEnableAccess</directive> directive controls whether
+ the -A test is enabled during conditional flow control processing.
+ <directive>SSIEnableAccess</directive> can take on the following values:</p>
+
+ <dl>
+
+ <dt><code>off</code></dt>
+ <dd><!--#if expr="-A /foo"--> will be interpreted as a series
+ of string and regular expression tokens, the -A has no special
+ meaning.</dd>
+
+ <dt><code>on</code></dt>
+ <dd><!--#if expr="-A /foo"--> will evaluate to false if the
+ URL /foo is inaccessible by configuration, or true otherwise.</dd>
+
+ </dl>
+
+</usage>
+</directivesynopsis>
+
<directivesynopsis>
<name>XBitHack</name>
<description>Parse SSI directives in files with the execute bit
</dd>
</dl>
- </usage>
+</usage>
</directivesynopsis>
</modulesynopsis>
TOKEN_GE,
TOKEN_LE,
TOKEN_GT,
- TOKEN_LT
+ TOKEN_LT,
+ TOKEN_ACCESS
} token_type_t;
typedef struct {
const char *default_time_fmt;
const char *undefined_echo;
xbithack_t xbithack;
+ const int accessenable;
} include_dir_config;
typedef struct {
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;
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;
}
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
}
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;
{
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;
*/
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;
}
switch (new->token.type) {
case TOKEN_STRING:
case TOKEN_NOT:
+ case TOKEN_ACCESS:
case TOKEN_LBRACE:
root = current = new;
continue;
break;
case TOKEN_NOT:
+ case TOKEN_ACCESS:
case TOKEN_LBRACE:
switch (current->token.type) {
case TOKEN_STRING:
}
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";
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;
"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}
};