DANE: move to mainline
[exim.git] / src / src / spool_in.c
index 1a5bf4ec8a3e0e9262b22b2aaec59faad7113247..c8ddffe4135b068d1d8a2c9322ad6d3de4c87148 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2015 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions for reading spool files. When compiling for a utility (eximon),
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions for reading spool files. When compiling for a utility (eximon),
@@ -25,19 +25,21 @@ fact it won't be written to. Just in case there's a major disaster (e.g.
 overwriting some other file descriptor with the value of this one), open it
 with append.
 
 overwriting some other file descriptor with the value of this one), open it
 with append.
 
+As called by deliver_message() (at least) we are operating as root.
+
 Argument: the id of the message
 Argument: the id of the message
-Returns:  TRUE if file successfully opened and locked
+Returns:  fd if file successfully opened and locked, else -1
 
 
-Side effect: deliver_datafile is set to the fd of the open file.
+Side effect: message_subdir is set for the (possibly split) spool directory
 */
 
 */
 
-BOOL
+int
 spool_open_datafile(uschar *id)
 {
 int i;
 struct stat statbuf;
 flock_t lock_data;
 spool_open_datafile(uschar *id)
 {
 int i;
 struct stat statbuf;
 flock_t lock_data;
-uschar spoolname[256];
+int fd;
 
 /* If split_spool_directory is set, first look for the file in the appropriate
 sub-directory of the input directory. If it is not found there, try the input
 
 /* If split_spool_directory is set, first look for the file in the appropriate
 sub-directory of the input directory. If it is not found there, try the input
@@ -48,22 +50,33 @@ splitting state. */
 
 for (i = 0; i < 2; i++)
   {
 
 for (i = 0; i < 2; i++)
   {
+  uschar * fname;
   int save_errno;
   int save_errno;
-  message_subdir[0] = (split_spool_directory == (i == 0))? id[5] : 0;
-  sprintf(CS spoolname, "%s/input/%s/%s-D", spool_directory, message_subdir, id);
-  deliver_datafile = Uopen(spoolname, O_RDWR | O_APPEND, 0);
-  if (deliver_datafile >= 0) break;
+
+  message_subdir[0] = split_spool_directory == i ? '\0' : id[5];
+  fname = spool_fname(US"input", message_subdir, id, US"-D");
+  DEBUG(D_deliver) debug_printf("Trying spool file %s\n", fname);
+
+  if ((fd = Uopen(fname,
+#ifdef O_CLOEXEC
+                     O_CLOEXEC |
+#endif
+                     O_RDWR | O_APPEND, 0)) >= 0)
+    break;
   save_errno = errno;
   if (errno == ENOENT)
     {
     if (i == 0) continue;
     if (!queue_running)
   save_errno = errno;
   if (errno == ENOENT)
     {
     if (i == 0) continue;
     if (!queue_running)
-      log_write(0, LOG_MAIN, "Spool file %s-D not found", id);
+      log_write(0, LOG_MAIN, "Spool%s%s file %s-D not found",
+       *queue_name ? US" Q=" : US"",
+       *queue_name ? queue_name : US"",
+       id);
     }
     }
-  else log_write(0, LOG_MAIN, "Spool error for %s: %s", spoolname,
-    strerror(errno));
+  else
+    log_write(0, LOG_MAIN, "Spool error for %s: %s", fname, strerror(errno));
   errno = save_errno;
   errno = save_errno;
-  return FALSE;
+  return -1;
   }
 
 /* File is open and message_subdir is set. Set the close-on-exec flag, and lock
   }
 
 /* File is open and message_subdir is set. Set the close-on-exec flag, and lock
@@ -74,35 +87,35 @@ an open file descriptor (at least, I think that's the Cygwin story). On real
 Unix systems it doesn't make any difference as long as Exim is consistent in
 what it locks. */
 
 Unix systems it doesn't make any difference as long as Exim is consistent in
 what it locks. */
 
-(void)fcntl(deliver_datafile, F_SETFD, fcntl(deliver_datafile, F_GETFD) |
-  FD_CLOEXEC);
+#ifndef O_CLOEXEC
+(void)fcntl(fd, F_SETFD, fcntl(fd, F_GETFD) | FD_CLOEXEC);
+#endif
 
 lock_data.l_type = F_WRLCK;
 lock_data.l_whence = SEEK_SET;
 lock_data.l_start = 0;
 lock_data.l_len = SPOOL_DATA_START_OFFSET;
 
 
 lock_data.l_type = F_WRLCK;
 lock_data.l_whence = SEEK_SET;
 lock_data.l_start = 0;
 lock_data.l_len = SPOOL_DATA_START_OFFSET;
 
-if (fcntl(deliver_datafile, F_SETLK, &lock_data) < 0)
+if (fcntl(fd, F_SETLK, &lock_data) < 0)
   {
   log_write(L_skip_delivery,
             LOG_MAIN,
             "Spool file is locked (another process is handling this message)");
   {
   log_write(L_skip_delivery,
             LOG_MAIN,
             "Spool file is locked (another process is handling this message)");
-  (void)close(deliver_datafile);
-  deliver_datafile = -1;
+  (void)close(fd);
   errno = 0;
   errno = 0;
-  return FALSE;
+  return -1;
   }
 
 /* Get the size of the data; don't include the leading filename line
 in the count, but add one for the newline before the data. */
 
   }
 
 /* Get the size of the data; don't include the leading filename line
 in the count, but add one for the newline before the data. */
 
-if (fstat(deliver_datafile, &statbuf) == 0)
+if (fstat(fd, &statbuf) == 0)
   {
   message_body_size = statbuf.st_size - SPOOL_DATA_START_OFFSET;
   message_size = message_body_size + 1;
   }
 
   {
   message_body_size = statbuf.st_size - SPOOL_DATA_START_OFFSET;
   message_size = message_body_size + 1;
   }
 
-return TRUE;
+return fd;
 }
 #endif  /* COMPILE_UTILITY */
 
 }
 #endif  /* COMPILE_UTILITY */
 
@@ -209,6 +222,8 @@ have been the cause of that incident, but in any case, this code must be robust
 against such an event, and if such a file is encountered, it must be treated as
 malformed.
 
 against such an event, and if such a file is encountered, it must be treated as
 malformed.
 
+As called from deliver_message() (at least) we are running as root.
+
 Arguments:
   name          name of the header file, including the -H
   read_headers  TRUE if in-store header structures are to be built
 Arguments:
   name          name of the header file, including the -H
   read_headers  TRUE if in-store header structures are to be built
@@ -217,7 +232,7 @@ Arguments:
 Returns:        spool_read_OK        success
                 spool_read_notopen   open failed
                 spool_read_enverror  error in the envelope portion
 Returns:        spool_read_OK        success
                 spool_read_notopen   open failed
                 spool_read_enverror  error in the envelope portion
-                spool_read_hdrdrror  error in the header portion
+                spool_read_hdrerror  error in the header portion
 */
 
 int
 */
 
 int
@@ -269,6 +284,9 @@ sender_ident = NULL;
 sender_local = FALSE;
 sender_set_untrusted = FALSE;
 smtp_active_hostname = primary_hostname;
 sender_local = FALSE;
 sender_set_untrusted = FALSE;
 smtp_active_hostname = primary_hostname;
+#ifndef COMPILE_UTILITY
+spool_file_wireformat = FALSE;
+#endif
 tree_nonrecipients = NULL;
 
 #ifdef EXPERIMENTAL_BRIGHTMAIL
 tree_nonrecipients = NULL;
 
 #ifdef EXPERIMENTAL_BRIGHTMAIL
@@ -284,7 +302,7 @@ dkim_collect_input = FALSE;
 
 #ifdef SUPPORT_TLS
 tls_in.certificate_verified = FALSE;
 
 #ifdef SUPPORT_TLS
 tls_in.certificate_verified = FALSE;
-# ifdef EXPERIMENTAL_DANE
+# ifdef SUPPORT_DANE
 tls_in.dane_verified = FALSE;
 # endif
 tls_in.cipher = NULL;
 tls_in.dane_verified = FALSE;
 # endif
 tls_in.cipher = NULL;
@@ -298,10 +316,12 @@ tls_in.ocsp = OCSP_NOT_REQ;
 #endif
 
 #ifdef WITH_CONTENT_SCAN
 #endif
 
 #ifdef WITH_CONTENT_SCAN
+spam_bar = NULL;
+spam_score = NULL;
 spam_score_int = NULL;
 #endif
 
 spam_score_int = NULL;
 #endif
 
-#if defined(EXPERIMENTAL_INTERNATIONAL) && !defined(COMPILE_UTILITY)
+#if defined(SUPPORT_I18N) && !defined(COMPILE_UTILITY)
 message_smtputf8 = FALSE;
 message_utf8_downconvert = 0;
 #endif
 message_smtputf8 = FALSE;
 message_utf8_downconvert = 0;
 #endif
@@ -316,12 +336,12 @@ and unsplit directories, as for the data file above. */
 for (n = 0; n < 2; n++)
   {
   if (!subdir_set)
 for (n = 0; n < 2; n++)
   {
   if (!subdir_set)
-    message_subdir[0] = (split_spool_directory == (n == 0))? name[5] : 0;
-  sprintf(CS big_buffer, "%s/input/%s/%s", spool_directory, message_subdir,
-    name);
-  f = Ufopen(big_buffer, "rb");
-  if (f != NULL) break;
-  if (n != 0 || subdir_set || errno != ENOENT) return spool_read_notopen;
+    message_subdir[0] = split_spool_directory == (n == 0) ? name[5] : 0;
+
+  if ((f = Ufopen(spool_fname(US"input", message_subdir, name, US""), "rb")))
+    break;
+  if (n != 0 || subdir_set || errno != ENOENT)
+    return spool_read_notopen;
   }
 
 errno = 0;
   }
 
 errno = 0;
@@ -377,10 +397,11 @@ sender_address[n-3] = 0;
 
 /* time */
 if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR;
 
 /* time */
 if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR;
-if (sscanf(CS big_buffer, "%d %d", &received_time, &warning_count) != 2)
+if (sscanf(CS big_buffer, TIME_T_FMT " %d", &received_time.tv_sec, &warning_count) != 2)
   goto SPOOL_FORMAT_ERROR;
   goto SPOOL_FORMAT_ERROR;
+received_time.tv_usec = 0;
 
 
-message_age = time(NULL) - received_time;
+message_age = time(NULL) - received_time.tv_sec;
 
 #ifndef COMPILE_UTILITY
 DEBUG(D_deliver) debug_printf("user=%s uid=%ld gid=%ld sender=%s\n",
 
 #ifndef COMPILE_UTILITY
 DEBUG(D_deliver) debug_printf("user=%s uid=%ld gid=%ld sender=%s\n",
@@ -438,8 +459,8 @@ for (;;)
       tree_node *node;
       endptr = Ustrchr(big_buffer + 6, ' ');
       if (endptr == NULL) goto SPOOL_FORMAT_ERROR;
       tree_node *node;
       endptr = Ustrchr(big_buffer + 6, ' ');
       if (endptr == NULL) goto SPOOL_FORMAT_ERROR;
-      name = string_sprintf("%c%.*s", big_buffer[4], endptr - big_buffer - 6,
-        big_buffer + 6);
+      name = string_sprintf("%c%.*s", big_buffer[4],
+        (int)(endptr - big_buffer - 6), big_buffer + 6);
       if (sscanf(CS endptr, " %d", &count) != 1) goto SPOOL_FORMAT_ERROR;
       node = acl_var_create(name);
       node->data.ptr = store_get(count + 1);
       if (sscanf(CS endptr, " %d", &count) != 1) goto SPOOL_FORMAT_ERROR;
       node = acl_var_create(name);
       node->data.ptr = store_get(count + 1);
@@ -468,19 +489,24 @@ for (;;)
 
     else if (Ustrncmp(p, "cl ", 3) == 0)
       {
 
     else if (Ustrncmp(p, "cl ", 3) == 0)
       {
-      int index, count;
-      uschar name[20];   /* Need plenty of space for %d format */
-      tree_node *node;
-      if (sscanf(CS big_buffer + 5, "%d %d", &index, &count) != 2)
+      unsigned index, count;
+      uschar name[20];   /* Need plenty of space for %u format */
+      tree_node * node;
+      if (  sscanf(CS big_buffer + 5, "%u %u", &index, &count) != 2
+        || index >= 20
+        || count > 16384       /* arbitrary limit on variable size */
+         )
         goto SPOOL_FORMAT_ERROR;
       if (index < 10)
         goto SPOOL_FORMAT_ERROR;
       if (index < 10)
-        (void) string_format(name, sizeof(name), "%c%d", 'c', index);
-      else if (index < 20) /* ignore out-of-range index */
-        (void) string_format(name, sizeof(name), "%c%d", 'm', index - 10);
+        (void) string_format(name, sizeof(name), "%c%u", 'c', index);
+      else
+        (void) string_format(name, sizeof(name), "%c%u", 'm', index - 10);
       node = acl_var_create(name);
       node->data.ptr = store_get(count + 1);
       node = acl_var_create(name);
       node->data.ptr = store_get(count + 1);
+      /* We sanity-checked the count, so disable the Coverity error */
+      /* coverity[tainted_data] */
       if (fread(node->data.ptr, 1, count+1, f) < count) goto SPOOL_READ_ERROR;
       if (fread(node->data.ptr, 1, count+1, f) < count) goto SPOOL_READ_ERROR;
-      ((uschar*)node->data.ptr)[count] = 0;
+      (US node->data.ptr)[count] = '\0';
       }
     break;
 
       }
     break;
 
@@ -509,7 +535,8 @@ for (;;)
     if (Ustrncmp(p, "rozen", 5) == 0)
       {
       deliver_freeze = TRUE;
     if (Ustrncmp(p, "rozen", 5) == 0)
       {
       deliver_freeze = TRUE;
-      sscanf(CS big_buffer+7, TIME_T_FMT, &deliver_frozen_at);
+      if (sscanf(CS big_buffer+7, TIME_T_FMT, &deliver_frozen_at) != 1)
+       goto SPOOL_READ_ERROR;
       }
     break;
 
       }
     break;
 
@@ -547,7 +574,8 @@ for (;;)
     break;
 
     case 'l':
     break;
 
     case 'l':
-    if (Ustrcmp(p, "ocal") == 0) sender_local = TRUE;
+    if (Ustrcmp(p, "ocal") == 0)
+      sender_local = TRUE;
     else if (Ustrcmp(big_buffer, "-localerror") == 0)
       local_error_message = TRUE;
     else if (Ustrncmp(p, "ocal_scan ", 10) == 0)
     else if (Ustrcmp(big_buffer, "-localerror") == 0)
       local_error_message = TRUE;
     else if (Ustrncmp(p, "ocal_scan ", 10) == 0)
@@ -567,16 +595,30 @@ for (;;)
     case 'r':
     if (Ustrncmp(p, "eceived_protocol", 16) == 0)
       received_protocol = string_copy(big_buffer + 19);
     case 'r':
     if (Ustrncmp(p, "eceived_protocol", 16) == 0)
       received_protocol = string_copy(big_buffer + 19);
+    else if (Ustrncmp(p, "eceived_time_usec", 17) == 0)
+      {
+      unsigned usec;
+      if (sscanf(CS big_buffer + 21, "%u", &usec) == 1)
+       received_time.tv_usec = usec;
+      }
     break;
 
     case 's':
     if (Ustrncmp(p, "ender_set_untrusted", 19) == 0)
       sender_set_untrusted = TRUE;
 #ifdef WITH_CONTENT_SCAN
     break;
 
     case 's':
     if (Ustrncmp(p, "ender_set_untrusted", 19) == 0)
       sender_set_untrusted = TRUE;
 #ifdef WITH_CONTENT_SCAN
+    else if (Ustrncmp(p, "pam_bar ", 8) == 0)
+      spam_bar = string_copy(big_buffer + 10);
+    else if (Ustrncmp(p, "pam_score ", 10) == 0)
+      spam_score = string_copy(big_buffer + 12);
     else if (Ustrncmp(p, "pam_score_int ", 14) == 0)
       spam_score_int = string_copy(big_buffer + 16);
 #endif
     else if (Ustrncmp(p, "pam_score_int ", 14) == 0)
       spam_score_int = string_copy(big_buffer + 16);
 #endif
-#if defined(EXPERIMENTAL_INTERNATIONAL) && !defined(COMPILE_UTILITY)
+#ifndef COMPILE_UTILITY
+    else if (Ustrncmp(p, "pool_file_wireformat", 20) == 0)
+      spool_file_wireformat = TRUE;
+#endif
+#if defined(SUPPORT_I18N) && !defined(COMPILE_UTILITY)
     else if (Ustrncmp(p, "mtputf8", 7) == 0)
       message_smtputf8 = TRUE;
 #endif
     else if (Ustrncmp(p, "mtputf8", 7) == 0)
       message_smtputf8 = TRUE;
 #endif
@@ -603,7 +645,7 @@ for (;;)
     break;
 #endif
 
     break;
 #endif
 
-#if defined(EXPERIMENTAL_INTERNATIONAL) && !defined(COMPILE_UTILITY)
+#if defined(SUPPORT_I18N) && !defined(COMPILE_UTILITY)
     case 'u':
     if (Ustrncmp(p, "tf8_downcvt", 11) == 0)
       message_utf8_downconvert = 1;
     case 'u':
     if (Ustrncmp(p, "tf8_downcvt", 11) == 0)
       message_utf8_downconvert = 1;
@@ -645,10 +687,12 @@ DEBUG(D_deliver)
 #endif  /* COMPILE_UTILITY */
 
 /* After reading the tree, the next line has not yet been read into the
 #endif  /* COMPILE_UTILITY */
 
 /* After reading the tree, the next line has not yet been read into the
-buffer. It contains the count of recipients which follow on separate lines. */
+buffer. It contains the count of recipients which follow on separate lines.
+Apply an arbitrary sanity check.*/
 
 if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR;
 
 if (Ufgets(big_buffer, big_buffer_size, f) == NULL) goto SPOOL_READ_ERROR;
-if (sscanf(CS big_buffer, "%d", &rcount) != 1) goto SPOOL_FORMAT_ERROR;
+if (sscanf(CS big_buffer, "%d", &rcount) != 1 || rcount > 16384)
+  goto SPOOL_FORMAT_ERROR;
 
 #ifndef COMPILE_UTILITY
 DEBUG(D_deliver) debug_printf("recipients_count=%d\n", rcount);
 
 #ifndef COMPILE_UTILITY
 DEBUG(D_deliver) debug_printf("recipients_count=%d\n", rcount);
@@ -657,6 +701,10 @@ DEBUG(D_deliver) debug_printf("recipients_count=%d\n", rcount);
 recipients_list_max = rcount;
 recipients_list = store_get(rcount * sizeof(recipient_item));
 
 recipients_list_max = rcount;
 recipients_list = store_get(rcount * sizeof(recipient_item));
 
+/* We sanitised the count and know we have enough memory, so disable
+the Coverity error on recipients_count */
+/* coverity[tainted_data] */
+
 for (recipients_count = 0; recipients_count < rcount; recipients_count++)
   {
   int nn;
 for (recipients_count = 0; recipients_count < rcount; recipients_count++)
   {
   int nn;
@@ -755,7 +803,7 @@ for (recipients_count = 0; recipients_count < rcount; recipients_count++)
         {
         p -= len;
         errors_to = string_copy(p);
         {
         p -= len;
         errors_to = string_copy(p);
-        }      
+        }
       }
 
     *(--p) = 0;   /* Terminate address */
       }
 
     *(--p) = 0;   /* Terminate address */
@@ -769,7 +817,7 @@ for (recipients_count = 0; recipients_count < rcount; recipients_count++)
         {
         p -= len;
         orcpt = string_copy(p);
         {
         p -= len;
         orcpt = string_copy(p);
-        }      
+        }
       }
 
     *(--p) = 0;   /* Terminate address */
       }
 
     *(--p) = 0;   /* Terminate address */