Taintcheck transport-process arguments
[exim.git] / src / src / expand.c
index 95bf4ad07fe8e90460543119be44f289f2ab958a..12edd195caf222e82d396389d00875e52d40e072 100644 (file)
@@ -5526,10 +5526,8 @@ while (*s)
     case EITEM_RUN:
       {
       FILE * f;
-      uschar * arg;
-      const uschar ** argv;
-      pid_t pid;
-      int fd_in, fd_out;
+      const uschar * arg, ** argv;
+      BOOL late_expand = TRUE;
 
       if ((expand_forbid & RDO_RUN) != 0)
         {
@@ -5537,17 +5535,45 @@ while (*s)
         goto EXPAND_FAILED;
         }
 
+      /* Handle options to the "run" */
+
+      while (*s == ',')
+       {
+       if (Ustrncmp(++s, "preexpand", 9) == 0)
+         { late_expand = FALSE; s += 9; }
+       else
+         {
+         const uschar * t = s;
+         while (isalpha(*++t)) ;
+         expand_string_message = string_sprintf("bad option '%.*s' for run",
+                                                 (int)(t-s), s);
+         goto EXPAND_FAILED;
+         }
+       }
       Uskip_whitespace(&s);
-      if (*s != '{')
+
+      if (*s != '{')                                   /*}*/
         {
        expand_string_message = US"missing '{' for command arg of run";
-       goto EXPAND_FAILED_CURLY;
+       goto EXPAND_FAILED_CURLY;                       /*"}*/
        }
-      if (!(arg = expand_string_internal(s+1, TRUE, &s, skipping, TRUE, &resetok)))
-       goto EXPAND_FAILED;
-      Uskip_whitespace(&s);
+      s++;
+
+      if (late_expand)         /* this is the default case */
+       {
+       int n = Ustrcspn(s, "}");
+       arg = skipping ? NULL : string_copyn(s, n);
+       s += n;
+       }
+      else
+       {
+       if (!(arg = expand_string_internal(s, TRUE, &s, skipping, TRUE, &resetok)))
+         goto EXPAND_FAILED;
+       Uskip_whitespace(&s);
+       }
+                                                       /*{*/
       if (*s++ != '}')
-        {
+        {                                              /*{*/
        expand_string_message = US"missing '}' closing command arg of run";
        goto EXPAND_FAILED_CURLY;
        }
@@ -5559,13 +5585,17 @@ while (*s)
        }
       else
         {
+       int fd_in, fd_out;
+       pid_t pid;
+
         if (!transport_set_up_command(&argv,    /* anchor for arg list */
             arg,                                /* raw command */
-            FALSE,                              /* don't expand the arguments */
-            0,                                  /* not relevant when... */
-            NULL,                               /* no transporting address */
-            US"${run} expansion",               /* for error messages */
-            &expand_string_message))            /* where to put error message */
+           late_expand,                /* expand args if not already done */
+            0,                          /* not relevant when... */
+            NULL,                       /* no transporting address */
+           late_expand,                /* allow tainted args, when expand-after-split */
+            US"${run} expansion",       /* for error messages */
+            &expand_string_message))    /* where to put error message */
           goto EXPAND_FAILED;
 
         /* Create the child process, making it a group leader. */
@@ -5576,7 +5606,7 @@ while (*s)
           expand_string_message =
             string_sprintf("couldn't create child process: %s", strerror(errno));
           goto EXPAND_FAILED;
-          }
+         }
 
         /* Nothing is written to the standard input. */
 
@@ -6417,7 +6447,6 @@ while (*s)
        goto EXPAND_FAILED;                                             /*{{*/
       if (*s++ != '}')
         {
-       /*{*/
        expand_string_message =
          string_sprintf("missing '}' closing first arg of %s", name);
        goto EXPAND_FAILED_CURLY;