tidying
[exim.git] / src / src / routers / queryprogram.c
CommitLineData
0756eb3c
PH
1/*************************************************
2* Exim - an Internet mail transport agent *
3*************************************************/
4
80fea873 5/* Copyright (c) University of Cambridge 1995 - 2016 */
0756eb3c
PH
6/* See the file NOTICE for conditions of use and distribution. */
7
8#include "../exim.h"
9#include "rf_functions.h"
10#include "queryprogram.h"
11
12
13
14/* Options specific to the queryprogram router. */
15
16optionlist 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)) }
35};
36
37/* Size of the options list. An extern variable has to be used so that its
38address can appear in the tables drtables.c. */
39
40int queryprogram_router_options_count =
41 sizeof(queryprogram_router_options)/sizeof(optionlist);
42
d185889f
JH
43
44#ifdef MACRO_PREDEF
45
46/* Dummy entries */
47queryprogram_router_options_block queryprogram_router_option_defaults = {0};
48void queryprogram_router_init(router_instance *rblock) {}
49int 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) {}
53
54#else /*!MACRO_PREDEF*/
55
56
0756eb3c
PH
57/* Default private options block for the queryprogram router. */
58
59queryprogram_router_options_block queryprogram_router_option_defaults = {
60 NULL, /* command */
61 60*60, /* timeout */
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 */
69};
70
71
72
73/*************************************************
74* Initialization entry point *
75*************************************************/
76
77/* Called for each instance, after its options have been read, to enable
78consistency checks to be done, or anything else that needs to be set up. */
79
80void
81queryprogram_router_init(router_instance *rblock)
82{
83queryprogram_router_options_block *ob =
84 (queryprogram_router_options_block *)(rblock->options_block);
85
86/* A command must be given */
87
88if (ob->command == NULL)
89 log_write(0, LOG_PANIC_DIE|LOG_CONFIG_FOR, "%s router:\n "
90 "a command specification is required", rblock->name);
91
92/* A uid/gid must be supplied */
93
94if (!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);
97}
98
99
100
101/*************************************************
102* Process a set of generated new addresses *
103*************************************************/
104
105/* This function sets up a set of newly generated child addresses and puts them
106on the new address chain.
107
108Arguments:
109 rblock router block
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
115
116Returns: nothing
117*/
118
119static void
120add_generated(router_instance *rblock, address_item **addr_new,
121 address_item *addr, address_item *generated,
122 address_item_propagated *addr_prop)
123{
124while (generated != NULL)
125 {
126 address_item *next = generated;
127 generated = next->next;
128
129 next->parent = addr;
130 orflag(next, addr, af_propagate);
d43cbe25 131 next->prop = *addr_prop;
0756eb3c
PH
132 next->start_router = rblock->redirect_router;
133
134 next->next = *addr_new;
135 *addr_new = next;
136
82f90600 137 if (addr->child_count == USHRT_MAX)
4362ff0d 138 log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s router generated more than %d "
82f90600 139 "child addresses for <%s>", rblock->name, USHRT_MAX, addr->address);
0756eb3c
PH
140 addr->child_count++;
141
142 DEBUG(D_route)
143 debug_printf("%s router generated %s\n", rblock->name, next->address);
144 }
145}
146
147
148
149
150/*************************************************
151* Main entry point *
152*************************************************/
153
154/* See local README for interface details. This router returns:
155
156DECLINE
157 . DECLINE returned
158 . self = DECLINE
159
160PASS
161 . PASS returned
162 . timeout of host lookup and pass_on_timeout set
163 . self = PASS
164
165DEFER
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
172 . bad lookup type
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
179 . :defer: in data
180 . DEFER or FREEZE returned
181 . problem in redirection data
182 . unknown transport name or trouble expanding router transport
183
184FAIL
185 . :fail: in data
186 . FAIL returned
187 . self = FAIL
188
189OK
190 . address added to addr_local or addr_remote for delivery
191 . new addresses added to addr_new
192*/
193
194int
195queryprogram_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 */
fd6de02e 199 int verify, /* v_none/v_recipient/v_sender/v_expn */
0756eb3c
PH
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 */
204{
205int fd_in, fd_out, len, rc;
206pid_t pid;
207struct passwd *upw = NULL;
208uschar buffer[1024];
55414b25 209const uschar **argvptr;
0756eb3c
PH
210uschar *rword, *rdata, *s;
211address_item_propagated addr_prop;
212queryprogram_router_options_block *ob =
213 (queryprogram_router_options_block *)(rblock->options_block);
214uschar *current_directory = ob->current_directory;
215ugid_block ugid;
59e82a2a
PH
216uid_t curr_uid = getuid();
217gid_t curr_gid = getgid();
0756eb3c
PH
218uid_t uid = ob->cmd_uid;
219gid_t gid = ob->cmd_gid;
59e82a2a
PH
220uid_t *puid = &uid;
221gid_t *pgid = &gid;
0756eb3c
PH
222
223DEBUG(D_route) debug_printf("%s router called for %s: domain = %s\n",
224 rblock->name, addr->address, addr->domain);
225
226ugid.uid_set = ugid.gid_set = FALSE;
227
228/* Set up the propagated data block with the current address_data and the
229errors address and extra header stuff. */
230
806c3df9 231bzero(&addr_prop, sizeof(addr_prop));
0756eb3c
PH
232addr_prop.address_data = deliver_address_data;
233
d43cbe25 234rc = rf_get_errors_address(addr, rblock, verify, &addr_prop.errors_address);
0756eb3c
PH
235if (rc != OK) return rc;
236
d43cbe25
JH
237rc = rf_get_munge_headers(addr, rblock, &addr_prop.extra_headers,
238 &addr_prop.remove_headers);
0756eb3c
PH
239if (rc != OK) return rc;
240
67e87fcf
JH
241#ifdef EXPERIMENTAL_SRS
242addr_prop.srs_sender = NULL;
243#endif
244
0756eb3c
PH
245/* Get the fixed or expanded uid under which the command is to run
246(initialization ensures that one or the other is set). */
247
248if (!ob->cmd_uid_set)
249 {
250 if (!route_find_expanded_user(ob->expand_cmd_uid, rblock->name, US"router",
251 &upw, &uid, &(addr->message)))
252 return DEFER;
253 }
254
255/* Get the fixed or expanded gid, or take the gid from the passwd entry. */
256
257if (!ob->cmd_gid_set)
258 {
259 if (ob->expand_cmd_gid != NULL)
260 {
261 if (route_find_expanded_group(ob->expand_cmd_gid, rblock->name,
262 US"router", &gid, &(addr->message)))
263 return DEFER;
264 }
265 else if (upw != NULL)
266 {
267 gid = upw->pw_gid;
268 }
269 else
270 {
271 addr->message = string_sprintf("command_user set without command_group "
272 "for %s router", rblock->name);
273 return DEFER;
274 }
275 }
276
59e82a2a 277DEBUG(D_route) debug_printf("requires uid=%ld gid=%ld current_directory=%s\n",
0756eb3c
PH
278 (long int)uid, (long int)gid, current_directory);
279
59e82a2a
PH
280/* If we are not running as root, we will not be able to change uid/gid. */
281
282if (curr_uid != root_uid && (uid != curr_uid || gid != curr_gid))
283 {
284 DEBUG(D_route)
285 {
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);
289 }
290 puid = pgid = NULL;
291 }
292
293/* Set up the command to run */
294
0756eb3c
PH
295if (!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 */
302 {
303 return DEFER;
304 }
305
306/* Create the child process, making it a group leader. */
307
59e82a2a 308pid = child_open_uid(argvptr, NULL, 0077, puid, pgid, &fd_in, &fd_out,
0756eb3c
PH
309 current_directory, TRUE);
310
311if (pid < 0)
312 {
313 addr->message = string_sprintf("%s router couldn't create child process: %s",
314 rblock->name, strerror(errno));
315 return DEFER;
316 }
317
318/* Nothing is written to the standard input. */
319
f1e894f3 320(void)close(fd_in);
0756eb3c
PH
321
322/* Wait for the process to finish, applying the timeout, and inspect its return
323code. */
324
325if ((rc = child_close(pid, ob->timeout)) != 0)
326 {
327 if (rc > 0)
328 addr->message = string_sprintf("%s router: command returned non-zero "
329 "code %d", rblock->name, rc);
330
331 else if (rc == -256)
332 {
333 addr->message = string_sprintf("%s router: command timed out",
334 rblock->name);
335 killpg(pid, SIGKILL); /* Kill the whole process group */
336 }
337
338 else if (rc == -257)
339 addr->message = string_sprintf("%s router: wait() failed: %s",
340 rblock->name, strerror(errno));
341
342 else
343 addr->message = string_sprintf("%s router: command killed by signal %d",
344 rblock->name, -rc);
345
346 return DEFER;
347 }
348
349/* Read the pipe to get the command's output, and then close it. */
350
351len = read(fd_out, buffer, sizeof(buffer) - 1);
f1e894f3 352(void)close(fd_out);
0756eb3c
PH
353
354/* Failure to return any data is an error. */
355
356if (len <= 0)
357 {
358 addr->message = string_sprintf("%s router: command failed to return data",
359 rblock->name);
360 return DEFER;
361 }
362
363/* Get rid of leading and trailing white space, and pick off the first word of
364the result. */
365
366while (len > 0 && isspace(buffer[len-1])) len--;
367buffer[len] = 0;
368
369DEBUG(D_route) debug_printf("command wrote: %s\n", buffer);
370
371rword = buffer;
372while (isspace(*rword)) rword++;
373rdata = rword;
374while (*rdata != 0 && !isspace(*rdata)) rdata++;
375if (*rdata != 0) *rdata++ = 0;
376
377/* The word must be a known yield name. If it is "REDIRECT", the rest of the
378line is redirection data, as for a .forward file. It may not contain filter
379data, and it may not contain anything other than addresses (no files, no pipes,
380no specials). */
381
382if (strcmpic(rword, US"REDIRECT") == 0)
383 {
384 int filtertype;
385 redirect_block redirect;
386 address_item *generated = NULL;
387
388 redirect.string = rdata;
389 redirect.isfile = FALSE;
390
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 */
efd9a422 398 NULL, /* sieve enotify mailto owner not relevant */
e4a89c47
PH
399 NULL, /* sieve useraddress not relevant */
400 NULL, /* sieve subaddress not relevant */
0756eb3c
PH
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));
407
408 switch (rc)
409 {
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. */
413
414 case FF_DEFER:
415 if (addr->message == NULL) addr->message = US"forced defer";
416 else addr->user_message = addr->message;
417 return DEFER;
418
419 case FF_FAIL:
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;
423 return FAIL;
424
425 case FF_DELIVERED:
426 break;
427
428 case FF_NOTDELIVERED: /* an empty redirection list is bad */
429 addr->message = US"no addresses supplied";
430 /* Fall through */
431
432 case FF_ERROR:
433 default:
434 addr->basic_errno = ERRNO_BADREDIRECT;
435 addr->message = string_sprintf("error in redirect data: %s", addr->message);
436 return DEFER;
437 }
438
439 /* Handle the generated addresses, if any. */
440
441 add_generated(rblock, addr_new, addr, generated, &addr_prop);
442
443 /* Put the original address onto the succeed queue so that any retry items
444 that get attached to it get processed. */
445
446 addr->next = *addr_succeed;
447 *addr_succeed = addr;
448
449 return OK;
450 }
451
452/* Handle other returns that are not ACCEPT */
453
454if (strcmpic(rword, US"accept") != 0)
455 {
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 */
447d236c
PH
459 if (strcmpic(rword, US"fail") == 0)
460 {
461 setflag(addr, af_pass_message);
462 return FAIL;
463 }
0756eb3c
PH
464 if (strcmpic(rword, US"freeze") == 0) addr->special_action = SPECIAL_FREEZE;
465 else if (strcmpic(rword, US"defer") != 0)
466 {
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);
469 }
470 return DEFER;
471 }
472
473/* The command yielded "ACCEPT". The rest of the string is a number of keyed
474fields from which we can fish out values using the "extract" expansion
475function. To use this feature, we must put the string into the $value variable,
476i.e. set lookup_value. */
477
478lookup_value = rdata;
479s = expand_string(US"${extract{data}{$value}}");
480if (*s != 0) addr_prop.address_data = string_copy(s);
481
482s = expand_string(US"${extract{transport}{$value}}");
483lookup_value = NULL;
484
485/* If we found a transport name, find the actual transport */
486
487if (*s != 0)
488 {
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)
493 {
494 addr->message = string_sprintf("unknown transport name %s yielded by "
495 "command", s);
496 log_write(0, LOG_PANIC, "%s router: %s", rblock->name, addr->message);
497 return DEFER;
498 }
499 addr->transport = transport;
500 }
501
502/* No transport given; get the transport from the router configuration. It may
503be fixed or expanded, but there will be an error if it is unset, requested by
504the last argument not being NULL. */
505
506else
507 {
508 if (!rf_get_transport(rblock->transport_name, &(rblock->transport), addr,
509 rblock->name, US"transport"))
510 return DEFER;
511 addr->transport = rblock->transport;
512 }
513
514/* See if a host list is given, and if so, look up the addresses. */
515
516lookup_value = rdata;
517s = expand_string(US"${extract{hosts}{$value}}");
518
519if (*s != 0)
520 {
521 int lookup_type = lk_default;
522 uschar *ss = expand_string(US"${extract{lookup}{$value}}");
523 lookup_value = NULL;
524
525 if (*ss != 0)
526 {
527 if (Ustrcmp(ss, "byname") == 0) lookup_type = lk_byname;
528 else if (Ustrcmp(ss, "bydns") == 0) lookup_type = lk_bydns;
529 else
530 {
531 addr->message = string_sprintf("bad lookup type \"%s\" yielded by "
532 "command", ss);
533 log_write(0, LOG_PANIC, "%s router: %s", rblock->name, addr->message);
534 return DEFER;
535 }
536 }
537
538 host_build_hostlist(&(addr->host_list), s, FALSE); /* pro tem no randomize */
539
540 rc = rf_lookup_hostlist(rblock, addr, rblock->ignore_target_hosts,
541 lookup_type, hff_defer, addr_new);
542 if (rc != OK) return rc;
543 }
544lookup_value = NULL;
545
546/* Put the errors address, extra headers, and address_data into this address */
547
d43cbe25 548addr->prop = addr_prop;
0756eb3c
PH
549
550/* Queue the address for local or remote delivery. */
551
552return rf_queue_add(addr, addr_local, addr_remote, rblock, pw)?
553 OK : DEFER;
554}
555
d185889f 556#endif /*!MACRO_PREDEF*/
0756eb3c 557/* End of routers/queryprogram.c */