CVE-2020-28012: Missing close-on-exec flag for privileged pipe
[exim.git] / src / src / readconf.c
index a506d9f1da0504af34642a7d7c7aed4b310b7c7b..f962f9029e1e926768b6754244d74896bae2527c 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
@@ -324,11 +326,13 @@ 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} },
   { "spool_wireformat",         opt_bool,        {&spool_wireformat} },
 #ifdef LOOKUP_SQLITE
+  { "sqlite_dbfile",            opt_stringptr,   {&sqlite_dbfile} },
   { "sqlite_lock_timeout",      opt_int,         {&sqlite_lock_timeout} },
 #endif
 #ifdef EXPERIMENTAL_SRS
@@ -412,7 +416,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);
 
@@ -429,7 +433,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++)
   {
@@ -674,7 +678,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;
@@ -691,7 +695,7 @@ while (isalnum(*s) || *s == '_')
   }
 name[namelen] = 0;
 
-while (isspace(*s)) s++;
+Uskip_whitespace(&s);
 if (*s++ != '=')
   {
   log_write(0, LOG_PANIC|LOG_CONFIG_IN, "malformed macro definition");
@@ -703,7 +707,7 @@ if (*s == '=')
   redef = TRUE;
   s++;
   }
-while (isspace(*s)) s++;
+Uskip_whitespace(&s);
 
 /* If an existing macro of the same name was defined on the command line, we
 just skip this definition. It's an error to attempt to redefine a macro without
@@ -795,7 +799,7 @@ uschar * s;
 /* Find the true start of the physical line - leading spaces are always
 ignored. */
 
-while (isspace(*ss)) ss++;
+Uskip_whitespace(&ss);
 
 /* Process the physical line for macros. If this is the start of the logical
 line, skip over initial text at the start of the line if it starts with an
@@ -807,8 +811,7 @@ s = ss;
 if (len == 0 && isupper(*s))
   {
   while (isalnum(*s) || *s == '_') s++;
-  while (isspace(*s)) s++;
-  if (*s != '=') s = ss;          /* Not a macro definition */
+  if (Uskip_whitespace(&s) != '=') s = ss;          /* Not a macro definition */
   }
 
 /* Skip leading chars which cannot start a macro name, to avoid multiple
@@ -871,7 +874,7 @@ if (*s) for (macro_item * m = *s == '_' ? macros : macros_user; m; m = m->next)
 /* An empty macro replacement at the start of a line could mean that ss no
 longer points to the first non-blank character. */
 
-while (isspace(*ss)) ss++;
+Uskip_whitespace(&ss);
 return ss;
 }
 
@@ -1044,7 +1047,7 @@ for (;;)
     struct stat statbuf;
 
     ss += 9 + include_if_exists;
-    while (isspace(*ss)) ss++;
+    Uskip_whitespace(&ss);
     t = ss + Ustrlen(ss);
     while (t > ss && isspace(t[-1])) t--;
     if (*ss == '\"' && t[-1] == '\"')
@@ -1135,7 +1138,7 @@ if (config_lines)
 if (strncmpic(s, US"begin ", 6) == 0)
   {
   s += 6;
-  while (isspace(*s)) s++;
+  Uskip_whitespace(&s);
   if (big_buffer + len - s > sizeof(next_section) - 2)
     s[sizeof(next_section) - 2] = 0;
   Ustrcpy(next_section, s);
@@ -1169,17 +1172,26 @@ uschar *
 readconf_readname(uschar *name, int len, uschar *s)
 {
 int p = 0;
-while (isspace(*s)) s++;
-if (isalpha(*s))
-  {
+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;
-while (isspace(*s)) s++;
+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;
 }
 
@@ -1303,7 +1315,7 @@ Returns:    pointer to an option entry, or NULL if not found
 */
 
 static optionlist *
-find_option(uschar *name, optionlist *ol, int last)
+find_option(const uschar *name, optionlist *ol, int last)
 {
 int first = 0;
 while (last > first)
@@ -1342,10 +1354,10 @@ Returns:        a pointer to the boolean flag.
 */
 
 static BOOL *
-get_set_flag(uschar *name, optionlist *oltop, int last, void *data_block)
+get_set_flag(const 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,
@@ -1415,15 +1427,15 @@ rewrite_rule *next = store_get(sizeof(rewrite_rule), FALSE);
 next->next = NULL;
 next->key = string_dequote(&p);
 
-while (isspace(*p)) p++;
-if (*p == 0)
+Uskip_whitespace(&p);
+if (!*p)
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
     "missing rewrite replacement string");
 
 next->flags = 0;
 next->replacement = string_dequote(&p);
 
-while (*p != 0) switch (*p++)
+while (*p) switch (*p++)
   {
   case ' ': case '\t': break;
 
@@ -1544,7 +1556,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 == '$')
@@ -1618,8 +1630,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. */
@@ -2078,7 +2090,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)
@@ -2415,7 +2427,7 @@ Returns:         boolean success
 */
 
 static BOOL
-print_ol(optionlist *ol, uschar *name, void *options_block,
+print_ol(optionlist *ol, const uschar *name, void *options_block,
   optionlist *oltop, int last, BOOL no_labels)
 {
 struct passwd *pw;
@@ -2425,7 +2437,7 @@ void *value;
 uid_t *uidlist;
 gid_t *gidlist;
 uschar *s;
-uschar name2[64];
+uschar name2[EXIM_DRIVERNAME_MAX];
 
 if (!ol)
   {
@@ -2461,7 +2473,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:
@@ -2725,7 +2737,7 @@ Returns:      Boolean success
 */
 
 BOOL
-readconf_print(uschar *name, uschar *type, BOOL no_labels)
+readconf_print(const uschar *name, uschar *type, BOOL no_labels)
 {
 BOOL names_only = FALSE;
 optionlist *ol2 = NULL;
@@ -2864,7 +2876,7 @@ if (!type)
 
   else
     return print_ol(find_option(name,
-      optionlist_config, nelem(optionlist_config)),
+                     optionlist_config, nelem(optionlist_config)),
       name, NULL, optionlist_config, nelem(optionlist_config), no_labels);
   }
 
@@ -3000,13 +3012,13 @@ if (*numberp >= max)
  log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "too many named %ss (max is %d)\n",
    tname, max);
 
-while (isspace(*s)) s++;
+Uskip_whitespace(&s);
 ss = s;
 while (isalnum(*s) || *s == '_') s++;
 t = store_get(sizeof(tree_node) + s-ss, is_tainted(ss));
 Ustrncpy(t->name, ss, s-ss);
 t->name[s-ss] = 0;
-while (isspace(*s)) s++;
+Uskip_whitespace(&s);
 
 if (!tree_insertnode(anchorp, t))
   log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
@@ -3019,7 +3031,7 @@ nb->hide = hide;
 
 if (*s++ != '=') log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
   "missing '=' after \"%s\"", t->name);
-while (isspace(*s)) s++;
+Uskip_whitespace(&s);
 nb->string = read_string(s, t->name);
 nb->cache_data = NULL;
 
@@ -3283,7 +3295,7 @@ while ((s = get_config_line()))
 
   if (isupper(*s))
     {
-    if (!macro_read_assignment(s)) exim_exit(EXIT_FAILURE, US"");
+    if (!macro_read_assignment(s)) exim_exit(EXIT_FAILURE);
     continue;
     }
 
@@ -3696,7 +3708,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
@@ -3719,7 +3731,7 @@ while ((buffer = get_config_line()))
       (d->info->init)(d);
       d = NULL;
       }
-    if (!macro_read_assignment(buffer)) exim_exit(EXIT_FAILURE, US"");
+    if (!macro_read_assignment(buffer)) exim_exit(EXIT_FAILURE);
     continue;
     }
 
