Commit | Line | Data |
---|---|---|
35af9f61 | 1 | /* $Cambridge: exim/src/src/transports/pipe.c,v 1.2 2004/10/14 14:52:45 ph10 Exp $ */ |
0756eb3c PH |
2 | |
3 | /************************************************* | |
4 | * Exim - an Internet mail transport agent * | |
5 | *************************************************/ | |
6 | ||
7 | /* Copyright (c) University of Cambridge 1995 - 2004 */ | |
8 | /* See the file NOTICE for conditions of use and distribution. */ | |
9 | ||
10 | ||
11 | #include "../exim.h" | |
12 | #include "pipe.h" | |
13 | ||
14 | ||
15 | ||
16 | /* Options specific to the pipe transport. They must be in alphabetic | |
17 | order (note that "_" comes before the lower case letters). Those starting | |
18 | with "*" are not settable by the user but are used by the option-reading | |
19 | software for alternative value types. Some options are stored in the transport | |
20 | instance block so as to be publicly visible; these are flagged with opt_public. | |
21 | */ | |
22 | ||
23 | optionlist pipe_transport_options[] = { | |
24 | { "allow_commands", opt_stringptr, | |
25 | (void *)offsetof(pipe_transport_options_block, allow_commands) }, | |
26 | { "batch_id", opt_stringptr | opt_public, | |
27 | (void *)offsetof(transport_instance, batch_id) }, | |
28 | { "batch_max", opt_int | opt_public, | |
29 | (void *)offsetof(transport_instance, batch_max) }, | |
30 | { "check_string", opt_stringptr, | |
31 | (void *)offsetof(pipe_transport_options_block, check_string) }, | |
32 | { "command", opt_stringptr, | |
33 | (void *)offsetof(pipe_transport_options_block, cmd) }, | |
34 | { "environment", opt_stringptr, | |
35 | (void *)offsetof(pipe_transport_options_block, environment) }, | |
36 | { "escape_string", opt_stringptr, | |
37 | (void *)offsetof(pipe_transport_options_block, escape_string) }, | |
38 | { "freeze_exec_fail", opt_bool, | |
39 | (void *)offsetof(pipe_transport_options_block, freeze_exec_fail) }, | |
40 | { "ignore_status", opt_bool, | |
41 | (void *)offsetof(pipe_transport_options_block, ignore_status) }, | |
42 | { "log_defer_output", opt_bool | opt_public, | |
43 | (void *)offsetof(transport_instance, log_defer_output) }, | |
44 | { "log_fail_output", opt_bool | opt_public, | |
45 | (void *)offsetof(transport_instance, log_fail_output) }, | |
46 | { "log_output", opt_bool | opt_public, | |
47 | (void *)offsetof(transport_instance, log_output) }, | |
48 | { "max_output", opt_mkint, | |
49 | (void *)offsetof(pipe_transport_options_block, max_output) }, | |
50 | { "message_prefix", opt_stringptr, | |
51 | (void *)offsetof(pipe_transport_options_block, message_prefix) }, | |
52 | { "message_suffix", opt_stringptr, | |
53 | (void *)offsetof(pipe_transport_options_block, message_suffix) }, | |
54 | { "path", opt_stringptr, | |
55 | (void *)offsetof(pipe_transport_options_block, path) }, | |
56 | { "pipe_as_creator", opt_bool | opt_public, | |
57 | (void *)offsetof(transport_instance, deliver_as_creator) }, | |
58 | { "restrict_to_path", opt_bool, | |
59 | (void *)offsetof(pipe_transport_options_block, restrict_to_path) }, | |
60 | { "return_fail_output",opt_bool | opt_public, | |
61 | (void *)offsetof(transport_instance, return_fail_output) }, | |
62 | { "return_output", opt_bool | opt_public, | |
63 | (void *)offsetof(transport_instance, return_output) }, | |
64 | { "temp_errors", opt_stringptr, | |
65 | (void *)offsetof(pipe_transport_options_block, temp_errors) }, | |
66 | { "timeout", opt_time, | |
67 | (void *)offsetof(pipe_transport_options_block, timeout) }, | |
68 | { "umask", opt_octint, | |
69 | (void *)offsetof(pipe_transport_options_block, umask) }, | |
70 | { "use_bsmtp", opt_bool, | |
71 | (void *)offsetof(pipe_transport_options_block, use_bsmtp) }, | |
72 | { "use_crlf", opt_bool, | |
73 | (void *)offsetof(pipe_transport_options_block, use_crlf) }, | |
74 | { "use_shell", opt_bool, | |
75 | (void *)offsetof(pipe_transport_options_block, use_shell) }, | |
76 | }; | |
77 | ||
78 | /* Size of the options list. An extern variable has to be used so that its | |
79 | address can appear in the tables drtables.c. */ | |
80 | ||
81 | int pipe_transport_options_count = | |
82 | sizeof(pipe_transport_options)/sizeof(optionlist); | |
83 | ||
84 | /* Default private options block for the pipe transport. */ | |
85 | ||
86 | pipe_transport_options_block pipe_transport_option_defaults = { | |
87 | NULL, /* cmd */ | |
88 | NULL, /* allow_commands */ | |
89 | NULL, /* environment */ | |
90 | US"/usr/bin", /* path */ | |
91 | NULL, /* message_prefix (reset in init if not bsmtp) */ | |
92 | NULL, /* message_suffix (ditto) */ | |
93 | US mac_expanded_string(EX_TEMPFAIL) ":" /* temp_errors */ | |
94 | mac_expanded_string(EX_CANTCREAT), | |
95 | NULL, /* check_string */ | |
96 | NULL, /* escape_string */ | |
97 | 022, /* umask */ | |
98 | 20480, /* max_output */ | |
99 | 60*60, /* timeout */ | |
100 | 0, /* options */ | |
101 | FALSE, /* freeze_exec_fail */ | |
102 | FALSE, /* ignore_status */ | |
103 | FALSE, /* restrict_to_path */ | |
104 | FALSE, /* use_shell */ | |
105 | FALSE, /* use_bsmtp */ | |
106 | FALSE /* use_crlf */ | |
107 | }; | |
108 | ||
109 | ||
110 | ||
111 | /************************************************* | |
112 | * Initialization entry point * | |
113 | *************************************************/ | |
114 | ||
115 | /* Called for each instance, after its options have been read, to | |
116 | enable consistency checks to be done, or anything else that needs | |
117 | to be set up. */ | |
118 | ||
119 | void | |
120 | pipe_transport_init(transport_instance *tblock) | |
121 | { | |
122 | pipe_transport_options_block *ob = | |
123 | (pipe_transport_options_block *)(tblock->options_block); | |
124 | ||
125 | /* If pipe_as_creator is set, then uid/gid should not be set. */ | |
126 | ||
127 | if (tblock->deliver_as_creator && (tblock->uid_set || tblock->gid_set || | |
128 | tblock->expand_uid != NULL || tblock->expand_gid != NULL)) | |
129 | log_write(0, LOG_PANIC_DIE|LOG_CONFIG, | |
130 | "both pipe_as_creator and an explicit uid/gid are set for the %s " | |
131 | "transport", tblock->name); | |
132 | ||
133 | /* If a fixed uid field is set, then a gid field must also be set. */ | |
134 | ||
135 | if (tblock->uid_set && !tblock->gid_set && tblock->expand_gid == NULL) | |
136 | log_write(0, LOG_PANIC_DIE|LOG_CONFIG, | |
137 | "user set without group for the %s transport", tblock->name); | |
138 | ||
139 | /* Temp_errors must consist only of digits and colons, but there can be | |
140 | spaces round the colons, so allow them too. */ | |
141 | ||
142 | if (ob->temp_errors != NULL && Ustrcmp(ob->temp_errors, "*") != 0) | |
143 | { | |
144 | size_t p = Ustrspn(ob->temp_errors, "0123456789: "); | |
145 | if (ob->temp_errors[p] != 0) | |
146 | log_write(0, LOG_PANIC_DIE|LOG_CONFIG, | |
147 | "temp_errors must be a list of numbers or an asterisk for the %s " | |
148 | "transport", tblock->name); | |
149 | } | |
150 | ||
151 | /* Only one of return_output/return_fail_output or log_output/log_fail_output | |
152 | should be set. */ | |
153 | ||
154 | if (tblock->return_output && tblock->return_fail_output) | |
155 | log_write(0, LOG_PANIC_DIE|LOG_CONFIG, | |
156 | "both return_output and return_fail_output set for %s transport", | |
157 | tblock->name); | |
158 | ||
159 | if (tblock->log_output && tblock->log_fail_output) | |
160 | log_write(0, LOG_PANIC_DIE|LOG_CONFIG, | |
161 | "both log_output and log_fail_output set for the %s transport", | |
162 | tblock->name); | |
163 | ||
164 | /* If batch SMTP is set, force the check and escape strings, and arrange that | |
165 | headers are also escaped. */ | |
166 | ||
167 | if (ob->use_bsmtp) | |
168 | { | |
169 | ob->check_string = US"."; | |
170 | ob->escape_string = US".."; | |
171 | ob->options |= topt_escape_headers; | |
172 | } | |
173 | ||
174 | /* If not batch SMTP, and message_prefix or message_suffix are unset, insert | |
175 | default values for them. */ | |
176 | ||
177 | else | |
178 | { | |
179 | if (ob->message_prefix == NULL) ob->message_prefix = | |
180 | US"From ${if def:return_path{$return_path}{MAILER-DAEMON}} ${tod_bsdinbox}\n"; | |
181 | if (ob->message_suffix == NULL) ob->message_suffix = US"\n"; | |
182 | } | |
183 | ||
184 | /* The restrict_to_path and use_shell options are incompatible */ | |
185 | ||
186 | if (ob->restrict_to_path && ob->use_shell) | |
187 | log_write(0, LOG_PANIC_DIE|LOG_CONFIG, | |
188 | "both restrict_to_path and use_shell set for %s transport", | |
189 | tblock->name); | |
190 | ||
191 | /* The allow_commands and use_shell options are incompatible */ | |
192 | ||
193 | if (ob->allow_commands && ob->use_shell) | |
194 | log_write(0, LOG_PANIC_DIE|LOG_CONFIG, | |
195 | "both allow_commands and use_shell set for %s transport", | |
196 | tblock->name); | |
197 | ||
198 | /* Set up the bitwise options for transport_write_message from the various | |
199 | driver options. Only one of body_only and headers_only can be set. */ | |
200 | ||
201 | ob->options |= | |
202 | (tblock->body_only? topt_no_headers : 0) | | |
203 | (tblock->headers_only? topt_no_body : 0) | | |
204 | (tblock->return_path_add? topt_add_return_path : 0) | | |
205 | (tblock->delivery_date_add? topt_add_delivery_date : 0) | | |
206 | (tblock->envelope_to_add? topt_add_envelope_to : 0) | | |
207 | (ob->use_crlf? topt_use_crlf : 0); | |
208 | } | |
209 | ||
210 | ||
211 | ||
212 | /************************************************* | |
213 | * Set up direct (non-shell) command * | |
214 | *************************************************/ | |
215 | ||
216 | /* This function is called when a command line is to be parsed by the transport | |
217 | and executed directly, without the use of /bin/sh. | |
218 | ||
219 | Arguments: | |
220 | argvptr pointer to anchor for argv vector | |
221 | cmd points to the command string | |
222 | expand_arguments true if expansion is to occur | |
223 | expand_fail error if expansion fails | |
224 | addr chain of addresses | |
225 | tname the transport name | |
226 | ob the transport options block | |
227 | ||
228 | Returns: TRUE if all went well; otherwise an error will be | |
229 | set in the first address and FALSE returned | |
230 | */ | |
231 | ||
232 | static BOOL | |
233 | set_up_direct_command(uschar ***argvptr, uschar *cmd, BOOL expand_arguments, | |
234 | int expand_fail, address_item *addr, uschar *tname, | |
235 | pipe_transport_options_block *ob) | |
236 | { | |
237 | BOOL permitted = FALSE; | |
238 | uschar **argv; | |
239 | uschar buffer[64]; | |
240 | ||
241 | /* Set up "transport <name>" to be put in any error messages, and then | |
242 | call the common function for creating an argument list and expanding | |
243 | the items if necessary. If it fails, this function fails (error information | |
244 | is in the addresses). */ | |
245 | ||
246 | sprintf(CS buffer, "%.50s transport", tname); | |
247 | if (!transport_set_up_command(argvptr, cmd, expand_arguments, expand_fail, | |
248 | addr, buffer, NULL)) | |
249 | return FALSE; | |
250 | ||
251 | /* Point to the set-up arguments. */ | |
252 | ||
253 | argv = *argvptr; | |
254 | ||
255 | /* If allow_commands is set, see if the command is in the permitted list. */ | |
256 | ||
257 | if (ob->allow_commands != NULL) | |
258 | { | |
259 | int sep = 0; | |
260 | uschar *s, *p; | |
261 | uschar buffer[256]; | |
262 | ||
263 | s = expand_string(ob->allow_commands); | |
264 | if (s == NULL) | |
265 | { | |
266 | addr->transport_return = DEFER; | |
267 | addr->message = string_sprintf("failed to expand string \"%s\" " | |
268 | "for %s transport: %s", ob->allow_commands, tname, expand_string_message); | |
269 | return FALSE; | |
270 | } | |
271 | ||
272 | while ((p = string_nextinlist(&s, &sep, buffer, sizeof(buffer))) != NULL) | |
273 | { | |
274 | if (Ustrcmp(p, argv[0]) == 0) { permitted = TRUE; break; } | |
275 | } | |
276 | } | |
277 | ||
278 | /* If permitted is TRUE it means the command was found in the allowed list, and | |
279 | no further checks are done. If permitted = FALSE, it either means | |
280 | allow_commands wasn't set, or that the command didn't match anything in the | |
281 | list. In both cases, if restrict_to_path is set, we fail if the command | |
282 | contains any slashes, but if restrict_to_path is not set, we must fail the | |
283 | command only if allow_commands is set. */ | |
284 | ||
285 | if (!permitted) | |
286 | { | |
287 | if (ob->restrict_to_path) | |
288 | { | |
289 | if (Ustrchr(argv[0], '/') != NULL) | |
290 | { | |
291 | addr->transport_return = FAIL; | |
292 | addr->message = string_sprintf("\"/\" found in \"%s\" (command for %s " | |
293 | "transport) - failed for security reasons", cmd, tname); | |
294 | return FALSE; | |
295 | } | |
296 | } | |
297 | ||
298 | else if (ob->allow_commands != NULL) | |
299 | { | |
300 | addr->transport_return = FAIL; | |
301 | addr->message = string_sprintf("\"%s\" command not permitted by %s " | |
302 | "transport", argv[0], tname); | |
303 | return FALSE; | |
304 | } | |
305 | } | |
306 | ||
307 | /* If the command is not an absolute path, search the PATH directories | |
308 | for it. */ | |
309 | ||
310 | if (argv[0][0] != '/') | |
311 | { | |
312 | int sep = 0; | |
313 | uschar *p; | |
314 | uschar *listptr = ob->path; | |
315 | uschar buffer[1024]; | |
316 | ||
317 | while ((p = string_nextinlist(&listptr, &sep, buffer, sizeof(buffer))) != NULL) | |
318 | { | |
319 | struct stat statbuf; | |
320 | sprintf(CS big_buffer, "%.256s/%.256s", p, argv[0]); | |
321 | if (Ustat(big_buffer, &statbuf) == 0) | |
322 | { | |
323 | argv[0] = string_copy(big_buffer); | |
324 | break; | |
325 | } | |
326 | } | |
327 | if (p == NULL) | |
328 | { | |
329 | addr->transport_return = FAIL; | |
330 | addr->message = string_sprintf("\"%s\" command not found for %s transport", | |
331 | argv[0], tname); | |
332 | return FALSE; | |
333 | } | |
334 | } | |
335 | ||
336 | return TRUE; | |
337 | } | |
338 | ||
339 | ||
340 | /************************************************* | |
341 | * Set up shell command * | |
342 | *************************************************/ | |
343 | ||
344 | /* This function is called when a command line is to be passed to /bin/sh | |
345 | without parsing inside the transport. | |
346 | ||
347 | Arguments: | |
348 | argvptr pointer to anchor for argv vector | |
349 | cmd points to the command string | |
350 | expand_arguments true if expansion is to occur | |
351 | expand_fail error if expansion fails | |
352 | addr chain of addresses | |
353 | tname the transport name | |
354 | ||
355 | Returns: TRUE if all went well; otherwise an error will be | |
356 | set in the first address and FALSE returned | |
357 | */ | |
358 | ||
359 | static BOOL | |
360 | set_up_shell_command(uschar ***argvptr, uschar *cmd, BOOL expand_arguments, | |
361 | int expand_fail, address_item *addr, uschar *tname) | |
362 | { | |
363 | uschar **argv; | |
364 | ||
365 | *argvptr = argv = store_get((4)*sizeof(uschar *)); | |
366 | ||
367 | argv[0] = US"/bin/sh"; | |
368 | argv[1] = US"-c"; | |
369 | ||
370 | /* We have to take special action to handle the special "variable" called | |
371 | $pipe_addresses, which is not recognized by the normal expansion function. */ | |
372 | ||
373 | DEBUG(D_transport) | |
374 | debug_printf("shell pipe command before expansion:\n %s\n", cmd); | |
375 | ||
376 | if (expand_arguments) | |
377 | { | |
378 | uschar *s = cmd; | |
379 | uschar *p = Ustrstr(cmd, "pipe_addresses"); | |
380 | ||
381 | if (p != NULL && ( | |
382 | (p > cmd && p[-1] == '$') || | |
383 | (p > cmd + 1 && p[-2] == '$' && p[-1] == '{' && p[14] == '}'))) | |
384 | { | |
385 | address_item *ad; | |
386 | uschar *q = p + 14; | |
387 | int size = Ustrlen(cmd) + 64; | |
388 | int offset; | |
389 | ||
390 | if (p[-1] == '{') { q++; p--; } | |
391 | ||
392 | s = store_get(size); | |
393 | offset = p - cmd - 1; | |
394 | Ustrncpy(s, cmd, offset); | |
395 | ||
396 | for (ad = addr; ad != NULL; ad = ad->next) | |
397 | { | |
398 | if (ad != addr) string_cat(s, &size, &offset, US" ", 1); | |
399 | string_cat(s, &size, &offset, ad->address, Ustrlen(ad->address)); | |
400 | } | |
401 | ||
402 | string_cat(s, &size, &offset, q, Ustrlen(q)); | |
403 | s[offset] = 0; | |
404 | } | |
405 | ||
406 | /* Allow $recipients in the expansion iff it comes from a system filter */ | |
407 | ||
408 | enable_dollar_recipients = addr != NULL && | |
409 | addr->parent != NULL && | |
410 | Ustrcmp(addr->parent->address, "system-filter") == 0; | |
411 | argv[2] = expand_string(s); | |
412 | enable_dollar_recipients = FALSE; | |
413 | ||
414 | if (argv[2] == NULL) | |
415 | { | |
416 | addr->transport_return = search_find_defer? DEFER : expand_fail; | |
417 | addr->message = string_sprintf("Expansion of command \"%s\" " | |
418 | "in %s transport failed: %s", | |
419 | cmd, tname, expand_string_message); | |
420 | return FALSE; | |
421 | } | |
422 | ||
423 | DEBUG(D_transport) | |
424 | debug_printf("shell pipe command after expansion:\n %s\n", argv[2]); | |
425 | } | |
426 | else argv[2] = cmd; | |
427 | ||
428 | argv[3] = (uschar *)0; | |
429 | return TRUE; | |
430 | } | |
431 | ||
432 | ||
433 | ||
434 | ||
435 | /************************************************* | |
436 | * Main entry point * | |
437 | *************************************************/ | |
438 | ||
439 | /* See local README for interface details. This transport always returns FALSE, | |
440 | indicating that the status in the first address is the status for all addresses | |
441 | in a batch. */ | |
442 | ||
443 | BOOL | |
444 | pipe_transport_entry( | |
445 | transport_instance *tblock, /* data for this instantiation */ | |
446 | address_item *addr) /* address(es) we are working on */ | |
447 | { | |
448 | pid_t pid, outpid; | |
449 | int fd_in, fd_out, rc; | |
450 | int envcount = 0; | |
451 | int envsep = 0; | |
452 | int expand_fail; | |
453 | pipe_transport_options_block *ob = | |
454 | (pipe_transport_options_block *)(tblock->options_block); | |
455 | int timeout = ob->timeout; | |
456 | BOOL written_ok = FALSE; | |
457 | BOOL expand_arguments; | |
458 | uschar **argv; | |
459 | uschar *envp[50]; | |
460 | uschar *envlist = ob->environment; | |
461 | uschar *cmd, *ss; | |
462 | uschar *eol = (ob->use_crlf)? US"\r\n" : US"\n"; | |
463 | ||
464 | DEBUG(D_transport) debug_printf("%s transport entered\n", tblock->name); | |
465 | ||
466 | /* Set up for the good case */ | |
467 | ||
468 | addr->transport_return = OK; | |
469 | addr->basic_errno = 0; | |
470 | ||
471 | /* Pipes are not accepted as general addresses, but they can be generated from | |
472 | .forward files or alias files. In those cases, the pfr flag is set, and the | |
473 | command to be obeyed is pointed to by addr->local_part; it starts with the pipe | |
474 | symbol. In other cases, the command is supplied as one of the pipe transport's | |
475 | options. */ | |
476 | ||
477 | if (testflag(addr, af_pfr) && addr->local_part[0] == '|') | |
478 | { | |
479 | cmd = addr->local_part + 1; | |
480 | while (isspace(*cmd)) cmd++; | |
481 | expand_arguments = testflag(addr, af_expand_pipe); | |
482 | expand_fail = FAIL; | |
483 | } | |
484 | else | |
485 | { | |
486 | cmd = ob->cmd; | |
487 | expand_arguments = TRUE; | |
488 | expand_fail = PANIC; | |
489 | } | |
490 | ||
491 | /* If no command has been supplied, we are in trouble. */ | |
492 | ||
493 | if (cmd == NULL) | |
494 | { | |
495 | addr->transport_return = DEFER; | |
496 | addr->message = string_sprintf("no command specified for %s transport", | |
497 | tblock->name); | |
498 | return FALSE; | |
499 | } | |
500 | ||
501 | /* When a pipe is set up by a filter file, there may be values for $thisaddress | |
502 | and numerical the variables in existence. These are passed in | |
503 | addr->pipe_expandn for use here. */ | |
504 | ||
505 | if (expand_arguments && addr->pipe_expandn != NULL) | |
506 | { | |
507 | uschar **ss = addr->pipe_expandn; | |
508 | expand_nmax = -1; | |
509 | if (*ss != NULL) filter_thisaddress = *ss++; | |
510 | while (*ss != NULL) | |
511 | { | |
512 | expand_nstring[++expand_nmax] = *ss; | |
513 | expand_nlength[expand_nmax] = Ustrlen(*ss++); | |
514 | } | |
515 | } | |
516 | ||
517 | /* The default way of processing the command is to split it up into arguments | |
518 | here, and run it directly. This offers some security advantages. However, there | |
519 | are installations that want by default to run commands under /bin/sh always, so | |
520 | there is an option to do that. */ | |
521 | ||
522 | if (ob->use_shell) | |
523 | { | |
524 | if (!set_up_shell_command(&argv, cmd, expand_arguments, expand_fail, addr, | |
525 | tblock->name)) return FALSE; | |
526 | } | |
527 | else if (!set_up_direct_command(&argv, cmd, expand_arguments, expand_fail, addr, | |
528 | tblock->name, ob)) return FALSE; | |
529 | ||
530 | expand_nmax = -1; /* Reset */ | |
531 | filter_thisaddress = NULL; | |
532 | ||
533 | /* Set up the environment for the command. */ | |
534 | ||
535 | envp[envcount++] = string_sprintf("LOCAL_PART=%s", deliver_localpart); | |
536 | envp[envcount++] = string_sprintf("LOGNAME=%s", deliver_localpart); | |
537 | envp[envcount++] = string_sprintf("USER=%s", deliver_localpart); | |
538 | envp[envcount++] = string_sprintf("LOCAL_PART_PREFIX=%#s", | |
539 | deliver_localpart_prefix); | |
540 | envp[envcount++] = string_sprintf("LOCAL_PART_SUFFIX=%#s", | |
541 | deliver_localpart_suffix); | |
542 | envp[envcount++] = string_sprintf("DOMAIN=%s", deliver_domain); | |
543 | envp[envcount++] = string_sprintf("HOME=%#s", deliver_home); | |
544 | envp[envcount++] = string_sprintf("MESSAGE_ID=%s", message_id); | |
545 | envp[envcount++] = string_sprintf("PATH=%s", ob->path); | |
546 | envp[envcount++] = string_sprintf("RECIPIENT=%#s%#s%#s@%#s", | |
547 | deliver_localpart_prefix, deliver_localpart, deliver_localpart_suffix, | |
548 | deliver_domain); | |
549 | envp[envcount++] = string_sprintf("QUALIFY_DOMAIN=%s", qualify_domain_sender); | |
550 | envp[envcount++] = string_sprintf("SENDER=%s", sender_address); | |
551 | envp[envcount++] = US"SHELL=/bin/sh"; | |
552 | ||
553 | if (addr->host_list != NULL) | |
554 | envp[envcount++] = string_sprintf("HOST=%s", addr->host_list->name); | |
555 | ||
556 | if (timestamps_utc) envp[envcount++] = US"TZ=UTC"; | |
557 | else if (timezone_string != NULL && timezone_string[0] != 0) | |
558 | envp[envcount++] = string_sprintf("TZ=%s", timezone_string); | |
559 | ||
560 | /* Add any requested items */ | |
561 | ||
562 | if (envlist != NULL) | |
563 | { | |
564 | envlist = expand_string(envlist); | |
565 | if (envlist == NULL) | |
566 | { | |
567 | addr->transport_return = DEFER; | |
568 | addr->message = string_sprintf("failed to expand string \"%s\" " | |
569 | "for %s transport: %s", ob->environment, tblock->name, | |
570 | expand_string_message); | |
571 | return FALSE; | |
572 | } | |
573 | } | |
574 | ||
575 | while ((ss = string_nextinlist(&envlist, &envsep, big_buffer, big_buffer_size)) | |
576 | != NULL) | |
577 | { | |
578 | if (envcount > sizeof(envp)/sizeof(uschar *) - 2) | |
579 | { | |
580 | addr->transport_return = DEFER; | |
581 | addr->message = string_sprintf("too many environment settings for " | |
582 | "%s transport", tblock->name); | |
583 | return FALSE; | |
584 | } | |
585 | envp[envcount++] = string_copy(ss); | |
586 | } | |
587 | ||
588 | envp[envcount] = NULL; | |
589 | ||
590 | /* If the -N option is set, can't do any more. */ | |
591 | ||
592 | if (dont_deliver) | |
593 | { | |
594 | DEBUG(D_transport) | |
595 | debug_printf("*** delivery by %s transport bypassed by -N option", | |
596 | tblock->name); | |
597 | return FALSE; | |
598 | } | |
599 | ||
600 | ||
601 | /* Handling the output from the pipe is tricky. If a file for catching this | |
602 | output is provided, we could in theory just hand that fd over to the process, | |
603 | but this isn't very safe because it might loop and carry on writing for | |
604 | ever (which is exactly what happened in early versions of Exim). Therefore we | |
605 | use the standard child_open() function, which creates pipes. We can then read | |
606 | our end of the output pipe and count the number of bytes that come through, | |
607 | chopping the sub-process if it exceeds some limit. | |
608 | ||
609 | However, this means we want to run a sub-process with both its input and output | |
610 | attached to pipes. We can't handle that easily from a single parent process | |
611 | using straightforward code such as the transport_write_message() function | |
612 | because the subprocess might not be reading its input because it is trying to | |
613 | write to a full output pipe. The complication of redesigning the world to | |
614 | handle this is too great - simpler just to run another process to do the | |
615 | reading of the output pipe. */ | |
616 | ||
617 | ||
618 | /* As this is a local transport, we are already running with the required | |
619 | uid/gid and current directory. Request that the new process be a process group | |
620 | leader, so we can kill it and all its children on a timeout. */ | |
621 | ||
622 | if ((pid = child_open(argv, envp, ob->umask, &fd_in, &fd_out, TRUE)) < 0) | |
623 | { | |
624 | addr->transport_return = DEFER; | |
625 | addr->message = string_sprintf( | |
626 | "Failed to create child process for %s transport: %s", tblock->name, | |
627 | strerror(errno)); | |
628 | return FALSE; | |
629 | } | |
630 | ||
631 | /* Now fork a process to handle the output that comes down the pipe. */ | |
632 | ||
633 | if ((outpid = fork()) < 0) | |
634 | { | |
635 | addr->basic_errno = errno; | |
636 | addr->transport_return = DEFER; | |
637 | addr->message = string_sprintf( | |
638 | "Failed to create process for handling output in %s transport", | |
639 | tblock->name); | |
640 | close(fd_in); | |
641 | close(fd_out); | |
642 | return FALSE; | |
643 | } | |
644 | ||
645 | /* This is the code for the output-handling subprocess. Read from the pipe | |
646 | in chunks, and write to the return file if one is provided. Keep track of | |
647 | the number of bytes handled. If the limit is exceeded, try to kill the | |
648 | subprocess group, and in any case close the pipe and exit, which should cause | |
649 | the subprocess to fail. */ | |
650 | ||
651 | if (outpid == 0) | |
652 | { | |
653 | int count = 0; | |
654 | close(fd_in); | |
655 | set_process_info("reading output from |%s", cmd); | |
656 | while ((rc = read(fd_out, big_buffer, big_buffer_size)) > 0) | |
657 | { | |
658 | if (addr->return_file >= 0) | |
659 | write(addr->return_file, big_buffer, rc); | |
660 | count += rc; | |
661 | if (count > ob->max_output) | |
662 | { | |
663 | uschar *message = US"\n\n*** Too much output - remainder discarded ***\n"; | |
664 | DEBUG(D_transport) debug_printf("Too much output from pipe - killed\n"); | |
665 | if (addr->return_file >= 0) | |
666 | write(addr->return_file, message, Ustrlen(message)); | |
667 | killpg(pid, SIGKILL); | |
668 | break; | |
669 | } | |
670 | } | |
671 | close(fd_out); | |
672 | _exit(0); | |
673 | } | |
674 | ||
675 | close(fd_out); /* Not used in this process */ | |
676 | ||
677 | ||
678 | /* Carrying on now with the main parent process. Attempt to write the message | |
679 | to it down the pipe. It is a fallacy to think that you can detect write errors | |
680 | when the sub-process fails to read the pipe. The parent process may complete | |
681 | writing and close the pipe before the sub-process completes. We could sleep a | |
682 | bit here to let the sub-process get going, but it may still not complete. So we | |
683 | ignore all writing errors. (When in the test harness, we do do a short sleep so | |
684 | any debugging output is likely to be in the same order.) */ | |
685 | ||
686 | if (running_in_test_harness) millisleep(500); | |
687 | ||
688 | DEBUG(D_transport) debug_printf("Writing message to pipe\n"); | |
689 | ||
690 | /* Arrange to time out writes if there is a timeout set. */ | |
691 | ||
692 | if (timeout > 0) | |
693 | { | |
694 | sigalrm_seen = FALSE; | |
695 | transport_write_timeout = timeout; | |
696 | } | |
697 | ||
698 | /* Reset the counter of bytes written */ | |
699 | ||
700 | transport_count = 0; | |
701 | ||
702 | /* First write any configured prefix information */ | |
703 | ||
704 | if (ob->message_prefix != NULL) | |
705 | { | |
706 | uschar *prefix = expand_string(ob->message_prefix); | |
707 | if (prefix == NULL) | |
708 | { | |
709 | addr->transport_return = search_find_defer? DEFER : PANIC; | |
710 | addr->message = string_sprintf("Expansion of \"%s\" (prefix for %s " | |
711 | "transport) failed: %s", ob->message_prefix, tblock->name, | |
712 | expand_string_message); | |
713 | return FALSE; | |
714 | } | |
715 | if (!transport_write_block(fd_in, prefix, Ustrlen(prefix))) | |
716 | goto END_WRITE; | |
717 | } | |
718 | ||
719 | /* If the use_bsmtp option is set, we need to write SMTP prefix information. | |
720 | The various different values for batching are handled outside; if there is more | |
721 | than one address available here, all must be included. Force SMTP dot-handling. | |
722 | */ | |
723 | ||
724 | if (ob->use_bsmtp) | |
725 | { | |
726 | address_item *a; | |
727 | ||
728 | if (!transport_write_string(fd_in, "MAIL FROM:<%s>%s", return_path, eol)) | |
729 | goto END_WRITE; | |
730 | ||
731 | for (a = addr; a != NULL; a = a->next) | |
732 | { | |
733 | if (!transport_write_string(fd_in, | |
734 | "RCPT TO:<%s>%s", | |
735 | transport_rcpt_address(a, tblock->rcpt_include_affixes), | |
736 | eol)) | |
737 | goto END_WRITE; | |
738 | } | |
739 | ||
740 | if (!transport_write_string(fd_in, "DATA%s", eol)) goto END_WRITE; | |
741 | } | |
742 | ||
743 | /* Now the actual message - the options were set at initialization time */ | |
744 | ||
745 | if (!transport_write_message(addr, fd_in, ob->options, 0, tblock->add_headers, | |
746 | tblock->remove_headers, ob->check_string, ob->escape_string, | |
747 | tblock->rewrite_rules, tblock->rewrite_existflags)) | |
748 | goto END_WRITE; | |
749 | ||
750 | /* Now any configured suffix */ | |
751 | ||
752 | if (ob->message_suffix != NULL) | |
753 | { | |
754 | uschar *suffix = expand_string(ob->message_suffix); | |
755 | if (suffix == NULL) | |
756 | { | |
757 | addr->transport_return = search_find_defer? DEFER : PANIC; | |
758 | addr->message = string_sprintf("Expansion of \"%s\" (suffix for %s " | |
759 | "transport) failed: %s", ob->message_suffix, tblock->name, | |
760 | expand_string_message); | |
761 | return FALSE; | |
762 | } | |
763 | if (!transport_write_block(fd_in, suffix, Ustrlen(suffix))) | |
764 | goto END_WRITE; | |
765 | } | |
766 | ||
767 | /* If local_smtp, write the terminating dot. */ | |
768 | ||
769 | if (ob->use_bsmtp && !transport_write_string(fd_in, ".%s", eol)) | |
770 | goto END_WRITE; | |
771 | ||
772 | /* Flag all writing completed successfully. */ | |
773 | ||
774 | written_ok = TRUE; | |
775 | ||
776 | /* Come here if there are errors during writing. */ | |
777 | ||
778 | END_WRITE: | |
779 | ||
780 | /* OK, the writing is now all done. Close the pipe. */ | |
781 | ||
782 | (void) close(fd_in); | |
783 | ||
784 | /* Handle errors during writing. For timeouts, set the timeout for waiting for | |
785 | the child process to 1 second. If the process at the far end of the pipe died | |
786 | without reading all of it, we expect an EPIPE error, which should be ignored. | |
787 | We used also to ignore WRITEINCOMPLETE but the writing function is now cleverer | |
788 | at handling OS where the death of a pipe doesn't give EPIPE immediately. See | |
789 | comments therein. This change made 04-Sep-98. Clean up this code in a year or | |
790 | so. */ | |
791 | ||
792 | if (!written_ok) | |
793 | { | |
794 | if (errno == ETIMEDOUT) | |
795 | timeout = 1; | |
796 | else if (errno == EPIPE /* || errno == ERRNO_WRITEINCOMPLETE */ ) | |
797 | { | |
798 | debug_printf("transport error %s ignored\n", | |
799 | (errno == EPIPE)? "EPIPE" : "WRITEINCOMPLETE"); | |
800 | } | |
801 | else | |
802 | { | |
35af9f61 | 803 | addr->transport_return = PANIC; |
0756eb3c PH |
804 | addr->basic_errno = errno; |
805 | if (errno == ERRNO_CHHEADER_FAIL) | |
806 | addr->message = | |
807 | string_sprintf("Failed to expand headers_add or headers_remove: %s", | |
808 | expand_string_message); | |
809 | else if (errno == ERRNO_FILTER_FAIL) | |
35af9f61 PH |
810 | addr->message = string_sprintf("Transport filter process failed (%d)%s", |
811 | addr->more_errno, | |
812 | (addr->more_errno == EX_EXECFAILED)? ": unable to execute command" : ""); | |
0756eb3c PH |
813 | else if (errno == ERRNO_WRITEINCOMPLETE) |
814 | addr->message = string_sprintf("Failed repeatedly to write data"); | |
815 | else | |
816 | addr->message = string_sprintf("Error %d", errno); | |
817 | return FALSE; | |
818 | } | |
819 | } | |
820 | ||
821 | /* Wait for the child process to complete and take action if the returned | |
822 | status is nonzero. The timeout will be just 1 second if any of the writes | |
823 | above timed out. */ | |
824 | ||
825 | if ((rc = child_close(pid, timeout)) != 0) | |
826 | { | |
827 | /* The process did not complete in time; kill its process group and fail | |
828 | the delivery. It appears to be necessary to kill the output process too, as | |
829 | otherwise it hangs on for some time if the actual pipe process is sleeping. | |
830 | (At least, that's what I observed on Solaris 2.5.1.) Since we are failing | |
831 | the delivery, that shouldn't cause any problem. */ | |
832 | ||
833 | if (rc == -256) | |
834 | { | |
835 | killpg(pid, SIGKILL); | |
836 | kill(outpid, SIGKILL); | |
837 | addr->transport_return = FAIL; | |
838 | addr->message = string_sprintf("pipe delivery process timed out"); | |
839 | } | |
840 | ||
841 | /* Wait() failed. */ | |
842 | ||
843 | else if (rc == -257) | |
844 | { | |
845 | addr->transport_return = PANIC; | |
846 | addr->message = string_sprintf("Wait() failed for child process of %s " | |
847 | "transport: %s", tblock->name, strerror(errno)); | |
848 | } | |
849 | ||
850 | /* Either the process completed, but yielded a non-zero (necessarily | |
851 | positive) status, or the process was terminated by a signal (rc will contain | |
852 | the negation of the signal number). Treat killing by signal as failure unless | |
853 | status is being ignored. */ | |
854 | ||
855 | else if (rc < 0) | |
856 | { | |
857 | if (!ob->ignore_status) | |
858 | { | |
859 | addr->transport_return = FAIL; | |
860 | addr->message = string_sprintf("Child process of %s transport (running " | |
861 | "command \"%s\") was terminated by signal %d (%s)", tblock->name, cmd, | |
862 | -rc, os_strsignal(-rc)); | |
863 | } | |
864 | } | |
865 | ||
866 | /* For positive values (process terminated with non-zero status), we need a | |
867 | status code to request deferral. A number of systems contain the following | |
868 | line in sysexits.h: | |
869 | ||
870 | #define EX_TEMPFAIL 75 | |
871 | ||
872 | with the description | |
873 | ||
874 | EX_TEMPFAIL -- temporary failure, indicating something that | |
875 | is not really an error. In sendmail, this means | |
876 | that a mailer (e.g.) could not create a connection, | |
877 | and the request should be reattempted later. | |
878 | ||
879 | Based on this, we use exit code EX_TEMPFAIL as a default to mean "defer" when | |
880 | not ignoring the returned status. However, there is now an option that | |
881 | contains a list of temporary codes, with TEMPFAIL and CANTCREAT as defaults. | |
882 | ||
883 | Another case that needs special treatment is if execve() failed (typically | |
884 | the command that was given is a non-existent path). By default this is | |
885 | treated as just another failure, but if freeze_exec_fail is set, the reaction | |
886 | is to freeze the message rather than bounce the address. Exim used to signal | |
887 | this failure with EX_UNAVAILABLE, which is definined in many systems as | |
888 | ||
889 | #define EX_UNAVAILABLE 69 | |
890 | ||
891 | with the description | |
892 | ||
893 | EX_UNAVAILABLE -- A service is unavailable. This can occur | |
894 | if a support program or file does not exist. This | |
895 | can also be used as a catchall message when something | |
896 | you wanted to do doesn't work, but you don't know why. | |
897 | ||
898 | However, this can be confused with a command that actually returns 69 because | |
899 | something *it* wanted is unavailable. At release 4.21, Exim was changed to | |
900 | use return code 127 instead, because this is what the shell returns when it | |
901 | is unable to exec a command. We define it as EX_EXECFAILED, and use it in | |
902 | child.c to signal execve() failure and other unexpected failures such as | |
903 | setuid() not working - though that won't be the case here because we aren't | |
904 | changing uid. */ | |
905 | ||
906 | else | |
907 | { | |
908 | /* Always handle execve() failure specially if requested to */ | |
909 | ||
910 | if (ob->freeze_exec_fail && (rc == EX_EXECFAILED)) | |
911 | { | |
912 | addr->transport_return = DEFER; | |
913 | addr->special_action = SPECIAL_FREEZE; | |
914 | addr->message = string_sprintf("pipe process failed to exec \"%s\"", | |
915 | cmd); | |
916 | } | |
917 | ||
918 | /* Otherwise take action only if not ignoring status */ | |
919 | ||
920 | else if (!ob->ignore_status) | |
921 | { | |
922 | uschar *ss; | |
923 | int size, ptr, i; | |
924 | ||
925 | /* If temp_errors is "*" all codes are temporary. Initializion checks | |
926 | that it's either "*" or a list of numbers. If not "*", scan the list of | |
927 | temporary failure codes; if any match, the result is DEFER. */ | |
928 | ||
929 | if (ob->temp_errors[0] == '*') | |
930 | addr->transport_return = DEFER; | |
931 | ||
932 | else | |
933 | { | |
934 | uschar *s = ob->temp_errors; | |
935 | uschar *p; | |
936 | uschar buffer[64]; | |
937 | int sep = 0; | |
938 | ||
939 | addr->transport_return = FAIL; | |
940 | while ((p = string_nextinlist(&s,&sep,buffer,sizeof(buffer))) != NULL) | |
941 | { | |
942 | if (rc == Uatoi(p)) { addr->transport_return = DEFER; break; } | |
943 | } | |
944 | } | |
945 | ||
946 | /* Ensure the message contains the expanded command and arguments. This | |
947 | doesn't have to be brilliantly efficient - it is an error situation. */ | |
948 | ||
949 | addr->message = string_sprintf("Child process of %s transport returned " | |
950 | "%d", tblock->name, rc); | |
951 | ||
952 | ptr = Ustrlen(addr->message); | |
953 | size = ptr + 1; | |
954 | ||
955 | /* If the return code is > 128, it often means that a shell command | |
956 | was terminated by a signal. */ | |
957 | ||
958 | ss = (rc > 128)? | |
959 | string_sprintf("(could mean shell command ended by signal %d (%s))", | |
960 | rc-128, os_strsignal(rc-128)) : | |
961 | US os_strexit(rc); | |
962 | ||
963 | if (*ss != 0) | |
964 | { | |
965 | addr->message = string_cat(addr->message, &size, &ptr, US" ", 1); | |
966 | addr->message = string_cat(addr->message, &size, &ptr, | |
967 | ss, Ustrlen(ss)); | |
968 | } | |
969 | ||
970 | /* Now add the command and arguments */ | |
971 | ||
972 | addr->message = string_cat(addr->message, &size, &ptr, | |
973 | US" from command:", 14); | |
974 | ||
975 | for (i = 0; i < sizeof(argv)/sizeof(int *) && argv[i] != NULL; i++) | |
976 | { | |
977 | BOOL quote = FALSE; | |
978 | addr->message = string_cat(addr->message, &size, &ptr, US" ", 1); | |
979 | if (Ustrpbrk(argv[i], " \t") != NULL) | |
980 | { | |
981 | quote = TRUE; | |
982 | addr->message = string_cat(addr->message, &size, &ptr, US"\"", 1); | |
983 | } | |
984 | addr->message = string_cat(addr->message, &size, &ptr, argv[i], | |
985 | Ustrlen(argv[i])); | |
986 | if (quote) | |
987 | addr->message = string_cat(addr->message, &size, &ptr, US"\"", 1); | |
988 | } | |
989 | addr->message[ptr] = 0; /* Ensure concatenated string terminated */ | |
990 | } | |
991 | } | |
992 | } | |
993 | ||
994 | /* Ensure all subprocesses (in particular, the output handling process) | |
995 | are complete before we pass this point. */ | |
996 | ||
997 | while (wait(&rc) >= 0); | |
998 | ||
999 | DEBUG(D_transport) debug_printf("%s transport yielded %d\n", tblock->name, | |
1000 | addr->transport_return); | |
1001 | ||
1002 | /* If there has been a problem, the message in addr->message contains details | |
1003 | of the pipe command. We don't want to expose these to the world, so we set up | |
1004 | something bland to return to the sender. */ | |
1005 | ||
1006 | if (addr->transport_return != OK) | |
1007 | addr->user_message = US"local delivery failed"; | |
1008 | ||
1009 | return FALSE; | |
1010 | } | |
1011 | ||
1012 | /* End of transport/pipe.c */ |