X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/0756eb3cb50d73a77b486e47528f7cb1bffdb299..1d28cc061677bd07d9bed48dd84bd5c590247043:/src/src/routers/queryprogram.c diff --git a/src/src/routers/queryprogram.c b/src/src/routers/queryprogram.c index f97c70755..51fdad229 100644 --- a/src/src/routers/queryprogram.c +++ b/src/src/routers/queryprogram.c @@ -1,11 +1,11 @@ -/* $Cambridge: exim/src/src/routers/queryprogram.c,v 1.1 2004/10/07 13:10:02 ph10 Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2004 */ +/* 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 */ #include "../exim.h" #include "rf_functions.h" @@ -17,23 +17,23 @@ optionlist queryprogram_router_options[] = { { "*expand_command_group", opt_bool | opt_hidden, - (void *)(offsetof(queryprogram_router_options_block, expand_cmd_gid)) }, + OPT_OFF(queryprogram_router_options_block, expand_cmd_gid) }, { "*expand_command_user", opt_bool | opt_hidden, - (void *)(offsetof(queryprogram_router_options_block, expand_cmd_uid)) }, + OPT_OFF(queryprogram_router_options_block, expand_cmd_uid) }, { "*set_command_group", opt_bool | opt_hidden, - (void *)(offsetof(queryprogram_router_options_block, cmd_gid_set)) }, + OPT_OFF(queryprogram_router_options_block, cmd_gid_set) }, { "*set_command_user", opt_bool | opt_hidden, - (void *)(offsetof(queryprogram_router_options_block, cmd_uid_set)) }, + OPT_OFF(queryprogram_router_options_block, cmd_uid_set) }, { "command", opt_stringptr, - (void *)(offsetof(queryprogram_router_options_block, command)) }, + OPT_OFF(queryprogram_router_options_block, command) }, { "command_group",opt_expand_gid, - (void *)(offsetof(queryprogram_router_options_block, cmd_gid)) }, + OPT_OFF(queryprogram_router_options_block, cmd_gid) }, { "command_user", opt_expand_uid, - (void *)(offsetof(queryprogram_router_options_block, cmd_uid)) }, + OPT_OFF(queryprogram_router_options_block, cmd_uid) }, { "current_directory", opt_stringptr, - (void *)(offsetof(queryprogram_router_options_block, current_directory)) }, + OPT_OFF(queryprogram_router_options_block, current_directory) }, { "timeout", opt_time, - (void *)(offsetof(queryprogram_router_options_block, timeout)) } + OPT_OFF(queryprogram_router_options_block, timeout) } }; /* Size of the options list. An extern variable has to be used so that its @@ -42,6 +42,20 @@ address can appear in the tables drtables.c. */ int queryprogram_router_options_count = sizeof(queryprogram_router_options)/sizeof(optionlist); + +#ifdef MACRO_PREDEF + +/* Dummy entries */ +queryprogram_router_options_block queryprogram_router_option_defaults = {0}; +void queryprogram_router_init(router_instance *rblock) {} +int queryprogram_router_entry(router_instance *rblock, address_item *addr, + struct passwd *pw, int verify, address_item **addr_local, + address_item **addr_remote, address_item **addr_new, + address_item **addr_succeed) {return 0;} + +#else /*!MACRO_PREDEF*/ + + /* Default private options block for the queryprogram router. */ queryprogram_router_options_block queryprogram_router_option_defaults = { @@ -73,13 +87,13 @@ queryprogram_router_options_block *ob = /* A command must be given */ -if (ob->command == NULL) +if (!ob->command) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n " "a command specification is required", rblock->name); /* A uid/gid must be supplied */ -if (!ob->cmd_uid_set && ob->expand_cmd_uid == NULL) +if (!ob->cmd_uid_set && !ob->expand_cmd_uid) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n " "command_user must be specified", rblock->name); } @@ -111,17 +125,22 @@ add_generated(router_instance *rblock, address_item **addr_new, { while (generated != NULL) { + BOOL ignore_error = addr->prop.ignore_error; address_item *next = generated; + generated = next->next; next->parent = addr; - orflag(next, addr, af_propagate); - next->p = *addr_prop; + next->prop = *addr_prop; + next->prop.ignore_error = next->prop.ignore_error || ignore_error; next->start_router = rblock->redirect_router; next->next = *addr_new; *addr_new = next; + if (addr->child_count == USHRT_MAX) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s router generated more than %d " + "child addresses for <%s>", rblock->name, USHRT_MAX, addr->address); addr->child_count++; DEBUG(D_route) @@ -181,7 +200,7 @@ queryprogram_router_entry( router_instance *rblock, /* data for this instantiation */ address_item *addr, /* address we are working on */ struct passwd *pw, /* passwd entry after check_local_user */ - BOOL verify, /* TRUE when verifying */ + int verify, /* v_none/v_recipient/v_sender/v_expn */ address_item **addr_local, /* add it to this if it's local */ address_item **addr_remote, /* add it to this if it's remote */ address_item **addr_new, /* put new addresses on here */ @@ -191,15 +210,19 @@ int fd_in, fd_out, len, rc; pid_t pid; struct passwd *upw = NULL; uschar buffer[1024]; -uschar **argvptr; +const uschar **argvptr; uschar *rword, *rdata, *s; address_item_propagated addr_prop; queryprogram_router_options_block *ob = (queryprogram_router_options_block *)(rblock->options_block); uschar *current_directory = ob->current_directory; ugid_block ugid; +uid_t curr_uid = getuid(); +gid_t curr_gid = getgid(); uid_t uid = ob->cmd_uid; gid_t gid = ob->cmd_gid; +uid_t *puid = &uid; +gid_t *pgid = &gid; DEBUG(D_route) debug_printf("%s router called for %s: domain = %s\n", rblock->name, addr->address, addr->domain); @@ -209,67 +232,75 @@ ugid.uid_set = ugid.gid_set = FALSE; /* Set up the propagated data block with the current address_data and the errors address and extra header stuff. */ +bzero(&addr_prop, sizeof(addr_prop)); addr_prop.address_data = deliver_address_data; +tree_dup((tree_node **)&addr_prop.variables, addr->prop.variables); -rc = rf_get_errors_address(addr, rblock, verify, &(addr_prop.errors_address)); +rc = rf_get_errors_address(addr, rblock, verify, &addr_prop.errors_address); if (rc != OK) return rc; -rc = rf_get_munge_headers(addr, rblock, &(addr_prop.extra_headers), - &(addr_prop.remove_headers)); +rc = rf_get_munge_headers(addr, rblock, &addr_prop.extra_headers, + &addr_prop.remove_headers); if (rc != OK) return rc; /* Get the fixed or expanded uid under which the command is to run (initialization ensures that one or the other is set). */ -if (!ob->cmd_uid_set) - { - if (!route_find_expanded_user(ob->expand_cmd_uid, rblock->name, US"router", - &upw, &uid, &(addr->message))) +if ( !ob->cmd_uid_set + && !route_find_expanded_user(ob->expand_cmd_uid, rblock->name, US"router", + &upw, &uid, &(addr->message))) return DEFER; - } /* Get the fixed or expanded gid, or take the gid from the passwd entry. */ if (!ob->cmd_gid_set) - { - if (ob->expand_cmd_gid != NULL) + if (ob->expand_cmd_gid) { if (route_find_expanded_group(ob->expand_cmd_gid, rblock->name, US"router", &gid, &(addr->message))) return DEFER; } - else if (upw != NULL) - { + else if (upw) gid = upw->pw_gid; - } else { addr->message = string_sprintf("command_user set without command_group " "for %s router", rblock->name); return DEFER; } - } -DEBUG(D_route) debug_printf("uid=%ld gid=%ld current_directory=%s\n", +DEBUG(D_route) debug_printf("requires uid=%ld gid=%ld current_directory=%s\n", (long int)uid, (long int)gid, current_directory); +/* If we are not running as root, we will not be able to change uid/gid. */ + +if (curr_uid != root_uid && (uid != curr_uid || gid != curr_gid)) + { + DEBUG(D_route) + { + debug_printf("not running as root: cannot change uid/gid\n"); + debug_printf("subprocess will run with uid=%ld gid=%ld\n", + (long int)curr_uid, (long int)curr_gid); + } + puid = pgid = NULL; + } + +/* Set up the command to run */ + if (!transport_set_up_command(&argvptr, /* anchor for arg list */ ob->command, /* raw command */ TRUE, /* expand the arguments */ 0, /* not relevant when... */ NULL, /* no transporting address */ + FALSE, /* args must be untainted */ US"queryprogram router", /* for error messages */ - &(addr->message))) /* where to put error message */ - { + &addr->message)) /* where to put error message */ return DEFER; - } /* Create the child process, making it a group leader. */ -pid = child_open_uid(argvptr, NULL, 0077, &uid, &gid, &fd_in, &fd_out, - current_directory, TRUE); - -if (pid < 0) +if ((pid = child_open_uid(argvptr, NULL, 0077, puid, pgid, &fd_in, &fd_out, + current_directory, TRUE, US"queryprogram-cmd")) < 0) { addr->message = string_sprintf("%s router couldn't create child process: %s", rblock->name, strerror(errno)); @@ -278,7 +309,7 @@ if (pid < 0) /* Nothing is written to the standard input. */ -close(fd_in); +(void)close(fd_in); /* Wait for the process to finish, applying the timeout, and inspect its return code. */ @@ -310,7 +341,7 @@ if ((rc = child_close(pid, ob->timeout)) != 0) /* Read the pipe to get the command's output, and then close it. */ len = read(fd_out, buffer, sizeof(buffer) - 1); -close(fd_out); +(void)close(fd_out); /* Failure to return any data is an error. */ @@ -332,8 +363,8 @@ DEBUG(D_route) debug_printf("command wrote: %s\n", buffer); rword = buffer; while (isspace(*rword)) rword++; rdata = rword; -while (*rdata != 0 && !isspace(*rdata)) rdata++; -if (*rdata != 0) *rdata++ = 0; +while (*rdata && !isspace(*rdata)) rdata++; +if (*rdata) *rdata++ = 0; /* The word must be a known yield name. If it is "REDIRECT", the rest of the line is redirection data, as for a .forward file. It may not contain filter @@ -356,9 +387,12 @@ if (strcmpic(rword, US"REDIRECT") == 0) RDO_REWRITE, /* rewrite generated addresses */ NULL, /* :include: directory not relevant */ NULL, /* sieve vacation directory not relevant */ + NULL, /* sieve enotify mailto owner not relevant */ + NULL, /* sieve useraddress not relevant */ + NULL, /* sieve subaddress not relevant */ &ugid, /* uid/gid (but not set) */ &generated, /* where to hang the results */ - &(addr->message), /* where to put messages */ + &addr->message, /* where to put messages */ NULL, /* don't skip syntax errors */ &filtertype, /* not used; will always be FILTER_FORWARD */ string_sprintf("%s router", rblock->name)); @@ -370,28 +404,28 @@ if (strcmpic(rword, US"REDIRECT") == 0) response after verifying. */ case FF_DEFER: - if (addr->message == NULL) addr->message = US"forced defer"; + if (!addr->message) addr->message = US"forced defer"; else addr->user_message = addr->message; - return DEFER; + return DEFER; case FF_FAIL: - add_generated(rblock, addr_new, addr, generated, &addr_prop); - if (addr->message == NULL) addr->message = US"forced rejection"; + add_generated(rblock, addr_new, addr, generated, &addr_prop); + if (!addr->message) addr->message = US"forced rejection"; else addr->user_message = addr->message; - return FAIL; + return FAIL; case FF_DELIVERED: - break; + break; case FF_NOTDELIVERED: /* an empty redirection list is bad */ - addr->message = US"no addresses supplied"; + addr->message = US"no addresses supplied"; /* Fall through */ case FF_ERROR: default: - addr->basic_errno = ERRNO_BADREDIRECT; - addr->message = string_sprintf("error in redirect data: %s", addr->message); - return DEFER; + addr->basic_errno = ERRNO_BADREDIRECT; + addr->message = string_sprintf("error in redirect data: %s", addr->message); + return DEFER; } /* Handle the generated addresses, if any. */ @@ -414,7 +448,11 @@ if (strcmpic(rword, US"accept") != 0) if (strcmpic(rword, US"decline") == 0) return DECLINE; if (strcmpic(rword, US"pass") == 0) return PASS; addr->message = string_copy(rdata); /* data is a message */ - if (strcmpic(rword, US"fail") == 0) return FAIL; + if (strcmpic(rword, US"fail") == 0) + { + setflag(addr, af_pass_message); + return FAIL; + } if (strcmpic(rword, US"freeze") == 0) addr->special_action = SPECIAL_FREEZE; else if (strcmpic(rword, US"defer") != 0) { @@ -425,25 +463,20 @@ if (strcmpic(rword, US"accept") != 0) } /* The command yielded "ACCEPT". The rest of the string is a number of keyed -fields from which we can fish out values using the "extract" expansion -function. To use this feature, we must put the string into the $value variable, -i.e. set lookup_value. */ - -lookup_value = rdata; -s = expand_string(US"${extract{data}{$value}}"); -if (*s != 0) addr_prop.address_data = string_copy(s); +fields from which we can fish out values using the equivalent of the "extract" +expansion function. */ -s = expand_string(US"${extract{transport}{$value}}"); -lookup_value = NULL; +if ((s = expand_getkeyed(US"data", rdata)) && *s) + addr_prop.address_data = string_copy(s); /* If we found a transport name, find the actual transport */ -if (*s != 0) +if ((s = expand_getkeyed(US"transport", rdata)) && *s) { transport_instance *transport; - for (transport = transports; transport != NULL; transport = transport->next) + for (transport = transports; transport; transport = transport->next) if (Ustrcmp(transport->name, s) == 0) break; - if (transport == NULL) + if (!transport) { addr->message = string_sprintf("unknown transport name %s yielded by " "command", s); @@ -459,7 +492,7 @@ the last argument not being NULL. */ else { - if (!rf_get_transport(rblock->transport_name, &(rblock->transport), addr, + if (!rf_get_transport(rblock->transport_name, &rblock->transport, addr, rblock->name, US"transport")) return DEFER; addr->transport = rblock->transport; @@ -467,19 +500,15 @@ else /* See if a host list is given, and if so, look up the addresses. */ -lookup_value = rdata; -s = expand_string(US"${extract{hosts}{$value}}"); - -if (*s != 0) +if ((s = expand_getkeyed(US"hosts", rdata)) && *s) { - int lookup_type = lk_default; - uschar *ss = expand_string(US"${extract{lookup}{$value}}"); - lookup_value = NULL; + int lookup_type = LK_DEFAULT; + uschar * ss = expand_getkeyed(US"lookup", rdata); - if (*ss != 0) + if (ss && *ss) { - if (Ustrcmp(ss, "byname") == 0) lookup_type = lk_byname; - else if (Ustrcmp(ss, "bydns") == 0) lookup_type = lk_bydns; + if (Ustrcmp(ss, "byname") == 0) lookup_type = LK_BYNAME; + else if (Ustrcmp(ss, "bydns") == 0) lookup_type = LK_BYDNS; else { addr->message = string_sprintf("bad lookup type \"%s\" yielded by " @@ -499,12 +528,12 @@ lookup_value = NULL; /* Put the errors address, extra headers, and address_data into this address */ -addr->p = addr_prop; +addr->prop = addr_prop; /* Queue the address for local or remote delivery. */ -return rf_queue_add(addr, addr_local, addr_remote, rblock, pw)? - OK : DEFER; +return rf_queue_add(addr, addr_local, addr_remote, rblock, pw) ? OK : DEFER; } +#endif /*!MACRO_PREDEF*/ /* End of routers/queryprogram.c */