X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/b4f579d134197249b448cb5d8abf801ba4c729bb..e89d95f1909c0972b0e854ae05f50246f4d727d6:/src/src/route.c diff --git a/src/src/route.c b/src/src/route.c index 0817a4eda..a5f5feeaf 100644 --- a/src/src/route.c +++ b/src/src/route.c @@ -3,6 +3,7 @@ *************************************************/ /* Copyright (c) University of Cambridge 1995 - 2018 */ +/* Copyright (c) The Exim Maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions concerned with routing, and the list of generic router options. */ @@ -14,132 +15,133 @@ /* 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); @@ -281,7 +283,8 @@ for (router_instance * r = routers; r; r = r->next) 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. */ @@ -333,19 +336,20 @@ wildcard. 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] == '*') @@ -353,10 +357,19 @@ while ((prefix = string_nextinlist(&listptr, &sep, prebuf, sizeof(prebuf)))) 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; @@ -375,31 +388,40 @@ is a wildcard. 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; @@ -715,7 +737,7 @@ while ((check = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer)))) 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 @@ -737,21 +759,20 @@ while ((check = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer)))) { 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; @@ -927,7 +948,7 @@ if ((rc = route_check_dls(r->name, US"local_parts", r->local_parts, 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) { @@ -938,6 +959,8 @@ 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; @@ -951,22 +974,19 @@ confusing. */ 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 @@ -1101,7 +1121,7 @@ route_finduser(const uschar *s, struct passwd **pw, uid_t *return_uid) 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) { @@ -1115,7 +1135,7 @@ 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 */ @@ -1416,7 +1436,7 @@ set_router_vars(address_item * addr, const router_instance * r) { const uschar * varlist = r->set; tree_node ** root = (tree_node **) &addr->prop.variables; -int sep = 0; +int sep = ';'; if (!varlist) return OK; @@ -1433,9 +1453,15 @@ for (uschar * ele; (ele = string_nextinlist(&varlist, &sep, NULL, 0)); ) /* 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 (!(val = expand_string(US assignment))) if (f.expand_string_forcedfail) { @@ -1468,13 +1494,18 @@ for (uschar * ele; (ele = string_nextinlist(&varlist, &sep, NULL, 0)); ) } 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; } return OK; } @@ -1607,9 +1638,9 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) /* 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); @@ -1620,10 +1651,22 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) 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); } @@ -1639,11 +1682,15 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) 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); } @@ -1685,54 +1732,12 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) 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 @@ -1822,9 +1827,10 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) /* 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; @@ -1841,7 +1847,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) 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); } @@ -1905,12 +1911,8 @@ if (!r) 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"" : 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""); goto ROUTE_EXIT; }