From: Jeremy Harris Date: Sun, 7 Apr 2013 16:09:10 +0000 (+0100) Subject: Merge branch 'ocsp_staple_rollup' X-Git-Tag: exim-4_82_RC2~61 X-Git-Url: https://git.exim.org/users/jgh/exim.git/commitdiff_plain/57233af5f91cdca9a0232a71fab2d12a538cb1a6?hp=983207c12082eb01a5a5a0552962de200f231bf4 Merge branch 'ocsp_staple_rollup' * ocsp_staple_rollup: tidying OCSP-stapling enhancement and testing. --- diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt index 92d0a2287..063d74a92 100644 --- a/doc/doc-docbook/spec.xfpt +++ b/doc/doc-docbook/spec.xfpt @@ -4335,7 +4335,7 @@ For compatibility with Sendmail, this option is equivalent to It sets the incoming protocol and host name (for trusted callers). The host name and its colon can be omitted when only the protocol is to be set. Note the Exim already has two private options, &%-pd%& and &%-ps%&, that refer -to embedded Perl. It is therefore impossible to set a protocol value of &`p`& +to embedded Perl. It is therefore impossible to set a protocol value of &`d`& or &`s`& using this option (but that does not seem a real limitation). .vitem &%-q%& @@ -21601,10 +21601,10 @@ that are routed to the transport. .vindex "&$address_pipe$&" A router redirects an address directly to a pipe command (for example, from an alias or forward file). In this case, &$address_pipe$& contains the text of the -pipe command, and the &%command%& option on the transport is ignored. If only -one address is being transported (&%batch_max%& is not greater than one, or -only one address was redirected to this pipe command), &$local_part$& contains -the local part that was redirected. +pipe command, and the &%command%& option on the transport is ignored unless +&%force_command%& is set. If only one address is being transported +(&%batch_max%& is not greater than one, or only one address was redirected to +this pipe command), &$local_part$& contains the local part that was redirected. .endlist @@ -21712,6 +21712,15 @@ inserted in the argument list at that point &'as a separate argument'&. This avoids any problems with spaces or shell metacharacters, and is of use when a &(pipe)& transport is handling groups of addresses in a batch. +If &%force_command%& is enabled on the transport, Special handling takes place +for an argument that consists of precisely the text &`$address_pipe`&. It +is handled similarly to &$pipe_addresses$& above. It is expanded and each +argument is inserted in the argument list at that point +&'as a separate argument'&. The &`$address_pipe`& item does not need to be +the only item in the argument; in fact, if it were then &%force_command%& +should behave as a no-op. Rather, it should be used to adjust the command +run while preserving the argument vector separation. + After splitting up into arguments and expansion, the resulting command is run in a subprocess directly from the transport, &'not'& under a shell. The message that is being delivered is supplied on the standard input, and the @@ -21864,6 +21873,23 @@ a bounce message is sent. If &%freeze_signal%& is set, the message will be frozen in Exim's queue instead. +.option force_command pipe boolean false +.cindex "force command" +.cindex "&(pipe)& transport", "force command" +Normally when a router redirects an address directly to a pipe command +the &%command%& option on the transport is ignored. If &%force_command%& +is set, the &%command%& option will used. This is especially +useful for forcing a wrapper or additional argument to be added to the +command. For example: +.code +command = /usr/bin/remote_exec myhost -- $address_pipe +force_command +.endd + +Note that &$address_pipe$& is handled specially in &%command%& when +&%force_command%& is set, expanding out to the original argument vector as +separate items, similarly to a Unix shell &`"$@"`& construct. + .option ignore_status pipe boolean false If this option is true, the status returned by the subprocess that is set up to run the command is ignored, and Exim behaves as if zero had been returned. diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog index a2e204d34..bfef5556d 100644 --- a/doc/doc-txt/ChangeLog +++ b/doc/doc-txt/ChangeLog @@ -181,6 +181,13 @@ PP/18 OpenSSL made graceful with empty tls_verify_certificates setting. unset was to force an expansion failure. That still works, and an empty string is now equivalent. +PP/19 Renamed DNSSEC-enabling option to "dns_dnssec_ok", to make it + clearer that Exim is using the DO (DNSSEC OK) EDNS0 resolver flag, + not performing validation itself. + +PP/20 Added force_command boolean option to pipe transport. + Patch from Nick Koston, of cPanel Inc. + Exim version 4.80.1 ------------------- diff --git a/doc/doc-txt/NewStuff b/doc/doc-txt/NewStuff index 47c5f6fec..e349fc855 100644 --- a/doc/doc-txt/NewStuff +++ b/doc/doc-txt/NewStuff @@ -32,10 +32,11 @@ Version 4.82 Unless you really know what you are doing, leave it alone. 4. If not built with DISABLE_DNSSEC, Exim now has the main option - dns_use_dnssec; if set to 1 then Exim will initialise the resolver library + dns_dnssec_ok; if set to 1 then Exim will initialise the resolver library to send the DO flag to your recursive resolver. If you have a recursive resolver, which can set the Authenticated Data (AD) flag in results, Exim - can now detect this. + can now detect this. Exim does not perform validation itself, instead + relying upon a trusted path to the resolver. Current status: work-in-progress; $sender_host_dnssec variable added. @@ -129,6 +130,10 @@ Version 4.82 18. If built with EXPERIMENTAL_PRDR, per-recipient data responses per a proposed extension to SMTP from Eric Hall. +19. The pipe transport has gained the force_command option, to allow + decorating commands from user .forward pipe aliases with prefix + wrappers, for instance. + Version 4.80 ------------ diff --git a/doc/doc-txt/OptionLists.txt b/doc/doc-txt/OptionLists.txt index fa692bfb1..cb5f35eb6 100644 --- a/doc/doc-txt/OptionLists.txt +++ b/doc/doc-txt/OptionLists.txt @@ -235,6 +235,7 @@ forbid_include boolean false redirect forbid_pipe boolean false redirect 4.00 forbid_sieve_filter boolean false redirect 4.44 forbid_smtp_code boolean false redirect 4.63 +force_command boolean false pipe 4.82 freeze_exec_fail boolean false pipe 1.89 freeze_signal boolean false pipe 4.75 freeze_tell boolean false main 4.00 replaces freeze_tell_mailmaster diff --git a/src/ACKNOWLEDGMENTS b/src/ACKNOWLEDGMENTS index ed005b3f8..4474de322 100644 --- a/src/ACKNOWLEDGMENTS +++ b/src/ACKNOWLEDGMENTS @@ -405,6 +405,7 @@ John Horne Patch adding $av_failed Pointed out ClamAV ExtendedDetectionInfo compat issue Regid Ichira Documentation fixes Andreas M. Kirchwitz Let /dev/null have normal permissions (4.73 fallout) +J. Nick Koston Patch adding force_command pipe transport option Roberto Lima Patch letting exicyclog rotate paniclog Todd Lyons Patch handling TAB in MAIL arguments Christof Meerwald Provided insight & suggested patch for GnuTLS update diff --git a/src/src/dns.c b/src/src/dns.c index 95db52686..820adff01 100644 --- a/src/src/dns.c +++ b/src/src/dns.c @@ -206,28 +206,28 @@ if (dns_use_edns0 >= 0) # ifndef RES_USE_EDNS0 # error Have RES_USE_DNSSEC but not RES_USE_EDNS0? Something hinky ... # endif -if (dns_use_dnssec >= 0) +if (dns_dnssec_ok >= 0) { - if (dns_use_edns0 == 0 && dns_use_dnssec != 0) + if (dns_use_edns0 == 0 && dns_dnssec_ok != 0) { DEBUG(D_resolver) - debug_printf("CONFLICT: dns_use_edns0 forced false, dns_use_dnssec forced true!\n"); + debug_printf("CONFLICT: dns_use_edns0 forced false, dns_dnssec_ok forced true, ignoring latter!\n"); } else { - if (dns_use_dnssec) + if (dns_dnssec_ok) resp->options |= RES_USE_DNSSEC; else resp->options &= ~RES_USE_DNSSEC; DEBUG(D_resolver) debug_printf("Coerced resolver DNSSEC support %s.\n", - dns_use_dnssec ? "on" : "off"); + dns_dnssec_ok ? "on" : "off"); } } # else -if (dns_use_dnssec >= 0) +if (dns_dnssec_ok >= 0) DEBUG(D_resolver) debug_printf("Unable to %sset DNSSEC without resolver support.\n", - dns_use_dnssec ? "" : "un"); + dns_dnssec_ok ? "" : "un"); # endif #endif /* DISABLE_DNSSEC */ diff --git a/src/src/globals.c b/src/src/globals.c index 5db858bfc..a4898fe3f 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -597,7 +597,7 @@ BOOL dns_csa_use_reverse = TRUE; uschar *dns_ipv4_lookup = NULL; int dns_retrans = 0; int dns_retry = 0; -int dns_use_dnssec = -1; /* <0 = not coerced */ +int dns_dnssec_ok = -1; /* <0 = not coerced */ int dns_use_edns0 = -1; /* <0 = not coerced */ uschar *dnslist_domain = NULL; uschar *dnslist_matched = NULL; diff --git a/src/src/globals.h b/src/src/globals.h index 8d83be710..df6132266 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -353,7 +353,7 @@ extern BOOL dns_csa_use_reverse; /* Check CSA in reverse DNS? (non-standar extern uschar *dns_ipv4_lookup; /* For these domains, don't look for AAAA (or A6) */ extern int dns_retrans; /* Retransmission time setting */ extern int dns_retry; /* Number of retries */ -extern int dns_use_dnssec; /* When constructing DNS query, set DO flag */ +extern int dns_dnssec_ok; /* When constructing DNS query, set DO flag */ extern int dns_use_edns0; /* Coerce EDNS0 support on/off in resolver. */ extern uschar *dnslist_domain; /* DNS (black) list domain */ extern uschar *dnslist_matched; /* DNS (black) list matched key */ diff --git a/src/src/readconf.c b/src/src/readconf.c index bba532594..77836d157 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -219,7 +219,7 @@ static optionlist optionlist_config[] = { { "dns_ipv4_lookup", opt_stringptr, &dns_ipv4_lookup }, { "dns_retrans", opt_time, &dns_retrans }, { "dns_retry", opt_int, &dns_retry }, - { "dns_use_dnssec", opt_int, &dns_use_dnssec }, + { "dns_dnssec_ok", opt_int, &dns_dnssec_ok }, { "dns_use_edns0", opt_int, &dns_use_edns0 }, /* This option is now a no-op, retained for compability */ { "drop_cr", opt_bool, &drop_cr }, diff --git a/src/src/structs.h b/src/src/structs.h index d11e91adb..53aa2106b 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -482,10 +482,10 @@ typedef struct address_item_propagated { #define af_cert_verified 0x01000000 /* delivered with verified TLS cert */ #define af_pass_message 0x02000000 /* pass message in bounces */ #define af_bad_reply 0x04000000 /* filter could not generate autoreply */ - #ifdef EXPERIMENTAL_PRDR # define af_prdr_used 0x08000000 /* delivery used SMTP PRDR */ #endif +#define af_force_command 0x10000000 /* force_command in pipe transport */ /* These flags must be propagated when a child is created */ diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 8b70b13c5..6f2646f03 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -1970,12 +1970,26 @@ vaguely_random_number(int max) { unsigned int r; int i, needed_len; +static pid_t pidlast = 0; +pid_t pidnow; uschar *p; uschar smallbuf[sizeof(r)]; if (max <= 1) return 0; +pidnow = getpid(); +if (pidnow != pidlast) + { + /* Although OpenSSL documents that "OpenSSL makes sure that the PRNG state + is unique for each thread", this doesn't apparently apply across processes, + so our own warning from vaguely_random_number_fallback() applies here too. + Fix per PostgreSQL. */ + if (pidlast != 0) + RAND_cleanup(); + pidlast = pidnow; + } + /* OpenSSL auto-seeds from /dev/random, etc, but this a double-check. */ if (!RAND_status()) { diff --git a/src/src/transport.c b/src/src/transport.c index 160d080b9..7dd1afb85 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -1997,7 +1997,122 @@ if (expand_arguments) memmove(argv + i + 1 + additional, argv + i + 1, (argcount - i)*sizeof(uschar *)); - for (ad = addr; ad != NULL; ad = ad->next) argv[i++] = ad->address; + for (ad = addr; ad != NULL; ad = ad->next) { + argv[i++] = ad->address; + argcount++; + } + + /* Subtract one since we replace $pipe_addresses */ + argcount--; + i--; + } + + /* Handle special case of $address_pipe when af_force_command is set */ + + else if (addr != NULL && testflag(addr,af_force_command) && + (Ustrcmp(argv[i], "$address_pipe") == 0 || + Ustrcmp(argv[i], "${address_pipe}") == 0)) + { + int address_pipe_i; + int address_pipe_argcount = 0; + int address_pipe_max_args; + uschar **address_pipe_argv; + + /* We can never have more then the argv we will be loading into */ + address_pipe_max_args = max_args - argcount + 1; + + DEBUG(D_transport) + debug_printf("address_pipe_max_args=%d\n", address_pipe_max_args); + + /* We allocate an additional for (uschar *)0 */ + address_pipe_argv = store_get((address_pipe_max_args+1)*sizeof(uschar *)); + + /* +1 because addr->local_part[0] == '|' since af_force_command is set */ + s = expand_string(addr->local_part + 1); + + if (s == NULL || *s == '\0') + { + addr->transport_return = FAIL; + addr->message = string_sprintf("Expansion of \"%s\" " + "from command \"%s\" in %s failed: %s", + (addr->local_part + 1), cmd, etext, expand_string_message); + return FALSE; + } + + while (isspace(*s)) s++; /* strip leading space */ + + while (*s != 0 && address_pipe_argcount < address_pipe_max_args) + { + if (*s == '\'') + { + ss = s + 1; + while (*ss != 0 && *ss != '\'') ss++; + address_pipe_argv[address_pipe_argcount++] = ss = store_get(ss - s++); + while (*s != 0 && *s != '\'') *ss++ = *s++; + if (*s != 0) s++; + *ss++ = 0; + } + else address_pipe_argv[address_pipe_argcount++] = string_dequote(&s); + while (isspace(*s)) s++; /* strip space after arg */ + } + + address_pipe_argv[address_pipe_argcount] = (uschar *)0; + + /* If *s != 0 we have run out of argument slots. */ + if (*s != 0) + { + uschar *msg = string_sprintf("Too many arguments in $address_pipe " + "\"%s\" in %s", addr->local_part + 1, etext); + if (addr != NULL) + { + addr->transport_return = FAIL; + addr->message = msg; + } + else *errptr = msg; + return FALSE; + } + + /* address_pipe_argcount - 1 + * because we are replacing $address_pipe in the argument list + * with the first thing it expands to */ + if (argcount + address_pipe_argcount - 1 > max_args) + { + addr->transport_return = FAIL; + addr->message = string_sprintf("Too many arguments to command " + "\"%s\" after expanding $address_pipe in %s", cmd, etext); + return FALSE; + } + + /* If we are not just able to replace the slot that contained + * $address_pipe (address_pipe_argcount == 1) + * We have to move the existing argv by address_pipe_argcount - 1 + * Visually if address_pipe_argcount == 2: + * [argv 0][argv 1][argv 2($address_pipe)][argv 3][0] + * [argv 0][argv 1][ap_arg0][ap_arg1][old argv 3][0] + */ + if (address_pipe_argcount > 1) + memmove( + /* current position + additonal args */ + argv + i + address_pipe_argcount, + /* current position + 1 (for the (uschar *)0 at the end) */ + argv + i + 1, + /* -1 for the (uschar *)0 at the end)*/ + (argcount - i)*sizeof(uschar *) + ); + + /* Now we fill in the slots we just moved argv out of + * [argv 0][argv 1][argv 2=pipeargv[0]][argv 3=pipeargv[1]][old argv 3][0] + */ + for (address_pipe_i = 0; + address_pipe_argv[address_pipe_i] != (uschar *)0; + address_pipe_i++) + { + argv[i++] = address_pipe_argv[address_pipe_i]; + argcount++; + } + + /* Subtract one since we replace $address_pipe */ + argcount--; i--; } diff --git a/src/src/transports/pipe.c b/src/src/transports/pipe.c index fe94e8575..54989410a 100644 --- a/src/src/transports/pipe.c +++ b/src/src/transports/pipe.c @@ -37,6 +37,8 @@ optionlist pipe_transport_options[] = { (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, @@ -110,6 +112,7 @@ pipe_transport_options_block pipe_transport_option_defaults = { 20480, /* max_output */ 60*60, /* timeout */ 0, /* options */ + FALSE, /* force_command */ FALSE, /* freeze_exec_fail */ FALSE, /* freeze_signal */ FALSE, /* ignore_status */ @@ -569,10 +572,21 @@ options. */ if (testflag(addr, af_pfr) && addr->local_part[0] == '|') { - cmd = addr->local_part + 1; - while (isspace(*cmd)) cmd++; - expand_arguments = testflag(addr, af_expand_pipe); - expand_fail = FAIL; + if (ob->force_command) + { + /* Enables expansion of $address_pipe into seperate arguments */ + setflag(addr, af_force_command); + cmd = ob->cmd; + expand_arguments = TRUE; + expand_fail = PANIC; + } + else + { + cmd = addr->local_part + 1; + while (isspace(*cmd)) cmd++; + expand_arguments = testflag(addr, af_expand_pipe); + expand_fail = FAIL; + } } else { @@ -581,9 +595,12 @@ else expand_fail = PANIC; } -/* If no command has been supplied, we are in trouble. */ +/* If no command has been supplied, we are in trouble. + * We also check for an empty string since it may be + * coming from addr->local_part[0] == '|' + */ -if (cmd == NULL) +if (cmd == NULL || *cmd == '\0') { addr->transport_return = DEFER; addr->message = string_sprintf("no command specified for %s transport", diff --git a/src/src/transports/pipe.h b/src/src/transports/pipe.h index 343628e20..e10117458 100644 --- a/src/src/transports/pipe.h +++ b/src/src/transports/pipe.h @@ -21,6 +21,7 @@ typedef struct { int max_output; int timeout; int options; + BOOL force_command; BOOL freeze_exec_fail; BOOL freeze_signal; BOOL ignore_status; diff --git a/test/stdout/0390 b/test/stdout/0390 index d573699ef..a80e34c38 100644 --- a/test/stdout/0390 +++ b/test/stdout/0390 @@ -69,6 +69,7 @@ check_string = command = /x/y environment = escape_string = +no_force_command no_freeze_exec_fail no_freeze_signal no_ignore_status @@ -122,6 +123,7 @@ check_string = command = /x/y environment = escape_string = +no_force_command no_freeze_exec_fail no_freeze_signal no_ignore_status