@@ -3762,7 +3774,7 @@ while ((buffer = get_config_line()))
 
     /* Check nothing more on this line, then do the next loop iteration. */
 
-    while (isspace(*s)) s++;
+    Uskip_whitespace(&s);
     if (*s) extra_chars_error(s, US"driver name ", name, US"");
     continue;
     }
@@ -4015,7 +4027,7 @@ const uschar *pp;
 
 if (*p++ != ',') log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "comma expected");
 
-while (isspace(*p)) p++;
+Uskip_whitespace(&p);
 pp = p;
 while (isalnum(*p) || (type == 1 && *p == '.')) p++;
 
@@ -4056,7 +4068,7 @@ while ((p = get_config_line()))
   rchain = &(next->rules);
 
   next->pattern = string_dequote(&p);
-  while (isspace(*p)) p++;
+  Uskip_whitespace(&p);
   pp = p;
   while (mac_isgraph(*p)) p++;
   if (p - pp <= 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
@@ -4073,22 +4085,22 @@ while ((p = get_config_line()))
   fudge. Anything that is not a retry rule starting "F," or "G," is treated as
   an address list. */
 
-  while (isspace(*p)) p++;
+  Uskip_whitespace(&p);
   if (Ustrncmp(p, "senders", 7) == 0)
     {
     p += 7;
-    while (isspace(*p)) p++;
+    Uskip_whitespace(&p);
     if (*p++ != '=') log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
       "\"=\" expected after \"senders\" in retry rule");
-    while (isspace(*p)) p++;
+    Uskip_whitespace(&p);
     next->senders = string_dequote(&p);
     }
 
   /* Now the retry rules. Keep the maximum timeout encountered. */
 
-  while (isspace(*p)) p++;
+  Uskip_whitespace(&p);
 
-  while (*p != 0)
+  while (*p)
     {
     retry_rule *rule = store_get(sizeof(retry_rule), FALSE);
     *rchain = rule;
@@ -4121,13 +4133,12 @@ while ((p = get_config_line()))
       log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
         "bad parameters for retry rule");
 
-    while (isspace(*p)) p++;
-    if (*p == ';')
+    if (Uskip_whitespace(&p) == ';')
       {
       p++;
-      while (isspace(*p)) p++;
+      Uskip_whitespace(&p);
       }
-    else if (*p != 0)
+    else if (*p)
       log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "semicolon expected");
     }
   }
@@ -4225,14 +4236,14 @@ acl_line = get_config_line();
 
 while(acl_line)
   {
-  uschar name[64];
+  uschar name[EXIM_DRIVERNAME_MAX];
   tree_node *node;
   uschar *error;
 
   p = readconf_readname(name, sizeof(name), acl_line);
   if (isupper(*name) && *p == '=')
     {
-    if (!macro_read_assignment(acl_line)) exim_exit(EXIT_FAILURE, US"");
+    if (!macro_read_assignment(acl_line)) exim_exit(EXIT_FAILURE);
     acl_line = get_config_line();
     continue;
     }