X-Git-Url: https://vcs.fsf.org/?p=exim.git;a=blobdiff_plain;f=src%2Fsrc%2Freadconf.c;h=60df37afc4bb321e9b535c784919dd69b27de1b8;hp=c3514aba5ddac919d3925e0af5c514f519fdf59e;hb=1ad6489edabbfce85ee7362370cee4b34739c234;hpb=83da1223921fe30362e8374951360dcc8f21c4e7 diff --git a/src/src/readconf.c b/src/src/readconf.c index c3514aba5..60df37afc 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -1,10 +1,8 @@ -/* $Cambridge: exim/src/src/readconf.c,v 1.27 2007/01/18 15:35:42 ph10 Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2007 */ +/* 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 @@ -13,6 +11,9 @@ implementation of the conditional .ifdef etc. */ #include "exim.h" +static void fn_smtp_receive_timeout(const uschar * name, const uschar * str); + + #define CSTATE_STACK_SIZE 10 @@ -142,6 +143,12 @@ static optionlist optionlist_config[] = { { "acl_smtp_auth", opt_stringptr, &acl_smtp_auth }, { "acl_smtp_connect", opt_stringptr, &acl_smtp_connect }, { "acl_smtp_data", opt_stringptr, &acl_smtp_data }, +#ifndef DISABLE_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 }, @@ -150,6 +157,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 }, @@ -190,25 +198,48 @@ static optionlist optionlist_config[] = { { "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_advertise_hosts", opt_stringptr, &dsn_advertise_hosts }, + { "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 }, +#ifdef EXPERIMENTAL_EVENT + { "event_action", opt_stringptr, &event_action }, +#endif { "exim_group", opt_gid, &exim_gid }, { "exim_path", opt_stringptr, &exim_path }, { "exim_user", opt_uid, &exim_uid }, @@ -219,6 +250,10 @@ static optionlist optionlist_config[] = { { "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 */ + /* From 4.83 we log a warning; a future relase will remove them */ { "gnutls_require_kx", opt_stringptr, &gnutls_require_kx }, { "gnutls_require_mac", opt_stringptr, &gnutls_require_mac }, { "gnutls_require_protocols", opt_stringptr, &gnutls_require_proto }, @@ -245,7 +280,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 }, @@ -260,6 +302,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 }, @@ -273,6 +316,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 @@ -286,11 +332,17 @@ static optionlist optionlist_config[] = { #endif { "pid_file_path", opt_stringptr, &pid_file_path }, { "pipelining_advertise_hosts", opt_stringptr, &pipelining_advertise_hosts }, +#ifndef DISABLE_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 }, @@ -298,6 +350,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 }, @@ -308,6 +361,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 }, @@ -339,11 +395,14 @@ static optionlist optionlist_config[] = { { "smtp_ratelimit_hosts", opt_stringptr, &smtp_ratelimit_hosts }, { "smtp_ratelimit_mail", opt_stringptr, &smtp_ratelimit_mail }, { "smtp_ratelimit_rcpt", opt_stringptr, &smtp_ratelimit_rcpt }, - { "smtp_receive_timeout", opt_time, &smtp_receive_timeout }, + { "smtp_receive_timeout", opt_func, &fn_smtp_receive_timeout }, { "smtp_reserve_hosts", opt_stringptr, &smtp_reserve_hosts }, { "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 }, @@ -374,14 +433,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 }, +# ifndef DISABLE_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 }, @@ -432,7 +498,7 @@ for (i = 0; i < optionlist_config_size; i++) for (r = routers; r != NULL; r = r->next) { router_info *ri = r->info; - for (i = 0; i < ri->options_count[0]; i++) + for (i = 0; i < *ri->options_count; i++) { if ((ri->options[i].type & opt_mask) != opt_stringptr) continue; if (p == (char *)(r->options_block) + (long int)(ri->options[i].value)) @@ -443,11 +509,16 @@ for (r = routers; r != NULL; r = r->next) for (t = transports; t != NULL; t = t->next) { transport_info *ti = t->info; - for (i = 0; i < ti->options_count[0]; i++) + for (i = 0; i < *ti->options_count; i++) { - if ((ti->options[i].type & opt_mask) != opt_stringptr) continue; - if (p == (char *)(t->options_block) + (long int)(ti->options[i].value)) - return US ti->options[i].name; + optionlist * op = &ti->options[i]; + if ((op->type & opt_mask) != opt_stringptr) continue; + if (p == ( op->type & opt_public + ? (char *)t + : (char *)t->options_block + ) + + (long int)op->value) + return US op->name; } } @@ -486,7 +557,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; @@ -959,7 +1030,7 @@ Returns: the time value, or -1 on syntax error */ int -readconf_readtime(uschar *s, int terminator, BOOL return_msec) +readconf_readtime(const uschar *s, int terminator, BOOL return_msec) { int yield = 0; for (;;) @@ -968,7 +1039,7 @@ for (;;) double fraction; if (!isdigit(*s)) return -1; - (void)sscanf(CS s, "%d%n", &value, &count); + (void)sscanf(CCS s, "%d%n", &value, &count); s += count; switch (*s) @@ -982,7 +1053,7 @@ for (;;) case '.': if (!return_msec) return -1; - (void)sscanf(CS s, "%lf%n", &fraction, &count); + (void)sscanf(CCS s, "%lf%n", &fraction, &count); s += count; if (*s++ != 's') return -1; yield += (int)(fraction * 1000.0); @@ -1282,6 +1353,26 @@ return yield; } +/************************************************* +* Custom-handler options * +*************************************************/ +static void +fn_smtp_receive_timeout(const uschar * name, const uschar * str) +{ +int value; + +if (*str == '$') + smtp_receive_timeout_s = string_copy(str); +else + { + /* "smtp_receive_timeout", opt_time, &smtp_receive_timeout */ + smtp_receive_timeout = readconf_readtime(str, 0, FALSE); + if (smtp_receive_timeout < 0) + log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN, "invalid time value for %s", + name); + } +} + /************************************************* * Handle option line * *************************************************/ @@ -1340,6 +1431,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]; @@ -1393,13 +1486,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; @@ -1420,7 +1509,7 @@ if (type < opt_bool || type > opt_bool_last) } /* If a boolean wasn't preceded by "no[t]_" it can be followed by = and -true/false/yes/no, or, in the case of opt_expanded_bool, a general string that +true/false/yes/no, or, in the case of opt_expand_bool, a general string that ultimately expands to one of those values. */ else if (*s != 0 && (offset != 0 || *s != '=')) @@ -1479,6 +1568,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; @@ -1825,8 +1963,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", @@ -1836,25 +1976,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) @@ -1996,9 +2139,15 @@ switch (type) name); if (count > 0 && list[2] == 0) count = 0; list[1] = count; + break; } - break; + case opt_func: + { + void (*fn)() = ol->value; + fn(name, s); + break; + } } return TRUE; @@ -2067,13 +2216,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; @@ -2095,7 +2245,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; } @@ -2114,11 +2268,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: @@ -2133,23 +2289,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 */ @@ -2161,7 +2324,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); @@ -2187,7 +2351,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; } } @@ -2195,14 +2360,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; @@ -2219,7 +2385,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; } } @@ -2227,31 +2394,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 = ':'; } } @@ -2260,18 +2430,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 = ':'; } } @@ -2279,14 +2451,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"); @@ -2309,7 +2482,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 */ @@ -2340,30 +2514,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) @@ -2384,8 +2562,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); } } @@ -2408,7 +2589,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; } @@ -2422,7 +2605,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; @@ -2445,11 +2628,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) @@ -2458,16 +2640,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; } } @@ -2497,6 +2695,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); @@ -2514,14 +2738,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; } @@ -2645,6 +2869,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 * *************************************************/ @@ -2746,7 +3035,12 @@ file is a serious disaster. */ if (config_file != NULL) { + uschar *p; config_filename = config_main_filename = string_copy(filename); + + p = Ustrrchr(filename, '/'); + config_main_directory = p ? string_copyn(filename, p - filename) + : string_copy(US"."); } else { @@ -2758,22 +3052,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 @@ -2916,8 +3209,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) { @@ -2941,7 +3234,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); } @@ -2990,6 +3283,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. */ @@ -3063,9 +3361,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, @@ -3085,7 +3388,38 @@ 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_"); -#endif + +/* 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 + } + +if (gnutls_require_kx || gnutls_require_mac || gnutls_require_proto) + log_write(0, LOG_MAIN, "WARNING: main options" + " gnutls_require_kx, gnutls_require_mac and gnutls_require_protocols" + " are obsolete\n"); +#endif /*SUPPORT_TLS*/ } @@ -3467,7 +3801,7 @@ 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; } @@ -3556,7 +3890,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. */ @@ -3699,27 +4033,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. */ @@ -3804,9 +4126,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 */ @@ -3820,7 +4140,7 @@ static uschar *section_list[] = { US"transports"}; void -readconf_rest(BOOL skip_acl) +readconf_rest(void) { int had = 0; @@ -3853,7 +4173,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; @@ -3866,4 +4186,6 @@ while(next_section[0] != 0) (void)fclose(config_file); } +/* vi: aw ai sw=2 +*/ /* End of readconf.c */