SPDX: Mass-update to GPL-2.0-or-later
[exim.git] / src / src / log.c
index 7a73b154a303ebf0884cf659885314983a7cefbc..6c483216ad61d79989e48351adc5342e69b68811 100644 (file)
@@ -2,9 +2,10 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
+/* Copyright (c) The Exim Maintainers 2020 - 2022 */
 /* Copyright (c) University of Cambridge 1995 - 2018 */
-/* Copyright (c) The Exim Maintainers 2020 - 2021 */
 /* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
 
 /* Functions for writing log files. The code for maintaining datestamped
 log files was originally contributed by Tony Sheen. */
@@ -12,7 +13,6 @@ log files was originally contributed by Tony Sheen. */
 
 #include "exim.h"
 
-#define LOG_NAME_SIZE 256
 #define MAX_SYSLOG_LEN 870
 
 #define LOG_MODE_FILE   1
@@ -30,7 +30,6 @@ static uschar *log_names[] = { US"main", US"reject", US"panic", US"debug" };
 
 static uschar mainlog_name[LOG_NAME_SIZE];
 static uschar rejectlog_name[LOG_NAME_SIZE];
-static uschar debuglog_name[LOG_NAME_SIZE];
 
 static uschar *mainlog_datestamp = NULL;
 static uschar *rejectlog_datestamp = NULL;
@@ -55,66 +54,68 @@ static size_t pid_position[2];
 number definitions in macros.h */
 
 static const uschar * exim_errstrings[] = {
-  US"",
-  US"unknown error",
-  US"user slash",
-  US"exist race",
-  US"not regular",
-  US"not directory",
-  US"bad ugid",
-  US"bad mode",
-  US"inode changed",
-  US"lock failed",
-  US"bad address2",
-  US"forbid pipe",
-  US"forbid file",
-  US"forbid reply",
-  US"missing pipe",
-  US"missing file",
-  US"missing reply",
-  US"bad redirect",
-  US"smtp closed",
-  US"smtp format",
-  US"spool format",
-  US"not absolute",
-  US"Exim-imposed quota",
-  US"held",
-  US"Delivery filter process failure",
-  US"Delivery add/remove header failure",
-  US"Delivery write incomplete error",
-  US"Some expansion failed",
-  US"Failed to get gid",
-  US"Failed to get uid",
-  US"Unset or non-existent transport",
-  US"MBX length mismatch",
-  US"Lookup failed routing or in smtp tpt",
-  US"Can't match format in appendfile",
-  US"Creation outside home in appendfile",
-  US"Can't check a list; lookup defer",
-  US"DNS lookup defer",
-  US"Failed to start TLS session",
-  US"Mandatory TLS session not started",
-  US"Failed to chown a file",
-  US"Failed to create a pipe",
-  US"When verifying",
-  US"When required by client",
-  US"Used internally in smtp transport",
-  US"RCPT gave 4xx error",
-  US"MAIL gave 4xx error",
-  US"DATA gave 4xx error",
-  US"Negotiation failed for proxy configured host",
-  US"Authenticator 'other' failure",
-  US"target not supporting SMTPUTF8",
-  US"host is local",
-  US"tainted filename",
-
-  US"Not time for routing",
-  US"Not time for local delivery",
-  US"Not time for any remote host",
-  US"Local-only delivery",
-  US"Domain in queue_domains",
-  US"Transport concurrency limit",
-  US"Event requests alternate response",
+  [0] = US"",
+  [- ERRNO_UNKNOWNERROR] =     US"unknown error",
+  [- ERRNO_USERSLASH] =                US"user slash",
+  [- ERRNO_EXISTRACE] =                US"exist race",
+  [- ERRNO_NOTREGULAR] =       US"not regular",
+  [- ERRNO_NOTDIRECTORY] =     US"not directory",
+  [- ERRNO_BADUGID] =          US"bad ugid",
+  [- ERRNO_BADMODE] =          US"bad mode",
+  [- ERRNO_INODECHANGED] =     US"inode changed",
+  [- ERRNO_LOCKFAILED] =       US"lock failed",
+  [- ERRNO_BADADDRESS2] =      US"bad address2",
+  [- ERRNO_FORBIDPIPE] =       US"forbid pipe",
+  [- ERRNO_FORBIDFILE] =       US"forbid file",
+  [- ERRNO_FORBIDREPLY] =      US"forbid reply",
+  [- ERRNO_MISSINGPIPE] =      US"missing pipe",
+  [- ERRNO_MISSINGFILE] =      US"missing file",
+  [- ERRNO_MISSINGREPLY] =     US"missing reply",
+  [- ERRNO_BADREDIRECT] =      US"bad redirect",
+  [- ERRNO_SMTPCLOSED] =       US"smtp closed",
+  [- ERRNO_SMTPFORMAT] =       US"smtp format",
+  [- ERRNO_SPOOLFORMAT] =      US"spool format",
+  [- ERRNO_NOTABSOLUTE] =      US"not absolute",
+  [- ERRNO_EXIMQUOTA] =                US"Exim-imposed quota",
+  [- ERRNO_HELD] =             US"held",
+  [- ERRNO_FILTER_FAIL] =      US"Delivery filter process failure",
+  [- ERRNO_CHHEADER_FAIL] =    US"Delivery add/remove header failure",
+  [- ERRNO_WRITEINCOMPLETE] =  US"Delivery write incomplete error",
+  [- ERRNO_EXPANDFAIL] =       US"Some expansion failed",
+  [- ERRNO_GIDFAIL] =          US"Failed to get gid",
+  [- ERRNO_UIDFAIL] =          US"Failed to get uid",
+  [- ERRNO_BADTRANSPORT] =     US"Unset or non-existent transport",
+  [- ERRNO_MBXLENGTH] =                US"MBX length mismatch",
+  [- ERRNO_UNKNOWNHOST] =      US"Lookup failed routing or in smtp tpt",
+  [- ERRNO_FORMATUNKNOWN] =    US"Can't match format in appendfile",
+  [- ERRNO_BADCREATE] =                US"Creation outside home in appendfile",
+  [- ERRNO_LISTDEFER] =                US"Can't check a list; lookup defer",
+  [- ERRNO_DNSDEFER] =         US"DNS lookup defer",
+  [- ERRNO_TLSFAILURE] =       US"Failed to start TLS session",
+  [- ERRNO_TLSREQUIRED] =      US"Mandatory TLS session not started",
+  [- ERRNO_CHOWNFAIL] =                US"Failed to chown a file",
+  [- ERRNO_PIPEFAIL] =         US"Failed to create a pipe",
+  [- ERRNO_CALLOUTDEFER] =     US"When verifying",
+  [- ERRNO_AUTHFAIL] =         US"When required by client",
+  [- ERRNO_CONNECTTIMEOUT] =   US"Used internally in smtp transport",
+  [- ERRNO_RCPT4XX] =          US"RCPT gave 4xx error",
+  [- ERRNO_MAIL4XX] =          US"MAIL gave 4xx error",
+  [- ERRNO_DATA4XX] =          US"DATA gave 4xx error",
+  [- ERRNO_PROXYFAIL] =                US"Negotiation failed for proxy configured host",
+  [- ERRNO_AUTHPROB] =         US"Authenticator 'other' failure",
+  [- ERRNO_UTF8_FWD] =         US"target not supporting SMTPUTF8",
+  [- ERRNO_HOST_IS_LOCAL] =    US"host is local",
+  [- ERRNO_TAINT] =            US"tainted filename",
+
+  [- ERRNO_RRETRY] =           US"Not time for routing",
+
+  [- ERRNO_LRETRY] =           US"Not time for local delivery",
+  [- ERRNO_HRETRY] =           US"Not time for any remote host",
+  [- ERRNO_LOCAL_ONLY] =       US"Local-only delivery",
+  [- ERRNO_QUEUE_DOMAIN] =     US"Domain in queue_domains",
+  [- ERRNO_TRETRY] =           US"Transport concurrency limit",
+
+  [- ERRNO_EVENT] =            US"Event requests alternate response",
 };
 
 
