add an internal error code definition
[exim.git] / src / src / log.c
index 2832f4ed009b6056f7595f752cb335b5893f2b3a..88cc12cd4dfe877355908059b7e818faf5ab1dc2 100644 (file)
@@ -3,6 +3,7 @@
 *************************************************/
 
 /* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions for writing log files. The code for maintaining datestamped
@@ -104,7 +105,8 @@ static const uschar * exim_errstrings[] = {
   US"Negotiation failed for proxy configured host",
   US"Authenticator 'other' failure",
   US"target not supporting SMTPUTF8",
-  US"",
+  US"host is local",
+  US"tainted filename",
 
   US"Not time for routing",
   US"Not time for local delivery",
@@ -112,6 +114,7 @@ static const uschar * exim_errstrings[] = {
   US"Local-only delivery",
   US"Domain in queue_domains",
   US"Transport concurrency limit",
+  US"Event requests alternate response",
 };
 
 
@@ -142,7 +145,7 @@ Returns:         nothing
 static void
 write_syslog(int priority, const uschar *s)
 {
-int len, pass;
+int len;
 int linecount = 0;
 
 if (!syslog_pid && LOGGING(pid))
@@ -171,12 +174,10 @@ if (!syslog_open && !f.running_in_test_harness)
 /* First do a scan through the message in order to determine how many lines
 it is going to end up as. Then rescan to output it. */
 
-for (pass = 0; pass < 2; pass++)
+for (int pass = 0; pass < 2; pass++)
   {
-  int i;
-  int tlen;
   const uschar * ss = s;
-  for (i = 1, tlen = len; tlen > 0; i++)
+  for (int i = 1, tlen = len; tlen > 0; i++)
     {
     int plen = tlen;
     uschar *nlptr = Ustrchr(ss, '\n');
@@ -243,7 +244,7 @@ if (s1)
   }
 if (f.receive_call_bombout) receive_bomb_out(NULL, s2);  /* does not return */
 if (smtp_input) smtp_closedown(s2);
-exim_exit(EXIT_FAILURE, NULL);
+exim_exit(EXIT_FAILURE);
 }
 
 
@@ -314,7 +315,7 @@ Returns:       a file descriptor, or < 0 on failure (errno set)
 int
 log_create_as_exim(uschar *name)
 {
-pid_t pid = fork();
+pid_t pid = exim_fork(US"logfile-create");
 int status = 1;
 int fd = -1;
 
@@ -511,7 +512,7 @@ non-setuid binary with log_arguments set, called in certain ways.) Rather than
 just bombing out, force the log to stderr and carry on if stderr is available.
 */
 
