Fix CVE-2016-1531
[exim.git] / src / src / readconf.c
index 10ebc5e17f89aa40ffb7162788a0b89aab1f4078..cf5f069e90b418a65a776002b8547e4d5d1ca8a2 100644 (file)
@@ -178,6 +178,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 +195,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 +253,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 },
@@ -285,6 +287,9 @@ static optionlist optionlist_config[] = {
   { "host_lookup_order",        opt_stringptr,   &host_lookup_order },
   { "host_reject_connection",   opt_stringptr,   &host_reject_connection },
   { "hosts_connection_nolog",   opt_stringptr,   &hosts_connection_nolog },
+#ifdef SUPPORT_PROXY
+  { "hosts_proxy",              opt_stringptr,   &hosts_proxy },
+#endif
   { "hosts_treat_as_local",     opt_stringptr,   &hosts_treat_as_local },
 #ifdef LOOKUP_IBASE
   { "ibase_servers",            opt_stringptr,   &ibase_servers },
@@ -292,6 +297,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 },
@@ -354,9 +360,6 @@ static optionlist optionlist_config[] = {
   { "print_topbitchars",        opt_bool,        &print_topbitchars },
   { "process_log_path",         opt_stringptr,   &process_log_path },
   { "prod_requires_admin",      opt_bool,        &prod_requires_admin },
-#ifdef EXPERIMENTAL_PROXY
-  { "proxy_required_hosts",     opt_stringptr,   &proxy_required_hosts },
-#endif
   { "qualify_domain",           opt_stringptr,   &qualify_domain_sender },
   { "qualify_recipient",        opt_stringptr,   &qualify_domain_recipient },
   { "queue_domains",            opt_stringptr,   &queue_domains },
@@ -375,7 +378,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 },
@@ -413,7 +416,7 @@ static optionlist optionlist_config[] = {
   { "smtp_receive_timeout",     opt_func,        &fn_smtp_receive_timeout },
   { "smtp_reserve_hosts",       opt_stringptr,   &smtp_reserve_hosts },
   { "smtp_return_error_details",opt_bool,        &smtp_return_error_details },
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
   { "smtputf8_advertise_hosts", opt_stringptr,   &smtputf8_advertise_hosts },
 #endif
 #ifdef WITH_CONTENT_SCAN
@@ -2534,7 +2537,9 @@ causes the value of any main configuration variable to be output if the
 second argument is NULL. There are some special values:
 
   all                print all main configuration options
-  configure_file     print the name of the configuration file
+  config_file        print the name of the configuration file
+                     (configure_file will still work, for backward
+                     compatibility)
   routers            print the routers' configurations
   transports         print the transports' configuration
   authenticators     print the authenticators' configuration
@@ -2546,6 +2551,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
@@ -2602,7 +2608,8 @@ if (type == NULL)
     return;
     }
 
-  if (Ustrcmp(name, "configure_file") == 0)
+  if ( Ustrcmp(name, "configure_file") == 0
+     ||Ustrcmp(name, "config_file") == 0)
     {
     printf("%s\n", CS config_main_filename);
     return;
@@ -2693,6 +2700,25 @@ if (type == NULL)
     names_only = TRUE;
     }
 
+  else if (Ustrcmp(name, "environment") == 0)
+    {
+    if (environ)
+      {
+      uschar **p;
+      size_t n;
+      for (p = USS environ; *p; p++) ;
+      n = p - USS environ;
+      qsort(environ, p - USS environ, sizeof(*p), (__compar_fn_t) string_compare_by_pointer);
+
+      for (p = USS environ; *p; p++)
+        {
+        if (no_labels) *(Ustrchr(*p, '=')) = '\0';
+        puts(*p);
+        }
+      }
+    return;
+    }
+
   else
     {
     print_ol(find_option(name, optionlist_config, optionlist_config_size),
@@ -3018,6 +3044,15 @@ const uschar *list = config_main_filelist;
 while((filename = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))
        != NULL)
   {
+
+  /* To avoid confusion: Exim changes to / at the very beginning and
+   * and to $spool_directory later. */
+  if (filename[0] != '/')
+    {
+    fprintf(stderr, "-C %s: only absolute names are allowed\n", filename);
+    exit(EXIT_FAILURE);
+  }
+
   /* Cut out all the fancy processing unless specifically wanted */
 
   #if defined(CONFIGURE_FILE_USE_NODE) || defined(CONFIGURE_FILE_USE_EUID)
@@ -3463,6 +3498,11 @@ if (gnutls_require_kx || gnutls_require_mac || gnutls_require_proto)
       " gnutls_require_kx, gnutls_require_mac and gnutls_require_protocols"
       " are obsolete\n");
 #endif /*SUPPORT_TLS*/
+
+if ((!add_environment || *add_environment == '\0') && !keep_environment)
+  log_write(0, LOG_MAIN,
+      "WARNING: purging the environment.\n"
+      " Suggested action: use keep_environment and add_environment.\n");
 }
 
 
@@ -4243,7 +4283,8 @@ save_config_position(const uschar *file, int line)
 
 /* Append a pre-parsed logical line to the config lines store,
 this operates on a global (static) list that holds all the pre-parsed
-config lines */
+config lines, we do no further processing here, output formatting and
+honouring of <hide> or macros will be done during output */
 static void
 save_config_line(const uschar* line)
 {
@@ -4271,7 +4312,7 @@ 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 */
@@ -4281,45 +4322,63 @@ 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);
+    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, '='))
     {
-    printf("%*s%s\n", TS, "", current);
+    printf("\n%*s%s\n", TS, "", current);
     indent = 2 * TS;
-    continue;
     }
 
-  /* as admin we don't care, as we do for "public" lines */
-  if (admin || (!isupper(*current) && (strcmp(current, "hide") != 0)))
+  /* hidden lines (all MACROS or lines prefixed with "hide") */
+  else if (  !admin
+         && (  isupper(*current)
+            || Ustrncmp(current, "hide", 4) == 0 && isspace(current[4])
+            )
+         )
     {
-    printf("%*s%s\n", indent, "", current);
-    continue;
+    if ((p = Ustrchr(current, '=')))
+      {
+      *p = '\0';
+      printf("%*s%s= %s\n", indent, "", current, hidden);
+      }
+    /* e.g.: hide split_spool_directory */
+    else
+      printf("%*s\n", indent, hidden);
     }
 
-  /* hidden lines */
-  if (p = strchr(current, '='))
-    {
-    *p = '\0';
-    printf("%*s%s = %s\n", indent, "", current, hidden);
-    continue;
-    }
+  else
+    /* rest is public */
+    printf("%*s%s\n", indent, "", current);
   }
 }