X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/8102279385f5f70c959aa219feca37031c0a1828..90bd3832bc0f:/src/src/route.c diff --git a/src/src/route.c b/src/src/route.c index c7833ebb4..0fdcdf2ed 100644 --- a/src/src/route.c +++ b/src/src/route.c @@ -2,8 +2,10 @@ * Exim - an Internet mail transport agent * *************************************************/ +/* Copyright (c) The Exim Maintainers 2020 - 2022 */ /* Copyright (c) University of Cambridge 1995 - 2018 */ /* See the file NOTICE for conditions of use and distribution. */ +/* SPDX-License-Identifier: GPL-2.0-or-later */ /* Functions concerned with routing, and the list of generic router options. */ @@ -287,7 +289,12 @@ for (router_instance * r = routers; r; r = r->next) /* 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 */ @@ -606,14 +613,13 @@ gid_t gid = 0; /* For picky compilers */ 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"); +DEBUG(D_route|D_expand) 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; @@ -631,7 +637,7 @@ while ((check = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer)))) /* Empty items are just skipped */ - if (*ss == 0) continue; + if (!*ss) continue; /* If there are no slashes in the string, we have a user name or uid, with optional group/gid. */ @@ -645,9 +651,9 @@ while ((check = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer)))) /* If there's a comma, temporarily terminate the user name/number at that point. Then set the uid. */ - if (comma != NULL) *comma = 0; + if (comma) *comma = 0; ok = route_finduser(ss, &pw, &uid); - if (comma != NULL) *comma = ','; + if (comma) *comma = ','; if (!ok) { @@ -657,24 +663,22 @@ while ((check = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer)))) /* If there was no comma, the gid is that associated with the user. */ - if (comma == NULL) - { - if (pw != NULL) gid = pw->pw_gid; else + if (!comma) + if (pw) + gid = pw->pw_gid; + else { *perror = string_sprintf("group missing after numerical uid %d for " "require_files", (int)uid); goto RETURN_DEFER; } - } else - { if (!route_findgroup(comma + 1, &gid)) { *perror = string_sprintf("group \"%s\" for require_files not found\n", comma + 1); goto RETURN_DEFER; } - } /* Note that we have values set, and proceed to next item */ @@ -853,12 +857,12 @@ Returns: OK if all the tests succeed */ static BOOL -check_router_conditions(router_instance *r, address_item *addr, int verify, - struct passwd **pw, uschar **perror) +check_router_conditions(router_instance * r, address_item * addr, int verify, + struct passwd ** pw, uschar ** perror) { int rc; -uschar *check_local_part; -unsigned int *localpart_cache; +uschar * check_local_part; +unsigned int * localpart_cache; /* Reset variables to hold a home directory and data from lookup of a domain or local part, and ensure search_find_defer is unset, in case there aren't any @@ -922,15 +926,12 @@ because that doesn't have the prefix or suffix stripped. A bit of massaging is required. Also, we only use the match cache for local parts that have not had a prefix or suffix stripped. */ +check_local_part = string_copy(addr->cc_local_part); if (!addr->prefix && !addr->suffix) - { localpart_cache = addr->localpart_cache; - check_local_part = addr->cc_local_part; - } else { localpart_cache = NULL; - check_local_part = string_copy(addr->cc_local_part); if (addr->prefix) check_local_part += Ustrlen(addr->prefix); if (addr->suffix) @@ -947,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} and local_part_verified. */ +local_user_{uid,gid} and local_part_data. */ if (r->check_local_user) { @@ -958,7 +959,8 @@ if (r->check_local_user) r->name, addr->local_part); return SKIP; } - deliver_localpart_verified = string_copy(US (*pw)->pw_name); + 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; @@ -970,24 +972,22 @@ check_local_user before any subsequent expansions are done. Otherwise, $home could mean different things for different options, which would be extremely confusing. */ +GET_OPTION("router_home_directory"); 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 @@ -1017,7 +1017,8 @@ if ((rc = check_files(r->require_files, perror)) != OK) if (r->condition) { - DEBUG(D_route) debug_printf("checking \"condition\" \"%.80s\"...\n", r->condition); + DEBUG(D_route|D_expand) + debug_printf("checking \"condition\" \"%.80s\"...\n", r->condition); if (!expand_check_condition(r->condition, r->name, US"router")) { if (f.search_find_defer) @@ -1439,6 +1440,7 @@ const uschar * varlist = r->set; tree_node ** root = (tree_node **) &addr->prop.variables; int sep = ';'; +GET_OPTION("set"); if (!varlist) return OK; /* Walk the varlist, creating variables */ @@ -1473,6 +1475,7 @@ for (uschar * ele; (ele = string_nextinlist(&varlist, &sep, NULL, 0)); ) /* Expand "more" if necessary; DEFER => an expansion failed */ + GET_OPTION("more"); yield = exp_bool(addr, US"router", r->name, D_route, US"more", r->more, r->expand_more, &more); if (yield != OK) return yield; @@ -1491,12 +1494,16 @@ for (uschar * ele; (ele = string_nextinlist(&varlist, &sep, NULL, 0)); ) { addr->message = string_sprintf("expansion of \"%s\" failed " "in %s router: %s", ele, r->name, expand_string_message); - return DEFER; + /* Caller will replace that for logging, if a DB lookup, to avoid exposing + passwords */ + DEBUG(D_route) debug_printf("%s\n", addr->message); + if (!f.search_find_defer) + return f.search_find_defer ? DEFER : FAIL; } if (!(node = tree_search(*root, name))) { /* name should never be tainted */ - node = store_get(sizeof(tree_node) + Ustrlen(name), FALSE); + node = store_get(sizeof(tree_node) + Ustrlen(name), GET_UNTAINTED); Ustrcpy(node->name, name); (void)tree_insertnode(root, node); } @@ -1545,8 +1552,8 @@ route_address(address_item *addr, address_item **paddr_local, { int yield = OK; BOOL unseen; -router_instance *r, *nextr; -const uschar *old_domain = addr->domain; +router_instance * r, * nextr; +const uschar * old_domain = addr->domain; HDEBUG(D_route) { @@ -1560,8 +1567,8 @@ instead of at the first router. */ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) { - uschar *error; - struct passwd *pw = NULL; + uschar * error; + struct passwd * pw = NULL; struct passwd pwcopy; BOOL loop_detected = FALSE; BOOL more; @@ -1656,8 +1663,18 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) int plen = route_check_prefix(addr->local_part, r->prefix, &vlen); if (plen > 0) { - addr->prefix = string_copyn(addr->local_part, plen); - if (vlen) addr->prefix_v = string_copyn(addr->local_part, vlen); + /* 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, GET_UNTAINTED); addr->local_part += plen; DEBUG(D_route) debug_printf("stripped prefix %s\n", addr->prefix); } @@ -1678,7 +1695,9 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) 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, GET_UNTAINTED); 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); @@ -1695,7 +1714,8 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) the local part sorted. */ router_name = r->name; - deliver_localpart_verified = NULL; + driver_srcfile = r->srcfile; + driver_srcline = r->srcline; deliver_set_expansions(addr); /* For convenience, the pre-router checks are in a separate function, which @@ -1703,7 +1723,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) if ((rc = check_router_conditions(r, addr, verify, &pw, &error)) != OK) { - router_name = NULL; + driver_srcfile = router_name = NULL; driver_srcline = 0; if (rc == SKIP) continue; addr->message = error; yield = rc; @@ -1722,11 +1742,11 @@ 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. */ - switch (set_router_vars(addr, r)) + switch (rc = set_router_vars(addr, r)) { case OK: break; case PASS: continue; /* with next router */ - default: goto ROUTE_EXIT; + default: yield = rc; goto ROUTE_EXIT; } /* Finally, expand the address_data field in the router. Forced failure @@ -1736,7 +1756,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) if (r->address_data) { - DEBUG(D_route) debug_printf("processing address_data\n"); + DEBUG(D_route|D_expand) debug_printf("processing address_data\n"); if (!(deliver_address_data = expand_string(r->address_data))) { if (f.expand_string_forcedfail) @@ -1746,6 +1766,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) /* Expand "more" if necessary; DEFER => an expansion failed */ + GET_OPTION("more"); yield = exp_bool(addr, US"router", r->name, D_route, US"more", r->more, r->expand_more, &more); if (yield != OK) goto ROUTE_EXIT; @@ -1754,7 +1775,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) { DEBUG(D_route) debug_printf("\"more\"=false: skipping remaining routers\n"); - router_name = NULL; + driver_srcfile = router_name = NULL; driver_srcline = 0; r = NULL; break; } @@ -1806,7 +1827,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) yield = (r->info->code)(r, addr, pw, verify, paddr_local, paddr_remote, addr_new, addr_succeed); - router_name = NULL; + driver_srcfile = router_name = NULL; driver_srcline = 0; if (yield == FAIL) { @@ -1854,6 +1875,7 @@ for (r = addr->start_router ? addr->start_router : routers; r; r = nextr) { /* Expand "more" if necessary */ + GET_OPTION("more"); yield = exp_bool(addr, US"router", r->name, D_route, US"more", r->more, r->expand_more, &more); if (yield != OK) goto ROUTE_EXIT; @@ -1879,18 +1901,21 @@ if (!r) HDEBUG(D_route) debug_printf("no more routers\n"); if (!addr->message) { - uschar *message = US"Unrouteable address"; - if (addr->router && addr->router->cannot_route_message) + uschar * message = US"Unrouteable address"; + if (addr->router) { - uschar *expmessage = expand_string(addr->router->cannot_route_message); - if (!expmessage) - { - if (!f.expand_string_forcedfail) - log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " - "cannot_route_message in %s router: %s", addr->router->name, - expand_string_message); - } - else message = expmessage; + uschar * s = addr->router->cannot_route_message; + GET_OPTION("cannot_route_message"); + if (s) + { + if ((s = expand_string(s))) + message = s; + else + if (!f.expand_string_forcedfail) + log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand " + "cannot_route_message in %s router: %s", addr->router->name, + expand_string_message); + } } addr->user_message = addr->message = message; } @@ -1932,6 +1957,7 @@ networking, so it is included in the binary only if requested. */ #ifdef SUPPORT_TRANSLATE_IP_ADDRESS +GET_OPTION("translate_ip_address"); if (r->translate_ip_address) { int rc; @@ -1990,6 +2016,7 @@ if (r->translate_ip_address) /* See if this is an unseen routing; first expand the option if necessary. DEFER can be given if the expansion fails */ +GET_OPTION("unseen"); yield = exp_bool(addr, US"router", r->name, D_route, US"unseen", r->unseen, r->expand_unseen, &unseen); if (yield != OK) goto ROUTE_EXIT; @@ -2034,10 +2061,23 @@ if (yield == DEFER && addr->message) addr->message = expand_hide_passwords(addr->message); deliver_set_expansions(NULL); -router_name = NULL; +driver_srcfile = router_name = NULL; driver_srcline = 0; f.disable_logging = FALSE; return yield; } + + +/* For error messages, a string describing the config location associated +with current processing. NULL if we are not in a router. */ +/* Name only, for now */ + +uschar * +router_current_name(void) +{ +if (!router_name) return NULL; +return string_sprintf(" (router %s, %s %d)", router_name, driver_srcfile, driver_srcline); +} + #endif /*!MACRO_PREDEF*/ /* End of route.c */