-if (euid != root_uid && euid != exim_uid && log_stderr != NULL)
+if (euid != root_uid && euid != exim_uid && log_stderr)
   {
   *fd = fileno(log_stderr);
   return;
@@ -520,7 +521,9 @@ if (euid != root_uid && euid != exim_uid && log_stderr != NULL)
 /* Otherwise this is a disaster. This call is deliberately ONLY to the panic
 log. If possible, save a copy of the original line that was being logged. If we
 are recursing (can't open the panic log either), the pointer will already be
-set. */
+set.  Also, when we had to use a subprocess for the create we didn't retrieve
+errno from it, so get the error from the open attempt above (which is often
+meaningful enough, so leave it). */
 
 if (!panic_save_buffer)
   if ((panic_save_buffer = US malloc(LOG_BUFFER_SIZE)))
@@ -554,23 +557,18 @@ Arguments:
 Returns:      updated pointer
 */
 
-static uschar *
-log_config_info(uschar *ptr, int flags)
+static gstring *
+log_config_info(gstring * g, int flags)
 {
-Ustrcpy(ptr, "Exim configuration error");
-ptr += 24;
+g = string_cat(g, US"Exim configuration error");
 
-if ((flags & (LOG_CONFIG_FOR & ~LOG_CONFIG)) != 0)
-  {
-  Ustrcpy(ptr, " for ");
-  return ptr + 5;
-  }
+if (flags & (LOG_CONFIG_FOR & ~LOG_CONFIG))
+  return string_cat(g, US" for ");
 
-if ((flags & (LOG_CONFIG_IN & ~LOG_CONFIG)) != 0)
-  ptr += sprintf(CS ptr, " in line %d of %s", config_lineno, config_filename);
+if (flags & (LOG_CONFIG_IN & ~LOG_CONFIG))
+  g = string_fmt_append(g, " in line %d of %s", config_lineno, config_filename);
 
-Ustrcpy(ptr, ":\n  ");
-return ptr + 4;
+return string_catn(g, US":\n  ", 4);
 }
 
 
@@ -741,10 +739,10 @@ Returns:    nothing
 void
 log_write(unsigned int selector, int flags, const char *format, ...)
 {
-uschar * ptr;
-int length;
 int paniclogfd;
 ssize_t written_len;
+gstring gs = { .size = LOG_BUFFER_SIZE-1, .ptr = 0, .s = log_buffer };
+gstring * g;
 va_list ap;
 
 /* If panic_recurseflag is set, we have failed to open the panic log. This is
@@ -771,7 +769,7 @@ if (!log_buffer)
   if (!(log_buffer = US malloc(LOG_BUFFER_SIZE)))
     {
     fprintf(stderr, "exim: failed to get store for log buffer\n");
-    exim_exit(EXIT_FAILURE, NULL);
+    exim_exit(EXIT_FAILURE);
     }
 
 /* If we haven't already done so, inspect the setting of log_file_path to
@@ -851,10 +849,8 @@ in one go so that it doesn't get split when multi-processing. */
 DEBUG(D_any|D_v)
   {
   int i;
-  ptr = log_buffer;
 
-  Ustrcpy(ptr, "LOG:");
-  ptr += 4;
+  g = string_catn(&gs, US"LOG:", 4);
 
   /* Show the selector that was passed into the call. */
 
@@ -862,31 +858,38 @@ DEBUG(D_any|D_v)
     {
     unsigned int bitnum = log_options[i].bit;
     if (bitnum < BITWORDSIZE && selector == BIT(bitnum))
-      {
-      *ptr++ = ' ';
-      Ustrcpy(ptr, log_options[i].name);
-      while (*ptr) ptr++;
-      }
+      g = string_fmt_append(g, " %s", log_options[i].name);
     }
 
-  ptr += sprintf(CS ptr, "%s%s%s%s\n  ",
+  g = string_fmt_append(g, "%s%s%s%s\n  ",
     flags & LOG_MAIN ?    " MAIN"   : "",
     flags & LOG_PANIC ?   " PANIC"  : "",
     (flags & LOG_PANIC_DIE) == LOG_PANIC_DIE ? " DIE" : "",
     flags & LOG_REJECT ?  " REJECT" : "");
 
-  if (flags & LOG_CONFIG) ptr = log_config_info(ptr, flags);
+  if (flags & LOG_CONFIG) g = log_config_info(g, flags);
+
+  /* We want to be able to log tainted info, but log_buffer is directly
+  malloc'd.  So use deliberately taint-nonchecking routines to build into
+  it, trusting that we will never expand the results. */
 
   va_start(ap, format);
-  if (!string_vformat(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer)-1, format, ap))
-    Ustrcpy(ptr, "**** log string overflowed log buffer ****");
+  i = g->ptr;
+  if (!string_vformat(g, SVFMT_TAINT_NOCHK, format, ap))
+    {
+    g->ptr = i;
+    g = string_cat(g, US"**** log string overflowed log buffer ****");
+    }
   va_end(ap);
 
-  while(*ptr) ptr++;
-  Ustrcat(ptr, "\n");
-  debug_printf("%s", log_buffer);
-  }
+  g->size = LOG_BUFFER_SIZE;
+  g = string_catn(g, US"\n", 1);
+  debug_printf("%s", string_from_gstring(g));
 
+  gs.size = LOG_BUFFER_SIZE-1; /* Having used the buffer for debug output, */
+  gs.ptr = 0;                  /* reset it for the real use. */
+  gs.s = log_buffer;
+  }
 /* If no log file is specified, we are in a mess. */
 
 if (!(flags & (LOG_MAIN|LOG_PANIC|LOG_REJECT)))
@@ -908,54 +911,64 @@ if (!write_rejectlog) flags &= ~LOG_REJECT;
 /* Create the main message in the log buffer. Do not include the message id
 when called by a utility. */
 
-ptr = log_buffer;
-ptr += sprintf(CS ptr, "%s ", tod_stamp(tod_log));
+g = string_fmt_append(&gs, "%s ", tod_stamp(tod_log));
 
 if (LOGGING(pid))
   {
-  if (!syslog_pid) pid_position[0] = ptr - log_buffer; /* remember begin … */
-  ptr += sprintf(CS ptr, "[%d] ", (int)getpid());
-  if (!syslog_pid) pid_position[1] = ptr - log_buffer; /*  … and end+1 of the PID */
+  if (!syslog_pid) pid_position[0] = g->ptr;           /* remember begin … */
+  g = string_fmt_append(g, "[%d] ", (int)getpid());
+  if (!syslog_pid) pid_position[1] = g->ptr;           /*  … and end+1 of the PID */
   }
 
 if (f.really_exim && message_id[0] != 0)
-  ptr += sprintf(CS ptr, "%s ", message_id);
+  g = string_fmt_append(g, "%s ", message_id);
 
-if (flags & LOG_CONFIG) ptr = log_config_info(ptr, flags);
+if (flags & LOG_CONFIG)
+  g = log_config_info(g, flags);
 
 va_start(ap, format);
-if (!string_vformat(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer)-1, format, ap))
-  Ustrcpy(ptr, "**** log string overflowed log buffer ****\n");
-while(*ptr) ptr++;
+  {
+  int i = g->ptr;
+
+  /* We want to be able to log tainted info, but log_buffer is directly
+  malloc'd.  So use deliberately taint-nonchecking routines to build into
+  it, trusting that we will never expand the results. */
+
+  if (!string_vformat(g, SVFMT_TAINT_NOCHK, format, ap))
+    {
+    g->ptr = i;
+    g = string_cat(g, US"**** log string overflowed log buffer ****\n");
+    }
+  }
 va_end(ap);
 
 /* Add the raw, unrewritten, sender to the message if required. This is done
 this way because it kind of fits with LOG_RECIPIENTS. */
 
 if (   flags & LOG_SENDER
-    && ptr < log_buffer + LOG_BUFFER_SIZE - 10 - Ustrlen(raw_sender))
-  ptr += sprintf(CS ptr, " from <%s>", raw_sender);
+   && g->ptr < LOG_BUFFER_SIZE - 10 - Ustrlen(raw_sender))
+  g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " from <%s>", raw_sender);
 
 /* Add list of recipients to the message if required; the raw list,
 before rewriting, was saved in raw_recipients. There may be none, if an ACL
 discarded them all. */
 
 if (  flags & LOG_RECIPIENTS
-   && ptr < log_buffer + LOG_BUFFER_SIZE - 6
+   && g->ptr < LOG_BUFFER_SIZE - 6
    && raw_recipients_count > 0)
   {
   int i;
-  ptr += sprintf(CS ptr, " for");
+  g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " for", NULL);
   for (i = 0; i < raw_recipients_count; i++)
     {
     uschar * s = raw_recipients[i];
-    if (log_buffer + LOG_BUFFER_SIZE - ptr < Ustrlen(s) + 3) break;
-    ptr += sprintf(CS ptr, " %s", s);
+    if (LOG_BUFFER_SIZE - g->ptr < Ustrlen(s) + 3) break;
+    g = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, " %s", s);
     }
   }
 
