Add perl_taintmode option
[exim.git] / src / src / readconf.c
index 30ff757ac3e9fe9e922d2b79919e07276f46e847..ba4cb668b51d1db832bb8da05238e8b5a7c7eb83 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2015 */
+/* Copyright (c) University of Cambridge 1995 - 2016 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 /* Functions for reading the configuration file, and for displaying
@@ -11,10 +11,13 @@ implementation of the conditional .ifdef etc. */
 
 #include "exim.h"
 
+extern char **environ;
+
 static void fn_smtp_receive_timeout(const uschar * name, const uschar * str);
 static void save_config_line(const uschar* line);
 static void save_config_position(const uschar *file, int line);
-static void print_config(BOOL admin);
+static void print_config(BOOL admin, BOOL terse);
+
 
 #define CSTATE_STACK_SIZE 10
 
@@ -178,6 +181,7 @@ static optionlist optionlist_config[] = {
   { "acl_smtp_starttls",        opt_stringptr,   &acl_smtp_starttls },
 #endif
   { "acl_smtp_vrfy",            opt_stringptr,   &acl_smtp_vrfy },
+  { "add_environment",          opt_stringptr,   &add_environment },
   { "admin_groups",             opt_gidlist,     &admin_groups },
   { "allow_domain_literals",    opt_bool,        &allow_domain_literals },
   { "allow_mx_to_ip",           opt_bool,        &allow_mx_to_ip },
@@ -194,6 +198,7 @@ static optionlist optionlist_config[] = {
   { "bounce_message_file",      opt_stringptr,   &bounce_message_file },
   { "bounce_message_text",      opt_stringptr,   &bounce_message_text },
   { "bounce_return_body",       opt_bool,        &bounce_return_body },
+  { "bounce_return_linesize_limit", opt_mkint,   &bounce_return_linesize_limit },
   { "bounce_return_message",    opt_bool,        &bounce_return_message },
   { "bounce_return_size_limit", opt_mkint,       &bounce_return_size_limit },
   { "bounce_sender_authentication",opt_stringptr,&bounce_sender_authentication },
@@ -251,7 +256,7 @@ static optionlist optionlist_config[] = {
   { "envelope_to_remove",       opt_bool,        &envelope_to_remove },
   { "errors_copy",              opt_stringptr,   &errors_copy },
   { "errors_reply_to",          opt_stringptr,   &errors_reply_to },
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
   { "event_action",             opt_stringptr,   &event_action },
 #endif
   { "exim_group",               opt_gid,         &exim_gid },
@@ -295,6 +300,7 @@ static optionlist optionlist_config[] = {
   { "ignore_bounce_errors_after", opt_time,      &ignore_bounce_errors_after },
   { "ignore_fromline_hosts",    opt_stringptr,   &ignore_fromline_hosts },
   { "ignore_fromline_local",    opt_bool,        &ignore_fromline_local },
+  { "keep_environment",         opt_stringptr,   &keep_environment },
   { "keep_malformed",           opt_time,        &keep_malformed },
 #ifdef LOOKUP_LDAP
   { "ldap_ca_cert_dir",         opt_stringptr,   &eldap_ca_cert_dir },
@@ -343,6 +349,7 @@ static optionlist optionlist_config[] = {
 #ifdef EXIM_PERL
   { "perl_at_start",            opt_bool,        &opt_perl_at_start },
   { "perl_startup",             opt_stringptr,   &opt_perl_startup },
+  { "perl_taintmode",           opt_bool,        &opt_perl_taintmode },
 #endif
 #ifdef LOOKUP_PGSQL
   { "pgsql_servers",            opt_stringptr,   &pgsql_servers },
@@ -375,7 +382,7 @@ static optionlist optionlist_config[] = {
   { "recipient_unqualified_hosts", opt_stringptr, &recipient_unqualified_hosts },
   { "recipients_max",           opt_int,         &recipients_max },
   { "recipients_max_reject",    opt_bool,        &recipients_max_reject },
-#ifdef EXPERIMENTAL_REDIS
+#ifdef LOOKUP_REDIS
   { "redis_servers",            opt_stringptr,   &redis_servers },
 #endif
   { "remote_max_parallel",      opt_int,         &remote_max_parallel },
@@ -2033,6 +2040,7 @@ switch (type)
 
   /*  Integer held in K: again, allow octal and hex formats, and suffixes K and
   M. */
+  /*XXX consider moving to int_eximarith_t (but mind the overflow test 0415) */
 
   case opt_Kint:
     {
@@ -2548,6 +2556,7 @@ second argument is NULL. There are some special values:
   +name              print a named list item
   local_scan         print the local_scan options
   config             print the configuration as it is parsed
+  environment        print the used execution environment
 
 If the second argument is not NULL, it must be one of "router", "transport",
 "authenticator" or "macro" in which case the first argument identifies the
@@ -2641,7 +2650,7 @@ if (type == NULL)
 
   if (Ustrcmp(name, "config") == 0)
     {
-    print_config(admin_user);
+    print_config(admin_user, no_labels);
     return;
     }
 
@@ -2696,6 +2705,24 @@ if (type == NULL)
     names_only = TRUE;
     }
 
+  else if (Ustrcmp(name, "environment") == 0)
+    {
+    if (environ)
+      {
+      uschar ** p;
+      for (p = USS environ; *p; p++) ;
+      qsort(environ, p - USS environ, sizeof(*p), string_compare_by_pointer);
+
+      for (p = USS environ; *p; p++)
+        {
+       uschar * q;
+        if (no_labels && (q = Ustrchr(*p, '='))) *q  = '\0';
+        puts(CS *p);
+        }
+      }
+    return;
+    }
+
   else
     {
     print_ol(find_option(name, optionlist_config, optionlist_config_size),
@@ -2921,7 +2948,7 @@ Returns:  bool for "okay"; false will cause caller to immediately exit.
 
 #ifdef SUPPORT_TLS
 static BOOL
-tls_dropprivs_validate_require_cipher(void)
+tls_dropprivs_validate_require_cipher(BOOL nowarn)
 {
 const uschar *errmsg;
 pid_t pid;
@@ -2935,7 +2962,7 @@ if (  !tls_advertise_hosts
    || Ustrcmp(tls_advertise_hosts, ":") == 0
    )
   return TRUE;
-else if (!tls_certificate)
+else if (!nowarn && !tls_certificate)
   log_write(0, LOG_MAIN|LOG_PANIC,
     "Warning: No server certificate defined; TLS connections will fail.\n"
     " Suggested action: either install a certificate or change tls_advertise_hosts option");
@@ -3009,7 +3036,7 @@ systems. Therefore they are available only when requested by compile-time
 options. */
 
 void
-readconf_main(void)
+readconf_main(BOOL nowarn)
 {
 int sep = 0;
 struct stat statbuf;
@@ -3021,6 +3048,7 @@ const uschar *list = config_main_filelist;
 while((filename = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))
        != NULL)
   {
+
   /* Cut out all the fancy processing unless specifically wanted */
 
   #if defined(CONFIGURE_FILE_USE_NODE) || defined(CONFIGURE_FILE_USE_EUID)
@@ -3074,6 +3102,15 @@ while((filename = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))
   if (config_file != NULL || errno != ENOENT) break;
   }
 
+/* Now, once we found and opened our configuration file, we change the directory
+to a safe place. Later we change to $spool_directory. */
+
+if (Uchdir("/") < 0)
+  {
+  perror("exim: chdir `/': ");
+  exit(EXIT_FAILURE);
+  }
+
 /* On success, save the name for verification; config_filename is used when
 logging configuration errors (it changes for .included files) whereas
 config_main_filename is the name shown by -bP. Failure to open a configuration
@@ -3437,7 +3474,7 @@ if ((tls_verify_hosts != NULL || tls_try_verify_hosts != NULL) &&
 
 /* This also checks that the library linkage is working and we can call
 routines in it, so call even if tls_require_ciphers is unset */
-if (!tls_dropprivs_validate_require_cipher())
+if (!tls_dropprivs_validate_require_cipher(nowarn))
   exit(1);
 
 /* Magic number: at time of writing, 1024 has been the long-standing value
@@ -3461,11 +3498,16 @@ if (openssl_options != NULL)
 # endif
   }
 
-if (gnutls_require_kx || gnutls_require_mac || gnutls_require_proto)
+if (!nowarn && (gnutls_require_kx || gnutls_require_mac || gnutls_require_proto))
   log_write(0, LOG_MAIN, "WARNING: main options"
       " gnutls_require_kx, gnutls_require_mac and gnutls_require_protocols"
       " are obsolete\n");
 #endif /*SUPPORT_TLS*/
+
+if (!nowarn && !keep_environment && environ && *environ)
+  log_write(0, LOG_MAIN,
+      "Warning: purging the environment.\n"
+      " Suggested action: use keep_environment.");
 }
 
 
@@ -3578,9 +3620,9 @@ while ((buffer = get_config_line()) != NULL)
 
   if (isupper(*name) && *s == '=')
     {
-    if (d != NULL)
+    if (d)
       {
-      if (d->driver_name == NULL)
+      if (!d->driver_name)
         log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
           "no driver defined for %s \"%s\"", class, d->name);
       (d->info->init)(d);
@@ -3600,9 +3642,9 @@ while ((buffer = get_config_line()) != NULL)
 
     /* Finish off initializing the previous driver. */
 
-    if (d != NULL)
+    if (d)
       {
-      if (d->driver_name == NULL)
+      if (!d->driver_name)
         log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
           "no driver defined for %s \"%s\"", class, d->name);
       (d->info->init)(d);
@@ -3610,7 +3652,7 @@ while ((buffer = get_config_line()) != NULL)
 
     /* Check that we haven't already got a driver of this name */
 
-    for (d = *anchor; d != NULL; d = d->next)
+    for (d = *anchor; d; d = d->next)
       if (Ustrcmp(name, d->name) == 0)
         log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
           "there are two %ss called \"%s\"", class, name);
@@ -3621,7 +3663,7 @@ while ((buffer = get_config_line()) != NULL)
     d = store_get(instance_size);
     memcpy(d, instance_default, instance_size);
     *p = d;
-    p = &(d->next);
+    p = &d->next;
     d->name = string_copy(name);
 
     /* Clear out the "set" bits in the generic options */
@@ -3639,8 +3681,8 @@ while ((buffer = get_config_line()) != NULL)
   /* Not the start of a new driver. Give an error if we have not set up a
   current driver yet. */
 
-  if (d == NULL) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
-    "%s name missing", class);
+  if (!d)
+    log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "%s name missing", class);
 
   /* First look to see if this is a generic option; if it is "driver",
   initialize the driver. If is it not a generic option, we can look for a
@@ -3649,7 +3691,7 @@ while ((buffer = get_config_line()) != NULL)
   if (readconf_handle_option(buffer, driver_optionlist,
         driver_optionlist_count, d, NULL))
     {
-    if (d->info == NULL && d->driver_name != NULL)
+    if (!d->info && d->driver_name)
       init_driver(d, drivers_available, size_of_info, class);
     }
 
@@ -3657,11 +3699,9 @@ while ((buffer = get_config_line()) != NULL)
   live therein. A flag with each option indicates if it is in the public
   block. */
 
-  else if (d->info != NULL)
-    {
+  else if (d->info)
     readconf_handle_option(buffer, d->info->options,
       *(d->info->options_count), d, US"option \"%s\" unknown");
-    }
 
   /* The option is not generic and the driver name has not yet been given. */
 
@@ -3671,9 +3711,9 @@ while ((buffer = get_config_line()) != NULL)
 
 /* Run the initialization function for the final driver. */
 
-if (d != NULL)
+if (d)
   {
-  if (d->driver_name == NULL)
+  if (!d->driver_name)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
       "no driver defined for %s \"%s\"", class, d->name);
   (d->info->init)(d);
@@ -4032,22 +4072,19 @@ readconf_driver_init(US"authenticator",
   optionlist_auths,                  /* generic options */
   optionlist_auths_size);
 
-for (au = auths; au != NULL; au = au->next)
+for (au = auths; au; au = au->next)
   {
-  if (au->public_name == NULL)
+  if (!au->public_name)
     log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "no public name specified for "
       "the %s authenticator", au->name);
-  for (bu = au->next; bu != NULL; bu = bu->next)
-    {
+
+  for (bu = au->next; bu; bu = bu->next)
     if (strcmpic(au->public_name, bu->public_name) == 0)
-      {
       if ((au->client && bu->client) || (au->server && bu->server))
         log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "two %s authenticators "
           "(%s and %s) have the same public name (%s)",
-          (au->client)? US"client" : US"server", au->name, bu->name,
+          au->client ? US"client" : US"server", au->name, bu->name,
           au->public_name);
-      }
-    }
   }
 }
 
@@ -4267,15 +4304,15 @@ current = next;
 /* List the parsed config lines, care about nice formatting and
 hide the <hide> values unless we're the admin user */
 void
-print_config(BOOL admin)
+print_config(BOOL admin, BOOL terse)
 {
 config_line_item *i;
-const int TS = 2;
+const int TS = terse ? 0 : 2;
 int indent = 0;
 
 for (i = config_lines; i; i = i->next)
   {
-  const uschar *current;
+  uschar *current;
   uschar *p;
 
   /* skip over to the first non-space */
@@ -4285,48 +4322,64 @@ for (i = config_lines; i; i = i->next)
   if (*current == '\0')
     continue;
 
-  /* TODO: Collapse or insert spaces around the first '=' */
+  /* Collapse runs of spaces. We stop this if we encounter one of the
+   * following characters: "'$, as this may indicate careful formatting */
+  for (p = current; *p; ++p)
+    {
+    uschar *next;
+    if (!isspace(*p)) continue;
+    if (*p != ' ') *p = ' ';
+
+    for (next = p; isspace(*next); ++next)
+      ;
+
+    if (next - p > 1)
+      memmove(p+1, next, Ustrlen(next)+1);
+
+    if (*next == '"' || *next == '\'' || *next == '$')
+      break;
+    }
 
   /* # lines */
   if (current[0] == '#')
-    {
-    puts(current);
-    continue;
-    }
+    puts(CCS current);
 
   /* begin lines are left aligned */
-  if (strncmp(current, "begin", 5) == 0 && isspace(current[5]))
+  else if (Ustrncmp(current, "begin", 5) == 0 && isspace(current[5]))
     {
-    puts(current);
+    if (!terse) puts("");
+    puts(CCS current);
     indent = TS;
-    continue;
     }
 
   /* router/acl/transport block names */
-  if (current[strlen(current)-1] == ':' && !strchr(current, '='))
+  else if (current[Ustrlen(current)-1] == ':' && !Ustrchr(current, '='))
     {
+    if (!terse) puts("");
     printf("%*s%s\n", TS, "", current);
     indent = 2 * TS;
-    continue;
     }
 
-  /* hidden lines (MACROS or prefixed with hide) */
-  if (!admin && (isupper(*current)
-    || (strncmp(current, "hide", 4) == 0 && isspace(current[4]))))
+  /* hidden lines (all MACROS or lines prefixed with "hide") */
+  else if (  !admin
+         && (  isupper(*current)
+            || Ustrncmp(current, "hide", 4) == 0 && isspace(current[4])
+            )
+         )
     {
-    if (p = strchr(current, '='))
+    if ((p = Ustrchr(current, '=')))
       {
       *p = '\0';
-      printf("%*s%s = %s\n", indent, "", current, hidden);
+      printf("%*s%s= %s\n", indent, "", current, hidden);
       }
     /* e.g.: hide split_spool_directory */
-    else printf("%*s\n", indent, hidden);
-    continue;
+    else
+      printf("%*s\n", indent, hidden);
     }
 
-  /* rest is public */
-  printf("%*s%s\n", indent, "", current);
-  continue;
+  else
+    /* rest is public */
+    printf("%*s%s\n", indent, "", current);
   }
 }