-/* $Cambridge: exim/src/src/readconf.c,v 1.12 2005/08/08 10:48:27 ph10 Exp $ */
+/* $Cambridge: exim/src/src/readconf.c,v 1.45 2010/06/12 17:56:32 jetmore Exp $ */
/*************************************************
* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2005 */
+/* Copyright (c) University of Cambridge 1995 - 2009 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions for reading the configuration file, and for displaying
#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 },
+#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 },
#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 },
{ "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
{ "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_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 },
{ "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_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 },
{ "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 },
{ "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 },
{ "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
{ "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 },
{ "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 },
{ "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 },
{ "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
}
*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));
gid_t gid;
BOOL boolvalue = TRUE;
BOOL freesptr = TRUE;
+BOOL extra_condition = FALSE;
optionlist *ol, *ol2;
struct passwd *pw;
void *reset_point;
uschar *inttype = US"";
uschar *sptr;
uschar *s = buffer;
+uschar *saved_condition, *strtemp;
+uschar **str_target;
uschar name[64];
uschar name2[64];
{
uschar *mname = name;
if (Ustrncmp(mname, "no_", 3) == 0) mname += 3;
- log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
- "\"%s\" option set for the second time", mname);
+ if (Ustrcmp(mname, "condition") == 0)
+ extra_condition = TRUE;
+ else
+ log_write(0, LOG_PANIC_DIE|LOG_CONFIG_IN,
+ "\"%s\" option set for the second time", mname);
}
ol->type |= opt_set | issecure;
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 (extra_condition)
+ {
+ /* We already have a condition, we're conducting a crude hack to let
+ multiple condition rules be chained together, despite storing them in
+ text form. */
+ 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 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
+ {
+ *str_target = sptr;
+ freesptr = FALSE;
+ }
+ break;
+
case opt_rewrite:
if (data_block == NULL)
*((uschar **)(ol->value)) = sptr;
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);
else
*((uid_t **)((uschar *)data_block + (long int)(ol->value))) = list;
- p = sptr;
+ p = op;
while (count-- > 1)
{
int sep = 0;
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);
else
*((gid_t **)((uschar *)data_block + (long int)(ol->value))) = list;
- p = sptr;
+ p = op;
while (count-- > 1)
{
int sep = 0;
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",
{
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)
int s, m, h, d, w;
uschar *p = time_buffer;
+if (t < 0)
+ {
+ *p++ = '-';
+ t = -t;
+ }
+
s = t % 60;
t /= 60;
m = t % 60;
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
optionlist *ol;
optionlist *ol2 = NULL;
driver_instance *d = NULL;
+macro_item *m;
int size = 0;
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)
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),
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);
"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
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
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;
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)
{
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);
}
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_");
+
+/* 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
}
}
}
-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" */
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);
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;
}
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");