-ptr += sprintf(CS  ptr, "\n");
-length = ptr - log_buffer;
+g = string_catn(g, US"\n", 1);
+string_from_gstring(g);
 
 /* Handle loggable errors when running a utility, or when address testing.
 Write to log_stderr unless debugging (when it will already have been written),
@@ -972,7 +985,7 @@ if (!f.really_exim || f.log_testing_mode)
     else
       fprintf(log_stderr, "%s", CS log_buffer);
 
-  if ((flags & LOG_PANIC_DIE) == LOG_PANIC_DIE) exim_exit(EXIT_FAILURE, US"");
+  if ((flags & LOG_PANIC_DIE) == LOG_PANIC_DIE) exim_exit(EXIT_FAILURE);
   return;
   }
 
@@ -1028,10 +1041,10 @@ if (  flags & LOG_MAIN
 
     /* Failing to write to the log is disastrous */
 
-    written_len = write_to_fd_buf(mainlogfd, log_buffer, length);
-    if (written_len != length)
+    written_len = write_to_fd_buf(mainlogfd, g->s, g->ptr);
+    if (written_len != g->ptr)
       {
-      log_write_failed(US"main log", length, written_len);
+      log_write_failed(US"main log", g->ptr, written_len);
       /* That function does not return */
       }
     }
