* Exim - an Internet mail transport agent *
*************************************************/
+/* Copyright (c) The Exim Maintainers 2020 - 2022 */
/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
+/* SPDX-License-Identifier: GPL-2.0-or-later */
/* Functions for reading the configuration file, and for displaying
overall configuration values. Thanks to Brian Candler for the original
#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
#ifdef SUPPORT_PROXY
{ "hosts_proxy", opt_stringptr, {&hosts_proxy} },
#endif
+#ifndef DISABLE_TLS
+ { "hosts_require_alpn", opt_stringptr, {&hosts_require_alpn} },
+#endif
+ { "hosts_require_helo", opt_stringptr, {&hosts_require_helo} },
{ "hosts_treat_as_local", opt_stringptr, {&hosts_treat_as_local} },
#ifdef LOOKUP_IBASE
{ "ibase_servers", opt_stringptr, {&ibase_servers} },
{ "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} },
{ "mysql_servers", opt_stringptr, {&mysql_servers} },
#endif
{ "never_users", opt_uidlist, {&never_users} },
+ { "notifier_socket", opt_stringptr, {¬ifier_socket} },
#ifndef DISABLE_TLS
{ "openssl_options", opt_stringptr, {&openssl_options} },
#endif
#ifdef LOOKUP_ORACLE
{ "oracle_servers", opt_stringptr, {&oracle_servers} },
#endif
+ { "panic_coredump", opt_bool, {&panic_coredump} },
{ "percent_hack_domains", opt_stringptr, {&percent_hack_domains} },
#ifdef EXIM_PERL
{ "perl_at_start", opt_bool, {&opt_perl_at_start} },
{ "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} },
{ "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} },
{ "smtp_accept_reserve", opt_int, {&smtp_accept_reserve} },
{ "smtp_active_hostname", opt_stringptr, {&raw_active_hostname} },
+ { "smtp_backlog_monitor", opt_int, {&smtp_backlog_monitor} },
{ "smtp_banner", opt_stringptr, {&smtp_banner} },
{ "smtp_check_spool_space", opt_bool, {&smtp_check_spool_space} },
{ "smtp_connect_backlog", opt_int, {&smtp_connect_backlog} },
#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
- { "srs_config", opt_stringptr, {&srs_config} },
- { "srs_hashlength", opt_int, {&srs_hashlength} },
- { "srs_hashmin", opt_int, {&srs_hashmin} },
- { "srs_maxage", opt_int, {&srs_maxage} },
- { "srs_secrets", opt_stringptr, {&srs_secrets} },
- { "srs_usehash", opt_bool, {&srs_usehash} },
- { "srs_usetimestamp", opt_bool, {&srs_usetimestamp} },
#endif
{ "strict_acl_vars", opt_bool, {&strict_acl_vars} },
{ "strip_excess_angle_brackets", opt_bool, {&strip_excess_angle_brackets} },
{ "timezone", opt_stringptr, {&timezone_string} },
{ "tls_advertise_hosts", opt_stringptr, {&tls_advertise_hosts} },
#ifndef DISABLE_TLS
+ { "tls_alpn", opt_stringptr, {&tls_alpn} },
{ "tls_certificate", opt_stringptr, {&tls_certificate} },
{ "tls_crl", opt_stringptr, {&tls_crl} },
{ "tls_dh_max_bits", opt_int, {&tls_dh_max_bits} },
{ "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} },
void
options_auths(void)
{
-uschar buf[64];
+uschar buf[EXIM_DRIVERNAME_MAX];
options_from_list(optionlist_auths, optionlist_auths_size, US"AUTHENTICATORS", NULL);
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++)
{
macro_item *
macro_create(const uschar * name, const uschar * val, BOOL command_line)
{
-macro_item * m = store_get(sizeof(macro_item), FALSE);
+macro_item * m = store_get(sizeof(macro_item), GET_UNTAINTED);
READCONF_DEBUG fprintf(stderr, "%s: '%s' '%s'\n", __FUNCTION__, name, val);
m->next = NULL;
BOOL
macro_read_assignment(uschar *s)
{
-uschar name[64];
+uschar name[EXIM_DRIVERNAME_MAX];
int namelen = 0;
BOOL redef = FALSE;
macro_item *m;
}
name[namelen] = 0;
-while (isspace(*s)) s++;
+Uskip_whitespace(&s);
if (*s++ != '=')
{
log_write(0, LOG_PANIC|LOG_CONFIG_IN, "malformed macro definition");
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
/* 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
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
/* 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;
}
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] == '\"')
if (config_lines)
save_config_position(config_filename, config_lineno);
- save = store_get(sizeof(config_file_item), FALSE);
+ save = store_get(sizeof(config_file_item), GET_UNTAINTED);
save->next = config_file_stack;
config_file_stack = save;
save->file = config_file;
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);
Returns: new input pointer
*/
-uschar *
-readconf_readname(uschar *name, int len, uschar *s)
+const uschar *
+readconf_readname(uschar * name, int len, const 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;
}
*/
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)
*/
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,
static rewrite_rule *
readconf_one_rewrite(const uschar *p, int *existflags, BOOL isglobal)
{
-rewrite_rule *next = store_get(sizeof(rewrite_rule), FALSE);
+rewrite_rule * next = store_get(sizeof(rewrite_rule), GET_UNTAINTED);
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;
{
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 == '$')
int intbase = 0;
uschar *inttype = US"";
uschar *sptr;
-uschar *s = buffer;
+const 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. */
case opt_gidlist:
case opt_rewrite:
- reset_point = store_mark();
- sptr = read_string(s, name);
+ reset_point = store_mark();
+ sptr = read_string(s, name);
- /* Having read a string, we now have several different ways of using it,
- depending on the data type, so do another switch. If keeping the actual
- string is not required (because it is interpreted), freesptr is set TRUE,
- and at the end we reset the pool. */
+ /* Having read a string, we now have several different ways of using it,
+ depending on the data type, so do another switch. If keeping the actual
+ string is not required (because it is interpreted), freesptr is set TRUE,
+ and at the end we reset the pool. */
- switch (type)
- {
- /* If this was a string, set the variable to point to the new string,
- and set the flag so its store isn't reclaimed. If it was a list of rewrite
- rules, we still keep the string (for printing), and parse the rules into a
- control block and flags word. */
-
- case opt_stringptr:
- str_target = data_block ? USS (US data_block + ol->v.offset)
- : USS ol->v.value;
- if (ol->type & opt_rep_con)
+ switch (type)
{
- uschar * saved_condition;
- /* We already have a condition, we're conducting a crude hack to let
- multiple condition rules be chained together, despite storing them in
- text form. */
- *str_target = string_copy_perm( (saved_condition = *str_target)
- ? string_sprintf("${if and{{bool_lax{%s}}{bool_lax{%s}}}}",
- saved_condition, sptr)
- : sptr,
- FALSE);
- /* TODO(pdp): there is a memory leak here and just below
- when we set 3 or more conditions; I still don't
- understand the store mechanism enough to know
- what's the safe way to free content from an earlier store.
- AFAICT, stores stack, so freeing an early stored item also stores
- all data alloc'd after it. If we knew conditions were adjacent,
- we could survive that, but we don't. So I *think* we need to take
- another bit from opt_type to indicate "malloced"; this seems like
- quite a hack, especially for this one case. It also means that
- we can't ever reclaim the store from the *first* condition.
-
- Because we only do this once, near process start-up, I'm prepared to
- let this slide for the time being, even though it rankles. */
- }
- else if (ol->type & opt_rep_str)
- {
- uschar sep_o =
- Ustrncmp(name, "headers_add", 11) == 0 ? '\n'
- : Ustrncmp(name, "set", 3) == 0 ? ';'
- : ':';
- int sep_i = -(int)sep_o;
- const uschar * list = sptr;
- uschar * s;
- gstring * list_o = NULL;
-
- if (*str_target)
- {
- list_o = string_get(Ustrlen(*str_target) + Ustrlen(sptr));
- list_o = string_cat(list_o, *str_target);
- }
+ /* If this was a string, set the variable to point to the new string,
+ and set the flag so its store isn't reclaimed. If it was a list of rewrite
+ rules, we still keep the string (for printing), and parse the rules into a
+ control block and flags word. */
+
+ case opt_stringptr:
+ str_target = data_block ? USS (US data_block + ol->v.offset)
+ : USS ol->v.value;
+ if (ol->type & opt_rep_con)
+ {
+ uschar * saved_condition;
+ /* We already have a condition, we're conducting a crude hack to let
+ multiple condition rules be chained together, despite storing them in
+ text form. */
+ *str_target = string_copy_perm( (saved_condition = *str_target)
+ ? string_sprintf("${if and{{bool_lax{%s}}{bool_lax{%s}}}}",
+ saved_condition, sptr)
+ : sptr,
+ FALSE);
+ /* TODO(pdp): there is a memory leak here and just below
+ when we set 3 or more conditions; I still don't
+ understand the store mechanism enough to know
+ what's the safe way to free content from an earlier store.
+ AFAICT, stores stack, so freeing an early stored item also stores
+ all data alloc'd after it. If we knew conditions were adjacent,
+ we could survive that, but we don't. So I *think* we need to take
+ another bit from opt_type to indicate "malloced"; this seems like
+ quite a hack, especially for this one case. It also means that
+ we can't ever reclaim the store from the *first* condition.
+
+ Because we only do this once, near process start-up, I'm prepared to
+ let this slide for the time being, even though it rankles. */
+ }
+ else if (ol->type & opt_rep_str)
+ {
+ uschar sep_o =
+ Ustrncmp(name, "headers_add", 11) == 0 ? '\n'
+ : Ustrncmp(name, "set", 3) == 0 ? ';'
+ : ':';
+ int sep_i = -(int)sep_o;
+ const uschar * list = sptr;
+ uschar * s;
+ gstring * list_o = NULL;
+
+ if (*str_target)
+ {
+ list_o = string_get(Ustrlen(*str_target) + Ustrlen(sptr));
+ list_o = string_cat(list_o, *str_target);
+ }
- while ((s = string_nextinlist(&list, &sep_i, NULL, 0)))
- list_o = string_append_listele(list_o, sep_o, s);
+ while ((s = string_nextinlist(&list, &sep_i, NULL, 0)))
+ list_o = string_append_listele(list_o, sep_o, s);
- if (list_o)
- *str_target = string_copy_perm(string_from_gstring(list_o), FALSE);
- }
- else
- {
- *str_target = sptr;
- freesptr = FALSE;
- }
- break;
+ if (list_o)
+ *str_target = string_copy_perm(string_from_gstring(list_o), FALSE);
+ }
+ else
+ {
+ *str_target = sptr;
+ freesptr = FALSE;
+ }
+ break;
- case opt_rewrite:
- if (data_block)
- *USS (US data_block + ol->v.offset) = sptr;
- else
- *USS ol->v.value = sptr;
- freesptr = FALSE;
- if (type == opt_rewrite)
- {
- int sep = 0;
- int *flagptr;
- uschar *p = sptr;
- rewrite_rule **chain;
- optionlist *ol3;
-
- sprintf(CS name2, "*%.50s_rules", name);
- ol2 = find_option(name2, oltop, last);
- sprintf(CS name2, "*%.50s_flags", name);
- ol3 = find_option(name2, oltop, last);
-
- if (!ol2 || !ol3)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
- "rewrite rules not available for driver");
+ case opt_rewrite:
+ if (data_block)
+ *USS (US data_block + ol->v.offset) = sptr;
+ else
+ *USS ol->v.value = sptr;
+ freesptr = FALSE;
+ if (type == opt_rewrite)
+ {
+ int sep = 0;
+ int *flagptr;
+ uschar *p = sptr;
+ rewrite_rule **chain;
+ optionlist *ol3;
+
+ sprintf(CS name2, "*%.50s_rules", name);
+ ol2 = find_option(name2, oltop, last);
+ sprintf(CS name2, "*%.50s_flags", name);
+ ol3 = find_option(name2, oltop, last);
+
+ if (!ol2 || !ol3)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+ "rewrite rules not available for driver");
+
+ if (data_block)
+ {
+ chain = (rewrite_rule **)(US data_block + ol2->v.offset);
+ flagptr = (int *)(US data_block + ol3->v.offset);
+ }
+ else
+ {
+ chain = (rewrite_rule **)ol2->v.value;
+ flagptr = (int *)ol3->v.value;
+ }
- if (data_block)
- {
- chain = (rewrite_rule **)(US data_block + ol2->v.offset);
- flagptr = (int *)(US data_block + ol3->v.offset);
- }
- else
- {
- chain = (rewrite_rule **)ol2->v.value;
- 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);
+ *chain = next;
+ chain = &(next->next);
+ }
- while ((p = string_nextinlist(CUSS &sptr, &sep, big_buffer, BIG_BUFFER_SIZE)))
- {
- rewrite_rule *next = readconf_one_rewrite(p, flagptr, FALSE);
- *chain = next;
- chain = &(next->next);
- }
+ if ((*flagptr & (rewrite_all_envelope | rewrite_smtp)) != 0)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "rewrite rule specifies a "
+ "non-header rewrite - not allowed at transport time -");
+ }
+ break;
- if ((*flagptr & (rewrite_all_envelope | rewrite_smtp)) != 0)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "rewrite rule specifies a "
- "non-header rewrite - not allowed at transport time -");
- }
- break;
+ /* If it was an expanded uid, see if there is any expansion to be
+ done by checking for the presence of a $ character. If there is, save it
+ in the corresponding *expand_user option field. Otherwise, fall through
+ to treat it as a fixed uid. Ensure mutual exclusivity of the two kinds
+ of data. */
- /* If it was an expanded uid, see if there is any expansion to be
- done by checking for the presence of a $ character. If there is, save it
- in the corresponding *expand_user option field. Otherwise, fall through
- to treat it as a fixed uid. Ensure mutual exclusivity of the two kinds
- of data. */
+ case opt_expand_uid:
+ sprintf(CS name2, "*expand_%.50s", name);
+ if ((ol2 = find_option(name2, oltop, last)))
+ {
+ uschar *ss = (Ustrchr(sptr, '$') != NULL) ? sptr : NULL;
- case opt_expand_uid:
- sprintf(CS name2, "*expand_%.50s", name);
- if ((ol2 = find_option(name2, oltop, last)))
- {
- uschar *ss = (Ustrchr(sptr, '$') != NULL) ? sptr : NULL;
+ if (data_block)
+ *(USS(US data_block + ol2->v.offset)) = ss;
+ else
+ *(USS ol2->v.value) = ss;
- if (data_block)
- *(USS(US data_block + ol2->v.offset)) = ss;
- else
- *(USS ol2->v.value) = ss;
-
- if (ss)
- {
- *(get_set_flag(name, oltop, last, data_block)) = FALSE;
- freesptr = FALSE;
- break;
- }
- }
+ if (ss)
+ {
+ *(get_set_flag(name, oltop, last, data_block)) = FALSE;
+ freesptr = FALSE;
+ break;
+ }
+ }
- /* Look up a fixed uid, and also make use of the corresponding gid
- if a passwd entry is returned and the gid has not been set. */
+ /* Look up a fixed uid, and also make use of the corresponding gid
+ if a passwd entry is returned and the gid has not been set. */
- case opt_uid:
- if (!route_finduser(sptr, &pw, &uid))
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "user %s was not found", sptr);
- if (data_block)
- *(uid_t *)(US data_block + ol->v.offset) = uid;
- else
- *(uid_t *)ol->v.value = uid;
+ case opt_uid:
+ if (!route_finduser(sptr, &pw, &uid))
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "user %s was not found", sptr);
+ if (data_block)
+ *(uid_t *)(US data_block + ol->v.offset) = uid;
+ else
+ *(uid_t *)ol->v.value = uid;
- /* Set the flag indicating a fixed value is set */
+ /* Set the flag indicating a fixed value is set */
- *(get_set_flag(name, oltop, last, data_block)) = TRUE;
+ *(get_set_flag(name, oltop, last, data_block)) = TRUE;
- /* Handle matching gid if we have a passwd entry: done by finding the
- same name with terminating "user" changed to "group"; if not found,
- ignore. Also ignore if the value is already set. */
+ /* Handle matching gid if we have a passwd entry: done by finding the
+ same name with terminating "user" changed to "group"; if not found,
+ ignore. Also ignore if the value is already set. */
- if (pw == NULL) break;
- Ustrcpy(name+Ustrlen(name)-4, US"group");
- ol2 = find_option(name, oltop, last);
- if (ol2 && ((ol2->type & opt_mask) == opt_gid ||
- (ol2->type & opt_mask) == opt_expand_gid))
- {
- BOOL *set_flag = get_set_flag(name, oltop, last, data_block);
- if (!*set_flag)
- {
- if (data_block)
- *((gid_t *)(US data_block + ol2->v.offset)) = pw->pw_gid;
- else
- *((gid_t *)ol2->v.value) = pw->pw_gid;
- *set_flag = TRUE;
- }
- }
- break;
+ if (pw == NULL) break;
+ Ustrcpy(name+Ustrlen(name)-4, US"group");
+ ol2 = find_option(name, oltop, last);
+ if (ol2 && ((ol2->type & opt_mask) == opt_gid ||
+ (ol2->type & opt_mask) == opt_expand_gid))
+ {
+ BOOL *set_flag = get_set_flag(name, oltop, last, data_block);
+ if (!*set_flag)
+ {
+ if (data_block)
+ *((gid_t *)(US data_block + ol2->v.offset)) = pw->pw_gid;
+ else
+ *((gid_t *)ol2->v.value) = pw->pw_gid;
+ *set_flag = TRUE;
+ }
+ }
+ break;
- /* If it was an expanded gid, see if there is any expansion to be
- done by checking for the presence of a $ character. If there is, save it
- in the corresponding *expand_user option field. Otherwise, fall through
- to treat it as a fixed gid. Ensure mutual exclusivity of the two kinds
- of data. */
+ /* If it was an expanded gid, see if there is any expansion to be
+ done by checking for the presence of a $ character. If there is, save it
+ in the corresponding *expand_user option field. Otherwise, fall through
+ to treat it as a fixed gid. Ensure mutual exclusivity of the two kinds
+ of data. */
- case opt_expand_gid:
- sprintf(CS name2, "*expand_%.50s", name);
- if ((ol2 = find_option(name2, oltop, last)))
- {
- uschar *ss = (Ustrchr(sptr, '$') != NULL) ? sptr : NULL;
+ case opt_expand_gid:
+ sprintf(CS name2, "*expand_%.50s", name);
+ if ((ol2 = find_option(name2, oltop, last)))
+ {
+ uschar *ss = (Ustrchr(sptr, '$') != NULL) ? sptr : NULL;
- if (data_block)
- *(USS(US data_block + ol2->v.offset)) = ss;
- else
- *(USS ol2->v.value) = ss;
+ if (data_block)
+ *(USS(US data_block + ol2->v.offset)) = ss;
+ else
+ *(USS ol2->v.value) = ss;
- if (ss)
- {
- *(get_set_flag(name, oltop, last, data_block)) = FALSE;
- freesptr = FALSE;
- break;
- }
- }
+ if (ss)
+ {
+ *(get_set_flag(name, oltop, last, data_block)) = FALSE;
+ freesptr = FALSE;
+ break;
+ }
+ }
- /* Handle freestanding gid */
+ /* Handle freestanding gid */
- case opt_gid:
- if (!route_findgroup(sptr, &gid))
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "group %s was not found", sptr);
- if (data_block)
- *((gid_t *)(US data_block + ol->v.offset)) = gid;
- else
- *((gid_t *)ol->v.value) = gid;
- *(get_set_flag(name, oltop, last, data_block)) = TRUE;
- break;
+ case opt_gid:
+ if (!route_findgroup(sptr, &gid))
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "group %s was not found", sptr);
+ if (data_block)
+ *((gid_t *)(US data_block + ol->v.offset)) = gid;
+ else
+ *((gid_t *)ol->v.value) = gid;
+ *(get_set_flag(name, oltop, last, data_block)) = TRUE;
+ break;
- /* If it was a uid list, look up each individual entry, and build
- a vector of uids, with a count in the first element. Put the vector
- in malloc store so we can free the string. (We are reading into
- permanent store already.) */
+ /* If it was a uid list, look up each individual entry, and build
+ a vector of uids, with a count in the first element. Put the vector
+ in malloc store so we can free the string. (We are reading into
+ permanent store already.) */
- case opt_uidlist:
- {
- int count = 1;
- uid_t *list;
- int ptr = 0;
- const uschar *p;
- const uschar *op = expand_string (sptr);
-
- if (op == NULL)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s",
- name, expand_string_message);
-
- p = op;
- if (*p != 0) count++;
- while (*p != 0) if (*p++ == ':' && *p != 0) count++;
- list = store_malloc(count*sizeof(uid_t));
- list[ptr++] = (uid_t)(count - 1);
-
- if (data_block)
- *((uid_t **)(US data_block + ol->v.offset)) = list;
- else
- *((uid_t **)ol->v.value) = list;
+ case opt_uidlist:
+ {
+ int count = 1;
+ uid_t *list;
+ int ptr = 0;
+ const uschar *p;
+ const uschar *op = expand_string (sptr);
+
+ if (op == NULL)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s",
+ name, expand_string_message);
+
+ p = op;
+ if (*p != 0) count++;
+ while (*p != 0) if (*p++ == ':' && *p != 0) count++;
+ list = store_malloc(count*sizeof(uid_t));
+ list[ptr++] = (uid_t)(count - 1);
+
+ if (data_block)
+ *((uid_t **)(US data_block + ol->v.offset)) = list;
+ else
+ *((uid_t **)ol->v.value) = list;
- p = op;
- while (count-- > 1)
- {
- int sep = 0;
- (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",
- big_buffer);
- list[ptr++] = uid;
- }
- }
- break;
+ p = op;
+ 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",
+ big_buffer);
+ list[ptr++] = uid;
+ }
+ break;
+ }
- /* If it was a gid list, look up each individual entry, and build
- a vector of gids, with a count in the first element. Put the vector
- in malloc store so we can free the string. (We are reading into permanent
- store already.) */
+ /* If it was a gid list, look up each individual entry, and build
+ a vector of gids, with a count in the first element. Put the vector
+ in malloc store so we can free the string. (We are reading into permanent
+ store already.) */
- case opt_gidlist:
- {
- int count = 1;
- gid_t *list;
- int ptr = 0;
- const uschar *p;
- const uschar *op = expand_string (sptr);
-
- if (!op)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s",
- name, expand_string_message);
-
- p = op;
- if (*p != 0) count++;
- while (*p != 0) if (*p++ == ':' && *p != 0) count++;
- list = store_malloc(count*sizeof(gid_t));
- list[ptr++] = (gid_t)(count - 1);
-
- if (data_block)
- *((gid_t **)(US data_block + ol->v.offset)) = list;
- else
- *((gid_t **)ol->v.value) = list;
+ case opt_gidlist:
+ {
+ int count = 1;
+ gid_t *list;
+ int ptr = 0;
+ const uschar *p;
+ const uschar *op = expand_string (sptr);
+
+ if (!op)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "failed to expand %s: %s",
+ name, expand_string_message);
+
+ p = op;
+ if (*p != 0) count++;
+ while (*p != 0) if (*p++ == ':' && *p != 0) count++;
+ list = store_malloc(count*sizeof(gid_t));
+ list[ptr++] = (gid_t)(count - 1);
+
+ if (data_block)
+ *((gid_t **)(US data_block + ol->v.offset)) = list;
+ else
+ *((gid_t **)ol->v.value) = list;
- p = op;
- while (count-- > 1)
- {
- int sep = 0;
- (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",
- big_buffer);
- list[ptr++] = gid;
- }
+ p = op;
+ 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",
+ big_buffer);
+ list[ptr++] = gid;
+ }
+ break;
+ }
}
- break;
- }
- /* Release store if the value of the string doesn't need to be kept. */
+ /* Release store if the value of the string doesn't need to be kept. */
- if (freesptr) reset_point = store_reset(reset_point);
- break;
+ if (freesptr) reset_point = store_reset(reset_point);
+ break;
/* Expanded boolean: if no characters follow, or if there are no dollar
characters, this is a fixed-valued boolean, and we fall through. Otherwise,
save the string for later expansion in the alternate place. */
case opt_expand_bool:
- if (*s && Ustrchr(s, '$') != 0)
- {
- sprintf(CS name2, "*expand_%.50s", name);
- if ((ol2 = find_option(name2, oltop, last)))
+ if (*s && Ustrchr(s, '$') != 0)
{
- reset_point = store_mark();
- sptr = read_string(s, name);
- if (data_block)
- *(USS(US data_block + ol2->v.offset)) = sptr;
- else
- *(USS ol2->v.value) = sptr;
- freesptr = FALSE;
- break;
+ sprintf(CS name2, "*expand_%.50s", name);
+ if ((ol2 = find_option(name2, oltop, last)))
+ {
+ reset_point = store_mark();
+ sptr = read_string(s, name);
+ if (data_block)
+ *(USS(US data_block + ol2->v.offset)) = sptr;
+ else
+ *(USS ol2->v.value) = sptr;
+ freesptr = FALSE;
+ break;
+ }
}
- }
- /* Fall through */
+ /* Fall through */
/* Boolean: if no characters follow, the value is boolvalue. Otherwise
look for yes/not/true/false. Some booleans are stored in a single bit in
case opt_bit:
case opt_bool_verify:
case opt_bool_set:
- if (*s != 0)
- {
- s = readconf_readname(name2, 64, 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)
- boolvalue = FALSE;
- else log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
- "\"%s\" is not a valid value for the \"%s\" option", name2, name);
- if (*s != 0) extra_chars_error(s, string_sprintf("\"%s\" ", name2),
- US"for boolean option ", name);
- }
+ if (*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)
+ boolvalue = FALSE;
+ else log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+ "\"%s\" is not a valid value for the \"%s\" option", name2, name);
+ if (*s != 0) extra_chars_error(s, string_sprintf("\"%s\" ", name2),
+ US"for boolean option ", name);
+ }
- /* Handle single-bit type. */
+ /* Handle single-bit type. */
- if (type == opt_bit)
- {
- int bit = 1 << ((ol->type >> 16) & 31);
- int * ptr = data_block
- ? (int *)(US data_block + ol->v.offset)
- : (int *)ol->v.value;
- if (boolvalue) *ptr |= bit; else *ptr &= ~bit;
- break;
- }
+ if (type == opt_bit)
+ {
+ int bit = 1 << ((ol->type >> 16) & 31);
+ int * ptr = data_block
+ ? (int *)(US data_block + ol->v.offset)
+ : (int *)ol->v.value;
+ if (boolvalue) *ptr |= bit; else *ptr &= ~bit;
+ break;
+ }
- /* Handle full BOOL types */
+ /* Handle full BOOL types */
- if (data_block)
- *((BOOL *)(US data_block + ol->v.offset)) = boolvalue;
- else
- *((BOOL *)ol->v.value) = boolvalue;
+ if (data_block)
+ *((BOOL *)(US data_block + ol->v.offset)) = boolvalue;
+ else
+ *((BOOL *)ol->v.value) = boolvalue;
- /* Verify fudge */
+ /* Verify fudge */
- if (type == opt_bool_verify)
- {
- sprintf(CS name2, "%.50s_recipient", name + offset);
- if ((ol2 = find_option(name2, oltop, last)))
- if (data_block)
- *((BOOL *)(US data_block + ol2->v.offset)) = boolvalue;
- else
- *((BOOL *)ol2->v.value) = boolvalue;
- }
+ if (type == opt_bool_verify)
+ {
+ sprintf(CS name2, "%.50s_recipient", name + offset);
+ if ((ol2 = find_option(name2, oltop, last)))
+ if (data_block)
+ *((BOOL *)(US data_block + ol2->v.offset)) = boolvalue;
+ else
+ *((BOOL *)ol2->v.value) = boolvalue;
+ }
- /* Note that opt_bool_set type is set, if there is somewhere to do so */
+ /* Note that opt_bool_set type is set, if there is somewhere to do so */
- else if (type == opt_bool_set)
- {
- sprintf(CS name2, "*set_%.50s", name + offset);
- if ((ol2 = find_option(name2, oltop, last)))
- if (data_block)
- *((BOOL *)(US data_block + ol2->v.offset)) = TRUE;
- else
- *((BOOL *)ol2->v.value) = TRUE;
- }
- break;
+ else if (type == opt_bool_set)
+ {
+ sprintf(CS name2, "*set_%.50s", name + offset);
+ if ((ol2 = find_option(name2, oltop, last)))
+ if (data_block)
+ *((BOOL *)(US data_block + ol2->v.offset)) = TRUE;
+ else
+ *((BOOL *)ol2->v.value) = TRUE;
+ }
+ break;
/* Octal integer */
case opt_octint:
- intbase = 8;
- inttype = US"octal ";
+ intbase = 8;
+ inttype = US"octal ";
/* Integer: a simple(ish) case; allow octal and hex formats, and
suffixes K, M, G, and T. The different types affect output, not input. */
case opt_mkint:
case opt_int:
- {
- uschar *endptr;
- long int lvalue;
-
- errno = 0;
- lvalue = strtol(CS s, CSS &endptr, intbase);
+ {
+ uschar *endptr;
+ long int lvalue;
- if (endptr == s)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "%sinteger expected for %s",
- inttype, name);
+ errno = 0;
+ lvalue = strtol(CS s, CSS &endptr, intbase);
- if (errno != ERANGE && *endptr)
- {
- uschar * mp = US"TtGgMmKk\0"; /* YyZzEePpTtGgMmKk */
+ if (endptr == s)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "%sinteger expected for %s",
+ inttype, name);
- if ((mp = Ustrchr(mp, *endptr)))
+ if (errno != ERANGE && *endptr)
{
- endptr++;
- do
+ uschar * mp = US"TtGgMmKk\0"; /* YyZzEePpTtGgMmKk */
+
+ if ((mp = Ustrchr(mp, *endptr)))
{
- if (lvalue > INT_MAX/1024 || lvalue < INT_MIN/1024)
+ endptr++;
+ do
{
- errno = ERANGE;
- break;
+ if (lvalue > INT_MAX/1024 || lvalue < INT_MIN/1024)
+ {
+ errno = ERANGE;
+ break;
+ }
+ lvalue *= 1024;
}
- lvalue *= 1024;
+ while (*(mp += 2));
}
- while (*(mp += 2));
}
- }
- if (errno == ERANGE || lvalue > INT_MAX || lvalue < INT_MIN)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
- "absolute value of integer \"%s\" is too large (overflow)", s);
+ if (errno == ERANGE || lvalue > INT_MAX || lvalue < INT_MIN)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+ "absolute value of integer \"%s\" is too large (overflow)", s);
- while (isspace(*endptr)) endptr++;
- if (*endptr)
- extra_chars_error(endptr, inttype, US"integer value for ", name);
+ while (isspace(*endptr)) endptr++;
+ if (*endptr)
+ extra_chars_error(endptr, inttype, US"integer value for ", name);
- value = (int)lvalue;
- }
+ value = (int)lvalue;
+ }
- if (data_block)
- *(int *)(US data_block + ol->v.offset) = value;
- else
- *(int *)ol->v.value = value;
- break;
+ if (data_block)
+ *(int *)(US data_block + ol->v.offset) = value;
+ else
+ *(int *)ol->v.value = value;
+ break;
/* Integer held in K: again, allow formats and suffixes as above. */
/* Fixed-point number: held to 3 decimal places. */
case opt_fixed:
- if (sscanf(CS s, "%d%n", &value, &count) != 1)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
- "fixed-point number expected for %s", name);
+ if (sscanf(CS s, "%d%n", &value, &count) != 1)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+ "fixed-point number expected for %s", name);
- if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
- "integer \"%s\" is too large (overflow)", s);
+ if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+ "integer \"%s\" is too large (overflow)", s);
- value *= 1000;
+ value *= 1000;
- if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
- "integer \"%s\" is too large (overflow)", s);
+ if (value < 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+ "integer \"%s\" is too large (overflow)", s);
- /* We get a coverity error here for using count, as it derived
- from the tainted buffer pointed to by s, as parsed by sscanf().
- By the definition of sscanf we must be accessing between start
- and end of s (assuming it is nul-terminated...) so ignore the error. */
- /* coverity[tainted_data] */
- if (s[count] == '.')
- {
- int d = 100;
- while (isdigit(s[++count]))
+ /* We get a coverity error here for using count, as it derived
+ from the tainted buffer pointed to by s, as parsed by sscanf().
+ By the definition of sscanf we must be accessing between start
+ and end of s (assuming it is nul-terminated...) so ignore the error. */
+ /* coverity[tainted_data] */
+ if (s[count] == '.')
{
- value += (s[count] - '0') * d;
- d /= 10;
+ int d = 100;
+ while (isdigit(s[++count]))
+ {
+ value += (s[count] - '0') * d;
+ d /= 10;
+ }
}
- }
- while (isspace(s[count])) count++;
+ while (isspace(s[count])) count++;
- if (s[count] != 0)
- extra_chars_error(s+count, US"fixed-point value for ", name, US"");
+ if (s[count] != 0)
+ extra_chars_error(s+count, US"fixed-point value for ", name, US"");
- if (data_block)
- *((int *)(US data_block + ol->v.offset)) = value;
- else
- *((int *)ol->v.value) = value;
- break;
+ if (data_block)
+ *((int *)(US data_block + ol->v.offset)) = value;
+ else
+ *((int *)ol->v.value) = value;
+ break;
/* There's a special routine to read time values. */
case opt_time:
- value = readconf_readtime(s, 0, FALSE);
- if (value < 0)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "invalid time value for %s",
- name);
- if (data_block)
- *((int *)(US data_block + ol->v.offset)) = value;
- else
- *((int *)ol->v.value) = value;
- break;
+ value = readconf_readtime(s, 0, FALSE);
+ if (value < 0)
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "invalid time value for %s",
+ name);
+ if (data_block)
+ *((int *)(US data_block + ol->v.offset)) = value;
+ else
+ *((int *)ol->v.value) = value;
+ break;
/* A time list is a list of colon-separated times, with the first
element holding the size of the list and the second the number of
*/
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;
uid_t *uidlist;
gid_t *gidlist;
uschar *s;
-uschar name2[64];
+uschar name2[EXIM_DRIVERNAME_MAX];
if (!ol)
{
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:
break;
case opt_bit:
- printf("%s%s\n", ((*((int *)value)) & (1 << ((ol->type >> 16) & 31)))?
- "" : "no_", name);
+ printf("%s%s\n", (*((int *)value)) & (1 << ((ol->type >> 16) & 31))
+ ? "" : "no_", name);
break;
case opt_expand_bool:
case opt_bool:
case opt_bool_verify:
case opt_bool_set:
- printf("%s%s\n", (*((BOOL *)value))? "" : "no_", name);
+ printf("%s%s\n", *((BOOL *)value) ? "" : "no_", name);
break;
case opt_func:
*/
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;
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);
}
BOOL forcecache = FALSE;
uschar *ss;
tree_node *t;
-namedlist_block * nb = store_get(sizeof(namedlist_block), FALSE);
+namedlist_block * nb = store_get_perm(sizeof(namedlist_block), FALSE);
if (Ustrncmp(s, "_cache", 6) == 0)
{
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));
+t = store_get(sizeof(tree_node) + s-ss, 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,
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;
/* Loop through the possible file names */
+/* Should never be a tainted list */
while((filename = string_nextinlist(&list, &sep, big_buffer, big_buffer_size)))
{
/* Cut out all the fancy processing unless specifically wanted */
- #if defined(CONFIGURE_FILE_USE_NODE) || defined(CONFIGURE_FILE_USE_EUID)
+#if defined(CONFIGURE_FILE_USE_NODE) || defined(CONFIGURE_FILE_USE_EUID)
uschar *suffix = filename + Ustrlen(filename);
/* Try for the node-specific file if a node name exists */
- #ifdef CONFIGURE_FILE_USE_NODE
+# ifdef CONFIGURE_FILE_USE_NODE
struct utsname uts;
if (uname(&uts) >= 0)
{
- #ifdef CONFIGURE_FILE_USE_EUID
+# ifdef CONFIGURE_FILE_USE_EUID
sprintf(CS suffix, ".%ld.%.256s", (long int)original_euid, uts.nodename);
- config_file = Ufopen(filename, "rb");
- if (config_file == NULL)
- #endif /* CONFIGURE_FILE_USE_EUID */
+ if (!(config_file = Ufopen(filename, "rb")))
+# endif /* CONFIGURE_FILE_USE_EUID */
{
sprintf(CS suffix, ".%.256s", uts.nodename);
config_file = Ufopen(filename, "rb");
}
}
- #endif /* CONFIGURE_FILE_USE_NODE */
+# endif /* CONFIGURE_FILE_USE_NODE */
/* Otherwise, try the generic name, possibly with the euid added */
- #ifdef CONFIGURE_FILE_USE_EUID
- if (config_file == NULL)
+# ifdef CONFIGURE_FILE_USE_EUID
+ if (!config_file)
{
sprintf(CS suffix, ".%ld", (long int)original_euid);
config_file = Ufopen(filename, "rb");
}
- #endif /* CONFIGURE_FILE_USE_EUID */
+# endif /* CONFIGURE_FILE_USE_EUID */
/* Finally, try the unadorned name */
- if (config_file == NULL)
+ if (!config_file)
{
*suffix = 0;
config_file = Ufopen(filename, "rb");
}
- #else /* if neither defined */
+#else /* if neither defined */
/* This is the common case when the fancy processing is not included. */
config_file = Ufopen(filename, "rb");
- #endif
+#endif
/* If the file does not exist, continue to try any others. For any other
error, break out (and die). */
- if (config_file != NULL || errno != ENOENT) break;
+ if (config_file || errno != ENOENT) break;
}
/* On success, save the name for verification; config_filename is used when
config_main_directory = last_slash == filename ? US"/" : string_copyn(filename, last_slash - filename);
else
{
- /* relative configuration file name: working dir + / + basename(filename) */
+ /* relative configuration file name: working dir + / + basename(filename) */
- uschar buf[PATH_MAX];
- gstring * g;
+ uschar buf[PATH_MAX];
+ gstring * g;
- if (os_getcwd(buf, PATH_MAX) == NULL)
- {
- perror("exim: getcwd");
- exit(EXIT_FAILURE);
- }
- g = string_cat(NULL, buf);
+ if (os_getcwd(buf, PATH_MAX) == NULL)
+ {
+ perror("exim: getcwd");
+ exit(EXIT_FAILURE);
+ }
+ g = string_cat(NULL, buf);
- /* If the dir does not end with a "/", append one */
- if (g->s[g->ptr-1] != '/')
- g = string_catn(g, US"/", 1);
+ /* If the dir does not end with a "/", append one */
+ if (gstring_last_char(g) != '/')
+ g = string_catn(g, US"/", 1);
- /* If the config file contains a "/", extract the directory part */
- if (last_slash)
- g = string_catn(g, filename, last_slash - filename);
+ /* If the config file contains a "/", extract the directory part */
+ if (last_slash)
+ g = string_catn(g, filename, last_slash - filename);
- config_main_directory = string_from_gstring(g);
+ config_main_directory = string_from_gstring(g);
}
config_directory = config_main_directory;
}
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
to a safe place. Later we change to $spool_directory. */
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to stat configuration file %s",
big_buffer);
- if ((statbuf.st_uid != root_uid /* owner not root */
- #ifdef CONFIGURE_OWNER
- && statbuf.st_uid != config_uid /* owner not the special one */
- #endif
- ) || /* or */
- (statbuf.st_gid != root_gid /* group not root & */
- #ifdef CONFIGURE_GROUP
- && statbuf.st_gid != config_gid /* group not the special one */
- #endif
- && (statbuf.st_mode & 020) != 0) || /* group writeable */
- /* or */
- ((statbuf.st_mode & 2) != 0)) /* world writeable */
-
+ if ( statbuf.st_uid != root_uid /* owner not root */
+#ifdef CONFIGURE_OWNER
+ && statbuf.st_uid != config_uid /* owner not the special one */
+#endif
+ || /* or */
+ statbuf.st_gid != root_gid /* group not root & */
+#ifdef CONFIGURE_GROUP
+ && statbuf.st_gid != config_gid /* group not the special one */
+#endif
+ && (statbuf.st_mode & 020) != 0 /* group writeable */
+ || /* or */
+ (statbuf.st_mode & 2) != 0 /* world writeable */
+ )
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Exim configuration file %s has the "
"wrong owner, group, or mode", big_buffer);
if (statbuf.st_size > 8192)
{
rmark r = store_mark();
- void * dummy = store_get((int)statbuf.st_size, FALSE);
+ void * dummy = store_get((int)statbuf.st_size, GET_UNTAINTED);
store_reset(r);
}
}
if (isupper(*s))
{
- if (!macro_read_assignment(s)) exim_exit(EXIT_FAILURE, US"");
+ if (!macro_read_assignment(s)) exim_exit(EXIT_FAILURE);
continue;
}
read_named_list(&hostlist_anchor, &hostlist_count,
MAX_NAMED_LIST, t+8, US"host list", hide);
- else if (Ustrncmp(t, US"addresslist", 11) == 0)
+ else if (Ustrncmp(t, "addresslist", 11) == 0)
read_named_list(&addresslist_anchor, &addresslist_count,
MAX_NAMED_LIST, t+11, US"address list", hide);
- else if (Ustrncmp(t, US"localpartlist", 13) == 0)
+ else if (Ustrncmp(t, "localpartlist", 13) == 0)
read_named_list(&localpartlist_anchor, &localpartlist_count,
MAX_NAMED_LIST, t+13, US"local part list", hide);
/* If the timezone string is empty, set it to NULL, implying no TZ variable
wanted. */
-if (timezone_string != NULL && *timezone_string == 0) timezone_string = NULL;
+if (timezone_string && !*timezone_string) timezone_string = NULL;
/* The max retry interval must not be greater than 24 hours. */
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;
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;
"\"%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;
/* Expand pid_file_path */
-if (*pid_file_path != 0)
+if (*pid_file_path)
{
if (!(s = expand_string(pid_file_path)))
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "failed to expand pid_file_path "
/* Set default value of process_log_path */
-if (!process_log_path || *process_log_path =='\0')
+if (!process_log_path || !*process_log_path)
process_log_path = string_sprintf("%s/exim-process.info", spool_directory);
/* Compile the regex for matching a UUCP-style "From_" line in an incoming
message. */
-regex_From = regex_must_compile(uucp_from_pattern, FALSE, TRUE);
+regex_From = regex_must_compile(uucp_from_pattern, MCS_NOFLAGS, TRUE);
/* Unpick the SMTP rate limiting options, if set */
log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
"error in errors_reply_to (%s): %s", errors_reply_to, errmess);
- if (domain == 0)
+ if (!domain)
log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
"errors_reply_to (%s) does not contain a domain", errors_reply_to);
}
/* If smtp_accept_queue or smtp_accept_max_per_host is set, then
smtp_accept_max must also be set. */
-if (smtp_accept_max == 0 &&
- (smtp_accept_queue > 0 || smtp_accept_max_per_host != NULL))
+if (smtp_accept_max == 0 && (smtp_accept_queue > 0 || smtp_accept_max_per_host))
log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
"smtp_accept_max must be set if smtp_accept_queue or "
"smtp_accept_max_per_host is set");
host_number_string, expand_string_message);
n = Ustrtol(s, &end, 0);
while (isspace(*end)) end++;
- if (*end != 0)
+ if (*end)
log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
"localhost_number value is not a number: %s", s);
if (n > LOCALHOST_MAX)
{
int len = dd->options_len;
d->info = dd;
- d->options_block = store_get(len, FALSE);
+ d->options_block = store_get_perm(len, FALSE);
memcpy(d->options_block, dd->options_block, len);
for (int i = 0; i < *(dd->options_count); i++)
dd->options[i].type &= ~opt_set;
+static void
+driver_init_fini(driver_instance * d, const uschar * class)
+{
+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);
+}
+
+
/*************************************************
* Initialize driver list *
*************************************************/
optionlist *driver_optionlist,
int driver_optionlist_count)
{
-driver_instance **p = anchor;
-driver_instance *d = NULL;
-uschar *buffer;
+driver_instance ** p = anchor;
+driver_instance * d = NULL;
+uschar * buffer;
while ((buffer = get_config_line()))
{
- uschar name[64];
- uschar *s;
+ uschar name[EXIM_DRIVERNAME_MAX];
+ const uschar * s;
/* Read the first name on the line and test for the start of a new driver. A
macro definition indicates the end of the previous driver. If this isn't the
{
if (d)
{
- if (!d->driver_name)
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG,
- "no driver defined for %s \"%s\"", class, d->name);
/* s is using big_buffer, so this call had better not */
- (d->info->init)(d);
+ driver_init_fini(d, class);
d = NULL;
}
- if (!macro_read_assignment(buffer)) exim_exit(EXIT_FAILURE, US"");
+ if (!macro_read_assignment(buffer)) exim_exit(EXIT_FAILURE);
continue;
}
/* Finish off initializing the previous driver. */
if (d)
- {
- 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);
- }
+ driver_init_fini(d, class);
/* Check that we haven't already got a driver of this name */
/* Set up a new driver instance data block on the chain, with
its default values installed. */
- d = store_get(instance_size, FALSE);
+ d = store_get_perm(instance_size, FALSE);
memcpy(d, instance_default, instance_size);
*p = d;
p = &d->next;
d->name = string_copy(name);
+ d->srcfile = config_filename;
+ d->srcline = config_lineno;
/* Clear out the "set" bits in the generic options */
/* 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;
}
/* Run the initialization function for the final driver. */
if (d)
- {
- 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);
- }
+ driver_init_fini(d, class);
}
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++;
const uschar *pp;
uschar *error;
- next = store_get(sizeof(retry_config), FALSE);
+ next = store_get(sizeof(retry_config), GET_UNTAINTED);
next->next = NULL;
*chain = next;
chain = &(next->next);
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,
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);
+ retry_rule * rule = store_get(sizeof(retry_rule), GET_UNTAINTED);
*rchain = rule;
rchain = &(rule->next);
rule->next = NULL;
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");
}
}
for (auth_instance * 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))
+ 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->public_name);
+ au->client && bu->client ? US"client" : US"server",
+ au->name, bu->name, au->public_name);
#ifndef DISABLE_PIPE_CONNECT
nauths++;
#endif
}
+/* For error messages, a string describing the config location associated
+with current processing. NULL if we are not in an authenticator. */
+
+uschar *
+authenticator_current_name(void)
+{
+if (!authenticator_name) return NULL;
+return string_sprintf(" (authenticator %s, %s %d)", authenticator_name, driver_srcfile, driver_srcline);
+}
+
+
+
/*************************************************
static void
readconf_acl(void)
{
-uschar *p;
-
/* Read each ACL and add it into the tree. Macro (re)definitions are allowed
between ACLs. */
while(acl_line)
{
- uschar name[64];
- tree_node *node;
- uschar *error;
+ uschar name[EXIM_DRIVERNAME_MAX];
+ tree_node * node;
+ uschar * error;
+ const uschar * p = readconf_readname(name, sizeof(name), acl_line);
- 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;
}
if (*p != ':' || name[0] == 0)
log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "missing or malformed ACL name");
- node = store_get(sizeof(tree_node) + Ustrlen(name), is_tainted(name));
+ node = store_get_perm(sizeof(tree_node) + Ustrlen(name), name);
Ustrcpy(node->name, name);
if (!tree_insertnode(&acl_anchor, node))
log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
static config_line_item *current;
config_line_item *next;
-next = (config_line_item*) store_get(sizeof(config_line_item), FALSE);
+next = (config_line_item*) store_get(sizeof(config_line_item), GET_UNTAINTED);
next->line = string_copy(line);
next->next = NULL;
{
const int TS = terse ? 0 : 2;
int indent = 0;
+rmark r = NULL;
-for (config_line_item * i = config_lines; i; i = i->next)
+for (const config_line_item * i = config_lines; i; i = i->next)
{
- uschar *current;
- uschar *p;
+ uschar * current, * p;
+
+ if (r) store_reset(r);
+ r = store_mark();
/* skip over to the first non-space */
- for (current = i->line; *current && isspace(*current); ++current)
+ for (current = string_copy(i->line); *current && isspace(*current); ++current)
;
- if (*current == '\0')
+ if (!*current)
continue;
/* 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)
+ following characters: "'$, as this may indicate careful formatting */
+
+ for (p = current; *p; p++) if (isspace(*p))
{
uschar *next;
- if (!isspace(*p)) continue;
if (*p != ' ') *p = ' ';
for (next = p; isspace(*next); ++next)
/* rest is public */
printf("%*s%s\n", indent, "", current);
}
+if (r) store_reset(r);
}
#endif /*!MACRO_PREDEF*/