SECURITY: length limits on many cmdline options
[exim.git] / src / src / readconf.c
index 04b3e1161966e3c734a67842b199d28807ad5fe3..8a7b710a271c675a966919ece8bd6183f566634d 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 reading the configuration file, and for displaying
@@ -119,6 +120,7 @@ static optionlist optionlist_config[] = {
 #ifndef DISABLE_DKIM
   { "dkim_verify_hashes",       opt_stringptr,   {&dkim_verify_hashes} },
   { "dkim_verify_keytypes",     opt_stringptr,   {&dkim_verify_keytypes} },
+  { "dkim_verify_min_keysizes", opt_stringptr,   {&dkim_verify_min_keysizes} },
   { "dkim_verify_minimal",      opt_bool,        {&dkim_verify_minimal} },
   { "dkim_verify_signers",      opt_stringptr,   {&dkim_verify_signers} },
 #endif
@@ -198,6 +200,9 @@ static optionlist optionlist_config[] = {
   { "ldap_require_cert",        opt_stringptr,   {&eldap_require_cert} },
   { "ldap_start_tls",           opt_bool,        {&eldap_start_tls} },
   { "ldap_version",             opt_int,         {&eldap_version} },
+#endif
+#ifdef EXPERIMENTAL_ESMTP_LIMITS
+  { "limits_advertise_hosts", opt_stringptr, {&limits_advertise_hosts} },
 #endif
   { "local_from_check",         opt_bool,        {&local_from_check} },
   { "local_from_prefix",        opt_stringptr,   {&local_from_prefix} },
@@ -257,10 +262,13 @@ 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 SUPPORT_PROXY
+  { "proxy_protocol_timeout",   opt_time,        {&proxy_protocol_timeout} },
+#endif
   { "qualify_domain",           opt_stringptr,   {&qualify_domain_sender} },
   { "qualify_recipient",        opt_stringptr,   {&qualify_domain_recipient} },
   { "queue_domains",            opt_stringptr,   {&queue_domains} },
-#ifdef EXPERIMENTAL_QUEUE_RAMP
+#ifndef DISABLE_QUEUE_RAMP
   { "queue_fast_ramp",          opt_bool,        {&queue_fast_ramp} },
 #endif
   { "queue_list_requires_admin",opt_bool,        {&queue_list_requires_admin} },
@@ -295,7 +303,7 @@ static optionlist optionlist_config[] = {
   { "smtp_accept_max",          opt_int,         {&smtp_accept_max} },
   { "smtp_accept_max_nonmail",  opt_int,         {&smtp_accept_max_nonmail} },
   { "smtp_accept_max_nonmail_hosts", opt_stringptr, {&smtp_accept_max_nonmail_hosts} },
-  { "smtp_accept_max_per_connection", opt_int,   {&smtp_accept_max_per_connection} },
+  { "smtp_accept_max_per_connection", opt_stringptr,   {&smtp_accept_max_per_connection} },
   { "smtp_accept_max_per_host", opt_stringptr,   {&smtp_accept_max_per_host} },
   { "smtp_accept_queue",        opt_int,         {&smtp_accept_queue} },
   { "smtp_accept_queue_per_connection", opt_int, {&smtp_accept_queue_per_connection} },
@@ -324,6 +332,7 @@ static optionlist optionlist_config[] = {
 #endif
 #ifdef SUPPORT_SPF
   { "spf_guess",                opt_stringptr,   {&spf_guess} },
+  { "spf_smtp_comment_template",opt_stringptr,   {&spf_smtp_comment_template} },
 #endif
   { "split_spool_directory",    opt_bool,        {&split_spool_directory} },
   { "spool_directory",          opt_stringptr,   {&spool_directory} },
@@ -332,7 +341,7 @@ static optionlist optionlist_config[] = {
   { "sqlite_dbfile",            opt_stringptr,   {&sqlite_dbfile} },
   { "sqlite_lock_timeout",      opt_int,         {&sqlite_lock_timeout} },
 #endif
-#ifdef EXPERIMENTAL_SRS
+#ifdef EXPERIMENTAL_SRS_ALT
   { "srs_config",               opt_stringptr,   {&srs_config} },
   { "srs_hashlength",           opt_int,         {&srs_hashlength} },
   { "srs_hashmin",              opt_int,         {&srs_hashmin} },
@@ -376,7 +385,7 @@ static optionlist optionlist_config[] = {
   { "tls_privatekey",           opt_stringptr,   {&tls_privatekey} },
   { "tls_remember_esmtp",       opt_bool,        {&tls_remember_esmtp} },
   { "tls_require_ciphers",      opt_stringptr,   {&tls_require_ciphers} },
-# ifdef EXPERIMENTAL_TLS_RESUME
+# ifndef DISABLE_TLS_RESUME
   { "tls_resumption_hosts",     opt_stringptr,   {&tls_resumption_hosts} },
 # endif
   { "tls_try_verify_hosts",     opt_stringptr,   {&tls_try_verify_hosts} },
@@ -413,7 +422,7 @@ options_from_list(optionlist_config, nelem(optionlist_config), US"MAIN", NULL);
 void
 options_auths(void)
 {
-uschar buf[64];
+uschar buf[EXIM_DRIVERNAME_MAX];
 
 options_from_list(optionlist_auths, optionlist_auths_size, US"AUTHENTICATORS", NULL);
 
@@ -430,7 +439,7 @@ for (struct auth_info * ai = auths_available; ai->driver_name[0]; ai++)
 void
 options_logging(void)
 {
-uschar buf[64];
+uschar buf[EXIM_DRIVERNAME_MAX];
 
 for (bit_table * bp = log_options; bp < log_options + log_options_count; bp++)
   {
@@ -675,7 +684,7 @@ Returns:       FALSE iff fatal error
 BOOL
 macro_read_assignment(uschar *s)
 {
-uschar name[64];
+uschar name[EXIM_DRIVERNAME_MAX];
 int namelen = 0;
 BOOL redef = FALSE;
 macro_item *m;
@@ -1169,15 +1178,25 @@ uschar *
 readconf_readname(uschar *name, int len, uschar *s)
 {
 int p = 0;
+BOOL broken = FALSE;
 
 if (isalpha(Uskip_whitespace(&s)))
   while (isalnum(*s) || *s == '_')
     {
     if (p < len-1) name[p++] = *s;
+    else {
+      broken = TRUE;
+      break;
+    }
     s++;
     }
 
 name[p] = 0;
+if (broken) {
+  log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+            "exim item name too long (>%d), unable to use \"%s\" (truncated)",
+            len, name);
+}
 Uskip_whitespace(&s);
 return s;
 }
@@ -1344,7 +1363,7 @@ static BOOL *
 get_set_flag(uschar *name, optionlist *oltop, int last, void *data_block)
 {
 optionlist *ol;
-uschar name2[64];
+uschar name2[EXIM_DRIVERNAME_MAX];
 sprintf(CS name2, "*set_%.50s", name);
 if (!(ol = find_option(name2, oltop, last)))
   log_write(0, LOG_MAIN|LOG_PANIC_DIE,
@@ -1543,7 +1562,7 @@ if (flags & opt_fn_print)
   {
   if (flags & opt_fn_print_label) printf("%s = ", name);
   printf("%s\n", smtp_receive_timeout_s
-    ? string_printing2(smtp_receive_timeout_s, FALSE)
+    ? string_printing2(smtp_receive_timeout_s, SP_TAB)
     : readconf_printtime(smtp_receive_timeout));
   }
 else if (*str == '$')
@@ -1617,8 +1636,8 @@ uschar *inttype = US"";
 uschar *sptr;
 uschar *s = buffer;
 uschar **str_target;
-uschar name[64];
-uschar name2[64];
+uschar name[EXIM_DRIVERNAME_MAX];
+uschar name2[EXIM_DRIVERNAME_MAX];
 
 /* There may be leading spaces; thereafter, we expect an option name starting
 with a letter. */
@@ -1842,6 +1861,7 @@ switch (type)
         flagptr = (int *)ol3->v.value;
         }
 
+      /* This will trap if sptr is tainted. Not sure if that can happen */
       while ((p = string_nextinlist(CUSS &sptr, &sep, big_buffer, BIG_BUFFER_SIZE)))
         {
         rewrite_rule *next = readconf_one_rewrite(p, flagptr, FALSE);
@@ -1986,6 +2006,7 @@ switch (type)
       while (count-- > 1)
         {
         int sep = 0;
+       /* If p is tainted we trap.  Not sure that can happen */
         (void)string_nextinlist(&p, &sep, big_buffer, BIG_BUFFER_SIZE);
         if (!route_finduser(big_buffer, NULL, &uid))
           log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "user %s was not found",
@@ -2027,6 +2048,7 @@ switch (type)
       while (count-- > 1)
         {
         int sep = 0;
+       /* If p is tainted we trap.  Not sure that can happen */
         (void)string_nextinlist(&p, &sep, big_buffer, BIG_BUFFER_SIZE);
         if (!route_findgroup(big_buffer, &gid))
           log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "group %s was not found",
@@ -2077,7 +2099,7 @@ switch (type)
   case opt_bool_set:
   if (*s != 0)
     {
-    s = readconf_readname(name2, 64, s);
+    s = readconf_readname(name2, EXIM_DRIVERNAME_MAX, s);
     if (strcmpic(name2, US"true") == 0 || strcmpic(name2, US"yes") == 0)
       boolvalue = TRUE;
     else if (strcmpic(name2, US"false") == 0 || strcmpic(name2, US"no") == 0)
@@ -2424,7 +2446,7 @@ void *value;
 uid_t *uidlist;
 gid_t *gidlist;
 uschar *s;
-uschar name2[64];
+uschar name2[EXIM_DRIVERNAME_MAX];
 
 if (!ol)
   {
@@ -2460,7 +2482,7 @@ switch(ol->type & opt_mask)
   case opt_rewrite:        /* Show the text value */
     s = *(USS value);
     if (!no_labels) printf("%s = ", name);
-    printf("%s\n", s ? string_printing2(s, FALSE) : US"");
+    printf("%s\n", s ? string_printing2(s, SP_TAB) : US"");
     break;
 
   case opt_int:
@@ -3109,6 +3131,7 @@ const uschar *list = config_main_filelist;
 
 /* Loop through the possible file names */
 
+/* Should never be a tainted list */
 while((filename = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
   {
 
@@ -3211,12 +3234,12 @@ if (config_file)
   }
 else
   {
-  if (filename == NULL)
+  if (!filename)
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "non-existent configuration file(s): "
       "%s", config_main_filelist);
   else
-    log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s", string_open_failed(errno,
-      "configuration file %s", filename));
+    log_write(0, LOG_MAIN|LOG_PANIC_DIE, "%s",
+      string_open_failed("configuration file %s", filename));
   }
 
 /* Now, once we found and opened our configuration file, we change the directory
@@ -3340,10 +3363,11 @@ but if that yields an unqualified value, make a FQDN by using gethostbyname to
 canonize it. Some people like upper case letters in their host names, so we
 don't force the case. */
 
-if (primary_hostname == NULL)
+if (!primary_hostname)
   {
-  const uschar *hostname;
+  const uschar * hostname;
   struct utsname uts;
+
   if (uname(&uts) < 0)
     log_write(0, LOG_MAIN|LOG_PANIC_DIE, "uname() failed to yield host name");
   hostname = US uts.nodename;
@@ -3353,33 +3377,29 @@ if (primary_hostname == NULL)
     int af = AF_INET;
     struct hostent *hostdata;
 
-    #if HAVE_IPV6
-    if (!disable_ipv6 && (dns_ipv4_lookup == NULL ||
-         match_isinlist(hostname, CUSS &dns_ipv4_lookup, 0, NULL, NULL,
+#if HAVE_IPV6
+    if (  !disable_ipv6
+       && (  !dns_ipv4_lookup
+         || match_isinlist(hostname, CUSS &dns_ipv4_lookup, 0, NULL, NULL,
            MCL_DOMAIN, TRUE, NULL) != OK))
       af = AF_INET6;
-    #else
-    af = AF_INET;
-    #endif
+#endif
 
     for (;;)
       {
-      #if HAVE_IPV6
-        #if HAVE_GETIPNODEBYNAME
+#if HAVE_IPV6
+if HAVE_GETIPNODEBYNAME
         int error_num;
         hostdata = getipnodebyname(CS hostname, af, 0, &error_num);
         #else
         hostdata = gethostbyname2(CS hostname, af);
-        #endif
-      #else
+endif
+#else
       hostdata = gethostbyname(CS hostname);
-      #endif
+#endif
 
-      if (hostdata != NULL)
-        {
-        hostname = US hostdata->h_name;
-        break;
-        }
+      if (hostdata)
+        { hostname = US hostdata->h_name; break; }
 
       if (af == AF_INET) break;
       af = AF_INET;
@@ -3422,6 +3442,7 @@ if (*log_file_path)
       "\"%s\": %s", log_file_path, expand_string_message);
 
   ss = s;
+  /* should never be a tainted list */
   while ((sss = string_nextinlist(&ss, &sep, big_buffer, big_buffer_size)))
     {
     uschar *t;
@@ -3695,7 +3716,7 @@ uschar *buffer;
 
 while ((buffer = get_config_line()))
   {
-  uschar name[64];
+  uschar name[EXIM_DRIVERNAME_MAX];
   uschar *s;
 
   /* Read the first name on the line and test for the start of a new driver. A
@@ -4223,7 +4244,7 @@ acl_line = get_config_line();
 
 while(acl_line)
   {
-  uschar name[64];
+  uschar name[EXIM_DRIVERNAME_MAX];
   tree_node *node;
   uschar *error;