X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Freadconf.c;h=f213b2c57f41112200468f1cc68fa0ed3b932cb7;hp=cf4048aae030c6633a8728b258e716f7efa1c925;hb=d2af03f4c11273d6f52c9043119e24e732080885;hpb=cf00dad6749f2b3c2ba9b1e02034b23944b7ee0c diff --git a/src/src/readconf.c b/src/src/readconf.c index cf4048aae..f213b2c57 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -1,10 +1,8 @@ -/* $Cambridge: exim/src/src/readconf.c,v 1.7 2005/04/05 13:58:35 ph10 Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2005 */ +/* Copyright (c) University of Cambridge 1995 - 2014 */ /* See the file NOTICE for conditions of use and distribution. */ /* Functions for reading the configuration file, and for displaying @@ -138,9 +136,16 @@ static optionlist optionlist_config[] = { #ifdef WITH_CONTENT_SCAN { "acl_not_smtp_mime", opt_stringptr, &acl_not_smtp_mime }, #endif + { "acl_not_smtp_start", opt_stringptr, &acl_not_smtp_start }, { "acl_smtp_auth", opt_stringptr, &acl_smtp_auth }, { "acl_smtp_connect", opt_stringptr, &acl_smtp_connect }, { "acl_smtp_data", opt_stringptr, &acl_smtp_data }, +#ifdef EXPERIMENTAL_PRDR + { "acl_smtp_data_prdr", opt_stringptr, &acl_smtp_data_prdr }, +#endif +#ifndef DISABLE_DKIM + { "acl_smtp_dkim", opt_stringptr, &acl_smtp_dkim }, +#endif { "acl_smtp_etrn", opt_stringptr, &acl_smtp_etrn }, { "acl_smtp_expn", opt_stringptr, &acl_smtp_expn }, { "acl_smtp_helo", opt_stringptr, &acl_smtp_helo }, @@ -149,6 +154,7 @@ static optionlist optionlist_config[] = { #ifdef WITH_CONTENT_SCAN { "acl_smtp_mime", opt_stringptr, &acl_smtp_mime }, #endif + { "acl_smtp_notquit", opt_stringptr, &acl_smtp_notquit }, { "acl_smtp_predata", opt_stringptr, &acl_smtp_predata }, { "acl_smtp_quit", opt_stringptr, &acl_smtp_quit }, { "acl_smtp_rcpt", opt_stringptr, &acl_smtp_rcpt }, @@ -182,23 +188,48 @@ static optionlist optionlist_config[] = { { "callout_random_local_part",opt_stringptr, &callout_random_local_part }, { "check_log_inodes", opt_int, &check_log_inodes }, { "check_log_space", opt_Kint, &check_log_space }, + { "check_rfc2047_length", opt_bool, &check_rfc2047_length }, { "check_spool_inodes", opt_int, &check_spool_inodes }, { "check_spool_space", opt_Kint, &check_spool_space }, { "daemon_smtp_port", opt_stringptr|opt_hidden, &daemon_smtp_port }, { "daemon_smtp_ports", opt_stringptr, &daemon_smtp_port }, + { "daemon_startup_retries", opt_int, &daemon_startup_retries }, + { "daemon_startup_sleep", opt_time, &daemon_startup_sleep }, +#ifdef EXPERIMENTAL_DCC + { "dcc_direct_add_header", opt_bool, &dcc_direct_add_header }, + { "dccifd_address", opt_stringptr, &dccifd_address }, + { "dccifd_options", opt_stringptr, &dccifd_options }, +#endif { "delay_warning", opt_timelist, &delay_warning }, { "delay_warning_condition", opt_stringptr, &delay_warning_condition }, { "deliver_drop_privilege", opt_bool, &deliver_drop_privilege }, { "deliver_queue_load_max", opt_fixed, &deliver_queue_load_max }, { "delivery_date_remove", opt_bool, &delivery_date_remove }, +#ifdef ENABLE_DISABLE_FSYNC + { "disable_fsync", opt_bool, &disable_fsync }, +#endif + { "disable_ipv6", opt_bool, &disable_ipv6 }, +#ifndef DISABLE_DKIM + { "dkim_verify_signers", opt_stringptr, &dkim_verify_signers }, +#endif +#ifdef EXPERIMENTAL_DMARC + { "dmarc_forensic_sender", opt_stringptr, &dmarc_forensic_sender }, + { "dmarc_history_file", opt_stringptr, &dmarc_history_file }, + { "dmarc_tld_file", opt_stringptr, &dmarc_tld_file }, +#endif { "dns_again_means_nonexist", opt_stringptr, &dns_again_means_nonexist }, { "dns_check_names_pattern", opt_stringptr, &check_dns_names_pattern }, + { "dns_csa_search_limit", opt_int, &dns_csa_search_limit }, + { "dns_csa_use_reverse", opt_bool, &dns_csa_use_reverse }, + { "dns_dnssec_ok", opt_int, &dns_dnssec_ok }, { "dns_ipv4_lookup", opt_stringptr, &dns_ipv4_lookup }, { "dns_retrans", opt_time, &dns_retrans }, { "dns_retry", opt_int, &dns_retry }, + { "dns_use_edns0", opt_int, &dns_use_edns0 }, /* This option is now a no-op, retained for compability */ { "drop_cr", opt_bool, &drop_cr }, /*********************************************************/ + { "dsn_from", opt_stringptr, &dsn_from }, { "envelope_to_remove", opt_bool, &envelope_to_remove }, { "errors_copy", opt_stringptr, &errors_copy }, { "errors_reply_to", opt_stringptr, &errors_reply_to }, @@ -211,6 +242,14 @@ static optionlist optionlist_config[] = { { "freeze_tell", opt_stringptr, &freeze_tell }, { "gecos_name", opt_stringptr, &gecos_name }, { "gecos_pattern", opt_stringptr, &gecos_pattern }, +#ifdef SUPPORT_TLS + { "gnutls_allow_auto_pkcs11", opt_bool, &gnutls_allow_auto_pkcs11 }, + { "gnutls_compat_mode", opt_bool, &gnutls_compat_mode }, + /* These three gnutls_require_* options stopped working in Exim 4.80 */ + { "gnutls_require_kx", opt_stringptr, &gnutls_require_kx }, + { "gnutls_require_mac", opt_stringptr, &gnutls_require_mac }, + { "gnutls_require_protocols", opt_stringptr, &gnutls_require_proto }, +#endif { "header_line_maxsize", opt_int, &header_line_maxsize }, { "header_maxsize", opt_int, &header_maxsize }, { "headers_charset", opt_stringptr, &headers_charset }, @@ -233,7 +272,14 @@ static optionlist optionlist_config[] = { { "ignore_fromline_local", opt_bool, &ignore_fromline_local }, { "keep_malformed", opt_time, &keep_malformed }, #ifdef LOOKUP_LDAP + { "ldap_ca_cert_dir", opt_stringptr, &eldap_ca_cert_dir }, + { "ldap_ca_cert_file", opt_stringptr, &eldap_ca_cert_file }, + { "ldap_cert_file", opt_stringptr, &eldap_cert_file }, + { "ldap_cert_key", opt_stringptr, &eldap_cert_key }, + { "ldap_cipher_suite", opt_stringptr, &eldap_cipher_suite }, { "ldap_default_servers", opt_stringptr, &eldap_default_servers }, + { "ldap_require_cert", opt_stringptr, &eldap_require_cert }, + { "ldap_start_tls", opt_bool, &eldap_start_tls }, { "ldap_version", opt_int, &eldap_version }, #endif { "local_from_check", opt_bool, &local_from_check }, @@ -248,6 +294,7 @@ static optionlist optionlist_config[] = { { "log_timezone", opt_bool, &log_timezone }, { "lookup_open_max", opt_int, &lookup_open_max }, { "max_username_length", opt_int, &max_username_length }, + { "message_body_newlines", opt_bool, &message_body_newlines }, { "message_body_visible", opt_mkint, &message_body_visible }, { "message_id_header_domain", opt_stringptr, &message_id_domain }, { "message_id_header_text", opt_stringptr, &message_id_text }, @@ -261,6 +308,9 @@ static optionlist optionlist_config[] = { { "mysql_servers", opt_stringptr, &mysql_servers }, #endif { "never_users", opt_uidlist, &never_users }, +#ifdef SUPPORT_TLS + { "openssl_options", opt_stringptr, &openssl_options }, +#endif #ifdef LOOKUP_ORACLE { "oracle_servers", opt_stringptr, &oracle_servers }, #endif @@ -274,11 +324,17 @@ static optionlist optionlist_config[] = { #endif { "pid_file_path", opt_stringptr, &pid_file_path }, { "pipelining_advertise_hosts", opt_stringptr, &pipelining_advertise_hosts }, +#ifdef EXPERIMENTAL_PRDR + { "prdr_enable", opt_bool, &prdr_enable }, +#endif { "preserve_message_logs", opt_bool, &preserve_message_logs }, { "primary_hostname", opt_stringptr, &primary_hostname }, { "print_topbitchars", opt_bool, &print_topbitchars }, { "process_log_path", opt_stringptr, &process_log_path }, { "prod_requires_admin", opt_bool, &prod_requires_admin }, +#ifdef EXPERIMENTAL_PROXY + { "proxy_required_hosts", opt_stringptr, &proxy_required_hosts }, +#endif { "qualify_domain", opt_stringptr, &qualify_domain_sender }, { "qualify_recipient", opt_stringptr, &qualify_domain_recipient }, { "queue_domains", opt_stringptr, &queue_domains }, @@ -286,6 +342,7 @@ static optionlist optionlist_config[] = { { "queue_only", opt_bool, &queue_only }, { "queue_only_file", opt_stringptr, &queue_only_file }, { "queue_only_load", opt_fixed, &queue_only_load }, + { "queue_only_load_latch", opt_bool, &queue_only_load_latch }, { "queue_only_override", opt_bool, &queue_only_override }, { "queue_run_in_order", opt_bool, &queue_run_in_order }, { "queue_run_max", opt_int, &queue_run_max }, @@ -296,6 +353,9 @@ static optionlist optionlist_config[] = { { "recipient_unqualified_hosts", opt_stringptr, &recipient_unqualified_hosts }, { "recipients_max", opt_int, &recipients_max }, { "recipients_max_reject", opt_bool, &recipients_max_reject }, +#ifdef EXPERIMENTAL_REDIS + { "redis_servers", opt_stringptr, &redis_servers }, +#endif { "remote_max_parallel", opt_int, &remote_max_parallel }, { "remote_sort_domains", opt_stringptr, &remote_sort_domains }, { "retry_data_expire", opt_time, &retry_data_expire }, @@ -332,12 +392,25 @@ static optionlist optionlist_config[] = { { "smtp_return_error_details",opt_bool, &smtp_return_error_details }, #ifdef WITH_CONTENT_SCAN { "spamd_address", opt_stringptr, &spamd_address }, +#endif +#ifdef EXPERIMENTAL_SPF + { "spf_guess", opt_stringptr, &spf_guess }, #endif { "split_spool_directory", opt_bool, &split_spool_directory }, { "spool_directory", opt_stringptr, &spool_directory }, +#ifdef LOOKUP_SQLITE + { "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 }, { "strip_trailing_dot", opt_bool, &strip_trailing_dot }, { "syslog_duplication", opt_bool, &syslog_duplication }, @@ -352,14 +425,21 @@ static optionlist optionlist_config[] = { { "system_filter_reply_transport",opt_stringptr,&system_filter_reply_transport }, { "system_filter_user", opt_uid, &system_filter_uid }, { "tcp_nodelay", opt_bool, &tcp_nodelay }, +#ifdef USE_TCP_WRAPPERS + { "tcp_wrappers_daemon_name", opt_stringptr, &tcp_wrappers_daemon_name }, +#endif { "timeout_frozen_after", opt_time, &timeout_frozen_after }, { "timezone", opt_stringptr, &timezone_string }, #ifdef SUPPORT_TLS { "tls_advertise_hosts", opt_stringptr, &tls_advertise_hosts }, { "tls_certificate", opt_stringptr, &tls_certificate }, { "tls_crl", opt_stringptr, &tls_crl }, + { "tls_dh_max_bits", opt_int, &tls_dh_max_bits }, { "tls_dhparam", opt_stringptr, &tls_dhparam }, - { "tls_on_connect_ports", opt_stringptr, &tls_on_connect_ports }, +# if defined(EXPERIMENTAL_OCSP) + { "tls_ocsp_file", opt_stringptr, &tls_ocsp_file }, +# endif + { "tls_on_connect_ports", opt_stringptr, &tls_in.on_connect_ports }, { "tls_privatekey", opt_stringptr, &tls_privatekey }, { "tls_remember_esmtp", opt_bool, &tls_remember_esmtp }, { "tls_require_ciphers", opt_stringptr, &tls_require_ciphers }, @@ -464,7 +544,7 @@ while (isalnum(*s) || *s == '_') { if (namelen >= sizeof(name) - 1) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, - "macro name too long (maximum is %d characters)", sizeof(name) - 1); + "macro name too long (maximum is " SIZE_T_FMT " characters)", sizeof(name) - 1); name[namelen++] = *s++; } name[namelen] = 0; @@ -593,7 +673,7 @@ for (;;) { if (config_file_stack != NULL) /* EOF inside .include */ { - fclose(config_file); + (void)fclose(config_file); config_file = config_file_stack->file; config_filename = config_file_stack->filename; config_lineno = config_file_stack->lineno; @@ -797,6 +877,10 @@ for (;;) } *t = 0; + if (*ss != '/') + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, ".include specifies a non-" + "absolute path \"%s\"", ss); + if (include_if_exists != 0 && (Ustat(ss, &statbuf) != 0)) continue; save = store_get(sizeof(config_file_item)); @@ -1314,6 +1398,8 @@ int intbase = 0; uschar *inttype = US""; uschar *sptr; uschar *s = buffer; +uschar *saved_condition, *strtemp; +uschar **str_target; uschar name[64]; uschar name2[64]; @@ -1367,13 +1453,9 @@ if (ol == NULL) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, CS unknown_txt, name); } -if ((ol->type & opt_set) != 0) - { - uschar *mname = name; - if (Ustrncmp(mname, "no_", 3) == 0) mname += 3; +if ((ol->type & opt_set) && !(ol->type & (opt_rep_con | opt_rep_str))) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, - "\"%s\" option set for the second time", mname); - } + "\"%s\" option set for the second time", name); ol->type |= opt_set | issecure; type = ol->type & opt_mask; @@ -1453,6 +1535,55 @@ switch (type) control block and flags word. */ case opt_stringptr: + if (data_block == NULL) + str_target = (uschar **)(ol->value); + else + str_target = (uschar **)((uschar *)data_block + (long int)(ol->value)); + if (ol->type & opt_rep_con) + { + /* 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. */ + saved_condition = *str_target; + strtemp = string_sprintf("${if and{{bool_lax{%s}}{bool_lax{%s}}}}", + saved_condition, sptr); + *str_target = string_copy_malloc(strtemp); + /* 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 = Ustrncmp(name, "headers_add", 11)==0 ? '\n' : ':'; + uschar * cp; + + /* Strip trailing whitespace and seperators */ + for (cp = sptr + Ustrlen(sptr) - 1; + cp >= sptr && (*cp == '\n' || *cp == '\t' || *cp == ' ' || *cp == sep); + cp--) *cp = '\0'; + + if (cp >= sptr) + *str_target = string_copy_malloc( + *str_target ? string_sprintf("%s%c%s", *str_target, sep, sptr) + : sptr); + } + else + { + *str_target = sptr; + freesptr = FALSE; + } + break; + case opt_rewrite: if (data_block == NULL) *((uschar **)(ol->value)) = sptr; @@ -1612,10 +1743,16 @@ switch (type) int count = 1; uid_t *list; int ptr = 0; - uschar *p = sptr; + uschar *p; + 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++ == ':') count++; + while (*p != 0) if (*p++ == ':' && *p != 0) count++; list = store_malloc(count*sizeof(uid_t)); list[ptr++] = (uid_t)(count - 1); @@ -1624,7 +1761,7 @@ switch (type) else *((uid_t **)((uschar *)data_block + (long int)(ol->value))) = list; - p = sptr; + p = op; while (count-- > 1) { int sep = 0; @@ -1647,10 +1784,16 @@ switch (type) int count = 1; gid_t *list; int ptr = 0; - uschar *p = sptr; + uschar *p; + 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++ == ':') count++; + while (*p != 0) if (*p++ == ':' && *p != 0) count++; list = store_malloc(count*sizeof(gid_t)); list[ptr++] = (gid_t)(count - 1); @@ -1659,7 +1802,7 @@ switch (type) else *((gid_t **)((uschar *)data_block + (long int)(ol->value))) = list; - p = sptr; + p = op; while (count-- > 1) { int sep = 0; @@ -1787,8 +1930,10 @@ switch (type) case opt_int: { uschar *endptr; + long int lvalue; + errno = 0; - value = strtol(CS s, CSS &endptr, intbase); + lvalue = strtol(CS s, CSS &endptr, intbase); if (endptr == s) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "%sinteger expected for %s", @@ -1798,25 +1943,28 @@ switch (type) { if (tolower(*endptr) == 'k') { - if (value > INT_MAX/1024 || value < INT_MIN/1024) errno = ERANGE; - else value *= 1024; + if (lvalue > INT_MAX/1024 || lvalue < INT_MIN/1024) errno = ERANGE; + else lvalue *= 1024; endptr++; } else if (tolower(*endptr) == 'm') { - if (value > INT_MAX/(1024*1024) || value < INT_MIN/(1024*1024)) + if (lvalue > INT_MAX/(1024*1024) || lvalue < INT_MIN/(1024*1024)) errno = ERANGE; - else value *= 1024*1024; + else lvalue *= 1024*1024; endptr++; } } - if (errno == ERANGE) 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 != 0) extra_chars_error(endptr, inttype, US"integer value for ", name); + + value = (int)lvalue; } if (data_block == NULL) @@ -1984,6 +2132,12 @@ readconf_printtime(int t) int s, m, h, d, w; uschar *p = time_buffer; +if (t < 0) + { + *p++ = '-'; + t = -t; + } + s = t % 60; t /= 60; m = t % 60; @@ -2023,13 +2177,14 @@ Arguments: resides. oltop points to the option list in which ol exists last one more than the offset of the last entry in optop + no_labels do not show "foo = " at the start. Returns: nothing */ static void print_ol(optionlist *ol, uschar *name, void *options_block, - optionlist *oltop, int last) + optionlist *oltop, int last, BOOL no_labels) { struct passwd *pw; struct group *gr; @@ -2051,7 +2206,11 @@ if (ol == NULL) if (!admin_user && (ol->type & opt_secure) != 0) { - printf("%s = \n", name); + const char * const hidden = ""; + if (no_labels) + printf("%s\n", hidden); + else + printf("%s = %s\n", name, hidden); return; } @@ -2070,11 +2229,13 @@ switch(ol->type & opt_mask) case opt_stringptr: case opt_rewrite: /* Show the text value */ s = *((uschar **)value); - printf("%s = %s\n", name, (s == NULL)? US"" : string_printing2(s, FALSE)); + if (!no_labels) printf("%s = ", name); + printf("%s\n", (s == NULL)? US"" : string_printing2(s, FALSE)); break; case opt_int: - printf("%s = %d\n", name, *((int *)value)); + if (!no_labels) printf("%s = ", name); + printf("%d\n", *((int *)value)); break; case opt_mkint: @@ -2089,23 +2250,30 @@ switch(ol->type & opt_mask) c = 'M'; x >>= 10; } - printf("%s = %d%c\n", name, x, c); + if (!no_labels) printf("%s = ", name); + printf("%d%c\n", x, c); + } + else + { + if (!no_labels) printf("%s = ", name); + printf("%d\n", x); } - else printf("%s = %d\n", name, x); } break; case opt_Kint: { int x = *((int *)value); - if (x == 0) printf("%s = 0\n", name); - else if ((x & 1023) == 0) printf("%s = %dM\n", name, x >> 10); - else printf("%s = %dK\n", name, x); + if (!no_labels) printf("%s = ", name); + if (x == 0) printf("0\n"); + else if ((x & 1023) == 0) printf("%dM\n", x >> 10); + else printf("%dK\n", x); } break; case opt_octint: - printf("%s = %#o\n", name, *((int *)value)); + if (!no_labels) printf("%s = ", name); + printf("%#o\n", *((int *)value)); break; /* Can be negative only when "unset", in which case integer */ @@ -2117,7 +2285,8 @@ switch(ol->type & opt_mask) int d = 100; if (x < 0) printf("%s =\n", name); else { - printf("%s = %d.", name, x/1000); + if (!no_labels) printf("%s = ", name); + printf("%d.", x/1000); do { printf("%d", f/d); @@ -2143,7 +2312,8 @@ switch(ol->type & opt_mask) if (options_block != NULL) value2 = (void *)((uschar *)options_block + (long int)value2); s = *((uschar **)value2); - printf("%s = %s\n", name, (s == NULL)? US"" : string_printing(s)); + if (!no_labels) printf("%s = ", name); + printf("%s\n", (s == NULL)? US"" : string_printing(s)); break; } } @@ -2151,14 +2321,15 @@ switch(ol->type & opt_mask) /* Else fall through */ case opt_uid: + if (!no_labels) printf("%s = ", name); if (! *get_set_flag(name, oltop, last, options_block)) - printf("%s =\n", name); + printf("\n"); else { pw = getpwuid(*((uid_t *)value)); if (pw == NULL) - printf("%s = %ld\n", name, (long int)(*((uid_t *)value))); - else printf("%s = %s\n", name, pw->pw_name); + printf("%ld\n", (long int)(*((uid_t *)value))); + else printf("%s\n", pw->pw_name); } break; @@ -2175,7 +2346,8 @@ switch(ol->type & opt_mask) if (options_block != NULL) value2 = (void *)((uschar *)options_block + (long int)value2); s = *((uschar **)value2); - printf("%s = %s\n", name, (s == NULL)? US"" : string_printing(s)); + if (!no_labels) printf("%s = ", name); + printf("%s\n", (s == NULL)? US"" : string_printing(s)); break; } } @@ -2183,31 +2355,34 @@ switch(ol->type & opt_mask) /* Else fall through */ case opt_gid: + if (!no_labels) printf("%s = ", name); if (! *get_set_flag(name, oltop, last, options_block)) - printf("%s =\n", name); + printf("\n"); else { gr = getgrgid(*((int *)value)); if (gr == NULL) - printf("%s = %ld\n", name, (long int)(*((int *)value))); - else printf("%s = %s\n", name, gr->gr_name); + printf("%ld\n", (long int)(*((int *)value))); + else printf("%s\n", gr->gr_name); } break; case opt_uidlist: uidlist = *((uid_t **)value); - printf("%s =", name); + if (!no_labels) printf("%s =", name); if (uidlist != NULL) { int i; uschar sep = ' '; + if (no_labels) sep = '\0'; for (i = 1; i <= (int)(uidlist[0]); i++) { uschar *name = NULL; pw = getpwuid(uidlist[i]); if (pw != NULL) name = US pw->pw_name; - if (name != NULL) printf("%c%s", sep, name); - else printf("%c%ld", sep, (long int)(uidlist[i])); + if (sep != '\0') printf("%c", sep); + if (name != NULL) printf("%s", name); + else printf("%ld", (long int)(uidlist[i])); sep = ':'; } } @@ -2216,18 +2391,20 @@ switch(ol->type & opt_mask) case opt_gidlist: gidlist = *((gid_t **)value); - printf("%s =", name); + if (!no_labels) printf("%s =", name); if (gidlist != NULL) { int i; uschar sep = ' '; + if (no_labels) sep = '\0'; for (i = 1; i <= (int)(gidlist[0]); i++) { uschar *name = NULL; gr = getgrgid(gidlist[i]); if (gr != NULL) name = US gr->gr_name; - if (name != NULL) printf("%c%s", sep, name); - else printf("%c%ld", sep, (long int)(gidlist[i])); + if (sep != '\0') printf("%c", sep); + if (name != NULL) printf("%s", name); + else printf("%ld", (long int)(gidlist[i])); sep = ':'; } } @@ -2235,14 +2412,15 @@ switch(ol->type & opt_mask) break; case opt_time: - printf("%s = %s\n", name, readconf_printtime(*((int *)value))); + if (!no_labels) printf("%s = ", name); + printf("%s\n", readconf_printtime(*((int *)value))); break; case opt_timelist: { int i; int *list = (int *)value; - printf("%s = ", name); + if (!no_labels) printf("%s = ", name); for (i = 0; i < list[1]; i++) printf("%s%s", (i == 0)? "" : ":", readconf_printtime(list[i+2])); printf("\n"); @@ -2265,7 +2443,8 @@ switch(ol->type & opt_mask) s = *((uschar **)value2); if (s != NULL) { - printf("%s = %s\n", name, string_printing(s)); + if (!no_labels) printf("%s = ", name); + printf("%s\n", string_printing(s)); break; } /* s == NULL => string not set; fall through */ @@ -2296,30 +2475,34 @@ second argument is NULL. There are some special values: routers print the routers' configurations transports print the transports' configuration authenticators print the authenticators' configuration + macros print the macros' configuration router_list print a list of router names transport_list print a list of transport names authenticator_list print a list of authentication mechanism names + macro_list print a list of macro names +name print a named list item local_scan print the local_scan options -If the second argument is not NULL, it must be one of "router", "transport", or -"authenticator" in which case the first argument identifies the driver whose -options are to be printed. +If the second argument is not NULL, it must be one of "router", "transport", +"authenticator" or "macro" in which case the first argument identifies the +driver whose options are to be printed. Arguments: name option name if type == NULL; else driver name type NULL or driver type name, as described above + no_labels avoid the "foo = " at the start of an item Returns: nothing */ void -readconf_print(uschar *name, uschar *type) +readconf_print(uschar *name, uschar *type, BOOL no_labels) { BOOL names_only = FALSE; optionlist *ol; optionlist *ol2 = NULL; driver_instance *d = NULL; +macro_item *m; int size = 0; if (type == NULL) @@ -2340,8 +2523,11 @@ if (type == NULL) if (t != NULL) { found = TRUE; - printf("%slist %s = %s\n", types[i], name+1, - ((namedlist_block *)(t->data.ptr))->string); + if (no_labels) + printf("%s\n", ((namedlist_block *)(t->data.ptr))->string); + else + printf("%slist %s = %s\n", types[i], name+1, + ((namedlist_block *)(t->data.ptr))->string); } } @@ -2364,7 +2550,9 @@ if (type == NULL) ol < optionlist_config + optionlist_config_size; ol++) { if ((ol->type & opt_hidden) == 0) - print_ol(ol, US ol->name, NULL, optionlist_config, optionlist_config_size); + print_ol(ol, US ol->name, NULL, + optionlist_config, optionlist_config_size, + no_labels); } return; } @@ -2378,7 +2566,7 @@ if (type == NULL) ol < local_scan_options + local_scan_options_count; ol++) { print_ol(ol, US ol->name, NULL, local_scan_options, - local_scan_options_count); + local_scan_options_count, no_labels); } #endif return; @@ -2401,11 +2589,10 @@ if (type == NULL) name = NULL; } - else if (Ustrcmp(name, "authenticator_list") == 0) + else if (Ustrcmp(name, "macros") == 0) { - type = US"authenticator"; + type = US"macro"; name = NULL; - names_only = TRUE; } else if (Ustrcmp(name, "router_list") == 0) @@ -2414,16 +2601,32 @@ if (type == NULL) name = NULL; names_only = TRUE; } + else if (Ustrcmp(name, "transport_list") == 0) { type = US"transport"; name = NULL; names_only = TRUE; } + + else if (Ustrcmp(name, "authenticator_list") == 0) + { + type = US"authenticator"; + name = NULL; + names_only = TRUE; + } + + else if (Ustrcmp(name, "macro_list") == 0) + { + type = US"macro"; + name = NULL; + names_only = TRUE; + } + else { print_ol(find_option(name, optionlist_config, optionlist_config_size), - name, NULL, optionlist_config, optionlist_config_size); + name, NULL, optionlist_config, optionlist_config_size, no_labels); return; } } @@ -2453,6 +2656,32 @@ else if (Ustrcmp(type, "authenticator") == 0) size = optionlist_auths_size; } +else if (Ustrcmp(type, "macro") == 0) + { + /* People store passwords in macros and they were previously not available + for printing. So we have an admin_users restriction. */ + if (!admin_user) + { + fprintf(stderr, "exim: permission denied\n"); + exit(EXIT_FAILURE); + } + for (m = macros; m != NULL; m = m->next) + { + if (name == NULL || Ustrcmp(name, m->name) == 0) + { + if (names_only) + printf("%s\n", CS m->name); + else + printf("%s=%s\n", CS m->name, CS m->replacement); + if (name != NULL) + return; + } + } + if (name != NULL) + printf("%s %s not found\n", type, name); + return; + } + if (names_only) { for (; d != NULL; d = d->next) printf("%s\n", CS d->name); @@ -2470,14 +2699,14 @@ for (; d != NULL; d = d->next) for (ol = ol2; ol < ol2 + size; ol++) { if ((ol->type & opt_hidden) == 0) - print_ol(ol, US ol->name, d, ol2, size); + print_ol(ol, US ol->name, d, ol2, size, no_labels); } for (ol = d->info->options; ol < d->info->options + *(d->info->options_count); ol++) { if ((ol->type & opt_hidden) == 0) - print_ol(ol, US ol->name, d, d->info->options, *(d->info->options_count)); + print_ol(ol, US ol->name, d, d->info->options, *(d->info->options_count), no_labels); } if (name != NULL) return; } @@ -2601,6 +2830,71 @@ log_write(0, LOG_MAIN|LOG_PANIC_DIE, "malformed ratelimit data: %s", s); +/************************************************* +* Drop privs for checking TLS config * +*************************************************/ + +/* We want to validate TLS options during readconf, but do not want to be +root when we call into the TLS library, in case of library linkage errors +which cause segfaults; before this check, those were always done as the Exim +runtime user and it makes sense to continue with that. + +Assumes: tls_require_ciphers has been set, if it will be + exim_user has been set, if it will be + exim_group has been set, if it will be + +Returns: bool for "okay"; false will cause caller to immediately exit. +*/ + +#ifdef SUPPORT_TLS +static BOOL +tls_dropprivs_validate_require_cipher(void) +{ +const uschar *errmsg; +pid_t pid; +int rc, status; +void (*oldsignal)(int); + +oldsignal = signal(SIGCHLD, SIG_DFL); + +fflush(NULL); +if ((pid = fork()) < 0) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, "fork failed for TLS check"); + +if (pid == 0) + { + /* in some modes, will have dropped privilege already */ + if (!geteuid()) + exim_setugid(exim_uid, exim_gid, FALSE, + US"calling tls_validate_require_cipher"); + + errmsg = tls_validate_require_cipher(); + if (errmsg) + { + log_write(0, LOG_PANIC_DIE|LOG_CONFIG, + "tls_require_ciphers invalid: %s", errmsg); + } + fflush(NULL); + _exit(0); + } + +do { + rc = waitpid(pid, &status, 0); +} while (rc < 0 && errno == EINTR); + +DEBUG(D_tls) + debug_printf("tls_validate_require_cipher child %d ended: status=0x%x\n", + (int)pid, status); + +signal(SIGCHLD, oldsignal); + +return status == 0; +} +#endif /* SUPPORT_TLS */ + + + + /************************************************* * Read main configuration options * *************************************************/ @@ -2714,22 +3008,21 @@ else "configuration file %s", filename)); } -/* Check the status of the file we have opened, unless it was specified on -the command line, in which case privilege was given away at the start. */ +/* Check the status of the file we have opened, if we have retained root +privileges and the file isn't /dev/null (which *should* be 0666). */ -if (!config_changed) +if (trusted_config && Ustrcmp(filename, US"/dev/null")) { if (fstat(fileno(config_file), &statbuf) != 0) 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 */ - statbuf.st_uid != exim_uid /* owner not exim */ + 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 != exim_gid /* group not exim & */ + (statbuf.st_gid != root_gid /* group not root & */ #ifdef CONFIGURE_GROUP && statbuf.st_gid != config_gid /* group not the special one */ #endif @@ -2782,10 +3075,19 @@ wanted. */ if (timezone_string != NULL && *timezone_string == 0) timezone_string = NULL; +/* The max retry interval must not be greater than 24 hours. */ + +if (retry_interval_max > 24*60*60) retry_interval_max = 24*60*60; + /* remote_max_parallel must be > 0 */ if (remote_max_parallel <= 0) remote_max_parallel = 1; +/* Save the configured setting of freeze_tell, so we can re-instate it at the +start of a new SMTP message. */ + +freeze_tell_config = freeze_tell; + /* The primary host name may be required for expansion of spool_directory and log_file_path, so make sure it is set asap. It is obtained from uname(), but if that yields an unqualified value, make a FQDN by using gethostbyname to @@ -2806,9 +3108,9 @@ if (primary_hostname == NULL) struct hostent *hostdata; #if HAVE_IPV6 - if (dns_ipv4_lookup == NULL || + if (!disable_ipv6 && (dns_ipv4_lookup == NULL || match_isinlist(hostname, &dns_ipv4_lookup, 0, NULL, NULL, MCL_DOMAIN, - TRUE, NULL) != OK) + TRUE, NULL) != OK)) af = AF_INET6; #else af = AF_INET; @@ -2863,8 +3165,8 @@ if (s == NULL) spool_directory = s; /* Expand log_file_path, which must contain "%s" in any component that isn't -the null string or "syslog". It is also allowed to contain one instance of %D. -However, it must NOT contain % followed by anything else. */ +the null string or "syslog". It is also allowed to contain one instance of %D +or %M. However, it must NOT contain % followed by anything else. */ if (*log_file_path != 0) { @@ -2888,7 +3190,7 @@ if (*log_file_path != 0) t = Ustrchr(sss, '%'); if (t != NULL) { - if (t[1] != 'D' || Ustrchr(t+2, '%') != NULL) + if ((t[1] != 'D' && t[1] != 'M') || Ustrchr(t+2, '%') != NULL) log_write(0, LOG_MAIN|LOG_PANIC_DIE, "log_file_path \"%s\" contains " "unexpected \"%%\" character", s); } @@ -2937,6 +3239,11 @@ if (*pid_file_path != 0) pid_file_path = s; } +/* Set default value of process_log_path */ + +if (process_log_path == NULL || *process_log_path =='\0') + 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. */ @@ -3010,9 +3317,14 @@ so as to ensure that everything else is set up before the expansion. */ if (host_number_string != NULL) { + long int n; uschar *end; uschar *s = expand_string(host_number_string); - long int n = Ustrtol(s, &end, 0); + if (s == NULL) + log_write(0, LOG_MAIN|LOG_PANIC_DIE, + "failed to expand localhost_number \"%s\": %s", + host_number_string, expand_string_message); + n = Ustrtol(s, &end, 0); while (isspace(*end)) end++; if (*end != 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG, @@ -3032,6 +3344,32 @@ if ((tls_verify_hosts != NULL || tls_try_verify_hosts != NULL) && log_write(0, LOG_PANIC_DIE|LOG_CONFIG, "tls_%sverify_hosts is set, but tls_verify_certificates is not set", (tls_verify_hosts != NULL)? "" : "try_"); + +/* This also checks that the library linkage is working and we can call +routines in it, so call even if tls_require_ciphers is unset */ +if (!tls_dropprivs_validate_require_cipher()) + exit(1); + +/* Magic number: at time of writing, 1024 has been the long-standing value +used by so many clients, and what Exim used to use always, that it makes +sense to just min-clamp this max-clamp at that. */ +if (tls_dh_max_bits < 1024) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG, + "tls_dh_max_bits is too small, must be at least 1024 for interop"); + +/* If openssl_options is set, validate it */ +if (openssl_options != NULL) + { +# ifdef USE_GNUTLS + log_write(0, LOG_PANIC_DIE|LOG_CONFIG, + "openssl_options is set but we're using GnuTLS"); +# else + long dummy; + if (!(tls_openssl_options_parse(openssl_options, &dummy))) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG, + "openssl_options parse error: %s", openssl_options); +# endif + } #endif } @@ -3375,7 +3713,9 @@ else if (len == 7 && strncmpic(pp, US"timeout", len) == 0) } } -else if (strncmpic(pp, US"rcpt_4", 6) == 0) +else if (strncmpic(pp, US"mail_4", 6) == 0 || + strncmpic(pp, US"rcpt_4", 6) == 0 || + strncmpic(pp, US"data_4", 6) == 0) { BOOL bad = FALSE; int x = 255; /* means "any 4xx code" */ @@ -3392,21 +3732,27 @@ else if (strncmpic(pp, US"rcpt_4", 6) == 0) else if (a != 'x' || b != 'x') bad = TRUE; } - if (bad) return US"rcpt_4 must be followed by xx, dx, or dd, where " - "x is literal and d is any digit"; + if (bad) + return string_sprintf("%.4s_4 must be followed by xx, dx, or dd, where " + "x is literal and d is any digit", pp); - *basic_errno = ERRNO_RCPT4XX; + *basic_errno = (*pp == 'm')? ERRNO_MAIL4XX : + (*pp == 'r')? ERRNO_RCPT4XX : ERRNO_DATA4XX; *more_errno = x << 8; } else if (len == 4 && strncmpic(pp, US"auth", len) == 0 && strncmpic(q+1, US"failed", p-q-1) == 0) - { *basic_errno = ERRNO_AUTHFAIL; - } + +else if (strncmpic(pp, US"lost_connection", p - pp) == 0) + *basic_errno = ERRNO_SMTPCLOSED; + +else if (strncmpic(pp, US"tls_required", p - pp) == 0) + *basic_errno = ERRNO_TLSREQUIRED; else if (len != 1 || Ustrncmp(pp, "*", 1) != 0) - return string_sprintf("unknown or malformed retry error \"%.*s\"", p-pp, pp); + return string_sprintf("unknown or malformed retry error \"%.*s\"", (int) (p-pp), pp); return NULL; } @@ -3495,7 +3841,7 @@ while ((p = get_config_line()) != NULL) pp = p; while (mac_isgraph(*p)) p++; if (p - pp <= 0) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, - "missing error type"); + "missing error type in retry rule"); /* Test error names for things we understand. */ @@ -3541,6 +3887,7 @@ while ((p = get_config_line()) != NULL) break; case 'G': /* Geometrically increasing intervals */ + case 'H': /* Ditto, but with randomness */ rule->p1 = retry_arg(&p, 0); rule->p2 = retry_arg(&p, 1); break; @@ -3551,7 +3898,7 @@ while ((p = get_config_line()) != NULL) } if (rule->timeout <= 0 || rule->p1 <= 0 || - (rule->rule == 'G' && rule->p2 < 1000)) + (rule->rule != 'F' && rule->p2 < 1000)) log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "bad parameters for retry rule"); @@ -3637,27 +3984,15 @@ return acl_line; /* Now the main function: -Arguments: - skip TRUE when this Exim process is doing something that will - not need the ACL data - +Arguments: none Returns: nothing */ static void -readconf_acl(BOOL skip) +readconf_acl(void) { uschar *p; -/* Not receiving messages, don't need to parse the ACL data */ - -if (skip) - { - DEBUG(D_acl) debug_printf("skipping ACL configuration - not needed\n"); - while ((p = get_config_line()) != NULL); - return; - } - /* Read each ACL and add it into the tree. Macro (re)definitions are allowed between ACLs. */ @@ -3742,9 +4077,7 @@ Because it may confuse people as to whether the names are singular or plural, we add "s" if it's missing. There is always enough room in next_section for this. This function is basically just a switch. -Arguments: - skip_acl TRUE if ACL information is not needed - +Arguments: none Returns: nothing */ @@ -3758,7 +4091,7 @@ static uschar *section_list[] = { US"transports"}; void -readconf_rest(BOOL skip_acl) +readconf_rest(void) { int had = 0; @@ -3791,7 +4124,7 @@ while(next_section[0] != 0) switch(mid) { - case 0: readconf_acl(skip_acl); break; + case 0: readconf_acl(); break; case 1: auths_init(); break; case 2: local_scan_init(); break; case 3: readconf_retries(); break; @@ -3801,7 +4134,9 @@ while(next_section[0] != 0) } } -fclose(config_file); +(void)fclose(config_file); } +/* vi: aw ai sw=2 +*/ /* End of readconf.c */