Fix taint issue with retry records. Bug 2492
[exim.git] / src / src / exim.c
index f6f15f44998bb6792ad4542a498a2541ce8fc434..0e839c5d407e373191f0aed6161691b6ff116fb9 100644 (file)
@@ -42,7 +42,9 @@ regular expression for a long time; the other for short-term use. */
 static void *
 function_store_get(size_t size)
 {
-return store_get((int)size);
+/* For now, regard all RE results as potentially tainted.  We might need
+more intelligence on this point. */
+return store_get((int)size, TRUE);
 }
 
 static void
@@ -145,9 +147,8 @@ BOOL yield = n >= 0;
 if (n == 0) n = EXPAND_MAXN + 1;
 if (yield)
   {
-  int nn;
   expand_nmax = setup < 0 ? 0 : setup + 1;
-  for (nn = setup < 0 ? 0 : 2; nn < n*2; nn += 2)
+  for (int nn = setup < 0 ? 0 : 2; nn < n*2; nn += 2)
     {
     expand_nstring[expand_nmax] = s + ovector[nn];
     expand_nlength[expand_nmax++] = ovector[nn+1] - ovector[nn];
@@ -182,7 +183,7 @@ va_list ap;
 g = string_fmt_append(&gs, "%5d ", (int)getpid());
 len = g->ptr;
 va_start(ap, format);
-if (!string_vformat(g, FALSE, format, ap))
+if (!string_vformat(g, 0, format, ap))
   {
   gs.ptr = len;
   g = string_cat(&gs, US"**** string overflowed buffer ****");
@@ -474,8 +475,6 @@ return f;
 }
 
 
-
-
 /*************************************************
 *   Ensure stdin, stdout, and stderr exist       *
 *************************************************/
@@ -497,16 +496,15 @@ Returns:    Nothing
 void
 exim_nullstd(void)
 {
-int i;
 int devnull = -1;
 struct stat statbuf;
-for (i = 0; i <= 2; i++)
+for (int i = 0; i <= 2; i++)
   {
   if (fstat(i, &statbuf) < 0 && errno == EBADF)
     {
     if (devnull < 0) devnull = open("/dev/null", O_RDWR);
     if (devnull < 0) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s",
-      string_open_failed(errno, "/dev/null"));
+      string_open_failed(errno, "/dev/null", NULL));
     if (devnull != i) (void)dup2(devnull, i);
     }
   }
@@ -557,7 +555,7 @@ close_unwanted(void)
 {
 if (smtp_input)
   {
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
   tls_close(NULL, TLS_NO_SHUTDOWN);      /* Shut down the TLS library */
 #endif
   (void)close(fileno(smtp_in));
@@ -642,10 +640,7 @@ DEBUG(D_uid)
   save_errno = errno;
   debug_printf("  auxiliary group list:");
   if (group_count > 0)
-    {
-    int i;
-    for (i = 0; i < group_count; i++) debug_printf(" %d", (int)group_list[i]);
-    }
+    for (int i = 0; i < group_count; i++) debug_printf(" %d", (int)group_list[i]);
   else if (group_count < 0)
     debug_printf(" <error: %s>", strerror(save_errno));
   else debug_printf(" <none>");
@@ -673,6 +668,7 @@ void
 exim_exit(int rc, const uschar * process)
 {
 search_tidyup();
+store_exit();
 DEBUG(D_any)
   debug_printf(">>>>>>>>>>>>>>>> Exim pid=%d %s%s%sterminating with rc=%d "
     ">>>>>>>>>>>>>>>>\n", (int)getpid(),
@@ -681,6 +677,14 @@ exit(rc);
 }
 
 
+void
+exim_underbar_exit(int rc)
+{
+store_exit();
+_exit(rc);
+}
+
+
 
 /* Print error string, then die */
 static void
@@ -692,6 +696,36 @@ vfprintf(stderr, fmt, ap);
 exit(EXIT_FAILURE);
 }
 
+/* exim_chown_failure() called from exim_chown()/exim_fchown() on failure
+of chown()/fchown().  See src/functions.h for more explanation */
+int
+exim_chown_failure(int fd, const uschar *name, uid_t owner, gid_t group)
+{
+int saved_errno = errno;  /* from the preceeding chown call */
+#if 1
+log_write(0, LOG_MAIN|LOG_PANIC,
+  __FILE__ ":%d: chown(%s, %d:%d) failed (%s)."
+  " Please contact the authors and refer to https://bugs.exim.org/show_bug.cgi?id=2391",
+  __LINE__, name?name:US"<unknown>", owner, group, strerror(errno));
+#else
+/* I leave this here, commented, in case the "bug"(?) comes up again.
+   It is not an Exim bug, but we can provide a workaround.
+   See Bug 2391
+   HS 2019-04-18 */
+
+struct stat buf;
+
+if (0 == (fd < 0 ? stat(name, &buf) : fstat(fd, &buf)))
+{
+  if (buf.st_uid == owner && buf.st_gid == group) return 0;
+  log_write(0, LOG_MAIN|LOG_PANIC, "Wrong ownership on %s", name);
+}
+else log_write(0, LOG_MAIN|LOG_PANIC, "Stat failed on %s: %s", name, strerror(errno));
+
+#endif
+errno = saved_errno;
+return -1;
+}
 
 
 /*************************************************
@@ -807,8 +841,6 @@ Returns:    nothing
 static void
 show_whats_supported(FILE * fp)
 {
-auth_info * authi;
-
 DEBUG(D_any) {} else show_db_version(fp);
 
 fprintf(fp, "Support for:");
@@ -836,12 +868,11 @@ fprintf(fp, "Support for:");
 #ifdef USE_TCP_WRAPPERS
   fprintf(fp, " TCPwrappers");
 #endif
-#ifdef SUPPORT_TLS
-# ifdef USE_GNUTLS
+#ifdef USE_GNUTLS
   fprintf(fp, " GnuTLS");
-# else
+#endif
+#ifdef USE_OPENSSL
   fprintf(fp, " OpenSSL");
-# endif
 #endif
 #ifdef SUPPORT_TRANSLATE_IP_ADDRESS
   fprintf(fp, " translate_ip_address");
@@ -870,6 +901,9 @@ fprintf(fp, "Support for:");
 #ifndef DISABLE_OCSP
   fprintf(fp, " OCSP");
 #endif
+#ifdef SUPPORT_PIPE_CONNECT
+  fprintf(fp, " PIPE_CONNECT");
+#endif
 #ifndef DISABLE_PRDR
   fprintf(fp, " PRDR");
 #endif
@@ -882,6 +916,9 @@ fprintf(fp, "Support for:");
 #ifdef SUPPORT_SPF
   fprintf(fp, " SPF");
 #endif
+#ifdef SUPPORT_DMARC
+  fprintf(fp, " DMARC");
+#endif
 #ifdef TCP_FASTOPEN
   deliver_init();
   if (f.tcp_fastopen_ok) fprintf(fp, " TCP_Fast_Open");
@@ -904,17 +941,11 @@ fprintf(fp, "Support for:");
 #ifdef EXPERIMENTAL_DCC
   fprintf(fp, " Experimental_DCC");
 #endif
-#ifdef EXPERIMENTAL_DMARC
-  fprintf(fp, " Experimental_DMARC");
-#endif
 #ifdef EXPERIMENTAL_DSN_INFO
   fprintf(fp, " Experimental_DSN_info");
 #endif
-#ifdef EXPERIMENTAL_REQUIRETLS
-  fprintf(fp, " Experimental_REQUIRETLS");
-#endif
-#ifdef EXPERIMENTAL_PIPE_CONNECT
-  fprintf(fp, " Experimental_PIPE_CONNECT");
+#ifdef EXPERIMENTAL_TLS_RESUME
+  fprintf(fp, " Experimental_TLS_resume");
 #endif
 fprintf(fp, "\n");
 
@@ -937,6 +968,9 @@ fprintf(fp, "Lookups (built-in):");
 #if defined(LOOKUP_IBASE) && LOOKUP_IBASE!=2
   fprintf(fp, " ibase");
 #endif
+#if defined(LOOKUP_JSON) && LOOKUP_JSON!=2
+  fprintf(fp, " json");
+#endif
 #if defined(LOOKUP_LDAP) && LOOKUP_LDAP!=2
   fprintf(fp, " ldap ldapdn ldapm");
 #endif
@@ -1000,8 +1034,6 @@ fprintf(fp, "Size of off_t: " SIZE_T_FMT "\n", sizeof(off_t));
 Perhaps the tls_version_report should move into this too. */
 DEBUG(D_any) do {
 
-  int i;
-
 /* clang defines __GNUC__ (at least, for me) so test for it first */
 #if defined(__clang__)
   fprintf(fp, "Compiler: CLang [%s]\n", __clang_version__);
@@ -1027,14 +1059,14 @@ DEBUG(D_any) do {
 
 show_db_version(fp);
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
   tls_version_report(fp);
 #endif
 #ifdef SUPPORT_I18N
   utf8_version_report(fp);
 #endif
 
-  for (authi = auths_available; *authi->driver_name != '\0'; ++authi)
+  for (auth_info * authi = auths_available; *authi->driver_name != '\0'; ++authi)
     if (authi->version_report)
       (*authi->version_report)(fp);
 
@@ -1055,7 +1087,7 @@ show_db_version(fp);
 #undef EXPAND_AND_QUOTE
 
   init_lookup_list();
-  for (i = 0; i < lookup_list_count; i++)
+  for (int i = 0; i < lookup_list_count; i++)
     if (lookup_list[i]->version_report)
       lookup_list[i]->version_report(fp);
 
@@ -1081,8 +1113,6 @@ show_db_version(fp);
 static void
 show_exim_information(enum commandline_info request, FILE *stream)
 {
-const uschar **pp;
-
 switch(request)
   {
   case CMDINFO_NONE:
@@ -1099,7 +1129,7 @@ switch(request)
 );
     return;
   case CMDINFO_SIEVE:
-    for (pp = exim_sieve_extension_list; *pp; ++pp)
+    for (const uschar ** pp = exim_sieve_extension_list; *pp; ++pp)
       fprintf(stream, "%s\n", *pp);
     return;
   case CMDINFO_DSCP:
@@ -1126,9 +1156,8 @@ local_part_quote(uschar *lpart)
 {
 BOOL needs_quote = FALSE;
 gstring * g;
-uschar *t;
 
-for (t = lpart; !needs_quote && *t != 0; t++)
+for (uschar * t = lpart; !needs_quote && *t != 0; t++)
   {
   needs_quote = !isalnum(*t) && strchr("!#$%&'*+-/=?^_`{|}~", *t) == NULL &&
     (*t != '.' || t == lpart || t[1] == 0);
@@ -1225,22 +1254,21 @@ Returns:        pointer to dynamic memory, or NULL at end of file
 static uschar *
 get_stdinput(char *(*fn_readline)(const char *), void(*fn_addhist)(const char *))
 {
-int i;
 gstring * g = NULL;
 
 if (!fn_readline) { printf("> "); fflush(stdout); }
 
-for (i = 0;; i++)
+for (int i = 0;; i++)
   {
   uschar buffer[1024];
   uschar *p, *ss;
 
   #ifdef USE_READLINE
   char *readline_line = NULL;
-  if (fn_readline != NULL)
+  if (fn_readline)
     {
-    if ((readline_line = fn_readline((i > 0)? "":"> ")) == NULL) break;
-    if (*readline_line != 0 && fn_addhist != NULL) fn_addhist(readline_line);
+    if (!(readline_line = fn_readline((i > 0)? "":"> "))) break;
+    if (*readline_line != 0 && fn_addhist) fn_addhist(readline_line);
     p = US readline_line;
     }
   else
@@ -1259,9 +1287,7 @@ for (i = 0;; i++)
   while (ss > p && isspace(ss[-1])) ss--;
 
   if (i > 0)
-    {
     while (p < ss && isspace(*p)) p++;   /* leading space after cont */
-    }
 
   g = string_catn(g, p, ss - p);
 
@@ -1330,8 +1356,7 @@ static BOOL
 macros_trusted(BOOL opt_D_used)
 {
 #ifdef WHITELIST_D_MACROS
-macro_item *m;
-uschar *whitelisted, *end, *p, **whites, **w;
+uschar *whitelisted, *end, *p, **whites;
 int white_count, i, n;
 size_t len;
 BOOL prev_char_item, found;
@@ -1359,7 +1384,7 @@ if ( ! ((real_uid == root_uid)
   }
 
 /* Get a list of macros which are whitelisted */
-whitelisted = string_copy_malloc(US WHITELIST_D_MACROS);
+whitelisted = string_copy_perm(US WHITELIST_D_MACROS, FALSE);
 prev_char_item = FALSE;
 white_count = 0;
 for (p = whitelisted; *p != '\0'; ++p)
@@ -1396,10 +1421,10 @@ whites[i] = NULL;
 
 /* The list of commandline macros should be very short.
 Accept the N*M complexity. */
-for (m = macros_user; m; m = m->next) if (m->command_line)
+for (macro_item * m = macros_user; m; m = m->next) if (m->command_line)
   {
   found = FALSE;
-  for (w = whites; *w; ++w)
+  for (uschar ** w = whites; *w; ++w)
     if (Ustrcmp(*w, m->name) == 0)
       {
       found = TRUE;
@@ -1546,7 +1571,7 @@ uschar *malware_test_file = NULL;
 uschar *real_sender_address;
 uschar *originator_home = US"/";
 size_t sz;
-void *reset_point;
+rmark reset_point;
 
 struct passwd *pw;
 struct stat statbuf;
@@ -1568,6 +1593,10 @@ because some OS define it in /usr/include/unistd.h. */
 
 extern char **environ;
 
+#ifdef MEASURE_TIMING
+(void)gettimeofday(&timestamp_startup, NULL);
+#endif
+
 /* If the Exim user and/or group and/or the configuration file owner/group were
 defined by ref:name at build time, we must now find the actual uid/gid values.
 This is a feature to make the lives of binary distributors easier. */
@@ -1675,6 +1704,7 @@ big_buffer = store_malloc(big_buffer_size);
 /* Set up the handler for the data request signal, and set the initial
 descriptive text. */
 
+process_info = store_get(PROCESS_INFO_SIZE, TRUE);     /* tainted */
 set_process_info("initializing");
 os_restarting_signal(SIGUSR1, usr1_handler);
 
@@ -2302,7 +2332,7 @@ for (i = 1; i < argc; i++)
            else
               {
               /* Well, the trust list at least is up to scratch... */
-              void *reset_point = store_get(0);
+              rmark reset_point = store_mark();
               uschar *trusted_configs[32];
               int nr_configs = 0;
               int i = 0;
@@ -2332,30 +2362,22 @@ for (i = 1; i < argc; i++)
                         &sep, big_buffer, big_buffer_size)) != NULL)
                   {
                   for (i=0; i < nr_configs; i++)
-                    {
                     if (Ustrcmp(filename, trusted_configs[i]) == 0)
                       break;
-                    }
                   if (i == nr_configs)
                     {
                     f.trusted_config = FALSE;
                     break;
                     }
                   }
-                store_reset(reset_point);
                 }
-              else
-                {
-                /* No valid prefixes found in trust_list file. */
+              else     /* No valid prefixes found in trust_list file. */
                 f.trusted_config = FALSE;
-                }
+              store_reset(reset_point);
               }
            }
-          else
-            {
-            /* Could not open trust_list file. */
+          else         /* Could not open trust_list file. */
             f.trusted_config = FALSE;
-            }
           }
       #else
         /* Not root; don't trust config */
@@ -2410,8 +2432,8 @@ for (i = 1; i < argc; i++)
 
       if (clmacro_count >= MAX_CLMACROS)
         exim_fail("exim: too many -D options on command line\n");
-      clmacros[clmacro_count++] = string_sprintf("-D%s=%s", m->name,
-        m->replacement);
+      clmacros[clmacro_count++] =
+       string_sprintf("-D%s=%s", m->name, m->replacement);
       }
     #endif
     break;
@@ -2521,7 +2543,7 @@ for (i = 1; i < argc; i++)
           { badarg = TRUE; break; }
         }
       if (*argrest == 0)
-        sender_address = string_sprintf("");  /* Ensure writeable memory */
+        *(sender_address = store_get(1, FALSE)) = '\0';  /* Ensure writeable memory */
       else
         {
         uschar *temp = argrest + Ustrlen(argrest) - 1;
@@ -2534,6 +2556,7 @@ for (i = 1; i < argc; i++)
 #endif
         sender_address = parse_extract_address(argrest, &errmess,
           &dummy_start, &dummy_end, &sender_address_domain, TRUE);
+       sender_address = string_copy_taint(sender_address, TRUE);
 #ifdef SUPPORT_I18N
        message_smtputf8 =  string_is_utf8(sender_address);
        allow_utf8_domains = FALSE;
@@ -2646,7 +2669,7 @@ for (i = 1; i < argc; i++)
          exim_fail("exim: getsockname() failed after -MC option: %s\n",
            strerror(errno));
 
-      if (f.running_in_test_harness) millisleep(500);
+      testharness_pause_ms(500);
       break;
       }
 
@@ -2695,7 +2718,7 @@ for (i = 1; i < argc; i++)
 
        case 'S': smtp_peer_options |= OPTION_SIZE; break;
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
     /* -MCt: similar to -MCT below but the connection is still open
     via a proxy process which handles the TLS context and coding.
     Require three arguments for the proxied local address and port,
@@ -2721,16 +2744,6 @@ for (i = 1; i < argc; i++)
       break;
       }
 
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
-    /* -MS   set REQUIRETLS on (new) message */
-
-    else if (*argrest == 'S')
-      {
-      tls_requiretls |= REQUIRETLS_MSG;
-      break;
-      }
-#endif
-
     /* -M[x]: various operations on the following list of message ids:
        -M    deliver the messages, ignoring next retry times and thawing
        -Mc   deliver the messages, checking next retry times, no thawing
@@ -2774,6 +2787,11 @@ for (i = 1; i < argc; i++)
       msg_action = MSG_DELIVER;
       deliver_give_up = TRUE;
       }
+   else if (Ustrcmp(argrest, "G") == 0)
+      {
+      msg_action = MSG_SETQUEUE;
+      queue_name_dest = argv[++i];
+      }
     else if (Ustrcmp(argrest, "mad") == 0)
       {
       msg_action = MSG_MARK_ALL_DELIVERED;
@@ -2822,8 +2840,7 @@ for (i = 1; i < argc; i++)
 
     if (!one_msg_action)
       {
-      int j;
-      for (j = msg_action_arg; j < argc; j++) if (!mac_ismsgid(argv[j]))
+      for (int j = msg_action_arg; j < argc; j++) if (!mac_ismsgid(argv[j]))
         exim_fail("exim: malformed message id %s after %s option\n",
           argv[j], arg);
       goto END_ARG;   /* Remaining args are ids */
@@ -2990,11 +3007,13 @@ for (i = 1; i < argc; i++)
 
       /* -oMas: setting authenticated sender */
 
-      else if (Ustrcmp(argrest, "Mas") == 0) authenticated_sender = argv[++i];
+      else if (Ustrcmp(argrest, "Mas") == 0)
+       authenticated_sender = string_copy_taint(argv[++i], TRUE);
 
       /* -oMai: setting authenticated id */
 
-      else if (Ustrcmp(argrest, "Mai") == 0) authenticated_id = argv[++i];
+      else if (Ustrcmp(argrest, "Mai") == 0)
+       authenticated_id = string_copy_taint(argv[++i], TRUE);
 
       /* -oMi: Set incoming interface address */
 
@@ -3022,7 +3041,8 @@ for (i = 1; i < argc; i++)
 
       /* -oMs: Set sender host name */
 
-      else if (Ustrcmp(argrest, "Ms") == 0) sender_host_name = argv[++i];
+      else if (Ustrcmp(argrest, "Ms") == 0)
+       sender_host_name = string_copy_taint(argv[++i], TRUE);
 
       /* -oMt: Set sender ident */
 
@@ -3187,22 +3207,23 @@ for (i = 1; i < argc; i++)
     /* -q[f][f][l][G<name>]: Run the queue, optionally forced, optionally local
     only, optionally named, optionally starting from a given message id. */
 
-    if (*argrest == 0 &&
-        (i + 1 >= argc || argv[i+1][0] == '-' || mac_ismsgid(argv[i+1])))
-      {
-      queue_interval = 0;
-      if (i+1 < argc && mac_ismsgid(argv[i+1]))
-        start_queue_run_id = argv[++i];
-      if (i+1 < argc && mac_ismsgid(argv[i+1]))
-        stop_queue_run_id = argv[++i];
-      }
+    if (!(list_queue || count_queue))
+      if (*argrest == 0
+        && (i + 1 >= argc || argv[i+1][0] == '-' || mac_ismsgid(argv[i+1])))
+       {
+       queue_interval = 0;
+       if (i+1 < argc && mac_ismsgid(argv[i+1]))
+         start_queue_run_id = argv[++i];
+       if (i+1 < argc && mac_ismsgid(argv[i+1]))
+         stop_queue_run_id = argv[++i];
+       }
 
     /* -q[f][f][l][G<name>/]<n>: Run the queue at regular intervals, optionally
     forced, optionally local only, optionally named. */
 
-    else if ((queue_interval = readconf_readtime(*argrest ? argrest : argv[++i],
-                                               0, FALSE)) <= 0)
-      exim_fail("exim: bad time value %s: abandoned\n", argv[i]);
+      else if ((queue_interval = readconf_readtime(*argrest ? argrest : argv[++i],
+                                                 0, FALSE)) <= 0)
+       exim_fail("exim: bad time value %s: abandoned\n", argv[i]);
     break;
 
 
@@ -3219,9 +3240,7 @@ for (i = 1; i < argc; i++)
     argument. */
 
     if (*argrest != 0)
-      {
-      int i;
-      for (i = 0; i < nelem(rsopts); i++)
+      for (int i = 0; i < nelem(rsopts); i++)
         if (Ustrcmp(argrest, rsopts[i]) == 0)
           {
           if (i != 2) f.queue_run_force = TRUE;
@@ -3229,7 +3248,6 @@ for (i = 1; i < argc; i++)
           if (i == 1 || i == 4) f.deliver_force_thaw = TRUE;
           argrest += Ustrlen(rsopts[i]);
           }
-      }
 
     /* -R: Set string to match in addresses for forced queue run to
     pick out particular messages. */
@@ -3261,9 +3279,7 @@ for (i = 1; i < argc; i++)
     argument. */
 
     if (*argrest)
-      {
-      int i;
-      for (i = 0; i < nelem(rsopts); i++)
+      for (int i = 0; i < nelem(rsopts); i++)
         if (Ustrcmp(argrest, rsopts[i]) == 0)
           {
           if (i != 2) f.queue_run_force = TRUE;
@@ -3271,7 +3287,6 @@ for (i = 1; i < argc; i++)
           if (i == 1 || i == 4) f.deliver_force_thaw = TRUE;
           argrest += Ustrlen(rsopts[i]);
           }
-      }
 
     /* -S: Set string to match in addresses for forced queue run to
     pick out particular messages. */
@@ -3312,7 +3327,7 @@ for (i = 1; i < argc; i++)
 
     /* -tls-on-connect: don't wait for STARTTLS (for old clients) */
 
-    #ifdef SUPPORT_TLS
+    #ifndef DISABLE_TLS
     else if (Ustrcmp(argrest, "ls-on-connect") == 0) tls_in.on_connect = TRUE;
     #endif
 
@@ -3458,7 +3473,7 @@ if (debug_selector != 0)
   debug_file = stderr;
   debug_fd = fileno(debug_file);
   f.background_daemon = FALSE;
-  if (f.running_in_test_harness) millisleep(100);   /* lets caller finish */
+  testharness_pause_ms(100);   /* lets caller finish */
   if (debug_selector != D_v)    /* -v only doesn't show this */
     {
     debug_printf("Exim version %s uid=%ld gid=%ld pid=%d D=%x\n",
@@ -3680,7 +3695,18 @@ If any of these options is set, we suppress warnings about configuration
 issues (currently about tls_advertise_hosts and keep_environment not being
 defined) */
 
-readconf_main(checking || list_options);
+  {
+#ifdef MEASURE_TIMING
+  struct timeval t0, diff;
+  (void)gettimeofday(&t0, NULL);
+#endif
+
+  readconf_main(checking || list_options);
+
+#ifdef MEASURE_TIMING
+  report_time_since(&t0, US"readconf_main (delta)");
+#endif
+  }
 
 
 /* Now in directory "/" */
@@ -3700,16 +3726,13 @@ for later interrogation. */
 if (real_uid == root_uid || real_uid == exim_uid || real_gid == exim_gid)
   f.admin_user = TRUE;
 else
-  {
-  int i, j;
-  for (i = 0; i < group_count && !f.admin_user; i++)
+  for (int i = 0; i < group_count && !f.admin_user; i++)
     if (group_list[i] == exim_gid)
       f.admin_user = TRUE;
     else if (admin_groups)
-      for (j = 1; j <= (int)admin_groups[0] && !f.admin_user; j++)
+      for (int j = 1; j <= (int)admin_groups[0] && !f.admin_user; j++)
         if (admin_groups[j] == group_list[i])
           f.admin_user = TRUE;
-  }
 
 /* Another group of privileged users are the trusted users. These are root,
 exim, and any caller matching trusted_users or trusted_groups. Trusted callers
@@ -3720,18 +3743,16 @@ if (real_uid == root_uid || real_uid == exim_uid)
   f.trusted_caller = TRUE;
 else
   {
-  int i, j;
-
   if (trusted_users)
-    for (i = 1; i <= (int)trusted_users[0] && !f.trusted_caller; i++)
+    for (int i = 1; i <= (int)trusted_users[0] && !f.trusted_caller; i++)
       if (trusted_users[i] == real_uid)
         f.trusted_caller = TRUE;
 
   if (trusted_groups)
-    for (i = 1; i <= (int)trusted_groups[0] && !f.trusted_caller; i++)
+    for (int i = 1; i <= (int)trusted_groups[0] && !f.trusted_caller; i++)
       if (trusted_groups[i] == real_gid)
         f.trusted_caller = TRUE;
-      else for (j = 0; j < group_count && !f.trusted_caller; j++)
+      else for (int j = 0; j < group_count && !f.trusted_caller; j++)
         if (trusted_groups[i] == group_list[j])
           f.trusted_caller = TRUE;
   }
@@ -3749,10 +3770,9 @@ decode_bits(log_selector, log_selector_size, log_notall,
 
 DEBUG(D_any)
   {
-  int i;
   debug_printf("configuration file is %s\n", config_main_filename);
   debug_printf("log selectors =");
-  for (i = 0; i < log_selector_size; i++)
+  for (int i = 0; i < log_selector_size; i++)
     debug_printf(" %08x", log_selector[i]);
   debug_printf("\n");
   }
@@ -3829,9 +3849,7 @@ EXIM_TMPDIR by the build scripts.
 */
 
 #ifdef EXIM_TMPDIR
-  {
-  uschar **p;
-  if (environ) for (p = USS environ; *p; p++)
+  if (environ) for (uschar ** p = USS environ; *p; p++)
     if (Ustrncmp(*p, "TMPDIR=", 7) == 0 && Ustrcmp(*p+7, EXIM_TMPDIR) != 0)
       {
       uschar * newp = store_malloc(Ustrlen(EXIM_TMPDIR) + 8);
@@ -3839,7 +3857,6 @@ EXIM_TMPDIR by the build scripts.
       *p = newp;
       DEBUG(D_any) debug_printf("reset TMPDIR=%s in environment\n", EXIM_TMPDIR);
       }
-  }
 #endif
 
 /* Timezone handling. If timezone_string is "utc", set a flag to cause all
@@ -3941,9 +3958,8 @@ verifying/testing addresses or expansions. */
 if (  (debug_selector & D_any  ||  LOGGING(arguments))
    && f.really_exim && !list_options && !checking)
   {
-  int i;
   uschar *p = big_buffer;
-  Ustrcpy(p, "cwd= (failed)");
+  Ustrcpy(p, US"cwd= (failed)");
 
   if (!initial_cwd)
     p += 13;
@@ -3958,16 +3974,16 @@ if (  (debug_selector & D_any  ||  LOGGING(arguments))
 
   (void)string_format(p, big_buffer_size - (p - big_buffer), " %d args:", argc);
   while (*p) p++;
-  for (i = 0; i < argc; i++)
+  for (int i = 0; i < argc; i++)
     {
     int len = Ustrlen(argv[i]);
     const uschar *printing;
     uschar *quote;
     if (p + len + 8 >= big_buffer + big_buffer_size)
       {
-      Ustrcpy(p, " ...");
+      Ustrcpy(p, US" ...");
       log_write(0, LOG_MAIN, "%s", big_buffer);
-      Ustrcpy(big_buffer, "...");
+      Ustrcpy(big_buffer, US"...");
       p = big_buffer + 3;
       }
     printing = string_printing(argv[i]);
@@ -4053,11 +4069,13 @@ count. Only an admin user can use the test interface to scan for email
 if (!f.admin_user)
   {
   BOOL debugset = (debug_selector & ~D_v) != 0;
-  if (deliver_give_up || f.daemon_listen || malware_test_file ||
-     (count_queue && queue_list_requires_admin) ||
-     (list_queue && queue_list_requires_admin) ||
-     (queue_interval >= 0 && prod_requires_admin) ||
-     (debugset && !f.running_in_test_harness))
+  if (  deliver_give_up || f.daemon_listen || malware_test_file
+     || count_queue && queue_list_requires_admin
+     || list_queue && queue_list_requires_admin
+     || queue_interval >= 0 && prod_requires_admin
+     || queue_name_dest && prod_requires_admin
+     || debugset && !f.running_in_test_harness
+     )
     exim_fail("exim:%s permission denied\n", debugset? " debugging" : "");
   }
 
@@ -4154,13 +4172,9 @@ now for those OS that require the first call to os_getloadavg() to be done as
 root. There will be further calls later for each message received. */
 
 #ifdef LOAD_AVG_NEEDS_ROOT
-if (receiving_message &&
-      (queue_only_load >= 0 ||
-        (f.is_inetd && smtp_load_reserve >= 0)
-      ))
-  {
+if (  receiving_message
+   && (queue_only_load >= 0 || (f.is_inetd && smtp_load_reserve >= 0)))
   load_average = OS_GETLOADAVG();
-  }
 #endif
 
 /* The queue_only configuration option can be overridden by -odx on the command
@@ -4205,6 +4219,7 @@ if (!unprivileged &&                      /* originally had root AND */
 else
   {
   int rv;
+  DEBUG(D_any) debug_printf("dropping to exim gid; retaining priv uid\n");
   rv = setgid(exim_gid);
   /* Impact of failure is that some stuff might end up with an incorrect group.
   We track this for failures from root, since any attempt to change privilege
@@ -4285,6 +4300,11 @@ if (msg_action_arg > 0 && msg_action != MSG_DELIVER && msg_action != MSG_LOAD)
     for (i = msg_action_arg; i < argc; i++)
       if (!queue_action(argv[i], msg_action, NULL, 0, 0))
         yield = EXIT_FAILURE;
+    switch (msg_action)
+      {
+      case MSG_REMOVE: MSG_DELETE: case MSG_FREEZE: case MSG_THAW: break;
+      default: printf("\n"); break;
+      }
     }
 
   else if (!queue_action(argv[msg_action_arg], msg_action, argv, argc,
@@ -4297,16 +4317,18 @@ if (msg_action_arg > 0 && msg_action != MSG_DELIVER && msg_action != MSG_LOAD)
 Now, since the intro of the ${acl } expansion, ACL definitions may be
 needed in transports so we lost the optimisation. */
 
-readconf_rest();
+  {
+#ifdef MEASURE_TIMING
+  struct timeval t0, diff;
+  (void)gettimeofday(&t0, NULL);
+#endif
 
-/* The configuration data will have been read into POOL_PERM because we won't
-ever want to reset back past it. Change the current pool to POOL_MAIN. In fact,
-this is just a bit of pedantic tidiness. It wouldn't really matter if the
-configuration were read into POOL_MAIN, because we don't do any resets till
-later on. However, it seems right, and it does ensure that both pools get used.
-*/
+  readconf_rest();
 
-store_pool = POOL_MAIN;
+#ifdef MEASURE_TIMING
+  report_time_since(&t0, US"readconf_rest (delta)");
+#endif
+  }
 
 /* Handle the -brt option. This is for checking out retry configurations.
 The next three arguments are a domain name or a complete address, and
@@ -4376,7 +4398,6 @@ if (test_retry_arg >= 0)
     printf("No retry information found\n");
   else
     {
-    retry_rule *r;
     more_errno = yield->more_errno;
     printf("Retry rule: %s  ", yield->pattern);
 
@@ -4406,7 +4427,7 @@ if (test_retry_arg >= 0)
       printf("auth_failed  ");
     else printf("*  ");
 
-    for (r = yield->rules; r; r = r->next)
+    for (retry_rule * r = yield->rules; r; r = r->next)
       {
       printf("%c,%s", r->rule, readconf_printtime(r->timeout)); /* Do not */
       printf(",%s", readconf_printtime(r->p1));                 /* amalgamate */
@@ -4469,9 +4490,28 @@ if (list_config)
 
 /* Initialise subsystems as required */
 #ifndef DISABLE_DKIM
-dkim_exim_init();
+  {
+# ifdef MEASURE_TIMING
+  struct timeval t0;
+  gettimeofday(&t0, NULL);
+# endif
+  dkim_exim_init();
+# ifdef MEASURE_TIMING
+  report_time_since(&t0, US"dkim_exim_init (delta)");
+# endif
+  }
+#endif
+
+  {
+#ifdef MEASURE_TIMING
+  struct timeval t0;
+  gettimeofday(&t0, NULL);
+#endif
+  deliver_init();
+#ifdef MEASURE_TIMING
+  report_time_since(&t0, US"deliver_init (delta)");
 #endif
-deliver_init();
+  }
 
 
 /* Handle a request to deliver one or more messages that are already on the
@@ -4505,7 +4545,7 @@ if (msg_action_arg > 0 && msg_action != MSG_LOAD)
     else if ((pid = fork()) == 0)
       {
       (void)deliver_message(argv[i], forced_delivery, deliver_give_up);
-      _exit(EXIT_SUCCESS);
+      exim_underbar_exit(EXIT_SUCCESS);
       }
     else if (pid < 0)
       {
@@ -4674,8 +4714,8 @@ if (f.daemon_listen || f.inetd_wait_mode || queue_interval > 0)
 the caller. This will get overwritten below for an inetd call. If a trusted
 caller has set it empty, unset it. */
 
-if (sender_ident == NULL) sender_ident = originator_login;
-  else if (sender_ident[0] == 0) sender_ident = NULL;
+if (!sender_ident) sender_ident = originator_login;
+else if (!*sender_ident) sender_ident = NULL;
 
 /* Handle the -brw option, which is for checking out rewriting rules. Cause log
 writes (on errors) to go to stderr instead. Can't do this earlier, as want the
@@ -4697,8 +4737,8 @@ if (test_rewrite_arg >= 0)
 unless a trusted caller supplies a sender address with -f, or is passing in the
 message via SMTP (inetd invocation or otherwise). */
 
-if ((sender_address == NULL && !smtp_input) ||
-    (!f.trusted_caller && filter_test == FTEST_NONE))
+if (  !sender_address && !smtp_input
+   || !f.trusted_caller && filter_test == FTEST_NONE)
   {
   f.sender_local = TRUE;
 
@@ -4706,10 +4746,10 @@ if ((sender_address == NULL && !smtp_input) ||
   via -oMas and -oMai and if so, they will already be set. Otherwise, force
   defaults except when host checking. */
 
-  if (authenticated_sender == NULL && !host_checking)
+  if (!authenticated_sender && !host_checking)
     authenticated_sender = string_sprintf("%s@%s", originator_login,
       qualify_domain_sender);
-  if (authenticated_id == NULL && !host_checking)
+  if (!authenticated_id && !host_checking)
     authenticated_id = originator_login;
   }
 
@@ -4719,8 +4759,8 @@ is specified is the empty address. However, if a trusted caller does not
 specify a sender address for SMTP input, we leave sender_address unset. This
 causes the MAIL commands to be honoured. */
 
-if ((!smtp_input && sender_address == NULL) ||
-    !receive_check_set_sender(sender_address))
+if (  !smtp_input && !sender_address
+   || !receive_check_set_sender(sender_address))
   {
   /* Either the caller is not permitted to set a general sender, or this is
   non-SMTP input and the trusted caller has not set a sender. If there is no
@@ -4746,8 +4786,7 @@ f.sender_set_untrusted = sender_address != originator_login && !f.trusted_caller
 address, which indicates an error message, or doesn't exist (root caller, smtp
 interface, no -f argument). */
 
-if (sender_address != NULL && sender_address[0] != 0 &&
-    sender_address_domain == 0)
+if (sender_address && *sender_address && sender_address_domain == 0)
   sender_address = string_sprintf("%s@%s", local_part_quote(sender_address),
     qualify_domain_sender);
 
@@ -4783,8 +4822,9 @@ if (verify_address_mode || f.address_test_mode)
     {
     while (recipients_arg < argc)
       {
-      uschar *s = argv[recipients_arg++];
-      while (*s != 0)
+      /* Supplied addresses are tainted since they come from a user */
+      uschar * s = string_copy_taint(argv[recipients_arg++], TRUE);
+      while (*s)
         {
         BOOL finished = FALSE;
         uschar *ss = parse_find_address_end(s, FALSE);
@@ -4792,16 +4832,16 @@ if (verify_address_mode || f.address_test_mode)
         test_address(s, flags, &exit_value);
         s = ss;
         if (!finished)
-          while (*(++s) != 0 && (*s == ',' || isspace(*s)));
+          while (*++s == ',' || isspace(*s)) ;
         }
       }
     }
 
   else for (;;)
     {
-    uschar *s = get_stdinput(NULL, NULL);
-    if (s == NULL) break;
-    test_address(s, flags, &exit_value);
+    uschar * s = get_stdinput(NULL, NULL);
+    if (!s) break;
+    test_address(string_copy_taint(s, TRUE), flags, &exit_value);
     }
 
   route_tidyup();
@@ -4937,7 +4977,7 @@ if (host_checking)
   it. The code works for both IPv4 and IPv6, as it happens. */
 
   size = host_aton(sender_host_address, x);
-  sender_host_address = store_get(48);  /* large enough for full IPv6 */
+  sender_host_address = store_get(48, FALSE);  /* large enough for full IPv6 */
   (void)host_nmtoa(size, x, -1, sender_host_address, ':');
 
   /* Now set up for testing */
@@ -4967,7 +5007,7 @@ if (host_checking)
 
   if (smtp_start_session())
     {
-    for (reset_point = store_get(0); ; store_reset(reset_point))
+    for (; (reset_point = store_mark()); store_reset(reset_point))
       {
       if (smtp_setup_msg() <= 0) break;
       if (!receive_msg(FALSE)) break;
@@ -5210,7 +5250,6 @@ if (!f.synchronous_delivery)
 /* Save the current store pool point, for resetting at the start of
 each message, and save the real sender address, if any. */
 
-reset_point = store_get(0);
 real_sender_address = sender_address;
 
 /* Loop to receive messages; receive_msg() returns TRUE if there are more
@@ -5219,6 +5258,7 @@ collapsed). */
 
 while (more)
   {
+  reset_point = store_mark();
   message_id[0] = 0;
 
   /* Handle the SMTP case; call smtp_setup_mst() to deal with the initial SMTP
@@ -5282,7 +5322,6 @@ while (more)
 
   else
     {
-    int i;
     int rcount = 0;
     int count = argc - recipients_arg;
     uschar **list = argv + recipients_arg;
@@ -5296,13 +5335,13 @@ while (more)
 
     raw_sender = string_copy(sender_address);
 
-    /* Loop for each argument */
+    /* Loop for each argument (supplied by user hence tainted) */
 
-    for (i = 0; i < count; i++)
+    for (int i = 0; i < count; i++)
       {
       int start, end, domain;
-      uschar *errmess;
-      uschar *s = list[i];
+      uschar * errmess;
+      uschar * s = string_copy_taint(list[i], TRUE);
 
       /* Loop for each comma-separated address */
 
@@ -5369,7 +5408,7 @@ while (more)
             }
           }
 
-        receive_add_recipient(recipient, -1);
+        receive_add_recipient(string_copy_taint(recipient, TRUE), -1);
         s = ss;
         if (!finished)
           while (*(++s) != 0 && (*s == ',' || isspace(*s)));
@@ -5380,12 +5419,11 @@ while (more)
 
     DEBUG(D_receive)
       {
-      int i;
       if (sender_address != NULL) debug_printf("Sender: %s\n", sender_address);
       if (recipients_list != NULL)
         {
         debug_printf("Recipients:\n");
-        for (i = 0; i < recipients_count; i++)
+        for (int i = 0; i < recipients_count; i++)
           debug_printf("  %s\n", recipients_list[i].address);
         }
       }
@@ -5592,8 +5630,8 @@ while (more)
 
       rc = deliver_message(message_id, FALSE, FALSE);
       search_tidyup();
-      _exit((!mua_wrapper || rc == DELIVER_MUA_SUCCEEDED)?
-        EXIT_SUCCESS : EXIT_FAILURE);
+      exim_underbar_exit(!mua_wrapper || rc == DELIVER_MUA_SUCCEEDED
+        EXIT_SUCCESS : EXIT_FAILURE);
       }
 
     if (pid < 0)
@@ -5644,7 +5682,7 @@ moreloop:
   callout_address = NULL;
   sending_ip_address = NULL;
   acl_var_m = NULL;
-  { int i; for(i=0; i<REGEX_VARS; i++) regex_vars[i] = NULL; }
+  for(int i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL;
 
   store_reset(reset_point);
   }