X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=src%2Fsrc%2Ftransports%2Fpipe.c;h=6a7f150ac248847f5ce1a394720a89c391a44f2b;hb=46d2a5e6f6e7709d172903b13945d23fc0a2c888;hp=0ce281fd0d2af39468cbb9a4290fb02c561c5431;hpb=94e1f16d6033683bdebaf5092f64c58bc044dd2d;p=exim.git diff --git a/src/src/transports/pipe.c b/src/src/transports/pipe.c index 0ce281fd0..6a7f150ac 100644 --- a/src/src/transports/pipe.c +++ b/src/src/transports/pipe.c @@ -2,7 +2,8 @@ * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2015 */ +/* Copyright (c) University of Cambridge 1995 - 2018 */ +/* Copyright (c) The Exim maintainers 2020 */ /* See the file NOTICE for conditions of use and distribution. */ @@ -21,72 +22,50 @@ with "*" are not settable by the user but are used by the option-reading software for alternative value types. Some options are stored in the transport instance block so as to be publicly visible; these are flagged with opt_public. */ +#define LOFF(field) OPT_OFF(pipe_transport_options_block, field) optionlist pipe_transport_options[] = { - { "allow_commands", opt_stringptr, - (void *)offsetof(pipe_transport_options_block, allow_commands) }, + { "allow_commands", opt_stringptr, LOFF(allow_commands) }, { "batch_id", opt_stringptr | opt_public, - (void *)offsetof(transport_instance, batch_id) }, + OPT_OFF(transport_instance, batch_id) }, { "batch_max", opt_int | opt_public, - (void *)offsetof(transport_instance, batch_max) }, - { "check_string", opt_stringptr, - (void *)offsetof(pipe_transport_options_block, check_string) }, - { "command", opt_stringptr, - (void *)offsetof(pipe_transport_options_block, cmd) }, - { "environment", opt_stringptr, - (void *)offsetof(pipe_transport_options_block, environment) }, - { "escape_string", opt_stringptr, - (void *)offsetof(pipe_transport_options_block, escape_string) }, - { "force_command", opt_bool, - (void *)offsetof(pipe_transport_options_block, force_command) }, - { "freeze_exec_fail", opt_bool, - (void *)offsetof(pipe_transport_options_block, freeze_exec_fail) }, - { "freeze_signal", opt_bool, - (void *)offsetof(pipe_transport_options_block, freeze_signal) }, - { "ignore_status", opt_bool, - (void *)offsetof(pipe_transport_options_block, ignore_status) }, + OPT_OFF(transport_instance, batch_max) }, + { "check_string", opt_stringptr, LOFF(check_string) }, + { "command", opt_stringptr, LOFF(cmd) }, + { "environment", opt_stringptr, LOFF(environment) }, + { "escape_string", opt_stringptr, LOFF(escape_string) }, + { "force_command", opt_bool, LOFF(force_command) }, + { "freeze_exec_fail", opt_bool, LOFF(freeze_exec_fail) }, + { "freeze_signal", opt_bool, LOFF(freeze_signal) }, + { "ignore_status", opt_bool, LOFF(ignore_status) }, { "log_defer_output", opt_bool | opt_public, - (void *)offsetof(transport_instance, log_defer_output) }, + OPT_OFF(transport_instance, log_defer_output) }, { "log_fail_output", opt_bool | opt_public, - (void *)offsetof(transport_instance, log_fail_output) }, + OPT_OFF(transport_instance, log_fail_output) }, { "log_output", opt_bool | opt_public, - (void *)offsetof(transport_instance, log_output) }, - { "max_output", opt_mkint, - (void *)offsetof(pipe_transport_options_block, max_output) }, - { "message_prefix", opt_stringptr, - (void *)offsetof(pipe_transport_options_block, message_prefix) }, - { "message_suffix", opt_stringptr, - (void *)offsetof(pipe_transport_options_block, message_suffix) }, - { "path", opt_stringptr, - (void *)offsetof(pipe_transport_options_block, path) }, - { "permit_coredump", opt_bool, - (void *)offsetof(pipe_transport_options_block, permit_coredump) }, + OPT_OFF(transport_instance, log_output) }, + { "max_output", opt_mkint, LOFF(max_output) }, + { "message_prefix", opt_stringptr, LOFF(message_prefix) }, + { "message_suffix", opt_stringptr, LOFF(message_suffix) }, + { "path", opt_stringptr, LOFF(path) }, + { "permit_coredump", opt_bool, LOFF(permit_coredump) }, { "pipe_as_creator", opt_bool | opt_public, - (void *)offsetof(transport_instance, deliver_as_creator) }, - { "restrict_to_path", opt_bool, - (void *)offsetof(pipe_transport_options_block, restrict_to_path) }, + OPT_OFF(transport_instance, deliver_as_creator) }, + { "restrict_to_path", opt_bool, LOFF(restrict_to_path) }, { "return_fail_output",opt_bool | opt_public, - (void *)offsetof(transport_instance, return_fail_output) }, + OPT_OFF(transport_instance, return_fail_output) }, { "return_output", opt_bool | opt_public, - (void *)offsetof(transport_instance, return_output) }, - { "temp_errors", opt_stringptr, - (void *)offsetof(pipe_transport_options_block, temp_errors) }, - { "timeout", opt_time, - (void *)offsetof(pipe_transport_options_block, timeout) }, - { "timeout_defer", opt_bool, - (void *)offsetof(pipe_transport_options_block, timeout_defer) }, - { "umask", opt_octint, - (void *)offsetof(pipe_transport_options_block, umask) }, - { "use_bsmtp", opt_bool, - (void *)offsetof(pipe_transport_options_block, use_bsmtp) }, + OPT_OFF(transport_instance, return_output) }, + { "temp_errors", opt_stringptr, LOFF(temp_errors) }, + { "timeout", opt_time, LOFF(timeout) }, + { "timeout_defer", opt_bool, LOFF(timeout_defer) }, + { "umask", opt_octint, LOFF(umask) }, + { "use_bsmtp", opt_bool, LOFF(use_bsmtp) }, #ifdef HAVE_SETCLASSRESOURCES - { "use_classresources", opt_bool, - (void *)offsetof(pipe_transport_options_block, use_classresources) }, + { "use_classresources", opt_bool, LOFF(use_classresources) }, #endif - { "use_crlf", opt_bool, - (void *)offsetof(pipe_transport_options_block, use_crlf) }, - { "use_shell", opt_bool, - (void *)offsetof(pipe_transport_options_block, use_shell) }, + { "use_crlf", opt_bool, LOFF(use_crlf) }, + { "use_shell", opt_bool, LOFF(use_shell) }, }; /* Size of the options list. An extern variable has to be used so that its @@ -463,7 +442,7 @@ set_up_shell_command(const uschar ***argvptr, uschar *cmd, { const uschar **argv; -*argvptr = argv = store_get((4)*sizeof(uschar *)); +*argvptr = argv = store_get((4)*sizeof(uschar *), FALSE); argv[0] = US"/bin/sh"; argv[1] = US"-c"; @@ -473,8 +452,7 @@ $pipe_addresses, which is not recognized by the normal expansion function. */ if (expand_arguments) { - uschar *s = cmd; - uschar *p = Ustrstr(cmd, "pipe_addresses"); + uschar * p = Ustrstr(cmd, "pipe_addresses"); gstring * g = NULL; DEBUG(D_transport) @@ -482,14 +460,13 @@ if (expand_arguments) /* Allow $recipients in the expansion iff it comes from a system filter */ - enable_dollar_recipients = addr && addr->parent && + f.enable_dollar_recipients = addr && addr->parent && Ustrcmp(addr->parent->address, "system-filter") == 0; if (p != NULL && ( (p > cmd && p[-1] == '$') || (p > cmd + 1 && p[-2] == '$' && p[-1] == '{' && p[14] == '}'))) { - address_item *ad; uschar *q = p + 14; if (p[-1] == '{') { q++; p--; } @@ -497,7 +474,7 @@ if (expand_arguments) g = string_get(Ustrlen(cmd) + 64); g = string_catn(g, cmd, p - cmd - 1); - for (ad = addr; ad; ad = ad->next) + for (address_item * ad = addr; ad; ad = ad->next) { /*XXX string_append_listele() ? */ if (ad != addr) g = string_catn(g, US" ", 1); @@ -510,11 +487,11 @@ if (expand_arguments) else argv[2] = expand_string(cmd); - enable_dollar_recipients = FALSE; + f.enable_dollar_recipients = FALSE; if (!argv[2]) { - addr->transport_return = search_find_defer ? DEFER : expand_fail; + addr->transport_return = f.search_find_defer ? DEFER : expand_fail; addr->message = string_sprintf("Expansion of command \"%s\" " "in %s transport failed: %s", cmd, tname, expand_string_message); @@ -567,12 +544,11 @@ const uschar *envlist = ob->environment; uschar *cmd, *ss; uschar *eol = ob->use_crlf ? US"\r\n" : US"\n"; transport_ctx tctx = { - {0}, - tblock, - addr, - ob->check_string, - ob->escape_string, - ob->options /* set at initialization time */ + .tblock = tblock, + .addr = addr, + .check_string = ob->check_string, + .escape_string = ob->escape_string, + ob->options | topt_not_socket /* set at initialization time */ }; DEBUG(D_transport) debug_printf("%s transport entered\n", tblock->name); @@ -589,7 +565,6 @@ symbol. In other cases, the command is supplied as one of the pipe transport's options. */ if (testflag(addr, af_pfr) && addr->local_part[0] == '|') - { if (ob->force_command) { /* Enables expansion of $address_pipe into separate arguments */ @@ -605,7 +580,6 @@ if (testflag(addr, af_pfr) && addr->local_part[0] == '|') expand_arguments = testflag(addr, af_expand_pipe); expand_fail = FAIL; } - } else { cmd = ob->cmd; @@ -618,13 +592,20 @@ else * coming from addr->local_part[0] == '|' */ -if (cmd == NULL || *cmd == '\0') +if (!cmd || !*cmd) { addr->transport_return = DEFER; addr->message = string_sprintf("no command specified for %s transport", tblock->name); return FALSE; } +if (is_tainted(cmd)) + { + addr->message = string_sprintf("Tainted '%s' (command " + "for %s transport) not permitted", cmd, tblock->name); + addr->transport_return = PANIC; + return FALSE; + } /* When a pipe is set up by a filter file, there may be values for $thisaddress and numerical the variables in existence. These are passed in @@ -634,8 +615,8 @@ if (expand_arguments && addr->pipe_expandn) { uschar **ss = addr->pipe_expandn; expand_nmax = -1; - if (*ss != NULL) filter_thisaddress = *ss++; - while (*ss != NULL) + if (*ss) filter_thisaddress = *ss++; + while (*ss) { expand_nstring[++expand_nmax] = *ss; expand_nlength[expand_nmax] = Ustrlen(*ss++); @@ -681,16 +662,14 @@ envp[envcount++] = US"SHELL=/bin/sh"; if (addr->host_list != NULL) envp[envcount++] = string_sprintf("HOST=%s", addr->host_list->name); -if (timestamps_utc) envp[envcount++] = US"TZ=UTC"; +if (f.timestamps_utc) envp[envcount++] = US"TZ=UTC"; else if (timezone_string != NULL && timezone_string[0] != 0) envp[envcount++] = string_sprintf("TZ=%s", timezone_string); /* Add any requested items */ if (envlist) - { - envlist = expand_cstring(envlist); - if (envlist == NULL) + if (!(envlist = expand_cstring(envlist))) { addr->transport_return = DEFER; addr->message = string_sprintf("failed to expand string \"%s\" " @@ -698,13 +677,13 @@ if (envlist) expand_string_message); return FALSE; } - } while ((ss = string_nextinlist(&envlist, &envsep, big_buffer, big_buffer_size))) { if (envcount > nelem(envp) - 2) { addr->transport_return = DEFER; + addr->basic_errno = E2BIG; addr->message = string_sprintf("too many environment settings for " "%s transport", tblock->name); return FALSE; @@ -716,7 +695,7 @@ envp[envcount] = NULL; /* If the -N option is set, can't do any more. */ -if (dont_deliver) +if (f.dont_deliver) { DEBUG(D_transport) debug_printf("*** delivery by %s transport bypassed by -N option", @@ -746,7 +725,8 @@ reading of the output pipe. */ uid/gid and current directory. Request that the new process be a process group leader, so we can kill it and all its children on a timeout. */ -if ((pid = child_open(USS argv, envp, ob->umask, &fd_in, &fd_out, TRUE)) < 0) +if ((pid = child_open(USS argv, envp, ob->umask, &fd_in, &fd_out, TRUE, + US"pipe-tpt-cmd")) < 0) { addr->transport_return = DEFER; addr->message = string_sprintf( @@ -758,7 +738,7 @@ tctx.u.fd = fd_in; /* Now fork a process to handle the output that comes down the pipe. */ -if ((outpid = fork()) < 0) +if ((outpid = exim_fork(US"pipe-tpt-output")) < 0) { addr->basic_errno = errno; addr->transport_return = DEFER; @@ -816,7 +796,7 @@ bit here to let the sub-process get going, but it may still not complete. So we ignore all writing errors. (When in the test harness, we do do a short sleep so any debugging output is likely to be in the same order.) */ -if (running_in_test_harness) millisleep(500); +testharness_pause_ms(500); DEBUG(D_transport) debug_printf("Writing message to pipe\n"); @@ -834,12 +814,12 @@ transport_count = 0; /* First write any configured prefix information */ -if (ob->message_prefix != NULL) +if (ob->message_prefix) { uschar *prefix = expand_string(ob->message_prefix); - if (prefix == NULL) + if (!prefix) { - addr->transport_return = search_find_defer? DEFER : PANIC; + addr->transport_return = f.search_find_defer? DEFER : PANIC; addr->message = string_sprintf("Expansion of \"%s\" (prefix for %s " "transport) failed: %s", ob->message_prefix, tblock->name, expand_string_message); @@ -856,12 +836,10 @@ than one address available here, all must be included. Force SMTP dot-handling. if (ob->use_bsmtp) { - address_item *a; - if (!transport_write_string(fd_in, "MAIL FROM:<%s>%s", return_path, eol)) goto END_WRITE; - for (a = addr; a; a = a->next) + for (address_item * a = addr; a; a = a->next) if (!transport_write_string(fd_in, "RCPT TO:<%s>%s", transport_rcpt_address(a, tblock->rcpt_include_affixes), @@ -883,7 +861,7 @@ if (ob->message_suffix) uschar *suffix = expand_string(ob->message_suffix); if (!suffix) { - addr->transport_return = search_find_defer? DEFER : PANIC; + addr->transport_return = f.search_find_defer? DEFER : PANIC; addr->message = string_sprintf("Expansion of \"%s\" (suffix for %s " "transport) failed: %s", ob->message_suffix, tblock->name, expand_string_message); @@ -922,7 +900,7 @@ if (!written_ok) if (errno == ETIMEDOUT) { addr->message = string_sprintf("%stimeout while writing to pipe", - transport_filter_timed_out? "transport filter " : ""); + f.transport_filter_timed_out ? "transport filter " : ""); addr->transport_return = ob->timeout_defer? DEFER : FAIL; timeout = 1; } @@ -943,7 +921,7 @@ if (!written_ok) addr->more_errno, (addr->more_errno == EX_EXECFAILED)? ": unable to execute command" : ""); else if (errno == ERRNO_WRITEINCOMPLETE) - addr->message = string_sprintf("Failed repeatedly to write data"); + addr->message = US"Failed repeatedly to write data"; else addr->message = string_sprintf("Error %d", errno); return FALSE; @@ -956,8 +934,8 @@ above timed out. */ if ((rc = child_close(pid, timeout)) != 0) { - uschar *tmsg = (addr->message == NULL)? US"" : - string_sprintf(" (preceded by %s)", addr->message); + uschar * tmsg = addr->message + ? string_sprintf(" (preceded by %s)", addr->message) : US""; /* The process did not complete in time; kill its process group and fail the delivery. It appears to be necessary to kill the output process too, as @@ -988,7 +966,7 @@ if ((rc = child_close(pid, timeout)) != 0) This prevents the transport_filter timeout message from getting overwritten by the exit error which is not the cause of the problem. */ - else if (transport_filter_timed_out) + else if (f.transport_filter_timed_out) { killpg(pid, SIGKILL); kill(outpid, SIGKILL); @@ -1063,7 +1041,7 @@ if ((rc = child_close(pid, timeout)) != 0) { /* Always handle execve() failure specially if requested to */ - if (ob->freeze_exec_fail && (rc == EX_EXECFAILED)) + if (ob->freeze_exec_fail && rc == EX_EXECFAILED) { addr->transport_return = DEFER; addr->special_action = SPECIAL_FREEZE; @@ -1077,7 +1055,6 @@ if ((rc = child_close(pid, timeout)) != 0) { uschar *ss; gstring * g; - int i; /* If temp_errors is "*" all codes are temporary. Initialization checks that it's either "*" or a list of numbers. If not "*", scan the list of @@ -1112,7 +1089,7 @@ if ((rc = child_close(pid, timeout)) != 0) rc-128, os_strsignal(rc-128)) : US os_strexit(rc); - if (*ss != 0) + if (*ss) { g = string_catn(g, US" ", 1); g = string_cat (g, ss); @@ -1122,7 +1099,7 @@ if ((rc = child_close(pid, timeout)) != 0) g = string_catn(g, US" from command:", 14); - for (i = 0; i < sizeof(argv)/sizeof(int *) && argv[i] != NULL; i++) + for (int i = 0; i < sizeof(argv)/sizeof(int *) && argv[i] != NULL; i++) { BOOL quote = FALSE; g = string_catn(g, US" ", 1);