Merge branch 'ocsp_staple_rollup'
authorJeremy Harris <jgh146exb@wizmail.org>
Sun, 7 Apr 2013 16:09:10 +0000 (17:09 +0100)
committerJeremy Harris <jgh146exb@wizmail.org>
Sun, 7 Apr 2013 16:09:10 +0000 (17:09 +0100)
* ocsp_staple_rollup:
  tidying
  OCSP-stapling enhancement and testing.

15 files changed:
doc/doc-docbook/spec.xfpt
doc/doc-txt/ChangeLog
doc/doc-txt/NewStuff
doc/doc-txt/OptionLists.txt
src/ACKNOWLEDGMENTS
src/src/dns.c
src/src/globals.c
src/src/globals.h
src/src/readconf.c
src/src/structs.h
src/src/tls-openssl.c
src/src/transport.c
src/src/transports/pipe.c
src/src/transports/pipe.h
test/stdout/0390

index 92d0a22879211a7b191de4e1d15406cdd04b08c1..063d74a9290ed79e066af496c627561767828932 100644 (file)
@@ -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.
index a2e204d342d4f8a9c867c20606eae1d6d0d5854c..bfef5556d7f87e9fe6b23c4a8af2c68c5b1486d3 100644 (file)
@@ -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
 -------------------
index 47c5f6fecd086a7c2a880641d8adf57f55585da2..e349fc855302c1525f8087d59b4288dd6d9a9b21 100644 (file)
@@ -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
 ------------
index fa692bfb175a7cea1ec61cf0aa4e4ec9297a56c4..cb5f35eb6af94e32d385e656a02916614c4f34a7 100644 (file)
@@ -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
index ed005b3f851fa8b717de96bbd32640e2a5d3521c..4474de32240ae0026f21ffcc6035c2262f95b92a 100644 (file)
@@ -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
index 95db526867029321275e247fbd986037247ef594..820adff014fa5b15ca632fcf98e5a58384680bcc 100644 (file)
@@ -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 */
 
index 5db858bfcd318953a642af0f4934f213f3358699..a4898fe3f4c14b4f91c6e3dd2404d10a4ba93985 100644 (file)
@@ -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;
index 8d83be7108958700ade5c620ba15665881b12c6b..df6132266aa6a321066f733a7ddce8f800c723c2 100644 (file)
@@ -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 */
index bba5325945bfaa5386382bd071bedf74e1a7fad0..77836d1574cc3b2c75a54ec820a70a4b8d2b0eda 100644 (file)
@@ -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 },
index d11e91adbbe49e8f3c8cb14770b49ffc68fbe5ce..53aa2106b7834d2095160b066416b20bcc94e497 100644 (file)
@@ -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 */
 
index 8b70b13c5e9d76dc2be0665181a1b092c38788d0..6f2646f038aee341c30e92fc4467640743e7dffb 100644 (file)
@@ -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())
   {
index 160d080b99f270df9a1420b387d5d323298c7bea..7dd1afb85d828f26dad55785feca43fdd661b32b 100644 (file)
@@ -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--;
       }
 
index fe94e8575f2c967b63e6eab8b6bb6d92f1f87c86..54989410a4266442504e476c99ab78267630e30e 100644 (file)
@@ -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",
index 343628e209e328a22842e967826b364ea1f5269d..e1011745856a377976d010649795bd7737dd5c51 100644 (file)
@@ -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;
index d573699ef570b8ee398aeea4443d1ac30905fd1d..a80e34c38b5eb6ed603e32323425ad873e72acaf 100644 (file)
@@ -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