X-Git-Url: https://git.exim.org/users/jgh/exim.git/blobdiff_plain/0925ede6bf47dea696153c927418025ad59a903c..41eff010fbb485d1db54b32c9bf4292c5b052eda:/src/src/readconf.c diff --git a/src/src/readconf.c b/src/src/readconf.c index 2a0b9d120..7f42bb7a9 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -1,10 +1,8 @@ -/* $Cambridge: exim/src/src/readconf.c,v 1.14 2005/09/19 14:01:51 ph10 Exp $ */ - /************************************************* * Exim - an Internet mail transport agent * *************************************************/ -/* Copyright (c) University of Cambridge 1995 - 2005 */ +/* Copyright (c) University of Cambridge 1995 - 2012 */ /* 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,27 +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 }, @@ -215,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_compat_mode", opt_bool, &gnutls_compat_mode }, + { "gnutls_enable_pkcs11", opt_bool, &gnutls_enable_pkcs11 }, + /* 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 }, @@ -237,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 }, @@ -252,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 }, @@ -265,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 @@ -278,6 +324,9 @@ 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 }, @@ -290,6 +339,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 }, @@ -336,6 +386,9 @@ 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 }, @@ -351,6 +404,7 @@ static optionlist optionlist_config[] = { { "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 }, @@ -365,14 +419,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) && !defined(USE_GNUTLS) + { "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 }, @@ -477,7 +538,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; @@ -810,6 +871,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)); @@ -1327,6 +1392,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]; @@ -1380,13 +1447,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; @@ -1466,6 +1529,49 @@ 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 (*str_target && (ol->type & opt_rep_str)) + { + uschar sep = Ustrncmp(name, "headers_add", 11)==0 ? '\n' : ':'; + saved_condition = *str_target; + strtemp = saved_condition + Ustrlen(saved_condition)-1; + if (*strtemp == sep) *strtemp = 0; /* eliminate trailing list-sep */ + strtemp = string_sprintf("%s%c%s", saved_condition, sep, sptr); + *str_target = string_copy_malloc(strtemp); + } + else + { + *str_target = sptr; + freesptr = FALSE; + } + break; + case opt_rewrite: if (data_block == NULL) *((uschar **)(ol->value)) = sptr; @@ -1812,8 +1918,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", @@ -1823,25 +1931,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) @@ -2009,6 +2120,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; @@ -2048,13 +2165,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; @@ -2076,7 +2194,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; } @@ -2095,11 +2217,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: @@ -2114,23 +2238,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 */ @@ -2142,7 +2273,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); @@ -2168,7 +2300,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; } } @@ -2176,14 +2309,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; @@ -2200,7 +2334,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; } } @@ -2208,31 +2343,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 = ':'; } } @@ -2241,18 +2379,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 = ':'; } } @@ -2260,14 +2400,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"); @@ -2290,7 +2431,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 */ @@ -2321,30 +2463,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) @@ -2365,8 +2511,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); } } @@ -2389,7 +2538,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; } @@ -2403,7 +2554,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; @@ -2426,11 +2577,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) @@ -2439,16 +2589,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; } } @@ -2478,6 +2644,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); @@ -2495,14 +2687,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; } @@ -2626,6 +2818,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 * *************************************************/ @@ -2739,22 +2996,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 @@ -2807,10 +3063,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 @@ -2831,9 +3096,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; @@ -2888,8 +3153,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) { @@ -2913,7 +3178,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); } @@ -2962,6 +3227,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. */ @@ -3035,9 +3305,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, @@ -3057,6 +3332,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 } @@ -3400,7 +3701,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" */ @@ -3417,21 +3720,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; } @@ -3520,7 +3829,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. */