X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/298849d8ea217fd104d167f5233bd11240b3ddae..dbac5a049acbe645a816b4a5e895c5be0de53483:/src/src/readconf.c diff --git a/src/src/readconf.c b/src/src/readconf.c index 2bf1e63d6..34ebf8769 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -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 @@ -67,6 +68,9 @@ static optionlist optionlist_config[] = { { "add_environment", opt_stringptr, {&add_environment} }, { "admin_groups", opt_gidlist, {&admin_groups} }, { "allow_domain_literals", opt_bool, {&allow_domain_literals} }, +#ifdef ALLOW_INSECURE_TAINTED_DATA + { "allow_insecure_tainted_data", opt_bool, {&allow_insecure_tainted_data} }, +#endif { "allow_mx_to_ip", opt_bool, {&allow_mx_to_ip} }, { "allow_utf8_domains", opt_bool, {&allow_utf8_domains} }, { "auth_advertise_hosts", opt_stringptr, {&auth_advertise_hosts} }, @@ -119,6 +123,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 @@ -179,6 +184,7 @@ static optionlist optionlist_config[] = { #ifdef SUPPORT_PROXY { "hosts_proxy", opt_stringptr, {&hosts_proxy} }, #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} }, @@ -198,6 +204,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 +266,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,12 +307,13 @@ 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} }, { "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} }, @@ -324,6 +337,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 +346,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 +390,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 +427,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 +444,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 +689,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; @@ -692,7 +706,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"); @@ -704,7 +718,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 @@ -796,7 +810,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 @@ -808,8 +822,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 @@ -872,7 +885,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; } @@ -1045,7 +1058,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] == '\"') @@ -1136,7 +1149,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); @@ -1170,17 +1183,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; } @@ -1304,7 +1326,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) @@ -1343,10 +1365,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, @@ -1416,15 +1438,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; @@ -1545,7 +1567,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 == '$') @@ -1619,8 +1641,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. */ @@ -1844,6 +1866,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); @@ -1988,6 +2011,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", @@ -2029,6 +2053,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", @@ -2079,7 +2104,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) @@ -2416,7 +2441,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; @@ -2426,7 +2451,7 @@ void *value; uid_t *uidlist; gid_t *gidlist; uschar *s; -uschar name2[64]; +uschar name2[EXIM_DRIVERNAME_MAX]; if (!ol) { @@ -2462,7 +2487,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: @@ -2726,7 +2751,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; @@ -2865,7 +2890,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); } @@ -3001,13 +3026,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, @@ -3020,7 +3045,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; @@ -3111,6 +3136,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))) { @@ -3213,12 +3239,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 @@ -3342,10 +3368,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; @@ -3355,33 +3382,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; @@ -3424,6 +3447,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; @@ -3697,7 +3721,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 @@ -3763,7 +3787,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; } @@ -4016,7 +4040,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++; @@ -4057,7 +4081,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, @@ -4074,22 +4098,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; @@ -4122,13 +4146,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"); } } @@ -4226,7 +4249,7 @@ acl_line = get_config_line(); while(acl_line) { - uschar name[64]; + uschar name[EXIM_DRIVERNAME_MAX]; tree_node *node; uschar *error;