* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2009 */
+/* Copyright (c) University of Cambridge 1995 - 2012 */
/* See the file NOTICE for conditions of use and distribution. */
alphabetical order. */
static uschar *item_table[] = {
+ US"acl",
US"dlfunc",
US"extract",
US"filter",
US"tr" };
enum {
+ EITEM_ACL,
EITEM_DLFUNC,
EITEM_EXTRACT,
EITEM_FILTER,
US"l",
US"lc",
US"length",
+ US"listcount",
+ US"listnamed",
US"mask",
US"md5",
US"nh",
EOP_L,
EOP_LC,
EOP_LENGTH,
+ EOP_LISTCOUNT,
+ EOP_LISTNAMED,
EOP_MASK,
EOP_MD5,
EOP_NH,
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 */
/* local_scan()) */
vtype_todbsdin, /* value not used; generate BSD inbox tod */
vtype_tode, /* value not used; generate tod in epoch format */
+ vtype_todel, /* value not used; generate tod in epoch/usec format */
vtype_todf, /* value not used; generate full tod */
vtype_todl, /* value not used; generate log tod */
vtype_todlf, /* value not used; generate log file datestamp tod */
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 },
{ "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 },
{ "srs_status", vtype_stringptr, &srs_status },
#endif
{ "thisaddress", vtype_stringptr, &filter_thisaddress },
- { "tls_certificate_verified", vtype_int, &tls_certificate_verified },
- { "tls_cipher", vtype_stringptr, &tls_cipher },
- { "tls_peerdn", vtype_stringptr, &tls_peerdn },
+
+ /* 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 },
+ { "tls_out_peerdn", vtype_stringptr, &tls_out.peerdn },
+#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! */
+#endif
+
{ "tod_bsdinbox", vtype_todbsdin, NULL },
{ "tod_epoch", vtype_tode, NULL },
+ { "tod_epoch_l", vtype_todel, NULL },
{ "tod_full", vtype_todf, NULL },
{ "tod_log", vtype_todl, NULL },
{ "tod_logfile", vtype_todlf, NULL },
+
/*************************************************
* Pseudo-random number generation *
*************************************************/
However, if we're stuck unable to provide this, then we'll fall back to
appallingly bad randomness.
-If SUPPORT_TLS is defined and OpenSSL is used, then this will not be used.
-The GNUTLS randomness functions found do not seem amenable to extracting
-random numbers outside of a TLS context. Any volunteers?
+If SUPPORT_TLS is defined then this will not be used except as an emergency
+fallback.
Arguments:
max range maximum
Returns a random number in range [0, max-1]
*/
-#if !defined(SUPPORT_TLS) || defined(USE_GNUTLS)
+#ifdef SUPPORT_TLS
+# define vaguely_random_number vaguely_random_number_fallback
+#endif
int
-pseudo_random_number(int max)
+vaguely_random_number(int max)
{
+#ifdef SUPPORT_TLS
+# undef vaguely_random_number
+#endif
static pid_t pid = 0;
pid_t p2;
#if defined(HAVE_SRANDOM) && !defined(HAVE_SRANDOMDEV)
#endif
}
-#endif
+
+
/*************************************************
* Pick out a name from a string *
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;
domain = Ustrrchr(s, '@');
if (domain == NULL) return s;
if (domain - s > sizeof(var_buffer) - 1)
- log_write(0, LOG_MAIN|LOG_PANIC_DIE, "local part longer than %d in "
- "string expansion", sizeof(var_buffer));
+ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "local part longer than " SIZE_T_FMT
+ " in string expansion", sizeof(var_buffer));
Ustrncpy(var_buffer, s, domain - s);
var_buffer[domain - s] = 0;
return var_buffer;
case vtype_tode: /* Unix epoch time of day */
return tod_stamp(tod_epoch);
+ case vtype_todel: /* Unix epoch/usec time of day */
+ return tod_stamp(tod_epoch_l);
+
case vtype_todf: /* Full time of day */
return tod_stamp(tod_full);
+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 *
*************************************************/
BOOL *subcondptr;
BOOL sub2_honour_dollar = TRUE;
int i, rc, cond_type, roffset;
-int num[2];
+int_eximarith_t num[2];
struct stat statbuf;
uschar name[256];
uschar *sub[4];
on failure: an undefined value, with *error = a message
*/
-static int eval_op_or(uschar **, BOOL, uschar **);
+static int_eximarith_t eval_op_or(uschar **, BOOL, uschar **);
-static int
+static int_eximarith_t
eval_expr(uschar **sptr, BOOL decimal, uschar **error, BOOL endket)
{
uschar *s = *sptr;
-int x = eval_op_or(&s, decimal, error);
+int_eximarith_t x = eval_op_or(&s, decimal, error);
if (*error == NULL)
{
if (endket)
}
-static int
+static int_eximarith_t
eval_number(uschar **sptr, BOOL decimal, uschar **error)
{
register int c;
-int n;
+int_eximarith_t n;
uschar *s = *sptr;
while (isspace(*s)) s++;
c = *s;
if (isdigit(c))
{
int count;
- (void)sscanf(CS s, (decimal? "%d%n" : "%i%n"), &n, &count);
+ (void)sscanf(CS s, (decimal? SC_EXIM_DEC "%n" : SC_EXIM_ARITH "%n"), &n, &count);
s += count;
- if (tolower(*s) == 'k') { n *= 1024; s++; }
- else if (tolower(*s) == 'm') { n *= 1024*1024; s++; }
+ switch (tolower(*s))
+ {
+ default: break;
+ case 'k': n *= 1024; s++; break;
+ case 'm': n *= 1024*1024; s++; break;
+ case 'g': n *= 1024*1024*1024; s++; break;
+ }
while (isspace (*s)) s++;
}
else if (c == '(')
}
-static int eval_op_unary(uschar **sptr, BOOL decimal, uschar **error)
+static int_eximarith_t
+eval_op_unary(uschar **sptr, BOOL decimal, uschar **error)
{
uschar *s = *sptr;
-int x;
+int_eximarith_t x;
while (isspace(*s)) s++;
if (*s == '+' || *s == '-' || *s == '~')
{
}
-static int eval_op_mult(uschar **sptr, BOOL decimal, uschar **error)
+static int_eximarith_t
+eval_op_mult(uschar **sptr, BOOL decimal, uschar **error)
{
uschar *s = *sptr;
-int x = eval_op_unary(&s, decimal, error);
+int_eximarith_t x = eval_op_unary(&s, decimal, error);
if (*error == NULL)
{
while (*s == '*' || *s == '/' || *s == '%')
{
int op = *s++;
- int y = eval_op_unary(&s, decimal, error);
+ int_eximarith_t y = eval_op_unary(&s, decimal, error);
if (*error != NULL) break;
/* SIGFPE both on div/mod by zero and on INT_MIN / -1, which would give
* a value of INT_MAX+1. Note that INT_MIN * -1 gives INT_MIN for me, which
* can just let the other invalid results occur otherwise, as they have
* until now. For this one case, we can coerce.
*/
- if (y == -1 && x == INT_MIN && op != '*')
+ if (y == -1 && x == LLONG_MIN && op != '*')
{
DEBUG(D_expand)
- debug_printf("Integer exception dodging: %d%c-1 coerced to %d\n",
- INT_MIN, op, INT_MAX);
- x = INT_MAX;
+ debug_printf("Integer exception dodging: " PR_EXIM_ARITH "%c-1 coerced to " PR_EXIM_ARITH "\n",
+ LLONG_MIN, op, LLONG_MAX);
+ x = LLONG_MAX;
continue;
}
if (op == '*')
}
-static int eval_op_sum(uschar **sptr, BOOL decimal, uschar **error)
+static int_eximarith_t
+eval_op_sum(uschar **sptr, BOOL decimal, uschar **error)
{
uschar *s = *sptr;
-int x = eval_op_mult(&s, decimal, error);
+int_eximarith_t x = eval_op_mult(&s, decimal, error);
if (*error == NULL)
{
while (*s == '+' || *s == '-')
{
int op = *s++;
- int y = eval_op_mult(&s, decimal, error);
+ int_eximarith_t y = eval_op_mult(&s, decimal, error);
if (*error != NULL) break;
if (op == '+') x += y; else x -= y;
}
}
-static int eval_op_shift(uschar **sptr, BOOL decimal, uschar **error)
+static int_eximarith_t
+eval_op_shift(uschar **sptr, BOOL decimal, uschar **error)
{
uschar *s = *sptr;
-int x = eval_op_sum(&s, decimal, error);
+int_eximarith_t x = eval_op_sum(&s, decimal, error);
if (*error == NULL)
{
while ((*s == '<' || *s == '>') && s[1] == s[0])
{
- int y;
+ int_eximarith_t y;
int op = *s++;
s++;
y = eval_op_sum(&s, decimal, error);
}
-static int eval_op_and(uschar **sptr, BOOL decimal, uschar **error)
+static int_eximarith_t
+eval_op_and(uschar **sptr, BOOL decimal, uschar **error)
{
uschar *s = *sptr;
-int x = eval_op_shift(&s, decimal, error);
+int_eximarith_t x = eval_op_shift(&s, decimal, error);
if (*error == NULL)
{
while (*s == '&')
{
- int y;
+ int_eximarith_t y;
s++;
y = eval_op_shift(&s, decimal, error);
if (*error != NULL) break;
}
-static int eval_op_xor(uschar **sptr, BOOL decimal, uschar **error)
+static int_eximarith_t
+eval_op_xor(uschar **sptr, BOOL decimal, uschar **error)
{
uschar *s = *sptr;
-int x = eval_op_and(&s, decimal, error);
+int_eximarith_t x = eval_op_and(&s, decimal, error);
if (*error == NULL)
{
while (*s == '^')
{
- int y;
+ int_eximarith_t y;
s++;
y = eval_op_and(&s, decimal, error);
if (*error != NULL) break;
}
-static int eval_op_or(uschar **sptr, BOOL decimal, uschar **error)
+static int_eximarith_t
+eval_op_or(uschar **sptr, BOOL decimal, uschar **error)
{
uschar *s = *sptr;
-int x = eval_op_xor(&s, decimal, error);
+int_eximarith_t x = eval_op_xor(&s, decimal, error);
if (*error == NULL)
{
while (*s == '|')
{
- int y;
+ int_eximarith_t y;
s++;
y = eval_op_xor(&s, decimal, error);
if (*error != NULL) break;
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 ="
+ 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:
+ {
+ int rc;
+ uschar *sub[10]; /* name + arg1-arg9, must match number of acl_arg[] */
+ uschar *new_yield;
+ uschar *user_msg;
+ uschar *log_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;
+
+ 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;
+
+ DEBUG(D_expand)
+ debug_printf("expanding: acl: %s arg: %s%s\n",
+ sub[0],
+ acl_narg>0 ? sub[1] : US"<none>",
+ acl_narg>1 ? " +more" : "");
+
+ switch(rc = acl_check(ACL_WHERE_EXPANSION, NULL, sub[0], &user_msg, &log_msg))
+ {
+ case OK:
+ if (user_msg)
+ yield = string_cat(yield, &size, &ptr, user_msg, Ustrlen(user_msg));
+ continue;
+ case DEFER:
+ continue;
+ default:
+ expand_string_message = string_sprintf("acl \"%s\" did not accept", 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
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. */
{
uschar *save_sub = sub;
uschar *error = NULL;
- int n = eval_expr(&sub, (c == EOP_EVAL10), &error, FALSE);
+ int_eximarith_t n = eval_expr(&sub, (c == EOP_EVAL10), &error, FALSE);
if (error != NULL)
{
expand_string_message = string_sprintf("error in expression "
save_sub);
goto EXPAND_FAILED;
}
- sprintf(CS var_buffer, "%d", n);
+ sprintf(CS var_buffer, PR_EXIM_ARITH, n);
yield = string_cat(yield, &size, &ptr, var_buffer, Ustrlen(var_buffer));
continue;
}
continue;
}
- /* pseudo-random number less than N */
+ /* vaguely random number less than N */
case EOP_RANDINT:
{
- int max;
+ int_eximarith_t max;
uschar *s;
max = expand_string_integer(sub, TRUE);
if (expand_string_message != NULL)
goto EXPAND_FAILED;
- s = string_sprintf("%d", pseudo_random_number(max));
+ s = string_sprintf("%d", vaguely_random_number((int)max));
yield = string_cat(yield, &size, &ptr, s, Ustrlen(s));
continue;
}
expand_string_message is set NULL for an OK integer
*/
-int
+int_eximarith_t
expand_string_integer(uschar *string, BOOL isplus)
{
-long int value;
+int_eximarith_t value;
uschar *s = expand_string(string);
uschar *msg = US"invalid integer \"%s\"";
uschar *endptr;
}
}
-value = strtol(CS s, CSS &endptr, 10);
+value = strtoll(CS s, CSS &endptr, 10);
if (endptr == s)
{
}
else
{
- /* Ensure we can cast this down to an int */
- if (value > INT_MAX || value < INT_MIN) errno = ERANGE;
-
- if (errno != ERANGE)
+ switch (tolower(*endptr))
{
- if (tolower(*endptr) == 'k')
- {
- if (value > INT_MAX/1024 || value < INT_MIN/1024) errno = ERANGE;
- else value *= 1024;
+ 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 > INT_MAX/(1024*1024) || value < INT_MIN/(1024*1024))
- errno = ERANGE;
+ 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)";