1 /*************************************************
2 * Exim - an Internet mail transport agent *
3 *************************************************/
5 /* Copyright (c) University of Cambridge 1995 - 2016 */
6 /* See the file NOTICE for conditions of use and distribution. */
9 #include "rf_functions.h"
10 #include "queryprogram.h"
14 /* Options specific to the queryprogram router. */
16 optionlist queryprogram_router_options[] = {
17 { "*expand_command_group", opt_bool | opt_hidden,
18 (void *)(offsetof(queryprogram_router_options_block, expand_cmd_gid)) },
19 { "*expand_command_user", opt_bool | opt_hidden,
20 (void *)(offsetof(queryprogram_router_options_block, expand_cmd_uid)) },
21 { "*set_command_group", opt_bool | opt_hidden,
22 (void *)(offsetof(queryprogram_router_options_block, cmd_gid_set)) },
23 { "*set_command_user", opt_bool | opt_hidden,
24 (void *)(offsetof(queryprogram_router_options_block, cmd_uid_set)) },
25 { "command", opt_stringptr,
26 (void *)(offsetof(queryprogram_router_options_block, command)) },
27 { "command_group",opt_expand_gid,
28 (void *)(offsetof(queryprogram_router_options_block, cmd_gid)) },
29 { "command_user", opt_expand_uid,
30 (void *)(offsetof(queryprogram_router_options_block, cmd_uid)) },
31 { "current_directory", opt_stringptr,
32 (void *)(offsetof(queryprogram_router_options_block, current_directory)) },
33 { "timeout", opt_time,
34 (void *)(offsetof(queryprogram_router_options_block, timeout)) }
37 /* Size of the options list. An extern variable has to be used so that its
38 address can appear in the tables drtables.c. */
40 int queryprogram_router_options_count =
41 sizeof(queryprogram_router_options)/sizeof(optionlist);
47 queryprogram_router_options_block queryprogram_router_option_defaults = {0};
48 void queryprogram_router_init(router_instance *rblock) {}
49 int queryprogram_router_entry(router_instance *rblock, address_item *addr,
50 struct passwd *pw, int verify, address_item **addr_local,
51 address_item **addr_remote, address_item **addr_new,
52 address_item **addr_succeed) {}
54 #else /*!MACRO_PREDEF*/
57 /* Default private options block for the queryprogram router. */
59 queryprogram_router_options_block queryprogram_router_option_defaults = {
62 (uid_t)(-1), /* cmd_uid */
63 (gid_t)(-1), /* cmd_gid */
64 FALSE, /* cmd_uid_set */
65 FALSE, /* cmd_gid_set */
66 US"/", /* current_directory */
67 NULL, /* expand_cmd_gid */
68 NULL /* expand_cmd_uid */
73 /*************************************************
74 * Initialization entry point *
75 *************************************************/
77 /* Called for each instance, after its options have been read, to enable
78 consistency checks to be done, or anything else that needs to be set up. */
81 queryprogram_router_init(router_instance *rblock)
83 queryprogram_router_options_block *ob =
84 (queryprogram_router_options_block *)(rblock->options_block);
86 /* A command must be given */
88 if (ob->command == NULL)
89 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
90 "a command specification is required", rblock->name);
92 /* A uid/gid must be supplied */
94 if (!ob->cmd_uid_set && ob->expand_cmd_uid == NULL)
95 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
96 "command_user must be specified", rblock->name);
101 /*************************************************
102 * Process a set of generated new addresses *
103 *************************************************/
105 /* This function sets up a set of newly generated child addresses and puts them
106 on the new address chain.
110 addr_new new address chain
111 addr original address
112 generated list of generated addresses
113 addr_prop the propagated data block, containing errors_to,
114 header change stuff, and address_data
120 add_generated(router_instance *rblock, address_item **addr_new,
121 address_item *addr, address_item *generated,
122 address_item_propagated *addr_prop)
124 while (generated != NULL)
126 address_item *next = generated;
127 generated = next->next;
130 orflag(next, addr, af_propagate);
131 next->prop = *addr_prop;
132 next->start_router = rblock->redirect_router;
134 next->next = *addr_new;
137 if (addr->child_count == USHRT_MAX)
138 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s router generated more than %d "
139 "child addresses for <%s>", rblock->name, USHRT_MAX, addr->address);
143 debug_printf("%s router generated %s\n", rblock->name, next->address);
150 /*************************************************
152 *************************************************/
154 /* See local README for interface details. This router returns:
162 . timeout of host lookup and pass_on_timeout set
166 . verifying the errors address caused a deferment or a big disaster such
167 as an expansion failure (rf_get_errors_address)
168 . expanding a headers_{add,remove} string caused a deferment or another
169 expansion error (rf_get_munge_headers)
170 . a problem in rf_get_transport: no transport when one is needed;
171 failed to expand dynamic transport; failed to find dynamic transport
173 . problem looking up host (rf_lookup_hostlist)
174 . self = DEFER or FREEZE
175 . failure to set up uid/gid for running the command
176 . failure of transport_set_up_command: too many arguments, expansion fail
177 . failure to create child process
178 . child process crashed or timed out or didn't return data
180 . DEFER or FREEZE returned
181 . problem in redirection data
182 . unknown transport name or trouble expanding router transport
190 . address added to addr_local or addr_remote for delivery
191 . new addresses added to addr_new
195 queryprogram_router_entry(
196 router_instance *rblock, /* data for this instantiation */
197 address_item *addr, /* address we are working on */
198 struct passwd *pw, /* passwd entry after check_local_user */
199 int verify, /* v_none/v_recipient/v_sender/v_expn */
200 address_item **addr_local, /* add it to this if it's local */
201 address_item **addr_remote, /* add it to this if it's remote */
202 address_item **addr_new, /* put new addresses on here */
203 address_item **addr_succeed) /* put old address here on success */
205 int fd_in, fd_out, len, rc;
207 struct passwd *upw = NULL;
209 const uschar **argvptr;
210 uschar *rword, *rdata, *s;
211 address_item_propagated addr_prop;
212 queryprogram_router_options_block *ob =
213 (queryprogram_router_options_block *)(rblock->options_block);
214 uschar *current_directory = ob->current_directory;
216 uid_t curr_uid = getuid();
217 gid_t curr_gid = getgid();
218 uid_t uid = ob->cmd_uid;
219 gid_t gid = ob->cmd_gid;
223 DEBUG(D_route) debug_printf("%s router called for %s: domain = %s\n",
224 rblock->name, addr->address, addr->domain);
226 ugid.uid_set = ugid.gid_set = FALSE;
228 /* Set up the propagated data block with the current address_data and the
229 errors address and extra header stuff. */
231 bzero(&addr_prop, sizeof(addr_prop));
232 addr_prop.address_data = deliver_address_data;
234 rc = rf_get_errors_address(addr, rblock, verify, &addr_prop.errors_address);
235 if (rc != OK) return rc;
237 rc = rf_get_munge_headers(addr, rblock, &addr_prop.extra_headers,
238 &addr_prop.remove_headers);
239 if (rc != OK) return rc;
241 #ifdef EXPERIMENTAL_SRS
242 addr_prop.srs_sender = NULL;
245 /* Get the fixed or expanded uid under which the command is to run
246 (initialization ensures that one or the other is set). */
248 if (!ob->cmd_uid_set)
250 if (!route_find_expanded_user(ob->expand_cmd_uid, rblock->name, US"router",
251 &upw, &uid, &(addr->message)))
255 /* Get the fixed or expanded gid, or take the gid from the passwd entry. */
257 if (!ob->cmd_gid_set)
259 if (ob->expand_cmd_gid != NULL)
261 if (route_find_expanded_group(ob->expand_cmd_gid, rblock->name,
262 US"router", &gid, &(addr->message)))
265 else if (upw != NULL)
271 addr->message = string_sprintf("command_user set without command_group "
272 "for %s router", rblock->name);
277 DEBUG(D_route) debug_printf("requires uid=%ld gid=%ld current_directory=%s\n",
278 (long int)uid, (long int)gid, current_directory);
280 /* If we are not running as root, we will not be able to change uid/gid. */
282 if (curr_uid != root_uid && (uid != curr_uid || gid != curr_gid))
286 debug_printf("not running as root: cannot change uid/gid\n");
287 debug_printf("subprocess will run with uid=%ld gid=%ld\n",
288 (long int)curr_uid, (long int)curr_gid);
293 /* Set up the command to run */
295 if (!transport_set_up_command(&argvptr, /* anchor for arg list */
296 ob->command, /* raw command */
297 TRUE, /* expand the arguments */
298 0, /* not relevant when... */
299 NULL, /* no transporting address */
300 US"queryprogram router", /* for error messages */
301 &(addr->message))) /* where to put error message */
306 /* Create the child process, making it a group leader. */
308 pid = child_open_uid(argvptr, NULL, 0077, puid, pgid, &fd_in, &fd_out,
309 current_directory, TRUE);
313 addr->message = string_sprintf("%s router couldn't create child process: %s",
314 rblock->name, strerror(errno));
318 /* Nothing is written to the standard input. */
322 /* Wait for the process to finish, applying the timeout, and inspect its return
325 if ((rc = child_close(pid, ob->timeout)) != 0)
328 addr->message = string_sprintf("%s router: command returned non-zero "
329 "code %d", rblock->name, rc);
333 addr->message = string_sprintf("%s router: command timed out",
335 killpg(pid, SIGKILL); /* Kill the whole process group */
339 addr->message = string_sprintf("%s router: wait() failed: %s",
340 rblock->name, strerror(errno));
343 addr->message = string_sprintf("%s router: command killed by signal %d",
349 /* Read the pipe to get the command's output, and then close it. */
351 len = read(fd_out, buffer, sizeof(buffer) - 1);
354 /* Failure to return any data is an error. */
358 addr->message = string_sprintf("%s router: command failed to return data",
363 /* Get rid of leading and trailing white space, and pick off the first word of
366 while (len > 0 && isspace(buffer[len-1])) len--;
369 DEBUG(D_route) debug_printf("command wrote: %s\n", buffer);
372 while (isspace(*rword)) rword++;
374 while (*rdata != 0 && !isspace(*rdata)) rdata++;
375 if (*rdata != 0) *rdata++ = 0;
377 /* The word must be a known yield name. If it is "REDIRECT", the rest of the
378 line is redirection data, as for a .forward file. It may not contain filter
379 data, and it may not contain anything other than addresses (no files, no pipes,
382 if (strcmpic(rword, US"REDIRECT") == 0)
385 redirect_block redirect;
386 address_item *generated = NULL;
388 redirect.string = rdata;
389 redirect.isfile = FALSE;
391 rc = rda_interpret(&redirect, /* redirection data */
392 RDO_BLACKHOLE | /* forbid :blackhole: */
393 RDO_FAIL | /* forbid :fail: */
394 RDO_INCLUDE | /* forbid :include: */
395 RDO_REWRITE, /* rewrite generated addresses */
396 NULL, /* :include: directory not relevant */
397 NULL, /* sieve vacation directory not relevant */
398 NULL, /* sieve enotify mailto owner not relevant */
399 NULL, /* sieve useraddress not relevant */
400 NULL, /* sieve subaddress not relevant */
401 &ugid, /* uid/gid (but not set) */
402 &generated, /* where to hang the results */
403 &(addr->message), /* where to put messages */
404 NULL, /* don't skip syntax errors */
405 &filtertype, /* not used; will always be FILTER_FORWARD */
406 string_sprintf("%s router", rblock->name));
410 /* FF_DEFER and FF_FAIL can arise only as a result of explicit commands.
411 If a configured message was supplied, allow it to be included in an SMTP
412 response after verifying. */
415 if (addr->message == NULL) addr->message = US"forced defer";
416 else addr->user_message = addr->message;
420 add_generated(rblock, addr_new, addr, generated, &addr_prop);
421 if (addr->message == NULL) addr->message = US"forced rejection";
422 else addr->user_message = addr->message;
428 case FF_NOTDELIVERED: /* an empty redirection list is bad */
429 addr->message = US"no addresses supplied";
434 addr->basic_errno = ERRNO_BADREDIRECT;
435 addr->message = string_sprintf("error in redirect data: %s", addr->message);
439 /* Handle the generated addresses, if any. */
441 add_generated(rblock, addr_new, addr, generated, &addr_prop);
443 /* Put the original address onto the succeed queue so that any retry items
444 that get attached to it get processed. */
446 addr->next = *addr_succeed;
447 *addr_succeed = addr;
452 /* Handle other returns that are not ACCEPT */
454 if (strcmpic(rword, US"accept") != 0)
456 if (strcmpic(rword, US"decline") == 0) return DECLINE;
457 if (strcmpic(rword, US"pass") == 0) return PASS;
458 addr->message = string_copy(rdata); /* data is a message */
459 if (strcmpic(rword, US"fail") == 0)
461 setflag(addr, af_pass_message);
464 if (strcmpic(rword, US"freeze") == 0) addr->special_action = SPECIAL_FREEZE;
465 else if (strcmpic(rword, US"defer") != 0)
467 addr->message = string_sprintf("bad command yield: %s %s", rword, rdata);
468 log_write(0, LOG_PANIC, "%s router: %s", rblock->name, addr->message);
473 /* The command yielded "ACCEPT". The rest of the string is a number of keyed
474 fields from which we can fish out values using the "extract" expansion
475 function. To use this feature, we must put the string into the $value variable,
476 i.e. set lookup_value. */
478 lookup_value = rdata;
479 s = expand_string(US"${extract{data}{$value}}");
480 if (*s != 0) addr_prop.address_data = string_copy(s);
482 s = expand_string(US"${extract{transport}{$value}}");
485 /* If we found a transport name, find the actual transport */
489 transport_instance *transport;
490 for (transport = transports; transport != NULL; transport = transport->next)
491 if (Ustrcmp(transport->name, s) == 0) break;
492 if (transport == NULL)
494 addr->message = string_sprintf("unknown transport name %s yielded by "
496 log_write(0, LOG_PANIC, "%s router: %s", rblock->name, addr->message);
499 addr->transport = transport;
502 /* No transport given; get the transport from the router configuration. It may
503 be fixed or expanded, but there will be an error if it is unset, requested by
504 the last argument not being NULL. */
508 if (!rf_get_transport(rblock->transport_name, &(rblock->transport), addr,
509 rblock->name, US"transport"))
511 addr->transport = rblock->transport;
514 /* See if a host list is given, and if so, look up the addresses. */
516 lookup_value = rdata;
517 s = expand_string(US"${extract{hosts}{$value}}");
521 int lookup_type = lk_default;
522 uschar *ss = expand_string(US"${extract{lookup}{$value}}");
527 if (Ustrcmp(ss, "byname") == 0) lookup_type = lk_byname;
528 else if (Ustrcmp(ss, "bydns") == 0) lookup_type = lk_bydns;
531 addr->message = string_sprintf("bad lookup type \"%s\" yielded by "
533 log_write(0, LOG_PANIC, "%s router: %s", rblock->name, addr->message);
538 host_build_hostlist(&(addr->host_list), s, FALSE); /* pro tem no randomize */
540 rc = rf_lookup_hostlist(rblock, addr, rblock->ignore_target_hosts,
541 lookup_type, hff_defer, addr_new);
542 if (rc != OK) return rc;
546 /* Put the errors address, extra headers, and address_data into this address */
548 addr->prop = addr_prop;
550 /* Queue the address for local or remote delivery. */
552 return rf_queue_add(addr, addr_local, addr_remote, rblock, pw)?
556 #endif /*!MACRO_PREDEF*/
557 /* End of routers/queryprogram.c */