@@ -1044,66 +1057,62 @@ headers. */
 
 if (flags & LOG_REJECT)
   {
-  header_line *h;
-
   if (header_list && LOGGING(rejected_header))
     {
+    gstring * g2;
+    int i;
+
     if (recipients_count > 0)
       {
-      int i;
-
       /* List the sender */
 
-      string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer),
-        "Envelope-from: <%s>\n", sender_address);
-      while (*ptr) ptr++;
+      g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK,
+                       "Envelope-from: <%s>\n", sender_address);
+      if (g2) g = g2;
 
       /* List up to 5 recipients */
 
-      string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer),
-        "Envelope-to: <%s>\n", recipients_list[0].address);
-      while (*ptr) ptr++;
+      g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK,
+                       "Envelope-to: <%s>\n", recipients_list[0].address);
+      if (g2) g = g2;
 
       for (i = 1; i < recipients_count && i < 5; i++)
         {
-        string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer), "    <%s>\n",
-          recipients_list[i].address);
-        while (*ptr) ptr++;
+        g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK,
+                       "    <%s>\n", recipients_list[i].address);
+       if (g2) g = g2;
         }
 
       if (i < recipients_count)
         {
-        (void)string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer),
-          "    ...\n");
-        while (*ptr) ptr++;
+        g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK, "    ...\n", NULL);
+       if (g2) g = g2;
         }
       }
 
     /* A header with a NULL text is an unfilled in Received: header */
 
-    for (h = header_list; h; h = h->next) if (h->text)
+    for (header_line * h = header_list; h; h = h->next) if (h->text)
       {
-      BOOL fitted = string_format(ptr, LOG_BUFFER_SIZE - (ptr-log_buffer),
-        "%c %s", h->type, h->text);
-      while(*ptr) ptr++;
-      if (!fitted)         /* Buffer is full; truncate */
+      g2 = string_fmt_append_f(g, SVFMT_TAINT_NOCHK,
+                       "%c %s", h->type, h->text);
+      if (g2)
+       g = g2;
+      else             /* Buffer is full; truncate */
         {
-        ptr -= 100;        /* For message and separator */
-        if (ptr[-1] == '\n') ptr--;
-        Ustrcpy(ptr, "\n*** truncated ***\n");
-        while (*ptr) ptr++;
+        g->ptr -= 100;        /* For message and separator */
+        if (g->s[g->ptr-1] == '\n') g->ptr--;
+        g = string_cat(g, US"\n*** truncated ***\n");
         break;
         }
       }
-
-    length = ptr - log_buffer;
     }
 
   /* Write to syslog or to a log file */
 
   if (  logging_mode & LOG_MODE_SYSLOG
      && (syslog_duplication || !(flags & LOG_PANIC)))
