diff options
Diffstat (limited to 'src')
39 files changed, 789 insertions, 388 deletions
diff --git a/src/OS/Makefile-Darwin b/src/OS/Makefile-Darwin index be0d9520b..517bbc493 100644 --- a/src/OS/Makefile-Darwin +++ b/src/OS/Makefile-Darwin @@ -10,8 +10,12 @@ HAVE_SA_LEN=YES # Removed -DBIND_8_COMPAT for 4.61 # CFLAGS=-O -no-cpp-precomp -DBIND_8_COMPAT +# +# 2020/05/12 disable TLS resume support; it results in +# "1 select() failure: No such file or directory" +# being logged by the daeomn (sending the testsuite red...) -CFLAGS=-O -no-cpp-precomp +CFLAGS=-O -no-cpp-precomp -DDISABLE_TLS_RESUME LIBRESOLV=-lresolv USE_DB = yes diff --git a/src/src/EDITME b/src/src/EDITME index e568bdbb1..13dc49414 100644 --- a/src/src/EDITME +++ b/src/src/EDITME @@ -276,6 +276,9 @@ SPOOL_DIRECTORY=/var/spool/exim # specified in INCLUDE. +# Uncomment the following line to remove support for TLS Resumption +# DISABLE_TLS_RESUME=yes + ############################################################################### # THESE ARE THINGS YOU PROBABLY WANT TO SPECIFY # @@ -411,6 +414,8 @@ LOOKUP_DNSDB=yes # LOOKUP_IBASE=yes # LOOKUP_JSON=yes # LOOKUP_LDAP=yes +# LOOKUP_LMDB=yes + # LOOKUP_MYSQL=yes # LOOKUP_MYSQL_PC=mariadb # LOOKUP_NIS=yes @@ -487,7 +492,8 @@ SUPPORT_DANE=yes # You do not need to use this for any lookup information added via pkg-config. # LOOKUP_INCLUDE=-I /usr/local/ldap/include -I /usr/local/mysql/include -I /usr/local/pgsql/include -# LOOKUP_LIBS=-L/usr/local/lib -lldap -llber -lmysqlclient -lpq -lgds -lsqlite3 +# LOOKUP_INCLUDE +=-I /usr/local/include +# LOOKUP_LIBS=-L/usr/local/lib -lldap -llber -lmysqlclient -lpq -lgds -lsqlite3 -llmdb #------------------------------------------------------------------------------ @@ -560,7 +566,6 @@ DISABLE_MAL_MKS=yes # DISABLE_DNSSEC=yes # To disable support for Events set DISABLE_EVENT to "yes" - # DISABLE_EVENT=yes @@ -569,6 +574,14 @@ DISABLE_MAL_MKS=yes # SUPPORT_PIPE_CONNECT=yes +# Uncomment the following to remove the fast-ramp two-phase-queue-run support +# DISABLE_QUEUE_RAMP=yes + +# Uncomment the following lines to add SRS (Sender Rewriting Scheme) support +# using only native facilities. See EXPERIMENTAL_SRS_ALT for an alternative. +# SUPPORT_SRS=yes + + #------------------------------------------------------------------------------ # Compiling Exim with experimental features. These are documented in # experimental-spec.txt. "Experimental" means that the way these features are @@ -580,19 +593,16 @@ DISABLE_MAL_MKS=yes # EXPERIMENTAL_DCC=yes -# Uncomment the following lines to add SRS (Sender rewriting scheme) support. +# Uncomment the following lines to add SRS (Sender rewriting scheme) support +# using the implementation in linbsrs_alt. # You need to have libsrs_alt installed on your system (srs.mirtol.com). # Depending on where it is installed you may have to edit the CFLAGS and # LDFLAGS lines. -# EXPERIMENTAL_SRS=yes +# EXPERIMENTAL_SRS_ALT=yes # CFLAGS += -I/usr/local/include # LDFLAGS += -lsrs_alt -# Uncomment the following lines to add SRS (Sender rewriting scheme) support -# using only native facilities. -# EXPERIMENTAL_SRS_NATIVE=yes - # Uncomment the following line to add DMARC checking capability, implemented # using libopendmarc libraries. You must have SPF and DKIM support enabled also. # SUPPORT_DMARC=yes @@ -618,22 +628,9 @@ DISABLE_MAL_MKS=yes # Uncomment the following to include extra information in fail DSN message (bounces) # EXPERIMENTAL_DSN_INFO=yes -# Uncomment the following to add LMDB lookup support -# You need to have LMDB installed on your system (https://github.com/LMDB/lmdb) -# Depending on where it is installed you may have to edit the CFLAGS and LDFLAGS lines. -# EXPERIMENTAL_LMDB=yes -# CFLAGS += -I/usr/local/include -# LDFLAGS += -llmdb - # Uncomment the following line to add queuefile transport support # EXPERIMENTAL_QUEUEFILE=yes -# Uncomment the following line to include support for TLS Resumption -# EXPERIMENTAL_TLS_RESUME=yes - -# Uncomment the following to include the fast-ramp two-phase-queue-run support -# EXPERIMENTAL_QUEUE_RAMP=yes - ############################################################################### # THESE ARE THINGS YOU MIGHT WANT TO SPECIFY # ############################################################################### diff --git a/src/src/acl.c b/src/src/acl.c index c1d60bbd9..57a07296f 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -15,6 +15,12 @@ #define CALLOUT_TIMEOUT_DEFAULT 30 +/* Default quota cache TTLs */ + +#define QUOTA_POS_DEFAULT (5*60) +#define QUOTA_NEG_DEFAULT (60*60) + + /* ACL verb codes - keep in step with the table of verbs that follows */ enum { ACL_ACCEPT, ACL_DEFER, ACL_DENY, ACL_DISCARD, ACL_DROP, ACL_REQUIRE, @@ -1515,7 +1521,7 @@ static verify_type_t verify_type_list[] = { { US"not_blind", VERIFY_NOT_BLIND, ACL_BIT_DATA | ACL_BIT_NOTSMTP, FALSE, 0 }, { US"header_sender", VERIFY_HDR_SNDR, ACL_BIT_DATA | ACL_BIT_NOTSMTP, FALSE, 0 }, { US"sender", VERIFY_SNDR, ACL_BIT_MAIL | ACL_BIT_RCPT - |ACL_BIT_PREDATA | ACL_BIT_DATA | ACL_BIT_NOTSMTP, + | ACL_BIT_PREDATA | ACL_BIT_DATA | ACL_BIT_NOTSMTP, FALSE, 6 }, { US"recipient", VERIFY_RCPT, ACL_BIT_RCPT, FALSE, 0 }, { US"header_names_ascii", VERIFY_HDR_NAMES_ASCII, ACL_BIT_DATA | ACL_BIT_NOTSMTP, TRUE, 0 }, @@ -1556,6 +1562,20 @@ static callout_opt_t callout_opt_list[] = { +static int +v_period(const uschar * s, const uschar * arg, uschar ** log_msgptr) +{ +int period; +if ((period = readconf_readtime(s, 0, FALSE)) < 0) + { + *log_msgptr = string_sprintf("bad time value in ACL condition " + "\"verify %s\"", arg); + } +return period; +} + + + /* This function implements the "verify" condition. It is called when encountered in any ACL, because some tests are almost always permitted. Some just don't make sense, and always fail (for example, an attempt to test a host @@ -1590,6 +1610,8 @@ BOOL defer_ok = FALSE; BOOL callout_defer_ok = FALSE; BOOL no_details = FALSE; BOOL success_on_redirect = FALSE; +BOOL quota = FALSE; +int quota_pos_cache = QUOTA_POS_DEFAULT, quota_neg_cache = QUOTA_NEG_DEFAULT; address_item *sender_vaddr = NULL; uschar *verify_sender_address = NULL; uschar *pm_mailfrom = NULL; @@ -1746,7 +1768,7 @@ switch(vp->value) in place of the actual sender (rare special-case requirement). */ { uschar *s = ss + 6; - if (*s == 0) + if (!*s) verify_sender_address = sender_address; else { @@ -1792,19 +1814,16 @@ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))) else if (strncmpic(ss, US"callout", 7) == 0) { callout = CALLOUT_TIMEOUT_DEFAULT; - ss += 7; - if (*ss != 0) + if (*(ss += 7)) { while (isspace(*ss)) ss++; if (*ss++ == '=') { const uschar * sublist = ss; int optsep = ','; - uschar buffer[256]; - uschar * opt; while (isspace(*sublist)) sublist++; - while ((opt = string_nextinlist(&sublist, &optsep, buffer, sizeof(buffer)))) + for (uschar * opt; opt = string_nextinlist(&sublist, &optsep, NULL, 0); ) { callout_opt_t * op; double period = 1.0F; @@ -1826,12 +1845,8 @@ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))) } while (isspace(*opt)) opt++; } - if (op->timeval && (period = readconf_readtime(opt, 0, FALSE)) < 0) - { - *log_msgptr = string_sprintf("bad time value in ACL condition " - "\"verify %s\"", arg); + if (op->timeval && (period = v_period(opt, arg, log_msgptr)) < 0) return ERROR; - } switch(op->value) { @@ -1864,6 +1879,38 @@ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))) } } + /* The quota option has sub-options, comma-separated */ + + else if (strncmpic(ss, US"quota", 5) == 0) + { + quota = TRUE; + if (*(ss += 5)) + { + while (isspace(*ss)) ss++; + if (*ss++ == '=') + { + const uschar * sublist = ss; + int optsep = ','; + int period; + + while (isspace(*sublist)) sublist++; + for (uschar * opt; opt = string_nextinlist(&sublist, &optsep, NULL, 0); ) + if (Ustrncmp(opt, "cachepos=", 9) == 0) + if ((period = v_period(opt += 9, arg, log_msgptr)) < 0) + return ERROR; + else + quota_pos_cache = period; + else if (Ustrncmp(opt, "cacheneg=", 9) == 0) + if ((period = v_period(opt += 9, arg, log_msgptr)) < 0) + return ERROR; + else + quota_neg_cache = period; + else if (Ustrcmp(opt, "no_cache") == 0) + quota_pos_cache = quota_neg_cache = 0; + } + } + } + /* Option not recognized */ else @@ -1882,6 +1929,31 @@ if ((verify_options & (vopt_callout_recipsender|vopt_callout_recippmaster)) == return ERROR; } +/* Handle quota verification */ +if (quota) + { + if (vp->value != VERIFY_RCPT) + { + *log_msgptr = US"can only verify quota of recipient"; + return ERROR; + } + + if ((rc = verify_quota_call(addr->address, + quota_pos_cache, quota_neg_cache, log_msgptr)) != OK) + { + *basic_errno = errno; + if (smtp_return_error_details) + { + if (!*user_msgptr && *log_msgptr) + *user_msgptr = string_sprintf("Rejected after %s: %s", + smtp_names[smtp_connection_had[smtp_ch_index-1]], *log_msgptr); + if (rc == DEFER) f.acl_temp_details = TRUE; + } + } + + return rc; + } + /* Handle sender-in-header verification. Default the user message to the log message if giving out verification details. */ @@ -1928,8 +2000,8 @@ else if (verify_sender_address) } sender_vaddr = verify_checked_sender(verify_sender_address); - if (sender_vaddr != NULL && /* Previously checked */ - callout <= 0) /* No callout needed this time */ + if ( sender_vaddr /* Previously checked */ + && callout <= 0) /* No callout needed this time */ { /* If the "routed" flag is set, it means that routing worked before, so this check can give OK (the saved return code value, if set, belongs to a @@ -1996,14 +2068,12 @@ else if (verify_sender_address) *basic_errno = sender_vaddr->basic_errno; else DEBUG(D_acl) - { if (Ustrcmp(sender_vaddr->address, verify_sender_address) != 0) debug_printf_indent("sender %s verified ok as %s\n", verify_sender_address, sender_vaddr->address); else debug_printf_indent("sender %s verified ok\n", verify_sender_address); - } } else rc = OK; /* Null sender */ @@ -2047,8 +2117,7 @@ else *basic_errno = addr2.basic_errno; *log_msgptr = addr2.message; - *user_msgptr = (addr2.user_message != NULL)? - addr2.user_message : addr2.message; + *user_msgptr = addr2.user_message ? addr2.user_message : addr2.message; /* Allow details for temporary error if the address is so flagged. */ if (testflag((&addr2), af_pass_message)) f.acl_temp_details = TRUE; @@ -2059,8 +2128,10 @@ else /* We have a result from the relevant test. Handle defer overrides first. */ -if (rc == DEFER && (defer_ok || - (callout_defer_ok && *basic_errno == ERRNO_CALLOUTDEFER))) +if ( rc == DEFER + && ( defer_ok + || callout_defer_ok && *basic_errno == ERRNO_CALLOUTDEFER + ) ) { HDEBUG(D_acl) debug_printf_indent("verify defer overridden by %s\n", defer_ok? "defer_ok" : "callout_defer_ok"); @@ -2070,7 +2141,7 @@ if (rc == DEFER && (defer_ok || /* If we've failed a sender, set up a recipient message, and point sender_verified_failed to the address item that actually failed. */ -if (rc != OK && verify_sender_address != NULL) +if (rc != OK && verify_sender_address) { if (rc != DEFER) *log_msgptr = *user_msgptr = US"Sender verify failed"; @@ -2089,7 +2160,7 @@ if (rc != OK && verify_sender_address != NULL) /* Verifying an address messes up the values of $domain and $local_part, so reset them before returning if this is a RCPT ACL. */ -if (addr != NULL) +if (addr) { deliver_domain = addr->domain; deliver_localpart = addr->local_part; diff --git a/src/src/auths/gsasl_exim.c b/src/src/auths/gsasl_exim.c index a3aaf1fa6..708957f04 100644 --- a/src/src/auths/gsasl_exim.c +++ b/src/src/auths/gsasl_exim.c @@ -365,7 +365,7 @@ HDEBUG(D_auth) #ifndef DISABLE_TLS if (tls_in.channelbinding && ob->server_channelbinding) { -# ifdef EXPERIMENTAL_TLS_RESUME +# ifndef DISABLE_TLS_RESUME if (!tls_in.ext_master_secret && tls_in.resumption == RESUME_USED) { /* per RFC 7677 section 4 */ HDEBUG(D_auth) debug_printf( @@ -814,7 +814,7 @@ HDEBUG(D_auth) #ifndef DISABLE_TLS if (tls_out.channelbinding && ob->client_channelbinding) { -# ifdef EXPERIMENTAL_TLS_RESUME +# ifndef DISABLE_TLS_RESUME if (!tls_out.ext_master_secret && tls_out.resumption == RESUME_USED) { /* per RFC 7677 section 4 */ string_format(buffer, buffsize, "%s", diff --git a/src/src/child.c b/src/src/child.c index b36a96fe1..1407b3718 100644 --- a/src/src/child.c +++ b/src/src/child.c @@ -27,7 +27,7 @@ Arguments: Returns: nothing */ -static void +void force_fd(int oldfd, int newfd) { if (oldfd == newfd) return; diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults index e17f015f9..07c0ecf81 100644 --- a/src/src/config.h.defaults +++ b/src/src/config.h.defaults @@ -52,7 +52,9 @@ Do not put spaces between # and the 'define'. #define DISABLE_OCSP #define DISABLE_PIPE_CONNECT #define DISABLE_PRDR +#define DISABLE_QUEUE_RAMP #define DISABLE_TLS +#define DISABLE_TLS_RESUME #define DISABLE_D_OPTION #define ENABLE_DISABLE_FSYNC @@ -97,6 +99,7 @@ Do not put spaces between # and the 'define'. #define LOOKUP_IBASE #define LOOKUP_JSON #define LOOKUP_LDAP +#define LOOKUP_LMDB #define LOOKUP_LSEARCH #define LOOKUP_MYSQL #define LOOKUP_NIS @@ -156,6 +159,7 @@ Do not put spaces between # and the 'define'. #define SUPPORT_PROXY #define SUPPORT_SOCKS #define SUPPORT_SPF +#define SUPPORT_SRS #define SUPPORT_TRANSLATE_IP_ADDRESS #define SYSLOG_LOG_PID @@ -201,12 +205,8 @@ Do not put spaces between # and the 'define'. #define EXPERIMENTAL_BRIGHTMAIL #define EXPERIMENTAL_DCC #define EXPERIMENTAL_DSN_INFO -#define EXPERIMENTAL_LMDB -#define EXPERIMENTAL_QUEUE_RAMP #define EXPERIMENTAL_QUEUEFILE -#define EXPERIMENTAL_SRS -#define EXPERIMENTAL_SRS_NATIVE -#define EXPERIMENTAL_TLS_RESUME +#define EXPERIMENTAL_SRS_ALT /* For developers */ diff --git a/src/src/configure.default b/src/src/configure.default index 3423ee0af..946137fc9 100644 --- a/src/src/configure.default +++ b/src/src/configure.default @@ -169,7 +169,16 @@ acl_smtp_data = acl_check_data # tls_privatekey = /etc/ssl/exim.pem # For OpenSSL, prefer EC- over RSA-authenticated ciphers -# tls_require_ciphers = ECDSA:RSA:!COMPLEMENTOFDEFAULT +.ifdef _HAVE_OPENSSL +tls_require_ciphers = ECDSA:RSA:!COMPLEMENTOFDEFAULT +.endif + +# Don't offer resumption to (most) MUAs, who we don't want to reuse +# tickets. Once the TLS extension for vended ticket numbers comes +# though, re-examine since resumption on a single-use ticket is still a benefit. +.ifdef _HAVE_TLS_RESUME +tls_resumption_hosts = ${if inlist {$received_port}{587:465} {:}{*}} +.endif # In order to support roaming users who wish to send email from anywhere, # you may want to make Exim listen on other ports as well as port 25, in @@ -801,13 +810,12 @@ begin transports # This transport is used for delivering messages over SMTP connections. -# Refuse to send any message with over-long lines, which could have -# been received other than via SMTP. The use of message_size_limit to -# enforce this is a red herring. remote_smtp: driver = smtp - message_size_limit = ${if > {$max_received_linelength}{998} {1}{0}} +.ifdef _HAVE_TLS_RESUME + tls_resumption_hosts = * +.endif # This transport is used for delivering messages to a smarthost, if the @@ -819,7 +827,6 @@ remote_smtp: smarthost_smtp: driver = smtp - message_size_limit = ${if > {$max_received_linelength}{998} {1}{0}} multi_domain # .ifdef _HAVE_TLS @@ -845,6 +852,9 @@ smarthost_smtp: .ifdef _HAVE_GNUTLS tls_require_ciphers = SECURE192:-VERS-SSL3.0:-VERS-TLS1.0:-VERS-TLS1.1 .endif +.ifdef _HAVE_TLS_RESUME + tls_resumption_hosts = * +.endif .endif diff --git a/src/src/daemon.c b/src/src/daemon.c index 2bed143a1..1816537ec 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -1138,14 +1138,14 @@ for (struct cmsghdr * cp = CMSG_FIRSTHDR(&msg); buf[sz] = 0; switch (buf[0]) { -#ifdef EXPERIMENTAL_QUEUE_RAMP +#ifndef DISABLE_QUEUE_RAMP case NOTIFY_MSG_QRUN: /* this should be a message_id */ DEBUG(D_queue_run) debug_printf("%s: qrunner trigger: %s\n", __FUNCTION__, buf+1); memcpy(queuerun_msgid, buf+1, MESSAGE_ID_LENGTH+1); return TRUE; -#endif /*EXPERIMENTAL_QUEUE_RAMP*/ +#endif case NOTIFY_QUEUE_SIZE_REQ: { @@ -2120,7 +2120,7 @@ for (;;) else { DEBUG(D_any) debug_printf("%s received\n", -#ifdef EXPERIMENTAL_QUEUE_RAMP +#ifndef DISABLE_QUEUE_RAMP *queuerun_msgid ? "qrun notification" : #endif "SIGALRM"); @@ -2165,7 +2165,7 @@ for (;;) *p++ = '-'; *p++ = 'q'; if ( f.queue_2stage -#ifdef EXPERIMENTAL_QUEUE_RAMP +#ifndef DISABLE_QUEUE_RAMP && !*queuerun_msgid #endif ) *p++ = 'q'; @@ -2177,7 +2177,7 @@ for (;;) extra[0] = *queue_name ? string_sprintf("%sG%s", opt, queue_name) : opt; -#ifdef EXPERIMENTAL_QUEUE_RAMP +#ifndef DISABLE_QUEUE_RAMP if (*queuerun_msgid) { log_write(0, LOG_MAIN, "notify triggered queue run"); @@ -2212,7 +2212,7 @@ for (;;) /* No need to re-exec; SIGALRM remains set to the default handler */ -#ifdef EXPERIMENTAL_QUEUE_RAMP +#ifndef DISABLE_QUEUE_RAMP if (*queuerun_msgid) { log_write(0, LOG_MAIN, "notify triggered queue run"); @@ -2248,7 +2248,7 @@ for (;;) /* Reset the alarm clock */ sigalrm_seen = FALSE; -#ifdef EXPERIMENTAL_QUEUE_RAMP +#ifndef DISABLE_QUEUE_RAMP if (*queuerun_msgid) *queuerun_msgid = 0; else diff --git a/src/src/deliver.c b/src/src/deliver.c index 40db50084..dd922c728 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -817,7 +817,7 @@ d_tlslog(gstring * g, address_item * addr) if (LOGGING(tls_cipher) && addr->cipher) { g = string_append(g, 2, US" X=", addr->cipher); -#ifdef EXPERIMENTAL_TLS_RESUME +#ifndef DISABLE_TLS_RESUME if (LOGGING(tls_resumption) && testflag(addr, af_tls_resume)) g = string_catn(g, US"*", 1); #endif @@ -1149,7 +1149,7 @@ if (LOGGING(sender_on_delivery) || msg) if (*queue_name) g = string_append(g, 2, US" Q=", queue_name); -#ifdef EXPERIMENTAL_SRS +#ifdef EXPERIMENTAL_SRS_ALT if(addr->prop.srs_sender) g = string_append(g, 3, US" SRS=<", addr->prop.srs_sender, US">"); #endif @@ -2125,7 +2125,7 @@ Arguments: Returns: nothing */ -static void +void deliver_local(address_item *addr, BOOL shadowing) { BOOL use_initgroups; @@ -2143,7 +2143,7 @@ has its own return path setting, expand it and replace the existing value. */ if(addr->prop.errors_address) return_path = addr->prop.errors_address; -#ifdef EXPERIMENTAL_SRS +#ifdef EXPERIMENTAL_SRS_ALT else if (addr->prop.srs_sender) return_path = addr->prop.srs_sender; #endif @@ -2152,18 +2152,16 @@ else if (tp->return_path) { - uschar *new_return_path = expand_string(tp->return_path); - if (!new_return_path) - { - if (!f.expand_string_forcedfail) - { - common_error(TRUE, addr, ERRNO_EXPANDFAIL, - US"Failed to expand return path \"%s\" in %s transport: %s", - tp->return_path, tp->name, expand_string_message); - return; - } + uschar * new_return_path = expand_string(tp->return_path); + if (new_return_path) + return_path = new_return_path; + else if (!f.expand_string_forcedfail) + { + common_error(TRUE, addr, ERRNO_EXPANDFAIL, + US"Failed to expand return path \"%s\" in %s transport: %s", + tp->return_path, tp->name, expand_string_message); + return; } - else return_path = new_return_path; } /* For local deliveries, one at a time, the value used for logging can just be @@ -4429,7 +4427,7 @@ for (int delivery_count = 0; addr_remote; delivery_count++) if(addr->prop.errors_address) return_path = addr->prop.errors_address; -#ifdef EXPERIMENTAL_SRS +#ifdef EXPERIMENTAL_SRS_ALT else if(addr->prop.srs_sender) return_path = addr->prop.srs_sender; #endif @@ -4773,7 +4771,7 @@ all pipes, so I do not see a reason to use non-blocking IO here #ifdef SUPPORT_DANE if (tls_out.dane_verified) setflag(addr, af_dane_verified); #endif -# ifdef EXPERIMENTAL_TLS_RESUME +# ifndef DISABLE_TLS_RESUME if (tls_out.resumption & RESUME_USED) setflag(addr, af_tls_resume); # endif diff --git a/src/src/drtables.c b/src/src/drtables.c index 0ca369cfc..67a2b8f52 100644 --- a/src/src/drtables.c +++ b/src/src/drtables.c @@ -602,7 +602,7 @@ extern lookup_module_info pgsql_lookup_module_info; #if defined(LOOKUP_REDIS) && LOOKUP_REDIS!=2 extern lookup_module_info redis_lookup_module_info; #endif -#if defined(EXPERIMENTAL_LMDB) +#if defined(LOOKUP_LMDB) extern lookup_module_info lmdb_lookup_module_info; #endif #if defined(SUPPORT_SPF) @@ -698,7 +698,7 @@ addlookupmodule(NULL, &pgsql_lookup_module_info); addlookupmodule(NULL, &redis_lookup_module_info); #endif -#ifdef EXPERIMENTAL_LMDB +#ifdef LOOKUP_LMDB addlookupmodule(NULL, &lmdb_lookup_module_info); #endif diff --git a/src/src/exim.c b/src/src/exim.c index a60488e95..2f4149f6f 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -931,6 +931,9 @@ g = string_cat(NULL, US"Support for:"); #ifdef USE_OPENSSL g = string_cat(g, US" OpenSSL"); #endif +#ifndef DISABLE_TLS_RESUME + g = string_cat(g, US" TLS_resume"); +#endif #ifdef SUPPORT_TRANSLATE_IP_ADDRESS g = string_cat(g, US" translate_ip_address"); #endif @@ -946,6 +949,9 @@ g = string_cat(NULL, US"Support for:"); #ifndef DISABLE_DKIM g = string_cat(g, US" DKIM"); #endif +#ifdef SUPPORT_DMARC + g = string_cat(g, US" DMARC"); +#endif #ifndef DISABLE_DNSSEC g = string_cat(g, US" DNSSEC"); #endif @@ -967,14 +973,17 @@ g = string_cat(NULL, US"Support for:"); #ifdef SUPPORT_PROXY g = string_cat(g, US" PROXY"); #endif +#ifndef DISABLE_QUEUE_RAMP + g = string_cat(g, US" Experimental_Queue_Ramp"); +#endif #ifdef SUPPORT_SOCKS g = string_cat(g, US" SOCKS"); #endif #ifdef SUPPORT_SPF g = string_cat(g, US" SPF"); #endif -#ifdef SUPPORT_DMARC - g = string_cat(g, US" DMARC"); +#if defined(SUPPORT_SRS) + g = string_cat(g, US" SRS"); #endif #ifdef TCP_FASTOPEN tcp_init(); @@ -992,21 +1001,12 @@ g = string_cat(NULL, US"Support for:"); #ifdef EXPERIMENTAL_DSN_INFO g = string_cat(g, US" Experimental_DSN_info"); #endif -#ifdef EXPERIMENTAL_LMDB - g = string_cat(g, US" Experimental_LMDB"); -#endif -#ifdef EXPERIMENTAL_QUEUE_RAMP - g = string_cat(g, US" Experimental_Queue_Ramp"); -#endif #ifdef EXPERIMENTAL_QUEUEFILE g = string_cat(g, US" Experimental_QUEUEFILE"); #endif -#if defined(EXPERIMENTAL_SRS) || defined(EXPERIMENTAL_SRS_NATIVE) +#if defined(EXPERIMENTAL_SRS_ALT) g = string_cat(g, US" Experimental_SRS"); #endif -#ifdef EXPERIMENTAL_TLS_RESUME - g = string_cat(g, US" Experimental_TLS_resume"); -#endif g = string_cat(g, US"\n"); g = string_cat(g, US"Lookups (built-in):"); @@ -1034,7 +1034,7 @@ g = string_cat(g, US"Lookups (built-in):"); #if defined(LOOKUP_LDAP) && LOOKUP_LDAP!=2 g = string_cat(g, US" ldap ldapdn ldapm"); #endif -#ifdef EXPERIMENTAL_LMDB +#ifdef LOOKUP_LMDB g = string_cat(g, US" lmdb"); #endif #if defined(LOOKUP_MYSQL) && LOOKUP_MYSQL!=2 @@ -1618,6 +1618,7 @@ BOOL removed_privilege = FALSE; BOOL usage_wanted = FALSE; BOOL verify_address_mode = FALSE; BOOL verify_as_sender = FALSE; +BOOL rcpt_verify_quota = FALSE; BOOL version_printed = FALSE; uschar *alias_arg = NULL; uschar *called_as = US""; @@ -2790,6 +2791,13 @@ on the second character (the one after '-'), to save some effort. */ else badarg = TRUE; break; + /* -MCq: do a quota check on the given recipient for the given size + of message. Separate from -MC. */ + case 'q': rcpt_verify_quota = TRUE; + if (++i < argc) message_size = Uatoi(argv[i]); + else badarg = TRUE; + break; + /* -MCS: set the smtp_use_size flag; this is useful only when it precedes -MC (see above) */ @@ -3504,54 +3512,40 @@ END_ARG: if (usage_wanted) exim_usage(called_as); /* Arguments have been processed. Check for incompatibilities. */ -if (( - (smtp_input || extract_recipients || recipients_arg < argc) && - (f.daemon_listen || queue_interval >= 0 || bi_option || - test_retry_arg >= 0 || test_rewrite_arg >= 0 || - filter_test != FTEST_NONE || (msg_action_arg > 0 && !one_msg_action)) - ) || - ( - msg_action_arg > 0 && - (f.daemon_listen || queue_interval > 0 || list_options || - (checking && msg_action != MSG_LOAD) || - bi_option || test_retry_arg >= 0 || test_rewrite_arg >= 0) - ) || - ( - (f.daemon_listen || queue_interval > 0) && - (sender_address != NULL || list_options || list_queue || checking || - bi_option) - ) || - ( - f.daemon_listen && queue_interval == 0 - ) || - ( - f.inetd_wait_mode && queue_interval >= 0 - ) || - ( - list_options && - (checking || smtp_input || extract_recipients || - filter_test != FTEST_NONE || bi_option) - ) || - ( - verify_address_mode && - (f.address_test_mode || smtp_input || extract_recipients || - filter_test != FTEST_NONE || bi_option) - ) || - ( - f.address_test_mode && (smtp_input || extract_recipients || - filter_test != FTEST_NONE || bi_option) - ) || - ( - smtp_input && (sender_address != NULL || filter_test != FTEST_NONE || - extract_recipients) - ) || - ( - deliver_selectstring != NULL && queue_interval < 0 - ) || - ( - msg_action == MSG_LOAD && - (!expansion_test || expansion_test_message != NULL) - ) +if ( ( (smtp_input || extract_recipients || recipients_arg < argc) + && ( f.daemon_listen || queue_interval >= 0 || bi_option + || test_retry_arg >= 0 || test_rewrite_arg >= 0 + || filter_test != FTEST_NONE + || msg_action_arg > 0 && !one_msg_action + ) ) + || ( msg_action_arg > 0 + && ( f.daemon_listen || queue_interval > 0 || list_options + || checking && msg_action != MSG_LOAD + || bi_option || test_retry_arg >= 0 || test_rewrite_arg >= 0 + ) ) + || ( (f.daemon_listen || queue_interval > 0) + && ( sender_address || list_options || list_queue || checking + || bi_option + ) ) + || f.daemon_listen && queue_interval == 0 + || f.inetd_wait_mode && queue_interval >= 0 + || ( list_options + && ( checking || smtp_input || extract_recipients + || filter_test != FTEST_NONE || bi_option + ) ) + || ( verify_address_mode + && ( f.address_test_mode || smtp_input || extract_recipients + || filter_test != FTEST_NONE || bi_option + ) ) + || ( f.address_test_mode + && ( smtp_input || extract_recipients || filter_test != FTEST_NONE + || bi_option + ) ) + || ( smtp_input + && (sender_address || filter_test != FTEST_NONE || extract_recipients) + ) + || deliver_selectstring && queue_interval < 0 + || msg_action == MSG_LOAD && (!expansion_test || expansion_test_message) ) exim_fail("exim: incompatible command-line options or arguments\n"); @@ -4172,7 +4166,7 @@ if (!f.admin_user) || queue_name_dest && prod_requires_admin || debugset && !f.running_in_test_harness ) - exim_fail("exim:%s permission denied\n", debugset? " debugging" : ""); + exim_fail("exim:%s permission denied\n", debugset ? " debugging" : ""); } /* If the real user is not root or the exim uid, the argument for passing @@ -4181,11 +4175,13 @@ running with the -N option for any delivery action, unless this call to exim is one that supplied an input message, or we are using a patched exim for regression testing. */ -if (real_uid != root_uid && real_uid != exim_uid && - (continue_hostname != NULL || - (f.dont_deliver && - (queue_interval >= 0 || f.daemon_listen || msg_action_arg > 0) - )) && !f.running_in_test_harness) +if ( real_uid != root_uid && real_uid != exim_uid + && ( continue_hostname + || ( f.dont_deliver + && (queue_interval >= 0 || f.daemon_listen || msg_action_arg > 0) + ) ) + && !f.running_in_test_harness + ) exim_fail("exim: Permission denied\n"); /* If the caller is not trusted, certain arguments are ignored when running for @@ -4207,9 +4203,9 @@ Exim exits if the syntax is bad. */ else { - if (sender_host_address != NULL) + if (sender_host_address) sender_host_port = check_port(sender_host_address); - if (interface_address != NULL) + if (interface_address) interface_port = check_port(interface_address); } @@ -4296,18 +4292,20 @@ retained only for starting the daemon. We always do the initgroups() in this situation (controlled by the TRUE below), in order to be as close as possible to the state Exim usually runs in. */ -if (!unprivileged && /* originally had root AND */ - !removed_privilege && /* still got root AND */ - !f.daemon_listen && /* not starting the daemon */ - queue_interval <= 0 && /* (either kind of daemon) */ - ( /* AND EITHER */ - deliver_drop_privilege || /* requested unprivileged */ - ( /* OR */ - queue_interval < 0 && /* not running the queue */ - (msg_action_arg < 0 || /* and */ - msg_action != MSG_DELIVER) && /* not delivering and */ - (!checking || !f.address_test_mode) /* not address checking */ - ) ) ) +if ( !unprivileged /* originally had root AND */ + && !removed_privilege /* still got root AND */ + && !f.daemon_listen /* not starting the daemon */ + && queue_interval <= 0 /* (either kind of daemon) */ + && ( /* AND EITHER */ + deliver_drop_privilege /* requested unprivileged */ + || ( /* OR */ + queue_interval < 0 /* not running the queue */ + && ( msg_action_arg < 0 /* and */ + || msg_action != MSG_DELIVER /* not delivering */ + ) /* and */ + && (!checking || !f.address_test_mode) /* not address checking */ + && !rcpt_verify_quota /* and not quota checking */ + ) ) ) exim_setugid(exim_uid, exim_gid, TRUE, US"privilege not needed"); /* When we are retaining a privileged uid, we still change to the exim gid. */ @@ -4338,8 +4336,7 @@ if (malware_test_file) #ifdef WITH_CONTENT_SCAN int result; set_process_info("scanning file for malware"); - result = malware_in_file(malware_test_file); - if (result == FAIL) + if ((result = malware_in_file(malware_test_file)) == FAIL) { printf("No malware found.\n"); exit(EXIT_SUCCESS); @@ -4428,6 +4425,18 @@ needed in transports so we lost the optimisation. */ #endif } +/* Handle a request to check quota */ +if (rcpt_verify_quota) + if (real_uid != root_uid && real_uid != exim_uid) + exim_fail("exim: Permission denied\n"); + else if (recipients_arg >= argc) + exim_fail("exim: missing recipient for quota check\n"); + else + { + verify_quota(argv[recipients_arg]); + exim_exit(EXIT_SUCCESS); + } + /* Handle the -brt option. This is for checking out retry configurations. The next three arguments are a domain name or a complete address, and optionally two error numbers. All it does is to call the function that @@ -4862,8 +4871,8 @@ if ( !smtp_input && !sender_address sender, or if a sender other than <> is set, override with the originator's login (which will get qualified below), except when checking things. */ - if (sender_address == NULL /* No sender_address set */ - || /* OR */ + if ( !sender_address /* No sender_address set */ + || /* OR */ (sender_address[0] != 0 && /* Non-empty sender address, AND */ !checking)) /* Not running tests, including filter tests */ { @@ -4914,7 +4923,6 @@ if (verify_address_mode || f.address_test_mode) } if (recipients_arg < argc) - { while (recipients_arg < argc) { /* Supplied addresses are tainted since they come from a user */ @@ -4930,7 +4938,6 @@ if (verify_address_mode || f.address_test_mode) while (*++s == ',' || isspace(*s)) ; } } - } else for (;;) { diff --git a/src/src/exim.h b/src/src/exim.h index 2cc2621c4..1ddba187b 100644 --- a/src/src/exim.h +++ b/src/src/exim.h @@ -493,7 +493,7 @@ config.h, mytypes.h, and store.h, so we don't need to mention them explicitly. #ifdef SUPPORT_SPF # include "spf.h" #endif -#ifdef EXPERIMENTAL_SRS +#ifdef EXPERIMENTAL_SRS_ALT # include "srs.h" #endif #ifndef DISABLE_DKIM diff --git a/src/src/expand.c b/src/src/expand.c index 26f7f10ac..b014533c9 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -131,7 +131,7 @@ static uschar *item_table[] = { US"run", US"sg", US"sort", -#ifdef EXPERIMENTAL_SRS_NATIVE +#ifdef SUPPORT_SRS US"srs_encode", #endif US"substr", @@ -166,7 +166,7 @@ enum { EITEM_RUN, EITEM_SG, EITEM_SORT, -#ifdef EXPERIMENTAL_SRS_NATIVE +#ifdef SUPPORT_SRS EITEM_SRS_ENCODE, #endif EITEM_SUBSTR, @@ -334,7 +334,7 @@ static uschar *cond_table[] = { US"gei", US"gt", US"gti", -#ifdef EXPERIMENTAL_SRS_NATIVE +#ifdef SUPPORT_SRS US"inbound_srs", #endif US"inlist", @@ -387,7 +387,7 @@ enum { ECOND_STR_GEI, ECOND_STR_GT, ECOND_STR_GTI, -#ifdef EXPERIMENTAL_SRS_NATIVE +#ifdef SUPPORT_SRS ECOND_INBOUND_SRS, #endif ECOND_INLIST, @@ -752,16 +752,16 @@ static var_entry var_table[] = { { "spool_directory", vtype_stringptr, &spool_directory }, { "spool_inodes", vtype_pinodes, (void *)TRUE }, { "spool_space", vtype_pspace, (void *)TRUE }, -#ifdef EXPERIMENTAL_SRS +#ifdef EXPERIMENTAL_SRS_ALT { "srs_db_address", vtype_stringptr, &srs_db_address }, { "srs_db_key", vtype_stringptr, &srs_db_key }, { "srs_orig_recipient", vtype_stringptr, &srs_orig_recipient }, { "srs_orig_sender", vtype_stringptr, &srs_orig_sender }, #endif -#if defined(EXPERIMENTAL_SRS) || defined(EXPERIMENTAL_SRS_NATIVE) +#if defined(EXPERIMENTAL_SRS_ALT) || defined(SUPPORT_SRS) { "srs_recipient", vtype_stringptr, &srs_recipient }, #endif -#ifdef EXPERIMENTAL_SRS +#ifdef EXPERIMENTAL_SRS_ALT { "srs_status", vtype_stringptr, &srs_status }, #endif { "thisaddress", vtype_stringptr, &filter_thisaddress }, @@ -779,7 +779,7 @@ static var_entry var_table[] = { { "tls_in_ourcert", vtype_cert, &tls_in.ourcert }, { "tls_in_peercert", vtype_cert, &tls_in.peercert }, { "tls_in_peerdn", vtype_stringptr, &tls_in.peerdn }, -#ifdef EXPERIMENTAL_TLS_RESUME +#ifndef DISABLE_TLS_RESUME { "tls_in_resumption", vtype_int, &tls_in.resumption }, #endif #ifndef DISABLE_TLS @@ -797,7 +797,7 @@ static var_entry var_table[] = { { "tls_out_ourcert", vtype_cert, &tls_out.ourcert }, { "tls_out_peercert", vtype_cert, &tls_out.peercert }, { "tls_out_peerdn", vtype_stringptr, &tls_out.peerdn }, -#ifdef EXPERIMENTAL_TLS_RESUME +#ifndef DISABLE_TLS_RESUME { "tls_out_resumption", vtype_int, &tls_out.resumption }, #endif #ifndef DISABLE_TLS @@ -2439,7 +2439,7 @@ else -#ifdef EXPERIMENTAL_SRS_NATIVE +#ifdef SUPPORT_SRS /* Do an hmac_md5. The result is _not_ nul-terminated, and is sized as the smaller of a full hmac_md5 result (16 bytes) or the supplied output buffer. @@ -2514,7 +2514,7 @@ for (int i = 0, j = len; i < MD5_HASHLEN; i++) } return; } -#endif /*EXPERIMENTAL_SRS_NATIVE*/ +#endif /*SUPPORT_SRS*/ /************************************************* @@ -3444,7 +3444,7 @@ switch(cond_type = identify_operator(&s, &opname)) return s; } -#ifdef EXPERIMENTAL_SRS_NATIVE +#ifdef SUPPORT_SRS case ECOND_INBOUND_SRS: /* ${if inbound_srs {local_part}{secret} {yes}{no}} */ { @@ -3536,7 +3536,7 @@ srs_result: if (yield) *yield = (boolvalue == testfor); return s; } -#endif /*EXPERIMENTAL_SRS_NATIVE*/ +#endif /*SUPPORT_SRS*/ /* Unknown condition */ @@ -6785,7 +6785,7 @@ while (*s != 0) continue; } -#ifdef EXPERIMENTAL_SRS_NATIVE +#ifdef SUPPORT_SRS case EITEM_SRS_ENCODE: /* ${srs_encode {secret} {return_path} {orig_domain}} */ { @@ -6839,7 +6839,7 @@ while (*s != 0) yield = string_cat(yield, sub[2]); continue; } -#endif /*EXPERIMENTAL_SRS_NATIVE*/ +#endif /*SUPPORT_SRS*/ } /* EITEM_* switch */ /* Control reaches here if the name is not recognized as one of the more diff --git a/src/src/functions.h b/src/src/functions.h index 0028deb0d..486a91595 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -189,6 +189,7 @@ extern void debug_print_socket(int); extern void decode_bits(unsigned int *, size_t, int *, uschar *, bit_table *, int, uschar *, int); extern void delete_pid_file(void); +extern void deliver_local(address_item *, BOOL); extern address_item *deliver_make_addr(uschar *, BOOL); extern void delivery_log(int, address_item *, int, uschar *); extern int deliver_message(uschar *, BOOL, BOOL); @@ -260,6 +261,7 @@ extern BOOL filter_runtest(int, uschar *, BOOL, BOOL); extern BOOL filter_system_interpret(address_item **, uschar **); extern uschar * fn_hdrs_added(void); +extern void force_fd(int, int); extern void header_add(int, const char *, ...); extern header_line *header_add_at_position_internal(BOOL, uschar *, BOOL, int, const char *, ...); @@ -379,7 +381,7 @@ extern void queue_check_only(void); extern unsigned queue_count(void); extern unsigned queue_count_cached(void); extern void queue_list(int, uschar **, int); -#ifdef EXPERIMENTAL_QUEUE_RAMP +#ifndef DISABLE_QUEUE_RAMP extern void queue_notify_daemon(const uschar * hostname); #endif extern void queue_run(uschar *, uschar *, BOOL); @@ -620,6 +622,8 @@ extern int verify_check_this_host(const uschar **, unsigned int *, const uschar*, const uschar *, const uschar **); extern address_item *verify_checked_sender(uschar *); extern void verify_get_ident(int); +extern void verify_quota(uschar *); +extern int verify_quota_call(const uschar *, int, int, uschar **); extern BOOL verify_sender(int *, uschar **); extern BOOL verify_sender_preliminary(int *, uschar **); extern void version_init(void); diff --git a/src/src/globals.c b/src/src/globals.c index fc3086f72..1b2333053 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -137,7 +137,7 @@ uschar *tls_ocsp_file = NULL; uschar *tls_privatekey = NULL; BOOL tls_remember_esmtp = FALSE; uschar *tls_require_ciphers = NULL; -# ifdef EXPERIMENTAL_TLS_RESUME +# ifndef DISABLE_TLS_RESUME uschar *tls_resumption_hosts = NULL; # endif uschar *tls_try_verify_hosts = NULL; @@ -382,7 +382,7 @@ BOOL prod_requires_admin = TRUE; BOOL proxy_session = FALSE; #endif -#ifdef EXPERIMENTAL_QUEUE_RAMP +#ifndef DISABLE_QUEUE_RAMP BOOL queue_fast_ramp = FALSE; #endif BOOL queue_list_requires_admin = TRUE; @@ -407,7 +407,7 @@ BOOL spf_result_guessed = FALSE; #endif BOOL split_spool_directory = FALSE; BOOL spool_wireformat = FALSE; -#ifdef EXPERIMENTAL_SRS +#ifdef EXPERIMENTAL_SRS_ALT BOOL srs_usehash = TRUE; BOOL srs_usetimestamp = TRUE; #endif @@ -596,7 +596,7 @@ address_item address_defaults = { .extra_headers = NULL, .remove_headers = NULL, .variables = NULL, -#ifdef EXPERIMENTAL_SRS +#ifdef EXPERIMENTAL_SRS_ALT .srs_sender = NULL, #endif .ignore_error = FALSE, @@ -1508,7 +1508,7 @@ uschar *spf_smtp_comment_template FILE *spool_data_file = NULL; uschar *spool_directory = US SPOOL_DIRECTORY "\0<--------------Space to patch spool_directory->"; -#ifdef EXPERIMENTAL_SRS +#ifdef EXPERIMENTAL_SRS_ALT uschar *srs_config = NULL; uschar *srs_db_address = NULL; uschar *srs_db_key = NULL; @@ -1521,7 +1521,7 @@ uschar *srs_recipient = NULL; uschar *srs_secrets = NULL; uschar *srs_status = NULL; #endif -#ifdef EXPERIMENTAL_SRS_NATIVE +#ifdef SUPPORT_SRS uschar *srs_recipient = NULL; #endif int string_datestamp_offset= -1; diff --git a/src/src/globals.h b/src/src/globals.h index c80c8532f..0c85c1150 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -107,7 +107,7 @@ typedef struct { OCSP_FAILED, /* verify failed */ OCSP_VFIED /* verified */ } ocsp; /* Stapled OCSP status */ -#ifdef EXPERIMENTAL_TLS_RESUME +#ifndef DISABLE_TLS_RESUME unsigned resumption; /* Session resumption */ BOOL host_resumable:1; BOOL ticket_received:1; @@ -134,7 +134,7 @@ extern uschar *tls_ocsp_file; /* OCSP stapling proof file */ extern uschar *tls_privatekey; /* Private key file */ extern BOOL tls_remember_esmtp; /* For YAEB */ extern uschar *tls_require_ciphers; /* So some can be avoided */ -# ifdef EXPERIMENTAL_TLS_RESUME +# ifndef DISABLE_TLS_RESUME extern uschar *tls_resumption_hosts; /* TLS session resumption */ # endif extern uschar *tls_try_verify_hosts; /* Optional client verification */ @@ -792,7 +792,7 @@ extern uschar *prvscheck_result; /* Set during prvscheck expansion item */ extern const uschar *qualify_domain_recipient; /* Domain to qualify recipients with */ extern uschar *qualify_domain_sender; /* Domain to qualify senders with */ extern uschar *queue_domains; /* Queue these domains */ -#ifdef EXPERIMENTAL_QUEUE_RAMP +#ifndef DISABLE_QUEUE_RAMP extern BOOL queue_fast_ramp; /* 2-phase queue-run overlap */ #endif extern BOOL queue_list_requires_admin; /* TRUE if -bp requires admin */ @@ -943,6 +943,7 @@ extern int smtp_load_reserve; /* Only from reserved if load > this */ extern int smtp_mailcmd_count; /* Count of MAIL commands */ extern int smtp_max_synprot_errors;/* Max syntax/protocol errors */ extern int smtp_max_unknown_commands; /* As it says */ +extern uschar *smtp_names[]; /* decode for command codes */ extern uschar *smtp_notquit_reason; /* Global for disconnect reason */ extern FILE *smtp_out; /* Incoming SMTP output file */ extern uschar *smtp_ratelimit_hosts; /* Rate limit these hosts */ @@ -989,7 +990,7 @@ extern BOOL split_spool_directory; /* TRUE to use multiple subdirs */ extern FILE *spool_data_file; /* handle for -D file */ extern uschar *spool_directory; /* Name of spool directory */ extern BOOL spool_wireformat; /* can write wireformat -D files */ -#ifdef EXPERIMENTAL_SRS +#ifdef EXPERIMENTAL_SRS_ALT extern uschar *srs_config; /* SRS config secret:max age:hash length:use timestamp:use hash */ extern uschar *srs_db_address; /* SRS db address */ extern uschar *srs_db_key; /* SRS db key */ @@ -1004,7 +1005,7 @@ extern uschar *srs_status; /* SRS staus */ extern BOOL srs_usehash; /* SRS use hash flag */ extern BOOL srs_usetimestamp; /* SRS use timestamp flag */ #endif -#ifdef EXPERIMENTAL_SRS_NATIVE +#ifdef SUPPORT_SRS extern uschar *srs_recipient; /* SRS recipient */ #endif extern BOOL strict_acl_vars; /* ACL variables have to be set before being used */ diff --git a/src/src/lookups/lmdb.c b/src/src/lookups/lmdb.c index 406675198..d9cf25d54 100644 --- a/src/src/lookups/lmdb.c +++ b/src/src/lookups/lmdb.c @@ -8,7 +8,7 @@ #include "../exim.h" -#ifdef EXPERIMENTAL_LMDB +#ifdef LOOKUP_LMDB #include <lmdb.h> @@ -158,4 +158,4 @@ static lookup_info lmdb_lookup_info = { static lookup_info *_lookup_list[] = { &lmdb_lookup_info }; lookup_module_info lmdb_lookup_module_info = { LOOKUP_MODULE_INFO_MAGIC, _lookup_list, 1 }; -#endif /* EXPERIMENTAL_LMDB */ +#endif /* LOOKUP_LMDB */ diff --git a/src/src/macro_predef.c b/src/src/macro_predef.c index 2b3269d90..9c3d34a96 100644 --- a/src/src/macro_predef.c +++ b/src/src/macro_predef.c @@ -174,21 +174,18 @@ due to conflicts with other common macros. */ #ifdef SUPPORT_SOCKS builtin_macro_create(US"_HAVE_SOCKS"); #endif +#if defined(SUPPORT_SRS) + builtin_macro_create(US"_HAVE_NATIVE_SRS"); /* beware clash with _HAVE_SRS */ +#endif #ifdef TCP_FASTOPEN builtin_macro_create(US"_HAVE_TCP_FASTOPEN"); #endif -#ifdef EXPERIMENTAL_LMDB - builtin_macro_create(US"_HAVE_LMDB"); -#endif #ifdef SUPPORT_SPF builtin_macro_create(US"_HAVE_SPF"); #endif -#if defined(EXPERIMENTAL_SRS) || defined(EXPERIMENTAL_SRS_NATIVE) +#if defined(EXPERIMENTAL_SRS_ALT) || defined(SUPPORT_SRS) builtin_macro_create(US"_HAVE_SRS"); #endif -#if defined(EXPERIMENTAL_SRS_NATIVE) - builtin_macro_create(US"_HAVE_NATIVE_SRS"); /* beware clash with _HAVE_SRS */ -#endif #ifdef EXPERIMENTAL_ARC builtin_macro_create(US"_HAVE_ARC"); #endif @@ -204,7 +201,7 @@ due to conflicts with other common macros. */ #ifdef EXPERIMENTAL_DSN_INFO builtin_macro_create(US"_HAVE_DSN_INFO"); #endif -#ifdef EXPERIMENTAL_TLS_RESUME +#ifndef DISABLE_TLS_RESUME builtin_macro_create(US"_HAVE_TLS_RESUME"); #endif @@ -226,6 +223,10 @@ due to conflicts with other common macros. */ #ifdef LOOKUP_IBASE builtin_macro_create(US"_HAVE_LOOKUP_IBASE"); #endif +#ifdef LOOKUP_LMDB + builtin_macro_create(US"_HAVE_LMDB"); + builtin_macro_create(US"_HAVE_LOOKUP_LMDB"); +#endif #ifdef LOOKUP_LDAP builtin_macro_create(US"_HAVE_LOOKUP_JSON"); #endif diff --git a/src/src/macros.h b/src/src/macros.h index a507bbf83..f6012447d 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -736,6 +736,7 @@ enum { v_none, v_sender, v_recipient, v_expn }; #define vopt_callout_recippmaster 0x0100 /* use postmaster to verify recip */ #define vopt_callout_hold 0x0200 /* lazy close connection */ #define vopt_success_on_redirect 0x0400 +#define vopt_quota 0x0800 /* quota check, to local/appendfile */ /* Values for fields in callout cache records */ diff --git a/src/src/queue.c b/src/src/queue.c index 37d612482..6748afd5d 100644 --- a/src/src/queue.c +++ b/src/src/queue.c @@ -1531,7 +1531,7 @@ if (s) /******************************************************************************/ /******************************************************************************/ -#ifdef EXPERIMENTAL_QUEUE_RAMP +#ifndef DISABLE_QUEUE_RAMP void queue_notify_daemon(const uschar * msgid) { diff --git a/src/src/readconf.c b/src/src/readconf.c index 0d0769c88..a8f13350a 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -262,7 +262,7 @@ static optionlist optionlist_config[] = { { "qualify_domain", opt_stringptr, {&qualify_domain_sender} }, { "qualify_recipient", opt_stringptr, {&qualify_domain_recipient} }, { "queue_domains", opt_stringptr, {&queue_domains} }, -#ifdef EXPERIMENTAL_QUEUE_RAMP +#ifndef DISABLE_QUEUE_RAMP { "queue_fast_ramp", opt_bool, {&queue_fast_ramp} }, #endif { "queue_list_requires_admin",opt_bool, {&queue_list_requires_admin} }, @@ -335,7 +335,7 @@ static optionlist optionlist_config[] = { { "sqlite_dbfile", opt_stringptr, {&sqlite_dbfile} }, { "sqlite_lock_timeout", opt_int, {&sqlite_lock_timeout} }, #endif -#ifdef EXPERIMENTAL_SRS +#ifdef EXPERIMENTAL_SRS_ALT { "srs_config", opt_stringptr, {&srs_config} }, { "srs_hashlength", opt_int, {&srs_hashlength} }, { "srs_hashmin", opt_int, {&srs_hashmin} }, @@ -379,7 +379,7 @@ static optionlist optionlist_config[] = { { "tls_privatekey", opt_stringptr, {&tls_privatekey} }, { "tls_remember_esmtp", opt_bool, {&tls_remember_esmtp} }, { "tls_require_ciphers", opt_stringptr, {&tls_require_ciphers} }, -# ifdef EXPERIMENTAL_TLS_RESUME +# ifndef DISABLE_TLS_RESUME { "tls_resumption_hosts", opt_stringptr, {&tls_resumption_hosts} }, # endif { "tls_try_verify_hosts", opt_stringptr, {&tls_try_verify_hosts} }, diff --git a/src/src/receive.c b/src/src/receive.c index 0fbd35f82..df8719ec6 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -3994,7 +3994,7 @@ g = add_host_info_for_log(g); if (LOGGING(tls_cipher) && tls_in.cipher) { g = string_append(g, 2, US" X=", tls_in.cipher); -# ifdef EXPERIMENTAL_TLS_RESUME +# ifndef DISABLE_TLS_RESUME if (LOGGING(tls_resumption) && tls_in.resumption & RESUME_USED) g = string_catn(g, US"*", 1); # endif diff --git a/src/src/routers/queryprogram.c b/src/src/routers/queryprogram.c index 03c84fc89..5b206c572 100644 --- a/src/src/routers/queryprogram.c +++ b/src/src/routers/queryprogram.c @@ -242,7 +242,7 @@ rc = rf_get_munge_headers(addr, rblock, &addr_prop.extra_headers, &addr_prop.remove_headers); if (rc != OK) return rc; -#ifdef EXPERIMENTAL_SRS +#ifdef EXPERIMENTAL_SRS_ALT addr_prop.srs_sender = NULL; #endif diff --git a/src/src/routers/redirect.c b/src/src/routers/redirect.c index cd225d8e9..13b6e5244 100644 --- a/src/src/routers/redirect.c +++ b/src/src/routers/redirect.c @@ -93,7 +93,7 @@ optionlist redirect_router_options[] = { { "sieve_useraddress", opt_stringptr, LOFF(sieve_useraddress) }, { "sieve_vacation_directory", opt_stringptr, LOFF(sieve_vacation_directory) }, { "skip_syntax_errors", opt_bool, LOFF(skip_syntax_errors) }, -#ifdef EXPERIMENTAL_SRS +#ifdef EXPERIMENTAL_SRS_ALT { "srs", opt_stringptr, LOFF(srs) }, { "srs_alias", opt_stringptr, LOFF(srs_alias) }, { "srs_condition", opt_stringptr, LOFF(srs_condition) }, @@ -149,7 +149,7 @@ redirect_router_options_block redirect_router_option_defaults = { NULL, /* qualify_domain */ NULL, /* owners */ NULL, /* owngroups */ -#ifdef EXPERIMENTAL_SRS +#ifdef EXPERIMENTAL_SRS_ALT NULL, /* srs */ NULL, /* srs_alias */ NULL, /* srs_condition */ @@ -543,7 +543,7 @@ addr_prop.remove_headers = NULL; addr_prop.variables = NULL; tree_dup((tree_node **)&addr_prop.variables, addr->prop.variables); -#ifdef EXPERIMENTAL_SRS +#ifdef EXPERIMENTAL_SRS_ALT addr_prop.srs_sender = NULL; #endif #ifdef SUPPORT_I18N @@ -578,7 +578,7 @@ if (!ugid.gid_set && pw != NULL) ugid.gid_set = TRUE; } -#ifdef EXPERIMENTAL_SRS +#ifdef EXPERIMENTAL_SRS_ALT /* Perform SRS on recipient/return-path as required */ if(ob->srs != NULL) diff --git a/src/src/routers/redirect.h b/src/src/routers/redirect.h index 74e14af74..7c9ee1ce0 100644 --- a/src/src/routers/redirect.h +++ b/src/src/routers/redirect.h @@ -34,7 +34,7 @@ typedef struct { uid_t *owners; gid_t *owngroups; -#ifdef EXPERIMENTAL_SRS +#ifdef EXPERIMENTAL_SRS_ALT uschar *srs; uschar *srs_alias; uschar *srs_condition; diff --git a/src/src/search.c b/src/src/search.c index f8aaacb04..d1633a5e1 100644 --- a/src/src/search.c +++ b/src/src/search.c @@ -462,6 +462,7 @@ Arguments: NULL for query-style searches keystring the keystring for single-key+file lookups, or the querystring for query-style lookups + cache_rd FALSE to avoid lookup in cache layer opts type-specific options Returns: a pointer to a dynamic string containing the answer, @@ -472,7 +473,7 @@ Returns: a pointer to a dynamic string containing the answer, static uschar * internal_search_find(void * handle, const uschar * filename, uschar * keystring, - const uschar * opts) + BOOL cache_rd, const uschar * opts) { tree_node * t = (tree_node *)handle; search_cache * c = (search_cache *)(t->data.ptr); @@ -501,11 +502,13 @@ if (keystring[0] == 0) return NULL; store_pool = POOL_SEARCH; /* Look up the data for the key, unless it is already in the cache for this -file. No need to check c->item_cache for NULL, tree_search will do so. */ +file. No need to check c->item_cache for NULL, tree_search will do so. Check +whether we want to use the cache entry last so that we can always replace it. */ if ( (t = tree_search(c->item_cache, keystring)) && (!(e = t->data.ptr)->expiry || e->expiry > time(NULL)) && (!opts && !e->opts || opts && e->opts && Ustrcmp(opts, e->opts) == 0) + && cache_rd ) { /* Data was in the cache already; set the pointer from the tree node */ data = e->data.ptr; @@ -522,7 +525,8 @@ else { if (t) debug_printf_indent("cached data found but %s; ", - e->expiry && e->expiry <= time(NULL) ? "out-of-date" : "wrong opts"); + e->expiry && e->expiry <= time(NULL) ? "out-of-date" + : cache_rd ? "wrong opts" : "no_rd option set"); debug_printf_indent("%s lookup required for %s%s%s\n", filename ? US"file" : US"database", keystring, @@ -541,14 +545,19 @@ else or points to a bit of dynamic store. Cache the result of the lookup if caching is permitted. Lookups can disable caching, when they did something that changes their data. The mysql and pgsql lookups do this when an - UPDATE/INSERT query was executed. */ + UPDATE/INSERT query was executed. Lookups can also set a TTL for the + cache entry; the dnsdb lookup does. + Finally, the caller can request no caching by setting an option. */ else if (do_cache) { + DEBUG(D_lookup) debug_printf_indent("%s cache entry\n", + t ? "replacing old" : "creating new"); if (!t) /* No existing entry. Create new one. */ { int len = keylength + 1; - e = store_get(sizeof(expiring_data) + sizeof(tree_node) + len, is_tainted(keystring)); + e = store_get(sizeof(expiring_data) + sizeof(tree_node) + len, + is_tainted(keystring)); t = (tree_node *)(e+1); memcpy(t->name, keystring, len); t->data.ptr = e; @@ -621,9 +630,9 @@ search_find(void * handle, const uschar * filename, uschar * keystring, int partial, const uschar * affix, int affixlen, int starflags, int * expand_setup, const uschar * opts) { -tree_node *t = (tree_node *)handle; -BOOL set_null_wild = FALSE; -uschar *yield; +tree_node * t = (tree_node *)handle; +BOOL set_null_wild = FALSE, cache_rd = TRUE, ret_key = FALSE; +uschar * yield; DEBUG(D_lookup) { @@ -636,6 +645,23 @@ DEBUG(D_lookup) } +/* Parse global lookup options. Also, create a new options list with +the global options dropped so that the cache-modifiers are not +used in the cache key. */ + +if (opts) + { + int sep = ','; + gstring * g = NULL; + + for (uschar * ele; ele = string_nextinlist(&opts, &sep, NULL, 0); ) + if (Ustrcmp(ele, "ret=key") == 0) ret_key = TRUE; + else if (Ustrcmp(ele, "cache=no_rd") == 0) cache_rd = FALSE; + else g = string_append_listele(g, ',', ele); + + opts = string_from_gstring(g); + } + /* Arrange to put this database at the top of the LRU chain if it is a type that opens real files. */ @@ -683,7 +709,7 @@ DEBUG(D_lookup) /* First of all, try to match the key string verbatim. If matched a complete entry but could have been partial, flag to set up variables. */ -yield = internal_search_find(handle, filename, keystring, opts); +yield = internal_search_find(handle, filename, keystring, cache_rd, opts); if (f.search_find_defer) return NULL; if (yield) { if (partial >= 0) set_null_wild = TRUE; } @@ -708,7 +734,7 @@ else if (partial >= 0) Ustrncpy(keystring2, affix, affixlen); Ustrcpy(keystring2 + affixlen, keystring); DEBUG(D_lookup) debug_printf_indent("trying partial match %s\n", keystring2); - yield = internal_search_find(handle, filename, keystring2, opts); + yield = internal_search_find(handle, filename, keystring2, cache_rd, opts); if (f.search_find_defer) return NULL; } @@ -746,7 +772,8 @@ else if (partial >= 0) } DEBUG(D_lookup) debug_printf_indent("trying partial match %s\n", keystring3); - yield = internal_search_find(handle, filename, keystring3, opts); + yield = internal_search_find(handle, filename, keystring3, + cache_rd, opts); if (f.search_find_defer) return NULL; if (yield) { @@ -787,7 +814,7 @@ if (!yield && starflags & SEARCH_STARAT) *atat = '*'; DEBUG(D_lookup) debug_printf_indent("trying default match %s\n", atat); - yield = internal_search_find(handle, filename, atat, opts); + yield = internal_search_find(handle, filename, atat, cache_rd, opts); *atat = savechar; if (f.search_find_defer) return NULL; @@ -810,7 +837,7 @@ and the second is empty. */ if (!yield && starflags & (SEARCH_STAR|SEARCH_STARAT)) { DEBUG(D_lookup) debug_printf_indent("trying to match *\n"); - yield = internal_search_find(handle, filename, US"*", opts); + yield = internal_search_find(handle, filename, US"*", cache_rd, opts); if (yield && expand_setup && *expand_setup >= 0) { *expand_setup += 1; @@ -843,17 +870,8 @@ if (set_null_wild && expand_setup && *expand_setup >= 0) than the result. Return a de-tainted version of the key on the grounds that it have been validated by the lookup. */ -if (yield && opts) - { - int sep = ','; - for (uschar * ele; ele = string_nextinlist(&opts, &sep, NULL, 0); ) - if (Ustrcmp(ele, "ret=key") == 0) - { - DEBUG(D_lookup) debug_printf_indent("lookup ret=key: %s\n", keystring); - yield = string_copy_taint(keystring, FALSE); - break; - } - } +if (yield && ret_key) + yield = string_copy_taint(keystring, FALSE); return yield; } diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 526164c46..8b0902b5d 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -227,7 +227,7 @@ static smtp_cmd_list *cmd_list_end = /* This list of names is used for performing the smtp_no_mail logging action. It must be kept in step with the SCH_xxx enumerations. */ -static uschar *smtp_names[] = +uschar * smtp_names[] = { US"NONE", US"AUTH", US"DATA", US"BDAT", US"EHLO", US"ETRN", US"EXPN", US"HELO", US"HELP", US"MAIL", US"NOOP", US"QUIT", US"RCPT", US"RSET", @@ -1801,7 +1801,7 @@ s_tlslog(gstring * g) if (LOGGING(tls_cipher) && tls_in.cipher) { g = string_append(g, 2, US" X=", tls_in.cipher); -#ifdef EXPERIMENTAL_TLS_RESUME +#ifndef DISABLE_TLS_RESUME if (LOGGING(tls_resumption) && tls_in.resumption & RESUME_USED) g = string_catn(g, US"*", 1); #endif @@ -2068,6 +2068,7 @@ sending_ip_address = NULL; return_path = sender_address = NULL; deliver_localpart_data = deliver_domain_data = recipient_data = sender_data = NULL; /* Can be set by ACL */ +recipient_verify_failure = NULL; deliver_localpart_parent = deliver_localpart_orig = NULL; deliver_domain_parent = deliver_domain_orig = NULL; callout_address = NULL; diff --git a/src/src/spool_in.c b/src/src/spool_in.c index a0147d5ee..4b70780bc 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -683,7 +683,7 @@ for (;;) tls_in.sni = string_unprinting(string_copy_taint(q+4, tainted)); else if (Ustrncmp(q, "ocsp", 4) == 0) tls_in.ocsp = q[5] - '0'; -# ifdef EXPERIMENTAL_TLS_RESUME +# ifndef DISABLE_TLS_RESUME else if (Ustrncmp(q, "resumption", 10) == 0) tls_in.resumption = q[11] - 'A'; # endif diff --git a/src/src/spool_out.c b/src/src/spool_out.c index 4b6539ecd..9a514b331 100644 --- a/src/src/spool_out.c +++ b/src/src/spool_out.c @@ -261,7 +261,7 @@ if (tls_in.ourcert) fprintf(fp, "-tls_ourcert %s\n", CS big_buffer); } if (tls_in.ocsp) fprintf(fp, "-tls_ocsp %d\n", tls_in.ocsp); -# ifdef EXPERIMENTAL_TLS_RESUME +# ifndef DISABLE_TLS_RESUME fprintf(fp, "-tls_resumption %c\n", 'A' + tls_in.resumption); # endif if (tls_in.ver) spool_var_write(fp, US"tls_ver", tls_in.ver); diff --git a/src/src/srs.c b/src/src/srs.c index aed88bc62..657cd1771 100644 --- a/src/src/srs.c +++ b/src/src/srs.c @@ -11,7 +11,7 @@ License: GPL */ #include "exim.h" -#ifdef EXPERIMENTAL_SRS +#ifdef EXPERIMENTAL_SRS_ALT #include <srs_alt.h> #include "srs.h" diff --git a/src/src/srs.h b/src/src/srs.h index 99463de97..32404128f 100644 --- a/src/src/srs.h +++ b/src/src/srs.h @@ -10,7 +10,7 @@ #define __SRS_H__ 1 -#ifdef EXPERIMENTAL_SRS +#ifdef EXPERIMENTAL_SRS_ALT #include "mytypes.h" #include <srs_alt.h> diff --git a/src/src/structs.h b/src/src/structs.h index c6700d513..9aab603f8 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -515,7 +515,7 @@ typedef struct address_item_propagated { uschar *remove_headers; /* list of those to remove */ void *variables; /* router-vasriables */ -#ifdef EXPERIMENTAL_SRS +#ifdef EXPERIMENTAL_SRS_ALT uschar *srs_sender; /* Change return path when delivering */ #endif BOOL ignore_error:1; /* ignore delivery error */ @@ -644,7 +644,7 @@ typedef struct address_item { #ifdef SUPPORT_I18N BOOL af_utf8_downcvt:1; /* downconvert was done for delivery */ #endif -#ifdef EXPERIMENTAL_TLS_RESUME +#ifndef DISABLE_TLS_RESUME BOOL af_tls_resume:1; /* TLS used a resumed session */ #endif } flags; diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 24114f05e..a351c34d6 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -111,9 +111,11 @@ require current GnuTLS, then we'll drop support for the ancient libraries). # endif #endif -#ifdef EXPERIMENTAL_TLS_RESUME -# if GNUTLS_VERSION_NUMBER < 0x030603 -# error GNUTLS version too early for session-resumption +#ifndef DISABLE_TLS_RESUME +# if GNUTLS_VERSION_NUMBER >= 0x030603 +# define EXIM_HAVE_TLS_RESUME +# else +# warning "GnuTLS library version too old; resumption unsupported" # endif #endif @@ -131,7 +133,7 @@ require current GnuTLS, then we'll drop support for the ancient libraries). void options_tls(void) { -# ifdef EXPERIMENTAL_TLS_RESUME +# ifndef DISABLE_TLS_RESUME builtin_macro_create_var(US"_RESUME_DECODE", RESUME_DECODE_STRING ); # endif # ifdef EXIM_HAVE_TLS1_3 @@ -266,7 +268,7 @@ static BOOL gnutls_buggy_ocsp = FALSE; static BOOL exim_testharness_disable_ocsp_validity_check = FALSE; #endif -#ifdef EXPERIMENTAL_TLS_RESUME +#ifdef EXIM_HAVE_TLS_RESUME static gnutls_datum_t server_sessticket_key; #endif @@ -326,7 +328,7 @@ static void exim_gnutls_logger_cb(int level, const char *message); static int exim_sni_handling_cb(gnutls_session_t session); -#ifdef EXPERIMENTAL_TLS_RESUME +#ifdef EXIM_HAVE_TLS_RESUME static int tls_server_ticket_cb(gnutls_session_t sess, u_int htype, unsigned when, unsigned incoming, const gnutls_datum_t * msg); @@ -337,7 +339,7 @@ tls_server_ticket_cb(gnutls_session_t sess, u_int htype, unsigned when, void tls_daemon_init(void) { -#ifdef EXPERIMENTAL_TLS_RESUME +#ifdef EXIM_HAVE_TLS_RESUME /* We are dependent on the GnuTLS implementation of the Session Ticket encryption; both the strength and the key rotation period. We hope that the strength at least matches that of the ciphersuite (but GnuTLS does not @@ -1000,10 +1002,10 @@ return gnutls_ext_raw_parse(NULL, tls_server_servercerts_ext, msg, 0); "Handshake Protocol: Certificate" record. So we need to spot the Certificate handshake message, parse it and spot any status_request extension(s) -This is different to tls1.2 - where it is a separate record (wireshake term) / handshake message (gnutls term). +This is different to tls1.2 - where it is a separate record (wireshark term) / handshake message (gnutls term). */ -#if defined(EXPERIMENTAL_TLS_RESUME) || defined(SUPPORT_GNUTLS_EXT_RAW_PARSE) +#if defined(EXIM_HAVE_TLS_RESUME) || defined(SUPPORT_GNUTLS_EXT_RAW_PARSE) /* Callback for certificate-status, on server. We sent stapled OCSP. */ static int tls_server_certstatus_cb(gnutls_session_t session, unsigned int htype, @@ -1035,7 +1037,7 @@ switch (htype) # endif case GNUTLS_HANDSHAKE_CERTIFICATE_STATUS: return tls_server_certstatus_cb(sess, htype, when, incoming, msg); -# ifdef EXPERIMENTAL_TLS_RESUME +# ifdef EXIM_HAVE_TLS_RESUME case GNUTLS_HANDSHAKE_NEW_SESSION_TICKET: return tls_server_ticket_cb(sess, htype, when, incoming, msg); # endif @@ -2328,7 +2330,7 @@ else } -#ifdef EXPERIMENTAL_TLS_RESUME +#ifdef EXIM_HAVE_TLS_RESUME static int tls_server_ticket_cb(gnutls_session_t sess, u_int htype, unsigned when, unsigned incoming, const gnutls_datum_t * msg) @@ -2442,7 +2444,7 @@ DEBUG(D_tls) debug_printf("initialising GnuTLS as a server\n"); #endif } -#ifdef EXPERIMENTAL_TLS_RESUME +#ifdef EXIM_HAVE_TLS_RESUME tls_server_resume_prehandshake(state); #endif @@ -2550,7 +2552,7 @@ if (gnutls_session_get_flags(state->session) & GNUTLS_SFLAGS_EXT_MASTER_SECRET) tls_in.ext_master_secret = TRUE; #endif -#ifdef EXPERIMENTAL_TLS_RESUME +#ifdef EXIM_HAVE_TLS_RESUME tls_server_resume_posthandshake(state); #endif @@ -2683,7 +2685,7 @@ return TRUE; -#ifdef EXPERIMENTAL_TLS_RESUME +#ifdef EXIM_HAVE_TLS_RESUME /* On the client, get any stashed session for the given IP from hints db and apply it to the ssl-connection for attempted resumption. Although there is a gnutls_session_ticket_enable_client() interface it is @@ -2816,7 +2818,7 @@ if (gnutls_session_is_resumed(state->session)) tls_save_session(tlsp, state->session, host); } -#endif /* EXPERIMENTAL_TLS_RESUME */ +#endif /* !DISABLE_TLS_RESUME */ /************************************************* @@ -2970,7 +2972,7 @@ if (request_ocsp) } #endif -#ifdef EXPERIMENTAL_TLS_RESUME +#ifdef EXIM_HAVE_TLS_RESUME tls_client_resume_prehandshake(state, tlsp, host, ob); #endif @@ -3070,7 +3072,7 @@ if (request_ocsp) } #endif -#ifdef EXPERIMENTAL_TLS_RESUME +#ifdef EXIM_HAVE_TLS_RESUME tls_client_resume_posthandshake(state, tlsp, host); #endif diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 8c9d8aa69..a1d299b69 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -116,7 +116,7 @@ change this guard and punt the issue for a while longer. */ # define DISABLE_OCSP #endif -#ifdef EXPERIMENTAL_TLS_RESUME +#ifndef DISABLE_TLS_RESUME # if OPENSSL_VERSION_NUMBER < 0x0101010L # error OpenSSL version too old for session-resumption # endif @@ -292,7 +292,7 @@ for (struct exim_openssl_option * o = exim_openssl_options; builtin_macro_create(buf); } -# ifdef EXPERIMENTAL_TLS_RESUME +# ifndef DISABLE_TLS_RESUME builtin_macro_create_var(US"_RESUME_DECODE", RESUME_DECODE_STRING ); # endif # ifdef SSL_OP_NO_TLSv1_3 @@ -422,7 +422,7 @@ static int tls_server_stapling_cb(SSL *s, void *arg); /* Daemon-called, before every connection, key create/rotate */ -#ifdef EXPERIMENTAL_TLS_RESUME +#ifndef DISABLE_TLS_RESUME static void tk_init(void); static int tls_exdata_idx = -1; #endif @@ -430,7 +430,7 @@ static int tls_exdata_idx = -1; void tls_daemon_init(void) { -#ifdef EXPERIMENTAL_TLS_RESUME +#ifndef DISABLE_TLS_RESUME tk_init(); #endif return; @@ -891,7 +891,7 @@ fclose(fp); #endif -#ifdef EXPERIMENTAL_TLS_RESUME +#ifndef DISABLE_TLS_RESUME /* Manage the keysets used for encrypting the session tickets, on the server. */ typedef struct { /* Session ticket encryption key */ @@ -2176,12 +2176,12 @@ availability of the option value macros from OpenSSL. */ if (!tls_openssl_options_parse(openssl_options, &init_options)) return tls_error(US"openssl_options parsing failed", host, NULL, errstr); -#ifdef EXPERIMENTAL_TLS_RESUME +#ifndef DISABLE_TLS_RESUME tlsp->resumption = RESUME_SUPPORTED; #endif if (init_options) { -#ifdef EXPERIMENTAL_TLS_RESUME +#ifndef DISABLE_TLS_RESUME /* Should the server offer session resumption? */ if (!host && verify_check_host(&tls_resumption_hosts) == OK) { @@ -2685,12 +2685,12 @@ else if (verify_check_host(&tls_try_verify_hosts) == OK) server_verify_optional = TRUE; } -#ifdef EXPERIMENTAL_TLS_RESUME +#ifndef DISABLE_TLS_RESUME SSL_CTX_set_tlsext_ticket_key_cb(server_ctx, ticket_key_callback); /* despite working, appears to always return failure, so ignoring */ #endif #ifdef OPENSSL_HAVE_NUM_TICKETS -# ifdef EXPERIMENTAL_TLS_RESUME +# ifndef DISABLE_TLS_RESUME SSL_CTX_set_num_tickets(server_ctx, tls_in.host_resumable ? 1 : 0); # else SSL_CTX_set_num_tickets(server_ctx, 0); /* send no TLS1.3 stateful-tickets */ @@ -2796,7 +2796,7 @@ DEBUG(D_tls) debug_printf("SSL_accept was successful\n"); ERR_clear_error(); /* Even success can leave errors in the stack. Seen with anon-authentication ciphersuite negotiated. */ -#ifdef EXPERIMENTAL_TLS_RESUME +#ifndef DISABLE_TLS_RESUME if (SSL_session_reused(server_ssl)) { tls_in.resumption |= RESUME_USED; @@ -2983,7 +2983,7 @@ return DEFER; -#ifdef EXPERIMENTAL_TLS_RESUME +#ifndef DISABLE_TLS_RESUME /* On the client, get any stashed session for the given IP from hints db and apply it to the ssl-connection for attempted resumption. */ @@ -3145,7 +3145,7 @@ if (SSL_session_reused(exim_client_ctx->ssl)) tlsp->resumption |= RESUME_USED; } } -#endif /* EXPERIMENTAL_TLS_RESUME */ +#endif /* !DISABLE_TLS_RESUME */ /************************************************* @@ -3294,7 +3294,7 @@ else client_static_cbinfo, errstr) != OK) return FALSE; -#ifdef EXPERIMENTAL_TLS_RESUME +#ifndef DISABLE_TLS_RESUME tls_client_ctx_resume_prehandshake(exim_client_ctx, tlsp, ob, host); #endif @@ -3365,7 +3365,7 @@ if (request_ocsp) } #endif -#ifdef EXPERIMENTAL_TLS_RESUME +#ifndef DISABLE_TLS_RESUME if (!tls_client_ssl_resume_prehandshake(exim_client_ctx->ssl, tlsp, host, errstr)) return FALSE; @@ -3406,7 +3406,7 @@ DEBUG(D_tls) #endif } -#ifdef EXPERIMENTAL_TLS_RESUME +#ifndef DISABLE_TLS_RESUME tls_client_resume_posthandshake(exim_client_ctx, tlsp); #endif diff --git a/src/src/transport.c b/src/src/transport.c index 2d8426f29..f9ff521f7 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -1571,7 +1571,7 @@ for (host_item * host = hostlist; host; host = host->next) { sprintf(CS buffer, "%.200s:%d", host->name, host_record->sequence); dbfn_write(dbm_file, buffer, host_record, sizeof(dbdata_wait) + host_length); -#ifdef EXPERIMENTAL_QUEUE_RAMP +#ifndef DISABLE_QUEUE_RAMP if (f.queue_2stage && queue_fast_ramp && !queue_run_in_order) queue_notify_daemon(message_id); #endif diff --git a/src/src/transports/appendfile.c b/src/src/transports/appendfile.c index f96d00182..c8c0a58b6 100644 --- a/src/src/transports/appendfile.c +++ b/src/src/transports/appendfile.c @@ -2287,14 +2287,14 @@ else { uschar *new_check_path = string_copy(check_path); uschar *slash = Ustrrchr(new_check_path, '/'); - if (slash != NULL) + if (slash) { - if (slash[1] == 0) + if (!slash[1]) { *slash = 0; slash = Ustrrchr(new_check_path, '/'); } - if (slash != NULL) + if (slash) { *slash = 0; check_path = new_check_path; @@ -2349,7 +2349,7 @@ else { uschar *s = path + check_path_len; while (*s == '/') s++; - s = (*s == 0) ? US "new" : string_sprintf("%s/new", s); + s = *s ? string_sprintf("%s/new", s) : US"new"; if (pcre_exec(dir_regex, NULL, CS s, Ustrlen(s), 0, 0, NULL, 0) < 0) { disable_quota = TRUE; @@ -2408,10 +2408,11 @@ else count. Note that ob->quota_filecount_value cannot be set without ob->quota_value being set. */ - if (!disable_quota && - (ob->quota_value > 0 || THRESHOLD_CHECK) && - (mailbox_size < 0 || - (mailbox_filecount < 0 && ob->quota_filecount_value > 0))) + if ( !disable_quota + && (ob->quota_value > 0 || THRESHOLD_CHECK) + && ( mailbox_size < 0 + || mailbox_filecount < 0 && ob->quota_filecount_value > 0 + ) ) { off_t size; int filecount = 0; @@ -2484,7 +2485,7 @@ else uschar *basename; (void)gettimeofday(&msg_tv, NULL); - basename = string_sprintf(TIME_T_FMT ".H%luP" PID_T_FMT ".%s", + basename = string_sprintf(TIME_T_FMT ".M%luP" PID_T_FMT ".%s", msg_tv.tv_sec, msg_tv.tv_usec, getpid(), primary_hostname); filename = dataname = string_sprintf("tmp/%s", basename); @@ -2556,11 +2557,12 @@ else dataname = string_sprintf("%s.msg", mailstore_basename); fd = Uopen(filename, O_WRONLY|O_CREAT|O_EXCL, mode); - if (fd < 0 && /* failed to open, and */ - (errno != ENOENT || /* either not non-exist */ - !ob->create_directory || /* or not allowed to make */ - !directory_make(NULL, path, ob->dirmode, FALSE) || /* or failed to create dir */ - (fd = Uopen(filename, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0)) /* or then failed to open */ + if ( fd < 0 /* failed to open, and */ + && ( errno != ENOENT /* either not non-exist */ + || !ob->create_directory /* or not allowed to make */ + || !directory_make(NULL, path, ob->dirmode, FALSE) /* or failed to create dir */ + || (fd = Uopen(filename, O_WRONLY|O_CREAT|O_EXCL, mode)) < 0 /* or then failed to open */ + ) ) { addr->basic_errno = errno; addr->message = string_sprintf("while creating file %s", filename); @@ -2739,6 +2741,18 @@ if (!disable_quota && ob->quota_value > 0) } +if (verify_mode) + { + addr->basic_errno = errno; + addr->message = US"Over quota"; + addr->transport_return = yield; + DEBUG(D_transport) + debug_printf("appendfile (verify) yields %d with errno=%d more_errno=%d\n", + yield, addr->basic_errno, addr->more_errno); + + goto RETURN; + } + /* If we are writing in MBX format, what we actually do is to write the message to a temporary file, and then copy it to the real file once we know its size. This is the most straightforward way of getting the correct length in the diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 8492a7f25..3d41a6767 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -111,6 +111,7 @@ optionlist smtp_transport_options[] = { { "lmtp_ignore_quota", opt_bool, LOFF(lmtp_ignore_quota) }, { "max_rcpt", opt_int | opt_public, OPT_OFF(transport_instance, max_addresses) }, + { "message_linelength_limit", opt_int, LOFF(message_linelength_limit) }, { "multi_domain", opt_expand_bool | opt_public, OPT_OFF(transport_instance, multi_domain) }, { "port", opt_stringptr, LOFF(port) }, @@ -127,7 +128,7 @@ optionlist smtp_transport_options[] = { { "tls_dh_min_bits", opt_int, LOFF(tls_dh_min_bits) }, { "tls_privatekey", opt_stringptr, LOFF(tls_privatekey) }, { "tls_require_ciphers", opt_stringptr, LOFF(tls_require_ciphers) }, -# ifdef EXPERIMENTAL_TLS_RESUME +# ifndef DISABLE_TLS_RESUME { "tls_resumption_hosts", opt_stringptr, LOFF(tls_resumption_hosts) }, # endif { "tls_sni", opt_stringptr, LOFF(tls_sni) }, @@ -207,6 +208,7 @@ smtp_transport_options_block smtp_transport_option_defaults = { .size_addition = 1024, .hosts_max_try = 5, .hosts_max_try_hardlimit = 50, + .message_linelength_limit = 998, .address_retry_include_sender = TRUE, .allow_localhost = FALSE, .authenticated_sender_force = FALSE, @@ -233,7 +235,7 @@ smtp_transport_options_block smtp_transport_option_defaults = { .tls_verify_certificates = US"system", .tls_dh_min_bits = EXIM_CLIENT_DH_DEFAULT_MIN_BITS, .tls_tempfail_tryclear = TRUE, -# ifdef EXPERIMENTAL_TLS_RESUME +# ifndef DISABLE_TLS_RESUME .tls_resumption_hosts = NULL, # endif .tls_verify_hosts = NULL, @@ -1970,7 +1972,7 @@ tls_out.peerdn = NULL; tls_out.sni = NULL; #endif tls_out.ocsp = OCSP_NOT_REQ; -#ifdef EXPERIMENTAL_TLS_RESUME +#ifndef DISABLE_TLS_RESUME tls_out.resumption = 0; #endif tls_out.ver = NULL; @@ -4524,6 +4526,22 @@ DEBUG(D_transport) cutthrough.cctx.sock >= 0 ? cutthrough.cctx.sock : 0); } +/* Check the restrictions on line length */ + +if (max_received_linelength > ob->message_linelength_limit) + { + struct timeval now; + gettimeofday(&now, NULL); + + for (address_item * addr = addrlist; addr; addr = addr->next) + if (addr->transport_return == DEFER) + addr->transport_return = PENDING_DEFER; + + set_errno_nohost(addrlist, ERRNO_SMTPFORMAT, + US"message has lines too long for transport", FAIL, TRUE, &now); + goto END_TRANSPORT; + } + /* Set the flag requesting that these hosts be added to the waiting database if the delivery fails temporarily or if we are running with queue_smtp or a 2-stage queue run. This gets unset for certain diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index 6e63a002d..607a3772d 100644 --- a/src/src/transports/smtp.h +++ b/src/src/transports/smtp.h @@ -62,6 +62,7 @@ typedef struct { int size_addition; int hosts_max_try; int hosts_max_try_hardlimit; + int message_linelength_limit; BOOL address_retry_include_sender; BOOL allow_localhost; BOOL authenticated_sender_force; @@ -84,7 +85,7 @@ typedef struct { uschar *tls_crl; uschar *tls_privatekey; uschar *tls_require_ciphers; -# ifdef EXPERIMENTAL_TLS_RESUME +# ifndef DISABLE_TLS_RESUME uschar *tls_resumption_hosts; # endif uschar *tls_sni; diff --git a/src/src/verify.c b/src/src/verify.c index fba1f6e9e..3a40cea26 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -71,7 +71,7 @@ dbdata_callout_cache *cache_record; if (!(cache_record = dbfn_read_with_length(dbm_file, key, &length))) { - HDEBUG(D_verify) debug_printf("callout cache: no %s record found for %s\n", type, key); + HDEBUG(D_verify) debug_printf_indent("callout cache: no %s record found for %s\n", type, key); return NULL; } @@ -85,7 +85,7 @@ now = time(NULL); if (now - cache_record->time_stamp > expire) { - HDEBUG(D_verify) debug_printf("callout cache: %s record expired for %s\n", type, key); + HDEBUG(D_verify) debug_printf_indent("callout cache: %s record expired for %s\n", type, key); return NULL; } @@ -112,7 +112,7 @@ if (type[0] == 'd' && cache_record->result != ccache_reject) cache_record->random_result = ccache_unknown; } -HDEBUG(D_verify) debug_printf("callout cache: found %s record for %s\n", type, key); +HDEBUG(D_verify) debug_printf_indent("callout cache: found %s record for %s\n", type, key); return cache_record; } @@ -139,11 +139,11 @@ stage, unless caching has been disabled. */ if (options & vopt_callout_no_cache) { - HDEBUG(D_verify) debug_printf("callout cache: disabled by no_cache\n"); + HDEBUG(D_verify) debug_printf_indent("callout cache: disabled by no_cache\n"); } else if (!(dbm_file = dbfn_open(US"callout", O_RDWR, &dbblock, FALSE, TRUE))) { - HDEBUG(D_verify) debug_printf("callout cache: not available\n"); + HDEBUG(D_verify) debug_printf_indent("callout cache: not available\n"); } else { @@ -174,7 +174,7 @@ else || *from_address == 0 && cache_record->result == ccache_reject_mfnull) { HDEBUG(D_verify) - debug_printf("callout cache: domain gave initial rejection, or " + debug_printf_indent("callout cache: domain gave initial rejection, or " "does not accept HELO or MAIL FROM:<>\n"); setflag(addr, af_verify_nsfail); addr->user_message = US"(result of an earlier callout reused)."; @@ -195,14 +195,14 @@ else { case ccache_accept: HDEBUG(D_verify) - debug_printf("callout cache: domain accepts random addresses\n"); + debug_printf_indent("callout cache: domain accepts random addresses\n"); *failure_ptr = US"random"; dbfn_close(dbm_file); return TRUE; /* Default yield is OK */ case ccache_reject: HDEBUG(D_verify) - debug_printf("callout cache: domain rejects random addresses\n"); + debug_printf_indent("callout cache: domain rejects random addresses\n"); *opt_ptr = options & ~vopt_callout_random; new_domain_record->random_result = ccache_reject; new_domain_record->random_stamp = cache_record->random_stamp; @@ -210,7 +210,7 @@ else default: HDEBUG(D_verify) - debug_printf("callout cache: need to check random address handling " + debug_printf_indent("callout cache: need to check random address handling " "(not cached or cache expired)\n"); dbfn_close(dbm_file); return FALSE; @@ -227,7 +227,7 @@ else { setflag(addr, af_verify_pmfail); HDEBUG(D_verify) - debug_printf("callout cache: domain does not accept " + debug_printf_indent("callout cache: domain does not accept " "RCPT TO:<postmaster@domain>\n"); *yield = FAIL; *failure_ptr = US"postmaster"; @@ -239,7 +239,7 @@ else if (cache_record->postmaster_result == ccache_unknown) { HDEBUG(D_verify) - debug_printf("callout cache: need to check RCPT " + debug_printf_indent("callout cache: need to check RCPT " "TO:<postmaster@domain> (not cached or cache expired)\n"); dbfn_close(dbm_file); return FALSE; @@ -250,7 +250,7 @@ else that the value in the cache record is preserved (with its old timestamp). */ - HDEBUG(D_verify) debug_printf("callout cache: domain accepts RCPT " + HDEBUG(D_verify) debug_printf_indent("callout cache: domain accepts RCPT " "TO:<postmaster@domain>\n"); *pm_ptr = NULL; new_domain_record->postmaster_result = ccache_accept; @@ -274,12 +274,12 @@ else if (cache_address_record->result == ccache_accept) { HDEBUG(D_verify) - debug_printf("callout cache: address record is positive\n"); + debug_printf_indent("callout cache: address record is positive\n"); } else { HDEBUG(D_verify) - debug_printf("callout cache: address record is negative\n"); + debug_printf_indent("callout cache: address record is negative\n"); addr->user_message = US"Previous (cached) callout verification failure"; *failure_ptr = US"recipient"; *yield = FAIL; @@ -316,13 +316,13 @@ Otherwise the value is ccache_accept, ccache_reject, or ccache_reject_mfnull. */ if (dom_rec->result != ccache_unknown) if (!(dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE, TRUE))) { - HDEBUG(D_verify) debug_printf("callout cache: not available\n"); + HDEBUG(D_verify) debug_printf_indent("callout cache: not available\n"); } else { (void)dbfn_write(dbm_file, domain, dom_rec, (int)sizeof(dbdata_callout_cache)); - HDEBUG(D_verify) debug_printf("wrote callout cache domain record for %s:\n" + HDEBUG(D_verify) debug_printf_indent("wrote callout cache domain record for %s:\n" " result=%d postmaster=%d random=%d\n", domain, dom_rec->result, @@ -339,13 +339,13 @@ if (done && addr_rec->result != ccache_unknown) dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE, TRUE); if (!dbm_file) { - HDEBUG(D_verify) debug_printf("no callout cache available\n"); + HDEBUG(D_verify) debug_printf_indent("no callout cache available\n"); } else { (void)dbfn_write(dbm_file, address_key, addr_rec, (int)sizeof(dbdata_callout_cache_address)); - HDEBUG(D_verify) debug_printf("wrote %s callout cache address record for %s\n", + HDEBUG(D_verify) debug_printf_indent("wrote %s callout cache address record for %s\n", addr_rec->result == ccache_accept ? "positive" : "negative", address_key); } @@ -568,6 +568,7 @@ if (!addr->transport) { HDEBUG(D_verify) debug_printf("cannot callout via null transport\n"); } + else if (Ustrcmp(addr->transport->driver_name, "smtp") != 0) log_write(0, LOG_MAIN|LOG_PANIC|LOG_CONFIG_FOR, "callout transport '%s': %s is non-smtp", addr->transport->name, addr->transport->driver_name); @@ -1612,7 +1613,7 @@ address testing (-bt), which is indicated by address_test_mode being set. Arguments: vaddr contains the address to verify; the next field in this block must be NULL - f if not NULL, write the result to this file + fp if not NULL, write the result to this file options various option bits: vopt_fake_sender => this sender verify is not for the real sender (it was verify=sender=xxxx or an address from a @@ -1663,9 +1664,9 @@ BOOL expn = (options & vopt_expn) != 0; BOOL success_on_redirect = (options & vopt_success_on_redirect) != 0; int i; int yield = OK; -int verify_type = expn? v_expn : - f.address_test_mode? v_none : - options & vopt_is_recipient? v_recipient : v_sender; +int verify_type = expn ? v_expn : + f.address_test_mode ? v_none : + options & vopt_is_recipient ? v_recipient : v_sender; address_item *addr_list; address_item *addr_new = NULL; address_item *addr_remote = NULL; @@ -1846,6 +1847,8 @@ while (addr_new) if (rc == OK) { + BOOL local_verify = FALSE; + if (routed) *routed = TRUE; if (callout > 0) { @@ -1872,72 +1875,76 @@ while (addr_new) transport's options, so as to mimic what would happen if we were really sending a message to this address. */ - if ((tp = addr->transport) && !tp->info->local) - { - (void)(tp->setup)(tp, addr, &tf, 0, 0, NULL); + if ((tp = addr->transport)) + if (!tp->info->local) + { + (void)(tp->setup)(tp, addr, &tf, 0, 0, NULL); - /* If the transport has hosts and the router does not, or if the - transport is configured to override the router's hosts, we must build a - host list of the transport's hosts, and find the IP addresses */ + /* If the transport has hosts and the router does not, or if the + transport is configured to override the router's hosts, we must build a + host list of the transport's hosts, and find the IP addresses */ - if (tf.hosts && (!host_list || tf.hosts_override)) - { - uschar *s; - const uschar *save_deliver_domain = deliver_domain; - uschar *save_deliver_localpart = deliver_localpart; - - host_list = NULL; /* Ignore the router's hosts */ - - deliver_domain = addr->domain; - deliver_localpart = addr->local_part; - s = expand_string(tf.hosts); - deliver_domain = save_deliver_domain; - deliver_localpart = save_deliver_localpart; - - if (!s) - { - log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand list of hosts " - "\"%s\" in %s transport for callout: %s", tf.hosts, - tp->name, expand_string_message); - } - else - { - int flags; - host_build_hostlist(&host_list, s, tf.hosts_randomize); - - /* Just ignore failures to find a host address. If we don't manage - to find any addresses, the callout will defer. Note that more than - one address may be found for a single host, which will result in - additional host items being inserted into the chain. Hence we must - save the next host first. */ - - flags = HOST_FIND_BY_A | HOST_FIND_BY_AAAA; - if (tf.qualify_single) flags |= HOST_FIND_QUALIFY_SINGLE; - if (tf.search_parents) flags |= HOST_FIND_SEARCH_PARENTS; - - for (host_item * host = host_list, * nexthost; host; host = nexthost) - { - nexthost = host->next; - if (tf.gethostbyname || - string_is_ip_address(host->name, NULL) != 0) - (void)host_find_byname(host, NULL, flags, NULL, TRUE); - else + if (tf.hosts && (!host_list || tf.hosts_override)) + { + uschar *s; + const uschar *save_deliver_domain = deliver_domain; + uschar *save_deliver_localpart = deliver_localpart; + + host_list = NULL; /* Ignore the router's hosts */ + + deliver_domain = addr->domain; + deliver_localpart = addr->local_part; + s = expand_string(tf.hosts); + deliver_domain = save_deliver_domain; + deliver_localpart = save_deliver_localpart; + + if (!s) + { + log_write(0, LOG_MAIN|LOG_PANIC, "failed to expand list of hosts " + "\"%s\" in %s transport for callout: %s", tf.hosts, + tp->name, expand_string_message); + } + else + { + int flags; + host_build_hostlist(&host_list, s, tf.hosts_randomize); + + /* Just ignore failures to find a host address. If we don't manage + to find any addresses, the callout will defer. Note that more than + one address may be found for a single host, which will result in + additional host items being inserted into the chain. Hence we must + save the next host first. */ + + flags = HOST_FIND_BY_A | HOST_FIND_BY_AAAA; + if (tf.qualify_single) flags |= HOST_FIND_QUALIFY_SINGLE; + if (tf.search_parents) flags |= HOST_FIND_SEARCH_PARENTS; + + for (host_item * host = host_list, * nexthost; host; host = nexthost) { - const dnssec_domains * dsp = NULL; - if (Ustrcmp(tp->driver_name, "smtp") == 0) + nexthost = host->next; + if (tf.gethostbyname || + string_is_ip_address(host->name, NULL) != 0) + (void)host_find_byname(host, NULL, flags, NULL, TRUE); + else { - smtp_transport_options_block * ob = - (smtp_transport_options_block *) tp->options_block; - dsp = &ob->dnssec; - } + const dnssec_domains * dsp = NULL; + if (Ustrcmp(tp->driver_name, "smtp") == 0) + { + smtp_transport_options_block * ob = + (smtp_transport_options_block *) tp->options_block; + dsp = &ob->dnssec; + } - (void) host_find_bydns(host, NULL, flags, NULL, NULL, NULL, - dsp, NULL, NULL); + (void) host_find_bydns(host, NULL, flags, NULL, NULL, NULL, + dsp, NULL, NULL); + } } - } - } - } - } + } + } + } + else if ( options & vopt_quota + && Ustrcmp(tp->driver_name, "appendfile") == 0) + local_verify = TRUE; /* Can only do a callout if we have at least one host! If the callout fails, it will have set ${sender,recipient}_verify_failure. */ @@ -1963,11 +1970,17 @@ while (addr_new) #endif } } + else if (local_verify) + { + HDEBUG(D_verify) debug_printf("Attempting quota verification\n"); + + deliver_set_expansions(addr); + deliver_local(addr, TRUE); + rc = addr->transport_return; + } else - { HDEBUG(D_verify) debug_printf("Cannot do callout: neither router nor " "transport provided a host list, or transport is not smtp\n"); - } } } @@ -2156,7 +2169,7 @@ for (addr_list = addr_local, i = 0; i < 2; addr_list = addr_remote, i++) addr_list = addr->next; fprintf(fp, "%s", CS addr->address); -#ifdef EXPERIMENTAL_SRS +#ifdef EXPERIMENTAL_SRS_ALT if(addr->prop.srs_sender) fprintf(fp, " [srs = %s]", addr->prop.srs_sender); #endif @@ -3919,6 +3932,246 @@ while ((domain = string_nextinlist(&list, &sep, NULL, 0))) return FAIL; } + + +/**************************************************** + Verify a local user account for quota sufficiency +****************************************************/ + +/* The real work, done via a re-exec for privs, calls +down to the transport for the quota check. + +Route and transport (in recipient-verify mode) the +given recipient. + +A routing result indicating any transport type other than appendfile +results in a fail. + +Return, on stdout, a result string containing: +- highlevel result code (OK, DEFER, FAIL) +- errno +- where string +- message string +*/ + +void +verify_quota(uschar * address) +{ +address_item vaddr = {.address = address}; +BOOL routed; +uschar * msg = US"\0"; +int rc, len = 1; + +if ((rc = verify_address(&vaddr, NULL, vopt_is_recipient | vopt_quota, + 1, 0, 0, NULL, NULL, &routed)) != OK) + { + uschar * where = recipient_verify_failure; + msg = acl_verify_message ? acl_verify_message : vaddr.message; + if (!msg) msg = US""; + if (rc == DEFER && vaddr.basic_errno == ERRNO_EXIMQUOTA) + { + rc = FAIL; /* DEFER -> FAIL */ + where = US"quota"; + vaddr.basic_errno = 0; + } + else if (!where) where = US""; + + len = 5 + Ustrlen(msg) + 1 + Ustrlen(where); + msg = string_sprintf("%c%c%c%c%c%s%c%s", (uschar)rc, + (vaddr.basic_errno >> 24) && 0xff, (vaddr.basic_errno >> 16) && 0xff, + (vaddr.basic_errno >> 8) && 0xff, vaddr.basic_errno && 0xff, + where, '\0', msg); + } + +DEBUG(D_verify) debug_printf_indent("verify_quota: len %d\n", len); +write(1, msg, len); +return; +} + + +/******************************************************************************/ + +/* Quota cache lookup. We use the callout hints db also for the quota cache. +Return TRUE if a nonexpired record was found, having filled in the yield +argument. +*/ + +static BOOL +cached_quota_lookup(const uschar * rcpt, int * yield, + int pos_cache, int neg_cache) +{ +open_db dbblock, *dbm_file = NULL; +dbdata_callout_cache_address * cache_address_record; + +if (!pos_cache && !neg_cache) + return FALSE; +if (!(dbm_file = dbfn_open(US"callout", O_RDWR, &dbblock, FALSE, TRUE))) + { + HDEBUG(D_verify) debug_printf_indent("quota cache: not available\n"); + return FALSE; + } +if (!(cache_address_record = (dbdata_callout_cache_address *) + get_callout_cache_record(dbm_file, rcpt, US"address", + pos_cache, neg_cache))) + { + dbfn_close(dbm_file); + return FALSE; + } +if (cache_address_record->result == ccache_accept) + *yield = OK; +dbfn_close(dbm_file); +return TRUE; +} + +/* Quota cache write */ + +static void +cache_quota_write(const uschar * rcpt, int yield, int pos_cache, int neg_cache) +{ +open_db dbblock, *dbm_file = NULL; +dbdata_callout_cache_address cache_address_record; + +if (!pos_cache && !neg_cache) + return; +if (!(dbm_file = dbfn_open(US"callout", O_RDWR|O_CREAT, &dbblock, FALSE, TRUE))) + { + HDEBUG(D_verify) debug_printf_indent("quota cache: not available\n"); + return; + } + +cache_address_record.result = yield == OK ? ccache_accept : ccache_reject; + +(void)dbfn_write(dbm_file, rcpt, &cache_address_record, + (int)sizeof(dbdata_callout_cache_address)); +HDEBUG(D_verify) debug_printf_indent("wrote %s quota cache record for %s\n", + yield == OK ? "positive" : "negative", rcpt); + +dbfn_close(dbm_file); +return; +} + + +/* To evaluate a local user's quota, starting in ACL, we need to +fork & exec to regain privileges, to that we can change to the user's +identity for access to their files. + +Arguments: + rcpt Recipient account + pos_cache Number of seconds to cache a positive result (delivery + to be accepted). Zero to disable caching. + neg_cache Number of seconds to cache a negative result. Zero to disable. + msg Pointer to result string pointer + +Return: OK/DEFER/FAIL code +*/ + +int +verify_quota_call(const uschar * rcpt, int pos_cache, int neg_cache, + uschar ** msg) +{ +int pfd[2], pid, save_errno, yield = FAIL; +void (*oldsignal)(int); +const uschar * where = US"socketpair"; + +*msg = NULL; + +if (cached_quota_lookup(rcpt, &yield, pos_cache, neg_cache)) + { + HDEBUG(D_verify) debug_printf_indent("quota cache: address record is %d\n", + yield == OK ? "positive" : "negative"); + if (yield != OK) + { + recipient_verify_failure = US"quota"; + acl_verify_message = *msg = + US"Previous (cached) quota verification failure"; + } + return yield; + } + +if (pipe(pfd) != 0) + goto fail; + +where = US"fork"; +oldsignal = signal(SIGCHLD, SIG_DFL); +if ((pid = exim_fork(US"quota-verify")) < 0) + { + save_errno = errno; + close(pfd[pipe_write]); + close(pfd[pipe_read]); + errno = save_errno; + goto fail; + } + +if (pid == 0) /* child */ + { + close(pfd[pipe_read]); + force_fd(pfd[pipe_write], 1); /* stdout to pipe */ + close(pfd[pipe_write]); + dup2(1, 0); + if (debug_fd > 0) force_fd(debug_fd, 2); + + child_exec_exim(CEE_EXEC_EXIT, FALSE, NULL, FALSE, 3, + US"-MCq", string_sprintf("%d", message_size), rcpt); + /*NOTREACHED*/ + } + +save_errno = errno; +close(pfd[pipe_write]); + +if (pid < 0) + { + DEBUG(D_verify) debug_printf_indent(" fork: %s\n", strerror(save_errno)); + } +else + { + uschar buf[128]; + int n = read(pfd[pipe_read], buf, sizeof(buf)); + int status; + + waitpid(pid, &status, 0); + if (status == 0) + { + uschar * s; + + if (n > 0) yield = buf[0]; + if (n > 4) + save_errno = (buf[1] << 24) | (buf[2] << 16) | (buf[3] << 8) | buf[4]; + if ((recipient_verify_failure = n > 5 + ? string_copyn_taint(buf+5, n-5, FALSE) : NULL)) + { + int m; + s = buf + 5 + Ustrlen(recipient_verify_failure) + 1; + m = n - (s - buf); + acl_verify_message = *msg = + m > 0 ? string_copyn_taint(s, m, FALSE) : NULL; + } + + DEBUG(D_verify) debug_printf_indent("verify call response:" + " len %d yield %s errno '%s' where '%s' msg '%s'\n", + n, rc_names[yield], strerror(save_errno), recipient_verify_failure, *msg); + + if ( yield == OK + || save_errno == 0 && Ustrcmp(recipient_verify_failure, "quota") == 0) + cache_quota_write(rcpt, yield, pos_cache, neg_cache); + else DEBUG(D_verify) + debug_printf_indent("result not cacheable\n"); + } + else + { + DEBUG(D_verify) + debug_printf_indent("verify call response: waitpid status 0x%04x\n", status); + } + } + +close(pfd[pipe_read]); +errno = save_errno; + +fail: + +return yield; +} + + /* vi: aw ai sw=2 */ /* End of verify.c */ |