X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Fexpand.c;h=767e4771a2c1129751f2b358866e085a9533f973;hp=1642c67b708178757e9bda28e927275397b0194b;hb=3e8abda0fa92b78c4a3dfbad940b12fc90c241e3;hpb=613dd4aed0ae1d1165f89a3d6819e51a033fcfb6 diff --git a/src/src/expand.c b/src/src/expand.c index 1642c67b7..767e4771a 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -102,6 +102,7 @@ bcrypt ({CRYPT}$2a$). alphabetical order. */ static uschar *item_table[] = { + US"acl", US"dlfunc", US"extract", US"filter", @@ -124,6 +125,7 @@ static uschar *item_table[] = { US"tr" }; enum { + EITEM_ACL, EITEM_DLFUNC, EITEM_EXTRACT, EITEM_FILTER, @@ -182,6 +184,8 @@ static uschar *op_table_main[] = { US"l", US"lc", US"length", + US"listcount", + US"listnamed", US"mask", US"md5", US"nh", @@ -215,6 +219,8 @@ enum { EOP_L, EOP_LC, EOP_LENGTH, + EOP_LISTCOUNT, + EOP_LISTNAMED, EOP_MASK, EOP_MD5, EOP_NH, @@ -243,6 +249,7 @@ static uschar *cond_table[] = { US"==", /* Backward compatibility */ US">", US">=", + US"acl", US"and", US"bool", US"bool_lax", @@ -288,6 +295,7 @@ enum { ECOND_NUM_EE, ECOND_NUM_G, ECOND_NUM_GE, + ECOND_ACL, ECOND_AND, ECOND_BOOL, ECOND_BOOL_LAX, @@ -351,6 +359,7 @@ enum { vtype_ino, /* value is address of ino_t (not always an int) */ vtype_uid, /* value is address of uid_t (not always an int) */ vtype_gid, /* value is address of gid_t (not always an int) */ + vtype_bool, /* value is address of bool */ vtype_stringptr, /* value is address of pointer to string */ vtype_msgbody, /* as stringptr, but read when first required */ vtype_msgbody_end, /* ditto, the end of the message */ @@ -385,6 +394,16 @@ enum { static var_entry var_table[] = { /* WARNING: Do not invent variables whose names start acl_c or acl_m because they will be confused with user-creatable ACL variables. */ + { "acl_arg1", vtype_stringptr, &acl_arg[0] }, + { "acl_arg2", vtype_stringptr, &acl_arg[1] }, + { "acl_arg3", vtype_stringptr, &acl_arg[2] }, + { "acl_arg4", vtype_stringptr, &acl_arg[3] }, + { "acl_arg5", vtype_stringptr, &acl_arg[4] }, + { "acl_arg6", vtype_stringptr, &acl_arg[5] }, + { "acl_arg7", vtype_stringptr, &acl_arg[6] }, + { "acl_arg8", vtype_stringptr, &acl_arg[7] }, + { "acl_arg9", vtype_stringptr, &acl_arg[8] }, + { "acl_narg", vtype_int, &acl_narg }, { "acl_verify_message", vtype_stringptr, &acl_verify_message }, { "address_data", vtype_stringptr, &deliver_address_data }, { "address_file", vtype_stringptr, &address_file }, @@ -562,6 +581,7 @@ static var_entry var_table[] = { { "sender_helo_name", vtype_stringptr, &sender_helo_name }, { "sender_host_address", vtype_stringptr, &sender_host_address }, { "sender_host_authenticated",vtype_stringptr, &sender_host_authenticated }, + { "sender_host_dnssec", vtype_bool, &sender_host_dnssec }, { "sender_host_name", vtype_host_lookup, NULL }, { "sender_host_port", vtype_int, &sender_host_port }, { "sender_ident", vtype_stringptr, &sender_ident }, @@ -613,9 +633,18 @@ static var_entry var_table[] = { #endif { "thisaddress", vtype_stringptr, &filter_thisaddress }, + /* The non-(in,out) variables are now deprecated */ { "tls_bits", vtype_int, &tls_in.bits }, { "tls_certificate_verified", vtype_int, &tls_in.certificate_verified }, { "tls_cipher", vtype_stringptr, &tls_in.cipher }, + + { "tls_in_bits", vtype_int, &tls_in.bits }, + { "tls_in_certificate_verified", vtype_int, &tls_in.certificate_verified }, + { "tls_in_cipher", vtype_stringptr, &tls_in.cipher }, + { "tls_in_peerdn", vtype_stringptr, &tls_in.peerdn }, +#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS) + { "tls_in_sni", vtype_stringptr, &tls_in.sni }, +#endif { "tls_out_bits", vtype_int, &tls_out.bits }, { "tls_out_certificate_verified", vtype_int,&tls_out.certificate_verified }, { "tls_out_cipher", vtype_stringptr, &tls_out.cipher }, @@ -623,6 +652,7 @@ static var_entry var_table[] = { #if defined(SUPPORT_TLS) && !defined(USE_GNUTLS) { "tls_out_sni", vtype_stringptr, &tls_out.sni }, #endif + { "tls_peerdn", vtype_stringptr, &tls_in.peerdn }, /* mind the alphabetical order! */ #if defined(SUPPORT_TLS) && !defined(USE_GNUTLS) { "tls_sni", vtype_stringptr, &tls_in.sni }, /* mind the alphabetical order! */ @@ -1512,6 +1542,10 @@ while (last > first) sprintf(CS var_buffer, "%ld", (long int)(*(uid_t *)(var_table[middle].value))); /* uid */ return var_buffer; + case vtype_bool: + sprintf(CS var_buffer, "%s", *(BOOL *)(var_table[middle].value) ? "yes" : "no"); /* bool */ + return var_buffer; + case vtype_stringptr: /* Pointer to string */ s = *((uschar **)(var_table[middle].value)); return (s == NULL)? US"" : s; @@ -1688,6 +1722,31 @@ return NULL; /* Unknown variable name */ +void +modify_variable(uschar *name, void * value) +{ +int first = 0; +int last = var_table_size; + +while (last > first) + { + int middle = (first + last)/2; + int c = Ustrcmp(name, var_table[middle].name); + + if (c > 0) { first = middle + 1; continue; } + if (c < 0) { last = middle; continue; } + + /* Found an existing variable; change the item it refers to */ + var_table[middle].value = value; + return; + } +return; /* Unknown variable name, fail silently */ +} + + + + + /************************************************* * Read and expand substrings * *************************************************/ @@ -1777,6 +1836,40 @@ if (Ustrncmp(name, "acl_", 4) == 0) +/* +Load args from sub array to globals, and call acl_check(). + +Returns: OK access is granted by an ACCEPT verb + DISCARD access is granted by a DISCARD verb + FAIL access is denied + FAIL_DROP access is denied; drop the connection + DEFER can't tell at the moment + ERROR disaster +*/ +static int +eval_acl(uschar ** sub, int nsub, uschar ** user_msgp) +{ +int i; +uschar *dummy_log_msg; + +for (i = 1; i < nsub && sub[i]; i++) + acl_arg[i-1] = sub[i]; +acl_narg = i-1; +while (i < nsub) + acl_arg[i++ - 1] = NULL; + +DEBUG(D_expand) + debug_printf("expanding: acl: %s arg: %s%s\n", + sub[0], + acl_narg>0 ? sub[1] : US"", + acl_narg>1 ? " +more" : ""); + +return acl_check(ACL_WHERE_EXPANSION, NULL, sub[0], user_msgp, &dummy_log_msg); +} + + + + /************************************************* * Read and evaluate a condition * *************************************************/ @@ -1804,7 +1897,7 @@ int i, rc, cond_type, roffset; int_eximarith_t num[2]; struct stat statbuf; uschar name[256]; -uschar *sub[4]; +uschar *sub[10]; const pcre *re; const uschar *rerror; @@ -2009,12 +2102,65 @@ 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. + Up to ten parameters are used; we use the braces round the name+args + like the saslauthd condition does, to permit a variable number of args. + See also the expansion-item version EITEM_ACL and the traditional + acl modifier ACLC_ACL. + */ + + case ECOND_ACL: + /* ${if acl {{name}{arg1}{arg2}...} {yes}{no}} */ + { + uschar *nameargs; + uschar *user_msg; + BOOL cond = FALSE; + int size = 0; + int ptr = 0; + + while (isspace(*s)) s++; + if (*s++ != '{') goto COND_FAILED_CURLY_START; + + switch(read_subs(sub, sizeof(sub)/sizeof(*sub), 1, + &s, yield == NULL, TRUE, US"acl")) + { + case 1: expand_string_message = US"too few arguments or bracketing " + "error for acl"; + case 2: + case 3: return NULL; + } + + if (yield != NULL) switch(eval_acl(sub, sizeof(sub)/sizeof(*sub), &user_msg)) + { + case OK: + cond = TRUE; + case FAIL: + lookup_value = NULL; + if (user_msg) + { + lookup_value = string_cat(NULL, &size, &ptr, user_msg, Ustrlen(user_msg)); + lookup_value[ptr] = '\0'; + } + *yield = cond; + break; + + case DEFER: + expand_string_forcedfail = TRUE; + default: + expand_string_message = string_sprintf("error from acl \"%s\"", sub[0]); + 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 @@ -3596,6 +3742,44 @@ 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 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. + See also the acl expansion condition ECOND_ACL and the traditional + acl modifier ACLC_ACL. + */ + + case EITEM_ACL: + /* ${acl {name} {arg1}{arg2}...} */ + { + uschar *sub[10]; /* name + arg1-arg9 (which must match number of acl_arg[]) */ + uschar *user_msg; + + switch(read_subs(sub, 10, 1, &s, skipping, TRUE, US"acl")) + { + case 1: goto EXPAND_FAILED_CURLY; + case 2: + case 3: goto EXPAND_FAILED; + } + if (skipping) continue; + + switch(eval_acl(sub, sizeof(sub)/sizeof(*sub), &user_msg)) + { + case OK: + case FAIL: + if (user_msg) + yield = string_cat(yield, &size, &ptr, user_msg, Ustrlen(user_msg)); + continue; + + case DEFER: + expand_string_forcedfail = TRUE; + default: + expand_string_message = string_sprintf("error from acl \"%s\"", sub[0]); + goto EXPAND_FAILED; + } + } + /* Handle conditionals - preserve the values of the numerical expansion variables in case they get changed by a regular expression match in the condition. If not, they retain their external settings. At the end @@ -5429,6 +5613,106 @@ while (*s != 0) continue; } + /* count the number of list elements */ + + case EOP_LISTCOUNT: + { + int cnt = 0; + int sep = 0; + uschar * cp; + uschar buffer[256]; + + while (string_nextinlist(&sub, &sep, buffer, sizeof(buffer)) != NULL) cnt++; + cp = string_sprintf("%d", cnt); + yield = string_cat(yield, &size, &ptr, cp, Ustrlen(cp)); + continue; + } + + /* expand a named list given the name */ + /* handles nested named lists; requotes as colon-sep list */ + + case EOP_LISTNAMED: + { + tree_node *t = NULL; + uschar * list; + int sep = 0; + uschar * item; + uschar * suffix = ""; + BOOL needsep = FALSE; + uschar buffer[256]; + + if (*sub == '+') sub++; + if (arg == NULL) /* no-argument version */ + { + if (!(t = tree_search(addresslist_anchor, sub)) && + !(t = tree_search(domainlist_anchor, sub)) && + !(t = tree_search(hostlist_anchor, sub))) + t = tree_search(localpartlist_anchor, sub); + } + else switch(*arg) /* specific list-type version */ + { + case 'a': t = tree_search(addresslist_anchor, sub); suffix = "_a"; break; + case 'd': t = tree_search(domainlist_anchor, sub); suffix = "_d"; break; + case 'h': t = tree_search(hostlist_anchor, sub); suffix = "_h"; break; + case 'l': t = tree_search(localpartlist_anchor, sub); suffix = "_l"; break; + default: + expand_string_message = string_sprintf("bad suffix on \"list\" operator"); + goto EXPAND_FAILED; + } + + if(!t) + { + expand_string_message = string_sprintf("\"%s\" is not a %snamed list", + sub, !arg?"" + : *arg=='a'?"address " + : *arg=='d'?"domain " + : *arg=='h'?"host " + : *arg=='l'?"localpart " + : 0); + goto EXPAND_FAILED; + } + + list = ((namedlist_block *)(t->data.ptr))->string; + + while ((item = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL) + { + uschar * buf = US" : "; + if (needsep) + yield = string_cat(yield, &size, &ptr, buf, 3); + else + needsep = TRUE; + + if (*item == '+') /* list item is itself a named list */ + { + uschar * sub = string_sprintf("${listnamed%s:%s}", suffix, item); + item = expand_string_internal(sub, FALSE, NULL, FALSE, TRUE); + } + else if (sep != ':') /* item from non-colon-sep list, re-quote for colon list-separator */ + { + char * cp; + char tok[3]; + tok[0] = sep; tok[1] = ':'; tok[2] = 0; + while ((cp= strpbrk((const char *)item, tok))) + { + yield = string_cat(yield, &size, &ptr, item, cp-(char *)item); + if (*cp++ == ':') /* colon in a non-colon-sep list item, needs doubling */ + { + yield = string_cat(yield, &size, &ptr, US"::", 2); + item = cp; + } + else /* sep in item; should already be doubled; emit once */ + { + yield = string_cat(yield, &size, &ptr, (uschar *)tok, 1); + if (*cp == sep) cp++; + item = cp; + } + } + } + yield = string_cat(yield, &size, &ptr, item, Ustrlen(item)); + } + continue; + } + /* mask applies a mask to an IP address; for example the result of ${mask:131.111.10.206/28} is 131.111.10.192/28. */ @@ -6182,18 +6466,25 @@ else if (value < 0 && isplus) } else { - if (tolower(*endptr) == 'k') + switch (tolower(*endptr)) { - if (value > LLONG_MAX/1024 || value < LLONG_MIN/1024) errno = ERANGE; + default: + break; + case 'k': + if (value > LLONG_MAX/1024 || value < LLONG_MIN/1024) errno = ERANGE; else value *= 1024; - endptr++; - } - else if (tolower(*endptr) == 'm') - { - if (value > LLONG_MAX/(1024*1024) || value < LLONG_MIN/(1024*1024)) - errno = ERANGE; - else value *= 1024*1024; - endptr++; + endptr++; + break; + case 'm': + if (value > LLONG_MAX/(1024*1024) || value < LLONG_MIN/(1024*1024)) errno = ERANGE; + else value *= 1024*1024; + endptr++; + break; + case 'g': + if (value > LLONG_MAX/(1024*1024*1024) || value < LLONG_MIN/(1024*1024*1024)) errno = ERANGE; + else value *= 1024*1024*1024; + endptr++; + break; } if (errno == ERANGE) msg = US"absolute value of integer \"%s\" is too large (overflow)";