taint-enforce DB filenames
[exim.git] / src / src / deliver.c
index 66e49d3712297f57489b3844ce82b73b1ab45bc8..c4160a50c380a635e7ecb2ad0770851304da1a1e 100644 (file)
@@ -328,6 +328,10 @@ Returns:    a file descriptor, or -1 (with errno set)
 static int
 open_msglog_file(uschar *filename, int mode, uschar **error)
 {
+if (Ustrstr(filename, US"/../"))
+  log_write(0, LOG_MAIN|LOG_PANIC,
+    "Attempt to open msglog file path with upward-traversal: '%s'\n", filename);
+
 for (int i = 2; i > 0; i--)
   {
   int fd = Uopen(filename,
@@ -425,7 +429,7 @@ for (address_item * addr2 = addr->next; addr2; addr2 = addr2->next)
   addr2->transport_return = addr->transport_return;
   addr2->basic_errno =     addr->basic_errno;
   addr2->more_errno =      addr->more_errno;
-  addr2->delivery_usec =    addr->delivery_usec;
+  addr2->delivery_time =    addr->delivery_time;
   addr2->special_action =   addr->special_action;
   addr2->message =         addr->message;
   addr2->user_message =            addr->user_message;
@@ -1088,42 +1092,6 @@ return g;
 
 
 
-void
-timesince(struct timeval * diff, struct timeval * then)
-{
-gettimeofday(diff, NULL);
-diff->tv_sec -= then->tv_sec;
-if ((diff->tv_usec -= then->tv_usec) < 0)
-  {
-  diff->tv_sec--;
-  diff->tv_usec += 1000*1000;
-  }
-}
-
-
-
-uschar *
-string_timediff(struct timeval * diff)
-{
-static uschar buf[sizeof("0.000s")];
-
-if (diff->tv_sec >= 5 || !LOGGING(millisec))
-  return readconf_printtime((int)diff->tv_sec);
-
-sprintf(CS buf, "%u.%03us", (uint)diff->tv_sec, (uint)diff->tv_usec/1000);
-return buf;
-}
-
-
-uschar *
-string_timesince(struct timeval * then)
-{
-struct timeval diff;
-
-timesince(&diff, then);
-return string_timediff(&diff);
-}
-
 /******************************************************************************/
 
 
@@ -1253,7 +1221,7 @@ else
     {
     if (testflag(addr, af_pipelining))
       g = string_catn(g, US" L", 2);
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
     if (testflag(addr, af_early_pipe))
       g = string_catn(g, US"*", 1);
 #endif
@@ -1296,10 +1264,7 @@ if (LOGGING(queue_time))
     string_timesince(&received_time));
 
 if (LOGGING(deliver_time))
-  {
-  struct timeval diff = {.tv_sec = addr->more_errno, .tv_usec = addr->delivery_usec};
-  g = string_append(g, 2, US" DT=", string_timediff(&diff));
-  }
+  g = string_append(g, 2, US" DT=", string_timediff(&addr->delivery_time));
 
 /* string_cat() always leaves room for the terminator. Release the
 store we used to build the line after writing it. */
@@ -1367,6 +1332,9 @@ if (addr->host_used)
     }
   }
 
+if (LOGGING(deliver_time))
+  g = string_append(g, 2, US" DT=", string_timediff(&addr->delivery_time));
+
 if (addr->message)
   g = string_append(g, 2, US": ", addr->message);
 
@@ -1446,6 +1414,9 @@ if (addr->basic_errno > 0)
 if (addr->message)
   g = string_append(g, 2, US": ", addr->message);
 
+if (LOGGING(deliver_time))
+  g = string_append(g, 2, US" DT=", string_timediff(&addr->delivery_time));
+
 (void) string_from_gstring(g);
 
 /* Do the logging. For the message log, "routing failed" for those cases,
@@ -1642,6 +1613,7 @@ if (result == OK)
   tls_out.peercert = addr->peercert;
   addr->peercert = NULL;
 
+  tls_out.ver = addr->tlsver;
   tls_out.cipher = addr->cipher;
   tls_out.peerdn = addr->peerdn;
   tls_out.ocsp = addr->ocsp;
@@ -1655,6 +1627,7 @@ if (result == OK)
 #ifndef DISABLE_TLS
   tls_free_cert(&tls_out.ourcert);
   tls_free_cert(&tls_out.peercert);
+  tls_out.ver = NULL;
   tls_out.cipher = NULL;
   tls_out.peerdn = NULL;
   tls_out.ocsp = OCSP_NOT_REQ;
@@ -2437,7 +2410,7 @@ if ((pid = fork()) == 0)
       || (ret = write(pfd[pipe_write], &addr2->flags, sizeof(addr2->flags))) != sizeof(addr2->flags)
       || (ret = write(pfd[pipe_write], &addr2->basic_errno,    sizeof(int))) != sizeof(int)
       || (ret = write(pfd[pipe_write], &addr2->more_errno,     sizeof(int))) != sizeof(int)
-      || (ret = write(pfd[pipe_write], &addr2->delivery_usec,  sizeof(int))) != sizeof(int)
+      || (ret = write(pfd[pipe_write], &addr2->delivery_time,  sizeof(struct timeval))) != sizeof(struct timeval)
       || (ret = write(pfd[pipe_write], &addr2->special_action, sizeof(int))) != sizeof(int)
       || (ret = write(pfd[pipe_write], &addr2->transport,
         sizeof(transport_instance *))) != sizeof(transport_instance *)
@@ -2505,7 +2478,7 @@ for (addr2 = addr; addr2; addr2 = addr2->next)
     len = read(pfd[pipe_read], &addr2->flags, sizeof(addr2->flags));
     len = read(pfd[pipe_read], &addr2->basic_errno,    sizeof(int));
     len = read(pfd[pipe_read], &addr2->more_errno,     sizeof(int));
-    len = read(pfd[pipe_read], &addr2->delivery_usec,  sizeof(int));
+    len = read(pfd[pipe_read], &addr2->delivery_time,  sizeof(struct timeval));
     len = read(pfd[pipe_read], &addr2->special_action, sizeof(int));
     len = read(pfd[pipe_read], &addr2->transport,
       sizeof(transport_instance *));
@@ -2575,7 +2548,7 @@ if (!shadowing)
       /* In the test harness, wait just a bit to let the subprocess finish off
       any debug output etc first. */
 
-      if (f.running_in_test_harness) millisleep(300);
+      testharness_pause_ms(300);
 
       DEBUG(D_deliver) debug_printf("journalling %s", big_buffer);
       len = Ustrlen(big_buffer);
@@ -3159,11 +3132,7 @@ while (addr_local)
 
     /* Done with this address */
 
-    if (result == OK)
-      {
-      addr2->more_errno = deliver_time.tv_sec;
-      addr2->delivery_usec = deliver_time.tv_usec;
-      }
+    addr2->delivery_time = deliver_time;
     post_process_one(addr2, result, logflags, EXIM_DTYPE_TRANSPORT, logchar);
 
     /* If a pipe delivery generated text to be sent back, the result may be
@@ -3512,11 +3481,13 @@ while (!done)
       switch (*subid)
        {
        case '1':
-         addr->cipher = NULL;
-         addr->peerdn = NULL;
+         addr->tlsver = addr->cipher = addr->peerdn = NULL;
 
          if (*ptr)
+           {
            addr->cipher = string_copy(ptr);
+           addr->tlsver = string_copyn(ptr, Ustrchr(ptr, ':') - ptr);
+           }
          while (*ptr++);
          if (*ptr)
            addr->peerdn = string_copy(ptr);
@@ -3565,7 +3536,7 @@ while (!done)
     case 'L':
       switch (*subid)
        {
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
        case 2: setflag(addr, af_early_pipe);   /*FALLTHROUGH*/
 #endif
        case 1: setflag(addr, af_pipelining); break;
@@ -3635,8 +3606,8 @@ while (!done)
          ptr += sizeof(addr->basic_errno);
          memcpy(&addr->more_errno, ptr, sizeof(addr->more_errno));
          ptr += sizeof(addr->more_errno);
-         memcpy(&addr->delivery_usec, ptr, sizeof(addr->delivery_usec));
-         ptr += sizeof(addr->delivery_usec);
+         memcpy(&addr->delivery_time, ptr, sizeof(addr->delivery_time));
+         ptr += sizeof(addr->delivery_time);
          memcpy(&addr->flags, ptr, sizeof(addr->flags));
          ptr += sizeof(addr->flags);
          addr->message = *ptr ? string_copy(ptr) : NULL;
@@ -4794,7 +4765,6 @@ all pipes, so I do not see a reason to use non-blocking IO here
     for(; addr; addr = addr->next)
       {
       uschar *ptr;
-      retry_item *r;
 
       /* The certificate verification status goes into the flags */
       if (tls_out.certificate_verified) setflag(addr, af_cert_verified);
@@ -4827,7 +4797,7 @@ all pipes, so I do not see a reason to use non-blocking IO here
       if (addr->peercert)
        {
         ptr = big_buffer;
-       if (!tls_export_cert(ptr, big_buffer_size-2, addr->peercert))
+       if (tls_export_cert(ptr, big_buffer_size-2, addr->peercert))
          while(*ptr++);
        else
          *ptr++ = 0;
@@ -4836,7 +4806,7 @@ all pipes, so I do not see a reason to use non-blocking IO here
       if (addr->ourcert)
        {
         ptr = big_buffer;
-       if (!tls_export_cert(ptr, big_buffer_size-2, addr->ourcert))
+       if (tls_export_cert(ptr, big_buffer_size-2, addr->ourcert))
          while(*ptr++);
        else
          *ptr++ = 0;
@@ -4873,7 +4843,7 @@ all pipes, so I do not see a reason to use non-blocking IO here
 #endif
 
       if (testflag(addr, af_pipelining))
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
        if (testflag(addr, af_early_pipe))
          rmt_dlv_checked_write(fd, 'L', '2', NULL, 0);
        else
@@ -4894,7 +4864,7 @@ all pipes, so I do not see a reason to use non-blocking IO here
 
       /* Retry information: for most success cases this will be null. */
 
-      for (r = addr->retries; r; r = r->next)
+      for (retry_item * r = addr->retries; r; r = r->next)
         {
         sprintf(CS big_buffer, "%c%.500s", r->flags, r->key);
         ptr = big_buffer + Ustrlen(big_buffer+2) + 3;
@@ -4953,8 +4923,8 @@ all pipes, so I do not see a reason to use non-blocking IO here
       ptr += sizeof(addr->basic_errno);
       memcpy(ptr, &addr->more_errno, sizeof(addr->more_errno));
       ptr += sizeof(addr->more_errno);
-      memcpy(ptr, &addr->delivery_usec, sizeof(addr->delivery_usec));
-      ptr += sizeof(addr->delivery_usec);
+      memcpy(ptr, &addr->delivery_time, sizeof(addr->delivery_time));
+      ptr += sizeof(addr->delivery_time);
       memcpy(ptr, &addr->flags, sizeof(addr->flags));
       ptr += sizeof(addr->flags);
 
@@ -5057,9 +5027,10 @@ all pipes, so I do not see a reason to use non-blocking IO here
 
   /* Otherwise, if we are running in the test harness, wait a bit, to let the
   newly created process get going before we create another process. This should
-  ensure repeatability in the tests. We only need to wait a tad. */
+  ensure repeatability in the tests. Wait long enough for most cases to complete
+  the transport. */
 
-  else if (f.running_in_test_harness) millisleep(500);
+  else testharness_pause_ms(600);
 
   continue;
 
@@ -5506,6 +5477,26 @@ fprintf(f, "Action: %s\n"
 }
 
 
+
+/* When running in the test harness, there's an option that allows us to
+fudge this time so as to get repeatability of the tests. Take the first
+time off the list. In queue runs, the list pointer gets updated in the
+calling process. */
+
+int
+test_harness_fudged_queue_time(int actual_time)
+{
+int qt;
+if (  f.running_in_test_harness && *fudged_queue_times
+   && (qt = readconf_readtime(fudged_queue_times, '/', FALSE)) >= 0)
+  {
+  DEBUG(D_deliver) debug_printf("fudged queue_times = %s\n",
+    fudged_queue_times);
+  return qt;
+  }
+return actual_time;
+}
+
 /*************************************************
 *              Deliver one message               *
 *************************************************/
@@ -5557,8 +5548,13 @@ int process_recipients = RECIP_ACCEPT;
 open_db dbblock;
 open_db *dbm_file;
 extern int acl_where;
+uschar *info;
 
-uschar *info = queue_run_pid == (pid_t)0
+#ifdef MEASURE_TIMING
+report_time_since(&timestamp_startup, US"delivery start");     /* testcase 0022, 2100 */
+#endif
+
+info = queue_run_pid == (pid_t)0
   ? string_sprintf("delivering %s", id)
   : string_sprintf("delivering %s (queue run pid %d)", id, queue_run_pid);
 
@@ -6191,7 +6187,8 @@ if (process_recipients != RECIP_IGNORE)
         new->onetime_parent = recipients_list[r->pno].address;
 
       /* If DSN support is enabled, set the dsn flags and the original receipt
-         to be passed on to other DSN enabled MTAs */
+      to be passed on to other DSN enabled MTAs */
+
       new->dsn_flags = r->dsn_flags & rf_dsnflags;
       new->dsn_orcpt = r->orcpt;
       DEBUG(D_deliver) debug_printf("DSN: set orcpt: %s  flags: 0x%x\n",
@@ -7174,7 +7171,7 @@ if (addr_remote)
   /* Precompile some regex that are used to recognize parameters in response
   to an EHLO command, if they aren't already compiled. */
 
-  deliver_init();
+  smtp_deliver_init();
 
   /* Now sort the addresses if required, and do the deliveries. The yield of
   do_remote_deliveries is FALSE when mua_wrapper is set and all addresses
@@ -7311,10 +7308,9 @@ for (address_item * a = addr_succeed; a; a = a->next)
       );
 
   /* send report if next hop not DSN aware or a router flagged "last DSN hop"
-     and a report was requested */
-  if (  (  a->dsn_aware != dsn_support_yes
-       || a->dsn_flags & rf_dsnlasthop
-        )
+  and a report was requested */
+
+  if (  (a->dsn_aware != dsn_support_yes || a->dsn_flags & rf_dsnlasthop)
      && a->dsn_flags & rf_notify_success
      )
     {
@@ -7341,7 +7337,7 @@ if (addr_senddsn)
   if (pid < 0)  /* Creation of child failed */
     {
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Process %d (parent %d) failed to "
-      "create child process to send failure message: %s", getpid(),
+      "create child process to send success-dsn message: %s", getpid(),
       getppid(), strerror(errno));
 
     DEBUG(D_deliver) debug_printf("DSN: child_open_exim failed\n");
@@ -7354,7 +7350,7 @@ if (addr_senddsn)
     transport_ctx tctx = {{0}};
 
     DEBUG(D_deliver)
-      debug_printf("sending error message to: %s\n", sender_address);
+      debug_printf("sending success-dsn to: %s\n", sender_address);
 
     /* build unique id for MIME boundary */
     bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand());
@@ -7366,8 +7362,11 @@ if (addr_senddsn)
     moan_write_from(f);
     fprintf(f, "Auto-Submitted: auto-generated\n"
        "To: %s\n"
-       "Subject: Delivery Status Notification\n"
-       "Content-Type: multipart/report; report-type=delivery-status; boundary=%s\n"
+       "Subject: Delivery Status Notification\n",
+      sender_address);
+    moan_write_references(f, NULL);
+    fprintf(f, "Content-Type: multipart/report;"
+               " report-type=delivery-status; boundary=%s\n"
        "MIME-Version: 1.0\n\n"
 
        "--%s\n"
@@ -7375,7 +7374,7 @@ if (addr_senddsn)
 
        "This message was created automatically by mail delivery software.\n"
        " ----- The following addresses had successful delivery notifications -----\n",
-      sender_address, bound, bound);
+      bound, bound);
 
     for (address_item * a = addr_senddsn; a; a = a->next)
       fprintf(f, "<%s> (relayed %s)\n\n",
@@ -7604,6 +7603,7 @@ while (addr_failed)
       fprintf(fp, "Auto-Submitted: auto-replied\n");
       moan_write_from(fp);
       fprintf(fp, "To: %s\n", bounce_recipient);
+      moan_write_references(fp, NULL);
 
       /* generate boundary string and output MIME-Headers */
       bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand());
@@ -7914,7 +7914,7 @@ wording. */
 
       /* In the test harness, let the child do it's thing first. */
 
-      if (f.running_in_test_harness) millisleep(500);
+      testharness_pause_ms(500);
 
       /* If the process failed, there was some disaster in setting up the
       error message. Unless the message is very old, ensure that addr_defer
@@ -8154,21 +8154,7 @@ else if (addr_defer != (address_item *)(+1))
     int show_time;
     int queue_time = time(NULL) - received_time.tv_sec;
 
-    /* When running in the test harness, there's an option that allows us to
-    fudge this time so as to get repeatability of the tests. Take the first
-    time off the list. In queue runs, the list pointer gets updated in the
-    calling process. */
-
-    if (f.running_in_test_harness && fudged_queue_times[0] != 0)
-      {
-      int qt = readconf_readtime(fudged_queue_times, '/', FALSE);
-      if (qt >= 0)
-        {
-        DEBUG(D_deliver) debug_printf("fudged queue_times = %s\n",
-          fudged_queue_times);
-        queue_time = qt;
-        }
-      }
+    queue_time = test_harness_fudged_queue_time(queue_time);
 
     /* See how many warnings we should have sent by now */
 
@@ -8189,7 +8175,8 @@ else if (addr_defer != (address_item *)(+1))
 
     DEBUG(D_deliver)
       {
-      debug_printf("time on queue = %s  id %s  addr %s\n", readconf_printtime(queue_time), message_id, addr_defer->address);
+      debug_printf("time on queue = %s  id %s  addr %s\n",
+       readconf_printtime(queue_time), message_id, addr_defer->address);
       debug_printf("warning counts: required %d done %d\n", count,
         warning_count);
       }
@@ -8227,6 +8214,7 @@ else if (addr_defer != (address_item *)(+1))
         fprintf(f, "Auto-Submitted: auto-replied\n");
         moan_write_from(f);
         fprintf(f, "To: %s\n", recipients);
+       moan_write_references(f, NULL);
 
         /* generated boundary string and output MIME-Headers */
         bound = string_sprintf(TIME_T_FMT "-eximdsn-%d", time(NULL), rand());
@@ -8488,6 +8476,9 @@ to try delivery. */
 (void)close(deliver_datafile);
 deliver_datafile = -1;
 DEBUG(D_deliver) debug_printf("end delivery of %s\n", id);
+#ifdef MEASURE_TIMING
+report_time_since(&timestamp_startup, US"delivery end"); /* testcase 0005 */
+#endif
 
 /* It is unlikely that there will be any cached resources, since they are
 released after routing, and in the delivery subprocesses. However, it's
@@ -8503,52 +8494,13 @@ return final_yield;
 
 
 void
-deliver_init(void)
+tcp_init(void)
 {
 #ifdef EXIM_TFO_PROBE
 tfo_probe();
 #else
 f.tcp_fastopen_ok = TRUE;
 #endif
-
-
-if (!regex_PIPELINING) regex_PIPELINING =
-  regex_must_compile(US"\\n250[\\s\\-]PIPELINING(\\s|\\n|$)", FALSE, TRUE);
-
-if (!regex_SIZE) regex_SIZE =
-  regex_must_compile(US"\\n250[\\s\\-]SIZE(\\s|\\n|$)", FALSE, TRUE);
-
-if (!regex_AUTH) regex_AUTH =
-  regex_must_compile(AUTHS_REGEX, FALSE, TRUE);
-
-#ifndef DISABLE_TLS
-if (!regex_STARTTLS) regex_STARTTLS =
-  regex_must_compile(US"\\n250[\\s\\-]STARTTLS(\\s|\\n|$)", FALSE, TRUE);
-#endif
-
-if (!regex_CHUNKING) regex_CHUNKING =
-  regex_must_compile(US"\\n250[\\s\\-]CHUNKING(\\s|\\n|$)", FALSE, TRUE);
-
-#ifndef DISABLE_PRDR
-if (!regex_PRDR) regex_PRDR =
-  regex_must_compile(US"\\n250[\\s\\-]PRDR(\\s|\\n|$)", FALSE, TRUE);
-#endif
-
-#ifdef SUPPORT_I18N
-if (!regex_UTF8) regex_UTF8 =
-  regex_must_compile(US"\\n250[\\s\\-]SMTPUTF8(\\s|\\n|$)", FALSE, TRUE);
-#endif
-
-if (!regex_DSN) regex_DSN  =
-  regex_must_compile(US"\\n250[\\s\\-]DSN(\\s|\\n|$)", FALSE, TRUE);
-
-if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA =
-  regex_must_compile(US"\\n250[\\s\\-]IGNOREQUOTA(\\s|\\n|$)", FALSE, TRUE);
-
-#ifdef SUPPORT_PIPE_CONNECT
-if (!regex_EARLY_PIPE) regex_EARLY_PIPE =
-  regex_must_compile(US"\\n250[\\s\\-]" EARLY_PIPE_FEATURE_NAME "(\\s|\\n|$)", FALSE, TRUE);
-#endif
 }
 
 
@@ -8630,7 +8582,7 @@ if (cutthrough.cctx.sock >= 0 && cutthrough.callout_hold_only)
 
     else if (pid == 0)         /* child: fork again to totally disconnect */
       {
-      if (f.running_in_test_harness) millisleep(100); /* let parent debug out */
+      testharness_pause_ms(100); /* let parent debug out */
       /* does not return */
       smtp_proxy_tls(cutthrough.cctx.tls_ctx, big_buffer, big_buffer_size,
                      pfd, 5*60);