@@ -266,7 +267,7 @@ Returns:       a file descriptor, or < 0 on failure (errno set)
 */
 
 static int
-log_open_already_exim(uschar * const name)
+log_open_already_exim(const uschar * const name)
 {
 int fd = -1;
 const int flags = O_WRONLY | O_APPEND | O_CREAT | O_NONBLOCK;
@@ -288,8 +289,11 @@ if (fd < 0 && errno == ENOENT)
   uschar *lastslash = Ustrrchr(name, '/');
   *lastslash = 0;
   created = directory_make(NULL, name, LOG_DIRECTORY_MODE, FALSE);
-  DEBUG(D_any) debug_printf("%s log directory %s\n",
-    created ? "created" : "failed to create", name);
+  DEBUG(D_any)
+    if (created)
+      debug_printf("created log directory %s\n", name);
+    else
+      debug_printf("failed to create log directory %s: %s\n", name, strerror(errno));
   *lastslash = '/';
   if (created) fd = Uopen(name, flags, LOG_MODE);
   }
@@ -387,7 +391,7 @@ Returns:       a file descriptor, or < 0 on failure (errno set)
 */
 
 int
-log_open_as_exim(uschar * const name)
+log_open_as_exim(const uschar * const name)
 {
 int fd = -1;
 const uid_t euid = geteuid();
@@ -452,7 +456,7 @@ return fd;
 it does not exist. This may be called recursively on failure, in order to open
 the panic log.
 
-The directory is in the static variable file_path. This is static so that it
+The directory is in the static variable file_path. This is static so that
 the work of sorting out the path is done just once per Exim process.
 
 Exim is normally configured to avoid running as root wherever possible, the log
@@ -470,7 +474,7 @@ Returns:   nothing
 */
 
 static void
-open_log(int *fd, int type, uschar *tag)
+open_log(int * fd, int type, const uschar * tag)
 {
 uid_t euid;
 BOOL ok, ok2;
@@ -512,6 +516,10 @@ switch (type)
     Ustrcpy(debuglog_name, buffer);
     if (tag)
       {
+      if (is_tainted(tag))
+       die(US"exim: tainted tag for debug log filename",
+             US"Logging failure; please try later");
+
       /* this won't change the offset of the datestamp */
       ok2 = string_format(buffer, sizeof(buffer), "%s%s",
         debuglog_name, tag);
@@ -522,8 +530,8 @@ switch (type)
 
   default:
     /* Remove any datestamp if this is the panic log. This is rare, so there's no
-  need to optimize getting the datestamp length. We remove one non-alphanumeric
-  char afterwards if at the start, otherwise one before. */
+    need to optimize getting the datestamp length. We remove one non-alphanumeric
+    char afterwards if at the start, otherwise one before. */
     if (string_datestamp_offset >= 0)
       {
       uschar * from = buffer + string_datestamp_offset;
@@ -720,10 +728,17 @@ while ((t = string_nextinlist(&tt, &sep, log_buffer, LOG_BUFFER_SIZE)))
 }
 
 
+/* Close mainlog, unless we do not see a chance to open the file mainlog later
+again.  This will happen if we log from a transport process (which has dropped
+privs); something we traditionally avoid, but the introduction of taint-tracking
+and resulting detection of errors is makinng harder. */
+
 void
 mainlog_close(void)
 {
-if (mainlogfd < 0) return;
+if (mainlogfd < 0
+   || !(geteuid() == 0 || geteuid() == exim_uid))
+  return;
 (void)close(mainlogfd);
 mainlogfd = -1;
 mainlog_inode = 0;
@@ -1264,7 +1279,10 @@ if (flags & LOG_PANIC)
   /* Give up if the DIE flag is set */
 
   if ((flags & LOG_PANIC_DIE) != LOG_PANIC)
-    die(NULL, US"Unexpected failure, please try later");
+    if (panic_coredump)
+      kill(getpid(), SIGSEGV); /* deliberate trap */
+    else
+      die(NULL, US"Unexpected failure, please try later");
   }
 }
 
@@ -1349,8 +1367,9 @@ Returns:         nothing on success - bomb out on failure
 */
 
 void
-decode_bits(unsigned int *selector, size_t selsize, int *notall,
-  uschar *string, bit_table *options, int count, uschar *which, int flags)
+decode_bits(unsigned int * selector, size_t selsize, int * notall,
+  const uschar * string, bit_table * options, int count, uschar * which,
+  int flags)
 {
 uschar *errmsg;
 if (!string) return;
@@ -1359,7 +1378,7 @@ if (*string == '=')
   {
   char *end;    /* Not uschar */
   memset(selector, 0, sizeof(*selector)*selsize);
-  *selector = strtoul(CS string+1, &end, 0);
+  *selector = strtoul(CCS string+1, &end, 0);
   if (!*end) return;
   errmsg = string_sprintf("malformed numeric %s_selector setting: %s", which,
     string);
@@ -1371,9 +1390,9 @@ if (*string == '=')
 else for(;;)
   {
   BOOL adding;
-  uschar *s;
+  const uschar * s;
   int len;
-  bit_table *start, *end;
+  bit_table * start, * end;
 
   Uskip_whitespace(&string);
   if (!*string) return;
@@ -1469,7 +1488,7 @@ immediately but only upon a trigger - but we'd need another cmdline option
 to pass the name through child_exxec_exim(). */
 
 void
-debug_logging_activate(uschar *tag_name, uschar *opts)
+debug_logging_activate(const uschar * tag_name, const uschar * opts)
 {
 if (debug_file)
   {
@@ -1505,6 +1524,23 @@ else
 }
 
 
+void
+debug_logging_from_spool(const uschar * filename)
+{
+if (debug_fd < 0)
+  {
+  Ustrncpy(debuglog_name, filename, sizeof(debuglog_name));
+  if ((debug_fd = log_open_as_exim(filename)) >= 0)
+    debug_file = fdopen(debug_fd, "w");
+  DEBUG(D_deliver) debug_printf("debug enabled by spoolfile\n");
+  }
+/*
+else DEBUG(D_deliver)
+  debug_printf("debug already active; ignoring spoolfile '%s'\n", filename);
+*/
+}
+
+
 void
 debug_logging_stop(BOOL kill)
 {