From 333eea9c862f2368e61fee5ce7231011c19f04ec Mon Sep 17 00:00:00 2001 From: Jeremy Harris Date: Thu, 14 Jun 2012 23:24:16 +0100 Subject: [PATCH 1/1] Add acl call as an expansion condition --- src/src/acl.c | 37 ++++++++++++++++++++++++ src/src/expand.c | 69 ++++++++++++++++++++++++++++++++++++--------- src/src/functions.h | 2 ++ 3 files changed, 95 insertions(+), 13 deletions(-) diff --git a/src/src/acl.c b/src/src/acl.c index a721665d4..84b0609cf 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -3863,6 +3863,43 @@ return FAIL; } + + +/* Same args as acl_check_internal() above, but the string s is +the name of an ACL followed optionally by up to 9 space-separated arguments. +The name and args are separately expanded. Args go into $acl_arg globals. */ +int +acl_check_args(int where, address_item *addr, uschar *s, int level, + uschar **user_msgptr, uschar **log_msgptr) +{ +uschar * tmp; +uschar * name; + +if (!(tmp = string_dequote(&s)) || !(name = expand_string(tmp))) + goto bad; + +for (acl_narg = 0; acl_narg < sizeof(acl_arg)/sizeof(*acl_arg); acl_narg++) + { + while (*s && isspace(*s)) s++; + if (!*s) break; + if (!(tmp = string_dequote(&s)) || !(acl_arg[acl_narg] = expand_string(tmp))) + { + tmp = name; + goto bad; + } + } + +return acl_check_internal(where, addr, name, level+1, user_msgptr, log_msgptr); + +bad: +if (expand_string_forcedfail) return ERROR; +*log_msgptr = string_sprintf("failed to expand ACL string \"%s\": %s", + tmp, expand_string_message); +return search_find_defer?DEFER:ERROR; +} + + + /************************************************* * Check access using an ACL * *************************************************/ diff --git a/src/src/expand.c b/src/src/expand.c index 913a808b8..7529ad1ca 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -249,6 +249,7 @@ static uschar *cond_table[] = { US"==", /* Backward compatibility */ US">", US">=", + US"acl", US"and", US"bool", US"bool_lax", @@ -294,6 +295,7 @@ enum { ECOND_NUM_EE, ECOND_NUM_G, ECOND_NUM_GE, + ECOND_ACL, ECOND_AND, ECOND_BOOL, ECOND_BOOL_LAX, @@ -2066,12 +2068,50 @@ switch(cond_type) return s; + /* call ACL (in a conditional context). Accept true, deny false. + Defer is a forced-fail. Anything set by message= goes to $value. + See also the expansion-item version EITEM_ACL. */ + + case ECOND_ACL: + /* ${if acl {name arg1 arg2...} {yes}{no}} + { + uschar *nameargs; + uschar *user_msg; + uschar *log_msg; + BOOL cond = FALSE; + int size = 0; + + while (isspace(*s)) s++; + if (*s++ != '{') goto COND_FAILED_CURLY_START; + if (!(nameargs = expand_string_internal(s, TRUE, &s, FALSE, FALSE)) return NULL; + if (*s++ != '}') goto COND_FAILED_CURLY_END; + + switch(acl_check_args(ACL_WHERE_EXPANSION, NULL, nameargs, &user_msg, &log_msg)) + { + case OK: + cond = TRUE; + case FAIL: + if (user_msg) + lookup_value = string_cat(NULL, &size, &ptr, user_msg, Ustrlen(user_msg)); + if (yield != NULL) *yield = cond; + return s; + + case DEFER: + expand_string_forcedfail = TRUE; + default: + expand_string_message = string_sprintf("error from acl \"%s\"", nameargs); + return NULL; + } + } + return s; + + /* saslauthd: does Cyrus saslauthd authentication. Four parameters are used: ${if saslauthd {{username}{password}{service}{realm}} {yes}[no}} However, the last two are optional. That is why the whole set is enclosed - in their own set or braces. */ + in their own set of braces. */ case ECOND_SASLAUTHD: #ifndef CYRUS_SASLAUTHD_SOCKET @@ -3654,18 +3694,19 @@ while (*s != 0) switch(item_type) { /* Call an ACL from an expansion. We feed data in via $acl_arg1 - $acl_arg9. - If the ACL returns acceptance we return content set by "message =" + If the ACL returns accept or reject we return content set by "message =" There is currently no limit on recursion; this would have us call acl_check_internal() directly and get a current level from somewhere. */ case EITEM_ACL: + /* ${acl {name} {arg1}{arg2}...} */ { - int rc; - uschar *sub[10]; /* name + arg1-arg9, must match number of acl_arg[] */ - uschar *new_yield; + int i; + uschar *sub[10]; /* name + arg1-arg9 (which must match number of acl_arg[]) */ uschar *user_msg; uschar *log_msg; + switch(read_subs(sub, 10, 1, &s, skipping, TRUE, US"acl")) { case 1: goto EXPAND_FAILED_CURLY; @@ -3674,11 +3715,11 @@ while (*s != 0) } if (skipping) continue; - for (rc = 1; rc < sizeof(sub)/sizeof(*sub) && sub[rc]; rc++) - acl_arg[rc-1] = sub[rc]; - acl_narg = rc-1; - while (rc < sizeof(sub)/sizeof(*sub)) - acl_arg[rc++ - 1] = NULL; + for (i = 1; i < sizeof(sub)/sizeof(*sub) && sub[i]; i++) + acl_arg[i-1] = sub[i]; + acl_narg = i-1; + while (i < sizeof(sub)/sizeof(*sub)) + acl_arg[i++ - 1] = NULL; DEBUG(D_expand) debug_printf("expanding: acl: %s arg: %s%s\n", @@ -3686,16 +3727,18 @@ while (*s != 0) acl_narg>0 ? sub[1] : US"", acl_narg>1 ? " +more" : ""); - switch(rc = acl_check(ACL_WHERE_EXPANSION, NULL, sub[0], &user_msg, &log_msg)) + switch(acl_check(ACL_WHERE_EXPANSION, NULL, sub[0], &user_msg, &log_msg)) { case OK: + case FAIL: if (user_msg) yield = string_cat(yield, &size, &ptr, user_msg, Ustrlen(user_msg)); continue; + case DEFER: - continue; + expand_string_forcedfail = TRUE; default: - expand_string_message = string_sprintf("acl \"%s\" did not accept", sub[0]); + expand_string_message = string_sprintf("error from acl \"%s\"", sub[0]); goto EXPAND_FAILED; } } diff --git a/src/src/functions.h b/src/src/functions.h index 09f7ab95c..2257a3d7c 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -49,6 +49,8 @@ extern BOOL tls_openssl_options_parse(uschar *, long *); extern acl_block *acl_read(uschar *(*)(void), uschar **); extern int acl_check(int, uschar *, uschar *, uschar **, uschar **); +extern int acl_check_args(int, address_item *, uschar *, int, uschar **, uschar **); + extern tree_node *acl_var_create(uschar *); extern void acl_var_write(uschar *, uschar *, void *); extern uschar *auth_b64encode(uschar *, int); -- 2.30.2