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