tidying
[exim.git] / src / src / routers / queryprogram.c
1 /*************************************************
2 *     Exim - an Internet mail transport agent    *
3 *************************************************/
4
5 /* Copyright (c) University of Cambridge 1995 - 2016 */
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
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)) }
35 };
36
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. */
39
40 int queryprogram_router_options_count =
41   sizeof(queryprogram_router_options)/sizeof(optionlist);
42
43
44 #ifdef MACRO_PREDEF
45
46 /* Dummy entries */
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) {return 0;}
53
54 #else   /*!MACRO_PREDEF*/
55
56
57 /* Default private options block for the queryprogram router. */
58
59 queryprogram_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
78 consistency checks to be done, or anything else that needs to be set up. */
79
80 void
81 queryprogram_router_init(router_instance *rblock)
82 {
83 queryprogram_router_options_block *ob =
84   (queryprogram_router_options_block *)(rblock->options_block);
85
86 /* A command must be given */
87
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);
91
92 /* A uid/gid must be supplied */
93
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);
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
106 on the new address chain.
107
108 Arguments:
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
116 Returns:         nothing
117 */
118
119 static void
120 add_generated(router_instance *rblock, address_item **addr_new,
121   address_item *addr, address_item *generated,
122   address_item_propagated *addr_prop)
123 {
124 while (generated != NULL)
125   {
126   address_item *next = generated;
127   generated = next->next;
128
129   next->parent = addr;
130   orflag(next, addr, af_propagate);
131   next->prop = *addr_prop;
132   next->start_router = rblock->redirect_router;
133
134   next->next = *addr_new;
135   *addr_new = next;
136
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);
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
156 DECLINE
157   . DECLINE returned
158   . self = DECLINE
159
160 PASS
161   . PASS returned
162   . timeout of host lookup and pass_on_timeout set
163   . self = PASS
164
165 DEFER
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
184 FAIL
185   . :fail: in data
186   . FAIL returned
187   . self = FAIL
188
189 OK
190   . address added to addr_local or addr_remote for delivery
191   . new addresses added to addr_new
192 */
193
194 int
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 */
204 {
205 int fd_in, fd_out, len, rc;
206 pid_t pid;
207 struct passwd *upw = NULL;
208 uschar buffer[1024];
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;
215 ugid_block ugid;
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;
220 uid_t *puid = &uid;
221 gid_t *pgid = &gid;
222
223 DEBUG(D_route) debug_printf("%s router called for %s: domain = %s\n",
224   rblock->name, addr->address, addr->domain);
225
226 ugid.uid_set = ugid.gid_set = FALSE;
227
228 /* Set up the propagated data block with the current address_data and the
229 errors address and extra header stuff. */
230
231 bzero(&addr_prop, sizeof(addr_prop));
232 addr_prop.address_data = deliver_address_data;
233
234 rc = rf_get_errors_address(addr, rblock, verify, &addr_prop.errors_address);
235 if (rc != OK) return rc;
236
237 rc = rf_get_munge_headers(addr, rblock, &addr_prop.extra_headers,
238   &addr_prop.remove_headers);
239 if (rc != OK) return rc;
240
241 #ifdef EXPERIMENTAL_SRS
242 addr_prop.srs_sender = NULL;
243 #endif
244
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
248 if (!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
257 if (!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
277 DEBUG(D_route) debug_printf("requires uid=%ld gid=%ld current_directory=%s\n",
278   (long int)uid, (long int)gid, current_directory);
279
280 /* If we are not running as root, we will not be able to change uid/gid. */
281
282 if (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
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 */
302   {
303   return DEFER;
304   }
305
306 /* Create the child process, making it a group leader. */
307
308 pid = child_open_uid(argvptr, NULL, 0077, puid, pgid, &fd_in, &fd_out,
309   current_directory, TRUE);
310
311 if (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
320 (void)close(fd_in);
321
322 /* Wait for the process to finish, applying the timeout, and inspect its return
323 code. */
324
325 if ((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
351 len = read(fd_out, buffer, sizeof(buffer) - 1);
352 (void)close(fd_out);
353
354 /* Failure to return any data is an error. */
355
356 if (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
364 the result. */
365
366 while (len > 0 && isspace(buffer[len-1])) len--;
367 buffer[len] = 0;
368
369 DEBUG(D_route) debug_printf("command wrote: %s\n", buffer);
370
371 rword = buffer;
372 while (isspace(*rword)) rword++;
373 rdata = rword;
374 while (*rdata != 0 && !isspace(*rdata)) rdata++;
375 if (*rdata != 0) *rdata++ = 0;
376
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,
380 no specials). */
381
382 if (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 */
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));
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
454 if (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 */
459   if (strcmpic(rword, US"fail") == 0)
460     {
461     setflag(addr, af_pass_message);
462     return FAIL;
463     }
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
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. */
477
478 lookup_value = rdata;
479 s = expand_string(US"${extract{data}{$value}}");
480 if (*s != 0) addr_prop.address_data = string_copy(s);
481
482 s = expand_string(US"${extract{transport}{$value}}");
483 lookup_value = NULL;
484
485 /* If we found a transport name, find the actual transport */
486
487 if (*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
503 be fixed or expanded, but there will be an error if it is unset, requested by
504 the last argument not being NULL. */
505
506 else
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
516 lookup_value = rdata;
517 s = expand_string(US"${extract{hosts}{$value}}");
518
519 if (*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   }
544 lookup_value = NULL;
545
546 /* Put the errors address, extra headers, and address_data into this address */
547
548 addr->prop = addr_prop;
549
550 /* Queue the address for local or remote delivery. */
551
552 return rf_queue_add(addr, addr_local, addr_remote, rblock, pw)?
553   OK : DEFER;
554 }
555
556 #endif   /*!MACRO_PREDEF*/
557 /* End of routers/queryprogram.c */