*************************************************/
/* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 - 2021 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions concerned with routing, and the list of generic router options. */
/* Generic options for routers, all of which live inside router_instance
data blocks and which therefore have the opt_public flag set. */
+#define LOFF(field) OPT_OFF(router_instance, field)
optionlist optionlist_routers[] = {
{ "*expand_group", opt_stringptr | opt_hidden | opt_public,
- (void *)(offsetof(router_instance, expand_gid)) },
+ LOFF(expand_gid) },
{ "*expand_more", opt_stringptr | opt_hidden | opt_public,
- (void *)(offsetof(router_instance, expand_more)) },
+ LOFF(expand_more) },
{ "*expand_unseen", opt_stringptr | opt_hidden | opt_public,
- (void *)(offsetof(router_instance, expand_unseen)) },
+ LOFF(expand_unseen) },
{ "*expand_user", opt_stringptr | opt_hidden | opt_public,
- (void *)(offsetof(router_instance, expand_uid)) },
+ LOFF(expand_uid) },
{ "*set_group", opt_bool | opt_hidden | opt_public,
- (void *)(offsetof(router_instance, gid_set)) },
+ LOFF(gid_set) },
{ "*set_user", opt_bool | opt_hidden | opt_public,
- (void *)(offsetof(router_instance, uid_set)) },
+ LOFF(uid_set) },
{ "address_data", opt_stringptr|opt_public,
- (void *)(offsetof(router_instance, address_data)) },
+ LOFF(address_data) },
{ "address_test", opt_bool|opt_public,
- (void *)(offsetof(router_instance, address_test)) },
+ LOFF(address_test) },
#ifdef EXPERIMENTAL_BRIGHTMAIL
{ "bmi_deliver_alternate", opt_bool | opt_public,
- (void *)(offsetof(router_instance, bmi_deliver_alternate)) },
+ LOFF(bmi_deliver_alternate) },
{ "bmi_deliver_default", opt_bool | opt_public,
- (void *)(offsetof(router_instance, bmi_deliver_default)) },
+ LOFF(bmi_deliver_default) },
{ "bmi_dont_deliver", opt_bool | opt_public,
- (void *)(offsetof(router_instance, bmi_dont_deliver)) },
+ LOFF(bmi_dont_deliver) },
{ "bmi_rule", opt_stringptr|opt_public,
- (void *)(offsetof(router_instance, bmi_rule)) },
+ LOFF(bmi_rule) },
#endif
{ "cannot_route_message", opt_stringptr | opt_public,
- (void *)(offsetof(router_instance, cannot_route_message)) },
+ LOFF(cannot_route_message) },
{ "caseful_local_part", opt_bool | opt_public,
- (void *)(offsetof(router_instance, caseful_local_part)) },
+ LOFF(caseful_local_part) },
{ "check_local_user", opt_bool | opt_public,
- (void *)(offsetof(router_instance, check_local_user)) },
+ LOFF(check_local_user) },
{ "condition", opt_stringptr|opt_public|opt_rep_con,
- (void *)offsetof(router_instance, condition) },
+ LOFF(condition) },
{ "debug_print", opt_stringptr | opt_public,
- (void *)offsetof(router_instance, debug_string) },
+ LOFF(debug_string) },
{ "disable_logging", opt_bool | opt_public,
- (void *)offsetof(router_instance, disable_logging) },
+ LOFF(disable_logging) },
{ "dnssec_request_domains", opt_stringptr|opt_public,
- (void *)offsetof(router_instance, dnssec.request) },
+ LOFF(dnssec.request) },
{ "dnssec_require_domains", opt_stringptr|opt_public,
- (void *)offsetof(router_instance, dnssec.require) },
+ LOFF(dnssec.require) },
{ "domains", opt_stringptr|opt_public,
- (void *)offsetof(router_instance, domains) },
+ LOFF(domains) },
{ "driver", opt_stringptr|opt_public,
- (void *)offsetof(router_instance, driver_name) },
+ LOFF(driver_name) },
{ "dsn_lasthop", opt_bool|opt_public,
- (void *)offsetof(router_instance, dsn_lasthop) },
+ LOFF(dsn_lasthop) },
{ "errors_to", opt_stringptr|opt_public,
- (void *)(offsetof(router_instance, errors_to)) },
+ LOFF(errors_to) },
{ "expn", opt_bool|opt_public,
- (void *)offsetof(router_instance, expn) },
+ LOFF(expn) },
{ "fail_verify", opt_bool_verify|opt_hidden|opt_public,
- (void *)offsetof(router_instance, fail_verify_sender) },
+ LOFF(fail_verify_sender) },
{ "fail_verify_recipient", opt_bool|opt_public,
- (void *)offsetof(router_instance, fail_verify_recipient) },
+ LOFF(fail_verify_recipient) },
{ "fail_verify_sender", opt_bool|opt_public,
- (void *)offsetof(router_instance, fail_verify_sender) },
+ LOFF(fail_verify_sender) },
{ "fallback_hosts", opt_stringptr|opt_public,
- (void *)offsetof(router_instance, fallback_hosts) },
+ LOFF(fallback_hosts) },
{ "group", opt_expand_gid | opt_public,
- (void *)(offsetof(router_instance, gid)) },
+ LOFF(gid) },
{ "headers_add", opt_stringptr|opt_public|opt_rep_str,
- (void *)offsetof(router_instance, extra_headers) },
+ LOFF(extra_headers) },
{ "headers_remove", opt_stringptr|opt_public|opt_rep_str,
- (void *)offsetof(router_instance, remove_headers) },
+ LOFF(remove_headers) },
{ "ignore_target_hosts",opt_stringptr|opt_public,
- (void *)offsetof(router_instance, ignore_target_hosts) },
+ LOFF(ignore_target_hosts) },
{ "initgroups", opt_bool | opt_public,
- (void *)(offsetof(router_instance, initgroups)) },
+ LOFF(initgroups) },
{ "local_part_prefix", opt_stringptr|opt_public,
- (void *)offsetof(router_instance, prefix) },
+ LOFF(prefix) },
{ "local_part_prefix_optional",opt_bool|opt_public,
- (void *)offsetof(router_instance, prefix_optional) },
+ LOFF(prefix_optional) },
{ "local_part_suffix", opt_stringptr|opt_public,
- (void *)offsetof(router_instance, suffix) },
+ LOFF(suffix) },
{ "local_part_suffix_optional",opt_bool|opt_public,
- (void *)offsetof(router_instance, suffix_optional) },
+ LOFF(suffix_optional) },
{ "local_parts", opt_stringptr|opt_public,
- (void *)offsetof(router_instance, local_parts) },
+ LOFF(local_parts) },
{ "log_as_local", opt_bool|opt_public,
- (void *)offsetof(router_instance, log_as_local) },
+ LOFF(log_as_local) },
{ "more", opt_expand_bool|opt_public,
- (void *)offsetof(router_instance, more) },
+ LOFF(more) },
{ "pass_on_timeout", opt_bool|opt_public,
- (void *)offsetof(router_instance, pass_on_timeout) },
+ LOFF(pass_on_timeout) },
{ "pass_router", opt_stringptr|opt_public,
- (void *)offsetof(router_instance, pass_router_name) },
+ LOFF(pass_router_name) },
{ "redirect_router", opt_stringptr|opt_public,
- (void *)offsetof(router_instance, redirect_router_name) },
+ LOFF(redirect_router_name) },
{ "require_files", opt_stringptr|opt_public,
- (void *)offsetof(router_instance, require_files) },
+ LOFF(require_files) },
{ "retry_use_local_part", opt_bool|opt_public,
- (void *)offsetof(router_instance, retry_use_local_part) },
+ LOFF(retry_use_local_part) },
{ "router_home_directory", opt_stringptr|opt_public,
- (void *)offsetof(router_instance, router_home_directory) },
+ LOFF(router_home_directory) },
{ "self", opt_stringptr|opt_public,
- (void *)(offsetof(router_instance, self)) },
+ LOFF(self) },
{ "senders", opt_stringptr|opt_public,
- (void *)offsetof(router_instance, senders) },
+ LOFF(senders) },
{ "set", opt_stringptr|opt_public|opt_rep_str,
- (void *)offsetof(router_instance, set) },
+ LOFF(set) },
#ifdef SUPPORT_TRANSLATE_IP_ADDRESS
{ "translate_ip_address", opt_stringptr|opt_public,
- (void *)offsetof(router_instance, translate_ip_address) },
+ LOFF(translate_ip_address) },
#endif
{ "transport", opt_stringptr|opt_public,
- (void *)offsetof(router_instance, transport_name) },
+ LOFF(transport_name) },
{ "transport_current_directory", opt_stringptr|opt_public,
- (void *)offsetof(router_instance, current_directory) },
+ LOFF(current_directory) },
{ "transport_home_directory", opt_stringptr|opt_public,
- (void *)offsetof(router_instance, home_directory) },
+ LOFF(home_directory) },
{ "unseen", opt_expand_bool|opt_public,
- (void *)offsetof(router_instance, unseen) },
+ LOFF(unseen) },
{ "user", opt_expand_uid | opt_public,
- (void *)(offsetof(router_instance, uid)) },
+ LOFF(uid) },
{ "verify", opt_bool_verify|opt_hidden|opt_public,
- (void *)offsetof(router_instance, verify_sender) },
+ LOFF(verify_sender) },
{ "verify_only", opt_bool|opt_public,
- (void *)offsetof(router_instance, verify_only) },
+ LOFF(verify_only) },
{ "verify_recipient", opt_bool|opt_public,
- (void *)offsetof(router_instance, verify_recipient) },
+ LOFF(verify_recipient) },
{ "verify_sender", opt_bool|opt_public,
- (void *)offsetof(router_instance, verify_sender) }
+ LOFF(verify_sender) }
};
int optionlist_routers_size = nelem(optionlist_routers);
TRUE; otherwise its default is FALSE. */
if (r->retry_use_local_part == TRUE_UNSET)
- r->retry_use_local_part = r->check_local_user;
+ r->retry_use_local_part =
+ r->check_local_user || r->local_parts || r->condition || r->prefix || r->suffix || r->senders || r->require_files;
/* Build a host list if fallback hosts is set. */
- host_build_hostlist(&(r->fallback_hostlist), r->fallback_hosts, FALSE);
+ {
+ int old_pool = store_pool;
+ store_pool = POOL_PERM;
+ host_build_hostlist(&r->fallback_hostlist, r->fallback_hosts, FALSE);
+ store_pool = old_pool;
+ }
/* Check redirect_router and pass_router are valid */
Arguments:
local_part the local part to check
prefixes the list of prefixes
+ vp if set, pointer to place for size of wildcard portion
Returns: length of matching prefix or zero
*/
int
-route_check_prefix(const uschar *local_part, const uschar *prefixes)
+route_check_prefix(const uschar * local_part, const uschar * prefixes,
+ unsigned * vp)
{
int sep = 0;
uschar *prefix;
const uschar *listptr = prefixes;
-uschar prebuf[64];
-while ((prefix = string_nextinlist(&listptr, &sep, prebuf, sizeof(prebuf))))
+while ((prefix = string_nextinlist(&listptr, &sep, NULL, 0)))
{
int plen = Ustrlen(prefix);
if (prefix[0] == '*')
prefix++;
for (const uschar * p = local_part + Ustrlen(local_part) - (--plen);
p >= local_part; p--)
- if (strncmpic(prefix, p, plen) == 0) return plen + p - local_part;
+ if (strncmpic(prefix, p, plen) == 0)
+ {
+ unsigned vlen = p - local_part;
+ if (vp) *vp = vlen;
+ return plen + vlen;
+ }
}
else
- if (strncmpic(prefix, local_part, plen) == 0) return plen;
+ if (strncmpic(prefix, local_part, plen) == 0)
+ {
+ if (vp) *vp = 0;
+ return plen;
+ }
}
return 0;
Arguments:
local_part the local part to check
suffixes the list of suffixes
+ vp if set, pointer to place for size of wildcard portion
Returns: length of matching suffix or zero
*/
int
-route_check_suffix(const uschar *local_part, const uschar *suffixes)
+route_check_suffix(const uschar * local_part, const uschar * suffixes,
+ unsigned * vp)
{
int sep = 0;
int alen = Ustrlen(local_part);
uschar *suffix;
const uschar *listptr = suffixes;
-uschar sufbuf[64];
-while ((suffix = string_nextinlist(&listptr, &sep, sufbuf, sizeof(sufbuf))))
+while ((suffix = string_nextinlist(&listptr, &sep, NULL, 0)))
{
int slen = Ustrlen(suffix);
if (suffix[slen-1] == '*')
{
- const uschar *pend = local_part + alen - (--slen) + 1;
+ const uschar * pend = local_part + alen - (--slen) + 1;
for (const uschar * p = local_part; p < pend; p++)
- if (strncmpic(suffix, p, slen) == 0) return alen - (p - local_part);
+ if (strncmpic(suffix, p, slen) == 0)
+ {
+ int tlen = alen - (p - local_part);
+ if (vp) *vp = tlen - slen;
+ return tlen;
+ }
}
else
if (alen > slen && strncmpic(suffix, local_part + alen - slen, slen) == 0)
+ {
+ if (vp) *vp = 0;
return slen;
+ }
}
return 0;
BOOL ugid_set = FALSE;
const uschar *listptr;
uschar *check;
-uschar buffer[1024];
if (!s) return OK;
DEBUG(D_route) debug_printf("checking require_files\n");
listptr = s;
-while ((check = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer))))
+while ((check = string_nextinlist(&listptr, &sep, NULL, 0)))
{
int rc;
int eacces_code = 0;
otherwise. Save the old state for resetting on the wait. */
oldsignal = signal(SIGCHLD, SIG_DFL);
- pid = fork();
+ pid = exim_fork(US"require-files");
/* If fork() fails, reinstate the original error and behave as if
this block of code were not present. This is the same behaviour as happens
{
exim_setugid(uid, gid, TRUE,
string_sprintf("require_files check, file=%s", ss));
- if (route_check_access(ss, uid, gid, 4)) _exit(0);
+ if (route_check_access(ss, uid, gid, 4))
+ exim_underbar_exit(EXIT_SUCCESS);
DEBUG(D_route) debug_printf("route_check_access() failed\n");
- _exit(1);
+ exim_underbar_exit(EXIT_FAILURE);
}
/* In the parent, wait for the child to finish */
while (waitpid(pid, &status, 0) < 0)
- {
if (errno != EINTR) /* unexpected error, interpret as failure */
{
status = 1;
break;
}
- }
signal(SIGCHLD, oldsignal); /* restore */
if ((status == 0) == invert) return SKIP;
login of a local user. Note: the third argument to route_finduser() must be
NULL here, to prevent a numeric string being taken as a numeric uid. If the
user is found, set deliver_home to the home directory, and also set
-local_user_{uid,gid}. */
+local_user_{uid,gid} and local_part_data. */
if (r->check_local_user)
{
r->name, addr->local_part);
return SKIP;
}
+ addr->prop.localpart_data =
+ deliver_localpart_data = string_copy(US (*pw)->pw_name);
deliver_home = string_copy(US (*pw)->pw_dir);
local_user_gid = (*pw)->pw_gid;
local_user_uid = (*pw)->pw_uid;
if (r->router_home_directory)
{
- uschar *router_home = expand_string(r->router_home_directory);
- if (!router_home)
- {
- if (!f.expand_string_forcedfail)
- {
- *perror = string_sprintf("failed to expand \"%s\" for "
- "router_home_directory: %s", r->router_home_directory,
- expand_string_message);
- return DEFER;
- }
- }
- else
+ uschar * router_home = expand_string(r->router_home_directory);
+ if (router_home)
{
setflag(addr, af_home_expanded); /* Note set from router_home_directory */
deliver_home = router_home;
}
+ else if (!f.expand_string_forcedfail)
+ {
+ *perror = string_sprintf("failed to expand \"%s\" for "
+ "router_home_directory: %s", r->router_home_directory,
+ expand_string_message);
+ return DEFER;
+ }
}
/* Skip if the sender condition is not met. We leave this one till after the
BOOL cache_set = (Ustrcmp(lastname, s) == 0);
DEBUG(D_uid) debug_printf("seeking password data for user \"%s\": %s\n", s,
- cache_set? "using cached result" : "cache not available");
+ cache_set ? "using cached result" : "cache not available");
if (!cache_set)
{
return TRUE;
}
- (void)string_format(lastname, sizeof(lastname), "%s", s);
+ string_format_nt(lastname, sizeof(lastname), "%s", s);
/* Force failure if string length is greater than given maximum */
{
const uschar * varlist = r->set;
tree_node ** root = (tree_node **) &addr->prop.variables;
-int sep = 0;
+int sep = ';';
if (!varlist) return OK;
/* Variable name must exist and start "r_". */
if (!name || name[0] != 'r' || name[1] != '_' || !name[2])
+ {
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "bad router variable name '%s' in router '%s'\n", name, r->name);
return FAIL;
+ }
name += 2;
while (isspace(*assignment)) assignment++;
}
if (!(node = tree_search(*root, name)))
- {
- node = store_get(sizeof(tree_node) + Ustrlen(name));
+ { /* name should never be tainted */
+ node = store_get(sizeof(tree_node) + Ustrlen(name), FALSE);
Ustrcpy(node->name, name);
(void)tree_insertnode(root, node);
}
node->data.ptr = US val;
- DEBUG(D_route) debug_printf("set r_%s = '%s'\n", name, val);
+ DEBUG(D_route) debug_printf("set r_%s%s = '%s'%s\n",
+ name, is_tainted(name)?" (tainted)":"",
+ val, is_tainted(val)?" (tainted)":"");
/* All expansions after this point need visibility of that variable */
router_var = *root;
/* Default no affixes and select whether to use a caseful or caseless local
part in this router. */
- addr->prefix = addr->suffix = NULL;
- addr->local_part = r->caseful_local_part?
- addr->cc_local_part : addr->lc_local_part;
+ addr->prefix = addr->prefix_v = addr->suffix = addr->suffix_v = NULL;
+ addr->local_part = r->caseful_local_part
+ ? addr->cc_local_part : addr->lc_local_part;
DEBUG(D_route) debug_printf("local_part=%s domain=%s\n", addr->local_part,
addr->domain);
if (r->prefix)
{
- int plen = route_check_prefix(addr->local_part, r->prefix);
+ unsigned vlen;
+ int plen = route_check_prefix(addr->local_part, r->prefix, &vlen);
if (plen > 0)
{
- addr->prefix = string_copyn(addr->local_part, plen);
+ /* If the variable-part is zero-length then the prefix was not
+ wildcarded and we can detaint-copy it since it matches the
+ (non-expandable) router option. Otherwise copy the (likely) tainted match
+ and the variable-part of the match from the local_part. */
+
+ if (vlen)
+ {
+ addr->prefix = string_copyn(addr->local_part, plen);
+ addr->prefix_v = string_copyn(addr->local_part, vlen);
+ }
+ else
+ addr->prefix = string_copyn_taint(addr->local_part, plen, FALSE);
addr->local_part += plen;
DEBUG(D_route) debug_printf("stripped prefix %s\n", addr->prefix);
}
if (r->suffix)
{
- int slen = route_check_suffix(addr->local_part, r->suffix);
+ unsigned vlen;
+ int slen = route_check_suffix(addr->local_part, r->suffix, &vlen);
if (slen > 0)
{
int lplen = Ustrlen(addr->local_part) - slen;
- addr->suffix = addr->local_part + lplen;
+ addr->suffix = vlen
+ ? addr->local_part + lplen
+ : string_copy_taint(addr->local_part + lplen, slen);
+ addr->suffix_v = addr->suffix + Ustrlen(addr->suffix) - vlen;
addr->local_part = string_copyn(addr->local_part, lplen);
DEBUG(D_route) debug_printf("stripped suffix %s\n", addr->suffix);
}
router traversal. On the addr string they are held as a variable tree, so
as to maintain the post-expansion taints separate. */
- if ((yield = set_router_vars(addr, r)) != OK)
- if (yield == PASS)
- continue; /* with next router */
- else
- goto ROUTE_EXIT;
-
-#ifdef notdef
- if (r->set)
+ switch (set_router_vars(addr, r))
{
- const uschar * list = r->set;
- int sep = 0;
- for (uschar * ele; (ele = string_nextinlist(&list, &sep, NULL, 0)); )
- {
- uschar * ee;
- if (!(ee = expand_string(ele)))
- if (f.expand_string_forcedfail)
- {
- DEBUG(D_route) debug_printf("forced failure in expansion of \"%s\" "
- "(router variable): decline action taken\n", ele);
-
- /* Expand "more" if necessary; DEFER => an expansion failed */
-
- yield = exp_bool(addr, US"router", r->name, D_route,
- US"more", r->more, r->expand_more, &more);
- if (yield != OK) goto ROUTE_EXIT;
-
- if (!more)
- {
- DEBUG(D_route)
- debug_printf("\"more\"=false: skipping remaining routers\n");
- router_name = NULL;
- r = NULL;
- break;
- }
- else continue; /* With next router */
- }
- else
- {
- addr->message = string_sprintf("expansion of \"%s\" failed "
- "in %s router: %s", ele, r->name, expand_string_message);
- yield = DEFER;
- goto ROUTE_EXIT;
- }
-
- addr->prop.set = string_append_listele(addr->prop.set, ':', ee);
- }
+ case OK: break;
+ case PASS: continue; /* with next router */
+ default: goto ROUTE_EXIT;
}
-#endif
/* Finally, expand the address_data field in the router. Forced failure
behaves as if the router declined. Any other failure is more serious. On
/* If succeeded while verifying but fail_verify is set, convert into
a failure, and take it off the local or remote delivery list. */
- if (((verify == v_sender && r->fail_verify_sender) ||
- (verify == v_recipient && r->fail_verify_recipient)) &&
- (yield == OK || yield == PASS))
+ if ( ( verify == v_sender && r->fail_verify_sender
+ || verify == v_recipient && r->fail_verify_recipient
+ )
+ && (yield == OK || yield == PASS))
{
addr->message = string_sprintf("%s router forced verify failure", r->name);
if (*paddr_remote == addr) *paddr_remote = addr->next;
HDEBUG(D_route)
{
debug_printf("%s router %s for %s\n", r->name,
- (yield == PASS)? "passed" : "declined", addr->address);
+ yield == PASS ? "passed" : "declined", addr->address);
if (Ustrcmp(old_domain, addr->domain) != 0)
debug_printf("domain %s rewritten\n", old_domain);
}
if (yield == DEFER)
{
- HDEBUG(D_route)
- {
- debug_printf("%s router: defer for %s\n", r->name, addr->address);
- debug_printf(" message: %s\n", (addr->message == NULL)?
- US"<none>" : addr->message);
- }
+ HDEBUG(D_route) debug_printf("%s router: defer for %s\n message: %s\n",
+ r->name, addr->address, addr->message ? addr->message : US"<none>");
goto ROUTE_EXIT;
}