-    write_syslog(LOG_NOTICE, log_buffer);
+    write_syslog(LOG_NOTICE, string_from_gstring(g));
 
   /* Check for a change to the rejectlog file name when datestamping is in
   operation. This happens at midnight, at which point we want to roll over
@@ -1147,10 +1156,10 @@ if (flags & LOG_REJECT)
       if (fstat(rejectlogfd, &statbuf) >= 0) rejectlog_inode = statbuf.st_ino;
       }
 
-    written_len = write_to_fd_buf(rejectlogfd, log_buffer, length);
-    if (written_len != length)
+    written_len = write_to_fd_buf(rejectlogfd, g->s, g->ptr);
+    if (written_len != g->ptr)
       {
-      log_write_failed(US"reject log", length, written_len);
+      log_write_failed(US"reject log", g->ptr, written_len);
       /* That function does not return */
       }
     }
@@ -1165,7 +1174,7 @@ all cases except mua_wrapper, try to write to log_stderr. */
 if (flags & LOG_PANIC)
   {
   if (log_stderr && log_stderr != debug_file && !mua_wrapper)
-    fprintf(log_stderr, "%s", CS log_buffer);
+    fprintf(log_stderr, "%s", CS string_from_gstring(g));
 
   if (logging_mode & LOG_MODE_SYSLOG)
     write_syslog(LOG_ALERT, log_buffer);
@@ -1180,19 +1189,16 @@ if (flags & LOG_PANIC)
     panic_recurseflag = FALSE;
 
     if (panic_save_buffer)
-      {
-      int i = write(paniclogfd, panic_save_buffer, Ustrlen(panic_save_buffer));
-      i = i;   /* compiler quietening */
-      }
+      (void) write(paniclogfd, panic_save_buffer, Ustrlen(panic_save_buffer));
 
-    written_len = write_to_fd_buf(paniclogfd, log_buffer, length);
-    if (written_len != length)
+    written_len = write_to_fd_buf(paniclogfd, g->s, g->ptr);
+    if (written_len != g->ptr)
       {
       int save_errno = errno;
       write_syslog(LOG_CRIT, log_buffer);
       sprintf(CS log_buffer, "write failed on panic log: length=%d result=%d "
-        "errno=%d (%s)", length, (int)written_len, save_errno, strerror(save_errno));
-      write_syslog(LOG_CRIT, log_buffer);
+        "errno=%d (%s)", g->ptr, (int)written_len, save_errno, strerror(save_errno));
+      write_syslog(LOG_CRIT, string_from_gstring(g));
       flags |= LOG_PANIC_DIE;
       }
 
@@ -1291,14 +1297,14 @@ decode_bits(unsigned int *selector, size_t selsize, int *notall,
   uschar *string, bit_table *options, int count, uschar *which, int flags)
 {
 uschar *errmsg;
-if (string == NULL) return;
+if (!string) return;
 
 if (*string == '=')
   {
   char *end;    /* Not uschar */
   memset(selector, 0, sizeof(*selector)*selsize);
   *selector = strtoul(CS string+1, &end, 0);
-  if (*end == 0) return;
+  if (!*end) return;
   errmsg = string_sprintf("malformed numeric %s_selector setting: %s", which,
     string);
   goto ERROR_RETURN;
@@ -1313,8 +1319,8 @@ else for(;;)
   int len;
   bit_table *start, *end;
 
-  while (isspace(*string)) string++;
-  if (*string == 0) return;
+  Uskip_whitespace(&string);
+  if (!*string) return;
 
   if (*string != '+' && *string != '-')
     {
@@ -1336,7 +1342,6 @@ else for(;;)
     bit_table *middle = start + (end - start)/2;
     int c = Ustrncmp(s, middle->name, len);
     if (c == 0)
-      {
       if (middle->name[len] != 0) c = -1; else
         {
         unsigned int bit = middle->bit;
@@ -1358,7 +1363,6 @@ else for(;;)
 
         break;  /* Out of loop to match selector name */
         }
-      }
     if (c < 0) end = middle; else start = middle + 1;
     }  /* Loop to match selector name */