commit 0111e9faaba8126eac5f50992968cce768050399 Author: Phil Pennock Date: Mon Apr 16 15:24:34 2018 -0400 Belated README.UPDATING notes for Exim 4.91 People skip versions and move past them later, so while it's too late for 4.91, this will still help people moving to 4.92 from pre-4.91 in future. Note that none of these strictly needed to be documented here: experimental features, features marked as deprecated for many many years, etc. But let's err on the side of caution and include "things which will break if you try to upgrade without changing Local/Makefile". diff --git a/src/README.UPDATING b/src/README.UPDATING index 05b3d9d9..73b52e4a 100644 --- a/src/README.UPDATING +++ b/src/README.UPDATING @@ -26,6 +26,27 @@ The rest of this document contains information about changes in 4.xx releases that might affect a running system. +Exim version 4.91 +----------------- + + * DANE and SPF have been promoted from Experimental to Supported status, thus + the options to enable them in Local/Makefile have been renamed. + See current src/EDITME for full details, including changes in dependencies, + but loosely: replace EXPERIMENTAL_SPF with SUPPORT_SPF and replace + EXPERIMENTAL_DANE with SUPPORT_DANE. + + * Ancient ClamAV stream support, long deprecated by ClamAV, has been removed; + if you were building with WITH_OLD_CLAMAV_STREAM enabled then your problems + have marginally increased. + + * A number of logging changes; if relying upon the previous DKIM additional + log-line, explicit log_selector configuration is needed to keep it. + + * Other incompatible changes in EXPERIMENTAL_* features, read NewStuff and + ChangeLog carefully if relying upon an experimental feature such as DMARC. + Note that this includes changes to SPF as it was promoted into Supported. + + Exim version 4.89 ----------------- commit 101702337f7eb652fc99f440b64904b1e1629c2f Author: Jeremy Harris Date: Mon Apr 23 12:41:31 2018 +0100 Avoid doing logging in signal-handlers. Bug 1007 Cherry-picked from 9723f96673, with the LOCAL_SCAN changes removed diff --git a/src/src/functions.h b/src/src/functions.h index 9be5c32a..5f9deab6 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -427,6 +427,10 @@ extern int sieve_interpret(uschar *, int, uschar *, uschar *, uschar *, extern void sigalrm_handler(int); extern BOOL smtp_buffered(void); extern void smtp_closedown(uschar *); +extern void smtp_command_timeout_exit(void); +extern void smtp_command_sigterm_exit(void); +extern void smtp_data_timeout_exit(void); +extern void smtp_data_sigint_exit(void); extern uschar *smtp_cmd_hist(void); extern int smtp_connect(host_item *, int, uschar *, int, transport_instance *); diff --git a/src/src/globals.c b/src/src/globals.c index 7d18b38b..5b480572 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -770,6 +770,10 @@ uschar *gecos_name = NULL; uschar *gecos_pattern = NULL; rewrite_rule *global_rewrite_rules = NULL; +volatile sig_atomic_t had_command_timeout = 0; +volatile sig_atomic_t had_command_sigterm = 0; +volatile sig_atomic_t had_data_timeout = 0; +volatile sig_atomic_t had_data_sigint = 0; uschar *headers_charset = US HEADERS_CHARSET; int header_insert_maxlen = 64 * 1024; header_line *header_last = NULL; diff --git a/src/src/globals.h b/src/src/globals.h index da1230b7..2915513f 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -492,6 +492,10 @@ extern uschar *gecos_name; /* To be expanded when pattern matches */ extern uschar *gecos_pattern; /* Pattern to match */ extern rewrite_rule *global_rewrite_rules; /* Chain of rewriting rules */ +extern volatile sig_atomic_t had_command_timeout; /* Alarm sighandler called */ +extern volatile sig_atomic_t had_command_sigterm; /* TERM sighandler called */ +extern volatile sig_atomic_t had_data_timeout; /* Alarm sighandler called */ +extern volatile sig_atomic_t had_data_sigint; /* TERM/INT sighandler called */ extern int header_insert_maxlen; /* Max for inserting headers */ extern int header_maxsize; /* Max total length for header */ extern int header_line_maxsize; /* Max for an individual line */ diff --git a/src/src/local_scan.c b/src/src/local_scan.c index 3500047c..4dd0b2ba 100644 --- a/src/src/local_scan.c +++ b/src/src/local_scan.c @@ -12,6 +12,7 @@ If you want to implement your own version, you should copy this file to, say Local/local_scan.c, and edit the copy. To use your version instead of the default, you must set +HAVE_LOCAL_SCAN=yes LOCAL_SCAN_SOURCE=Local/local_scan.c in your Local/Makefile. This makes it easy to copy your version for use with diff --git a/src/src/receive.c b/src/src/receive.c index cba53c20..3b215f23 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -8,6 +8,7 @@ /* Code for receiving a message and setting up spool files. */ #include "exim.h" +#include #ifdef EXPERIMENTAL_DCC extern int dcc_ok; @@ -27,6 +28,10 @@ static uschar *spool_name = US""; enum CH_STATE {LF_SEEN, MID_LINE, CR_SEEN}; +jmp_buf local_scan_env; /* error-handling context for local_scan */ +unsigned had_local_scan_crash; +unsigned had_local_scan_timeout; + /************************************************* * Non-SMTP character reading functions * @@ -40,7 +45,27 @@ changing the pointer variables.) */ int stdin_getc(unsigned lim) { -return getc(stdin); +int c = getc(stdin); + +if (had_data_timeout) + { + fprintf(stderr, "exim: timed out while reading - message abandoned\n"); + log_write(L_lost_incoming_connection, + LOG_MAIN, "timed out while reading local message"); + receive_bomb_out(US"data-timeout", NULL); /* Does not return */ + } +if (had_data_sigint) + { + if (filter_test == FTEST_NONE) + { + fprintf(stderr, "\nexim: %s received - message abandoned\n", + had_data_sigint == SIGTERM ? "SIGTERM" : "SIGINT"); + log_write(0, LOG_MAIN, "%s received while reading local message", + had_data_sigint == SIGTERM ? "SIGTERM" : "SIGINT"); + } + receive_bomb_out(US"signal-exit", NULL); /* Does not return */ + } +return c; } int @@ -316,11 +341,13 @@ if (spool_name[0] != '\0') /* Now close the file if it is open, either as a fd or a stream. */ -if (data_file != NULL) +if (data_file) { (void)fclose(data_file); data_file = NULL; -} else if (data_fd >= 0) { + } +else if (data_fd >= 0) + { (void)close(data_fd); data_fd = -1; } @@ -361,27 +388,7 @@ Returns: nothing static void data_timeout_handler(int sig) { -uschar *msg = NULL; - -sig = sig; /* Keep picky compilers happy */ - -if (smtp_input) - { - msg = US"SMTP incoming data timeout"; - log_write(L_lost_incoming_connection, - LOG_MAIN, "SMTP data timeout (message abandoned) on connection " - "from %s F=<%s>", - (sender_fullhost != NULL)? sender_fullhost : US"local process", - sender_address); - } -else - { - fprintf(stderr, "exim: timed out while reading - message abandoned\n"); - log_write(L_lost_incoming_connection, - LOG_MAIN, "timed out while reading local message"); - } - -receive_bomb_out(US"data-timeout", msg); /* Does not return */ +had_data_timeout = sig; } @@ -391,7 +398,18 @@ receive_bomb_out(US"data-timeout", msg); /* Does not return */ *************************************************/ /* Handler function for timeouts that occur while running a local_scan() -function. +function. Posix recommends against calling longjmp() from a signal-handler, +but the GCC manual says you can so we will, and trust that it's better than +calling probably non-signal-safe functions during logging from within the +handler, even with other compilers. + +See also https://cwe.mitre.org/data/definitions/745.html which also lists +it as unsafe. + +This is all because we have no control over what might be written for a +local-scan function, so cannot sprinkle had-signal checks after each +call-site. At least with the default "do-nothing" function we won't +ever get here. Argument: the signal number Returns: nothing @@ -400,11 +418,8 @@ Returns: nothing static void local_scan_timeout_handler(int sig) { -sig = sig; /* Keep picky compilers happy */ -log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() function timed out - " - "message temporarily rejected (size %d)", message_size); -/* Does not return */ -receive_bomb_out(US"local-scan-timeout", US"local verification problem"); +had_local_scan_timeout = sig; +siglongjmp(local_scan_env, 1); } @@ -423,13 +438,12 @@ Returns: nothing static void local_scan_crash_handler(int sig) { -log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() function crashed with " - "signal %d - message temporarily rejected (size %d)", sig, message_size); -/* Does not return */ -receive_bomb_out(US"local-scan-error", US"local verification problem"); +had_local_scan_crash = sig; +siglongjmp(local_scan_env, 1); } + /************************************************* * SIGTERM or SIGINT received * *************************************************/ @@ -444,26 +458,7 @@ Returns: nothing static void data_sigterm_sigint_handler(int sig) { -uschar *msg = NULL; - -if (smtp_input) - { - msg = US"Service not available - SIGTERM or SIGINT received"; - log_write(0, LOG_MAIN, "%s closed after %s", smtp_get_connection_info(), - (sig == SIGTERM)? "SIGTERM" : "SIGINT"); - } -else - { - if (filter_test == FTEST_NONE) - { - fprintf(stderr, "\nexim: %s received - message abandoned\n", - (sig == SIGTERM)? "SIGTERM" : "SIGINT"); - log_write(0, LOG_MAIN, "%s received while reading local message", - (sig == SIGTERM)? "SIGTERM" : "SIGINT"); - } - } - -receive_bomb_out(US"signal-exit", msg); /* Does not return */ +had_data_sigint = sig; } @@ -1678,6 +1673,7 @@ int dmarc_up = 0; uschar *timestamp; int tslen; + /* Release any open files that might have been cached while preparing to accept the message - e.g. by verifying addresses - because reading a message might take a fair bit of real time. */ @@ -1751,7 +1747,9 @@ received_time = message_id_tv; /* If SMTP input, set the special handler for timeouts. The alarm() calls happen in the smtp_getc() function when it refills its buffer. */ -if (smtp_input) os_non_restarting_signal(SIGALRM, data_timeout_handler); +had_data_timeout = 0; +if (smtp_input) + os_non_restarting_signal(SIGALRM, data_timeout_handler); /* If not SMTP input, timeout happens only if configured, and we just set a single timeout for the whole message. */ @@ -1764,6 +1762,7 @@ else if (receive_timeout > 0) /* SIGTERM and SIGINT are caught always. */ +had_data_sigint = 0; signal(SIGTERM, data_sigterm_sigint_handler); signal(SIGINT, data_sigterm_sigint_handler); @@ -3638,43 +3637,65 @@ dcc_ok = 0; version supplied with Exim always accepts, but this is a hook for sysadmins to supply their own checking code. The local_scan() function is run even when all the recipients have been discarded. */ -/*XXS could we avoid this for the standard case, given that few people will use it? */ lseek(data_fd, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); /* Arrange to catch crashes in local_scan(), so that the -D file gets deleted, and the incident gets logged. */ -os_non_restarting_signal(SIGSEGV, local_scan_crash_handler); -os_non_restarting_signal(SIGFPE, local_scan_crash_handler); -os_non_restarting_signal(SIGILL, local_scan_crash_handler); -os_non_restarting_signal(SIGBUS, local_scan_crash_handler); - -DEBUG(D_receive) debug_printf("calling local_scan(); timeout=%d\n", - local_scan_timeout); -local_scan_data = NULL; - -os_non_restarting_signal(SIGALRM, local_scan_timeout_handler); -if (local_scan_timeout > 0) alarm(local_scan_timeout); -rc = local_scan(data_fd, &local_scan_data); -alarm(0); -os_non_restarting_signal(SIGALRM, sigalrm_handler); - -enable_dollar_recipients = FALSE; - -store_pool = POOL_MAIN; /* In case changed */ -DEBUG(D_receive) debug_printf("local_scan() returned %d %s\n", rc, - local_scan_data); - -os_non_restarting_signal(SIGSEGV, SIG_DFL); -os_non_restarting_signal(SIGFPE, SIG_DFL); -os_non_restarting_signal(SIGILL, SIG_DFL); -os_non_restarting_signal(SIGBUS, SIG_DFL); +if (sigsetjmp(local_scan_env, 1) == 0) + { + had_local_scan_crash = 0; + os_non_restarting_signal(SIGSEGV, local_scan_crash_handler); + os_non_restarting_signal(SIGFPE, local_scan_crash_handler); + os_non_restarting_signal(SIGILL, local_scan_crash_handler); + os_non_restarting_signal(SIGBUS, local_scan_crash_handler); + + DEBUG(D_receive) debug_printf("calling local_scan(); timeout=%d\n", + local_scan_timeout); + local_scan_data = NULL; + + had_local_scan_timeout = 0; + os_non_restarting_signal(SIGALRM, local_scan_timeout_handler); + if (local_scan_timeout > 0) alarm(local_scan_timeout); + rc = local_scan(data_fd, &local_scan_data); + alarm(0); + os_non_restarting_signal(SIGALRM, sigalrm_handler); + + enable_dollar_recipients = FALSE; + + store_pool = POOL_MAIN; /* In case changed */ + DEBUG(D_receive) debug_printf("local_scan() returned %d %s\n", rc, + local_scan_data); + + os_non_restarting_signal(SIGSEGV, SIG_DFL); + os_non_restarting_signal(SIGFPE, SIG_DFL); + os_non_restarting_signal(SIGILL, SIG_DFL); + os_non_restarting_signal(SIGBUS, SIG_DFL); + } +else + { + if (had_local_scan_crash) + { + log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() function crashed with " + "signal %d - message temporarily rejected (size %d)", + had_local_scan_crash, message_size); + /* Does not return */ + receive_bomb_out(US"local-scan-error", US"local verification problem"); + } + if (had_local_scan_timeout) + { + log_write(0, LOG_MAIN|LOG_REJECT, "local_scan() function timed out - " + "message temporarily rejected (size %d)", message_size); + /* Does not return */ + receive_bomb_out(US"local-scan-timeout", US"local verification problem"); + } + } /* The length check is paranoia against some runaway code, and also because (for a success return) lines in the spool file are read into big_buffer. */ -if (local_scan_data != NULL) +if (local_scan_data) { int len = Ustrlen(local_scan_data); if (len > LOCAL_SCAN_MAX_RETURN) len = LOCAL_SCAN_MAX_RETURN; @@ -3706,7 +3727,7 @@ the spool file gets corrupted. Ensure that all recipients are qualified. */ if (rc == LOCAL_SCAN_ACCEPT) { - if (local_scan_data != NULL) + if (local_scan_data) { uschar *s; for (s = local_scan_data; *s != 0; s++) if (*s == '\n') *s = ' '; @@ -4331,9 +4352,9 @@ starting. */ if (blackholed_by) { - const uschar *detail = local_scan_data - ? string_printing(local_scan_data) - : string_sprintf("(%s discarded recipients)", blackholed_by); + const uschar *detail = + local_scan_data ? string_printing(local_scan_data) : + string_sprintf("(%s discarded recipients)", blackholed_by); log_write(0, LOG_MAIN, "=> blackhole %s%s", detail, blackhole_log_msg); log_write(0, LOG_MAIN, "Completed"); message_id[0] = 0; diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 33d6d3cc..433c677e 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -424,6 +424,53 @@ log_write(L_smtp_incomplete_transaction, LOG_MAIN|LOG_SENDER|LOG_RECIPIENTS, +void +smtp_command_timeout_exit(void) +{ +log_write(L_lost_incoming_connection, + LOG_MAIN, "SMTP command timeout on%s connection from %s", + tls_in.active >= 0 ? " TLS" : "", host_and_ident(FALSE)); +if (smtp_batched_input) + moan_smtp_batch(NULL, "421 SMTP command timeout"); /* Does not return */ +smtp_notquit_exit(US"command-timeout", US"421", + US"%s: SMTP command timeout - closing connection", + smtp_active_hostname); +exim_exit(EXIT_FAILURE, US"receiving"); +} + +void +smtp_command_sigterm_exit(void) +{ +log_write(0, LOG_MAIN, "%s closed after SIGTERM", smtp_get_connection_info()); +if (smtp_batched_input) + moan_smtp_batch(NULL, "421 SIGTERM received"); /* Does not return */ +smtp_notquit_exit(US"signal-exit", US"421", + US"%s: Service not available - closing connection", smtp_active_hostname); +exim_exit(EXIT_FAILURE, US"receiving"); +} + +void +smtp_data_timeout_exit(void) +{ +log_write(L_lost_incoming_connection, + LOG_MAIN, "SMTP data timeout (message abandoned) on connection from %s F=<%s>", + sender_fullhost ? sender_fullhost : US"local process", sender_address); +receive_bomb_out(US"data-timeout", US"SMTP incoming data timeout"); +/* Does not return */ +} + +void +smtp_data_sigint_exit(void) +{ +log_write(0, LOG_MAIN, "%s closed after %s", + smtp_get_connection_info(), had_data_sigint == SIGTERM ? "SIGTERM":"SIGINT"); +receive_bomb_out(US"signal-exit", + US"Service not available - SIGTERM or SIGINT received"); +/* Does not return */ +} + + + /* Refill the buffer, and notify DKIM verification code. Return false for error or EOF. */ @@ -441,18 +488,28 @@ Take care to not touch the safety NUL at the end of the buffer. */ rc = read(fileno(smtp_in), smtp_inbuffer, MIN(IN_BUFFER_SIZE-1, lim)); save_errno = errno; -alarm(0); +if (smtp_receive_timeout > 0) alarm(0); if (rc <= 0) { /* Must put the error text in fixed store, because this might be during header reading, where it releases unused store above the header. */ if (rc < 0) { + if (had_command_timeout) /* set by signal handler */ + smtp_command_timeout_exit(); /* does not return */ + if (had_command_sigterm) + smtp_command_sigterm_exit(); + if (had_data_timeout) + smtp_data_timeout_exit(); + if (had_data_sigint) + smtp_data_sigint_exit(); + smtp_had_error = save_errno; smtp_read_error = string_copy_malloc( string_sprintf(" (error: %s)", strerror(save_errno))); } - else smtp_had_eof = 1; + else + smtp_had_eof = 1; return FALSE; } #ifndef DISABLE_DKIM @@ -914,16 +971,7 @@ Returns: nothing static void command_timeout_handler(int sig) { -sig = sig; /* Keep picky compilers happy */ -log_write(L_lost_incoming_connection, - LOG_MAIN, "SMTP command timeout on%s connection from %s", - (tls_in.active >= 0)? " TLS" : "", - host_and_ident(FALSE)); -if (smtp_batched_input) - moan_smtp_batch(NULL, "421 SMTP command timeout"); /* Does not return */ -smtp_notquit_exit(US"command-timeout", US"421", - US"%s: SMTP command timeout - closing connection", smtp_active_hostname); -exim_exit(EXIT_FAILURE, US"receiving"); +had_command_timeout = sig; } @@ -941,13 +989,7 @@ Returns: nothing static void command_sigterm_handler(int sig) { -sig = sig; /* Keep picky compilers happy */ -log_write(0, LOG_MAIN, "%s closed after SIGTERM", smtp_get_connection_info()); -if (smtp_batched_input) - moan_smtp_batch(NULL, "421 SIGTERM received"); /* Does not return */ -smtp_notquit_exit(US"signal-exit", US"421", - US"%s: Service not available - closing connection", smtp_active_hostname); -exim_exit(EXIT_FAILURE, US"receiving"); +had_command_sigterm = sig; } @@ -1507,6 +1549,7 @@ int ptr = 0; smtp_cmd_list *p; BOOL hadnull = FALSE; +had_command_timeout = 0; os_non_restarting_signal(SIGALRM, command_timeout_handler); while ((c = (receive_getc)(buffer_lim)) != '\n' && c != EOF) @@ -3811,6 +3854,7 @@ cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = TRUE; /* Set the local signal handler for SIGTERM - it tries to end off tidily */ +had_command_sigterm = 0; os_non_restarting_signal(SIGTERM, command_sigterm_handler); /* Batched SMTP is handled in a different function. */ diff --git a/src/src/spool_out.c b/src/src/spool_out.c index 8bebf107..2aa60322 100644 --- a/src/src/spool_out.c +++ b/src/src/spool_out.c @@ -221,7 +221,7 @@ if (host_lookup_deferred) fprintf(f, "-host_lookup_deferred\n"); if (host_lookup_failed) fprintf(f, "-host_lookup_failed\n"); if (sender_local) fprintf(f, "-local\n"); if (local_error_message) fprintf(f, "-localerror\n"); -if (local_scan_data != NULL) fprintf(f, "-local_scan %s\n", local_scan_data); +if (local_scan_data) fprintf(f, "-local_scan %s\n", local_scan_data); #ifdef WITH_CONTENT_SCAN if (spam_bar) fprintf(f,"-spam_bar %s\n", spam_bar); if (spam_score) fprintf(f,"-spam_score %s\n", spam_score); @@ -231,7 +231,7 @@ if (deliver_manual_thaw) fprintf(f, "-manual_thaw\n"); if (sender_set_untrusted) fprintf(f, "-sender_set_untrusted\n"); #ifdef EXPERIMENTAL_BRIGHTMAIL -if (bmi_verdicts != NULL) fprintf(f, "-bmi_verdicts %s\n", bmi_verdicts); +if (bmi_verdicts) fprintf(f, "-bmi_verdicts %s\n", bmi_verdicts); #endif #ifdef SUPPORT_TLS diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index d7318827..35816cd6 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -2511,12 +2511,20 @@ sigalrm_seen = FALSE; if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout); inbytes = gnutls_record_recv(state->session, state->xfer_buffer, MIN(ssl_xfer_buffer_size, lim)); -alarm(0); - -/* Timeouts do not get this far; see command_timeout_handler(). - A zero-byte return appears to mean that the TLS session has been - closed down, not that the socket itself has been closed down. Revert to - non-TLS handling. */ +if (smtp_receive_timeout > 0) alarm(0); + +if (had_command_timeout) /* set by signal handler */ + smtp_command_timeout_exit(); /* does not return */ +if (had_command_sigterm) + smtp_command_sigterm_exit(); +if (had_data_timeout) + smtp_data_timeout_exit(); +if (had_data_sigint) + smtp_data_sigint_exit(); + +/* Timeouts do not get this far. A zero-byte return appears to mean that the +TLS session has been closed down, not that the socket itself has been closed +down. Revert to non-TLS handling. */ if (sigalrm_seen) { diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index bfdfe211..fb59217d 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -2475,7 +2475,16 @@ if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout); inbytes = SSL_read(server_ssl, CS ssl_xfer_buffer, MIN(ssl_xfer_buffer_size, lim)); error = SSL_get_error(server_ssl, inbytes); -alarm(0); +if (smtp_receive_timeout > 0) alarm(0); + +if (had_command_timeout) /* set by signal handler */ + smtp_command_timeout_exit(); /* does not return */ +if (had_command_sigterm) + smtp_command_sigterm_exit(); +if (had_data_timeout) + smtp_data_timeout_exit(); +if (had_data_sigint) + smtp_data_sigint_exit(); /* SSL_ERROR_ZERO_RETURN appears to mean that the SSL session has been closed down, not that the socket itself has been closed down. Revert to commit d862036b77ae978f3d105adb7c11f0795bbb2732 Author: Jeremy Harris Date: Mon Apr 16 09:15:17 2018 +0100 Fix typo in arc. Bug 2262 (cherry picked from commit 69a82da3e9018b082cee870030ecc557497301df) diff --git a/src/src/arc.c b/src/src/arc.c index 6f567dc5..557ea851 100644 --- a/src/src/arc.c +++ b/src/src/arc.c @@ -1546,7 +1546,7 @@ pdkim_bodyhash * b; identity = string_nextinlist(&signspec, &sep, NULL, 0); selector = string_nextinlist(&signspec, &sep, NULL, 0); -if ( !*identity | !*selector +if ( !*identity || !*selector || !(privkey = string_nextinlist(&signspec, &sep, NULL, 0)) || !*privkey) { log_write(0, LOG_MAIN, "ARC: bad signing-specification (%s)", commit 2349d6c47c1922b661dcd2e3ce57fc34ade42230 Author: Jeremy Harris Date: Mon Apr 16 18:45:04 2018 +0100 Fix OpenSSL non-OCSP build (cherry picked from commit 37f0ce65959019e417ff79b9d0959e13470c5290) diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index fb59217d..cefa94fe 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -2505,10 +2505,12 @@ if (error == SSL_ERROR_ZERO_RETURN) if (SSL_get_shutdown(server_ssl) == SSL_RECEIVED_SHUTDOWN) SSL_shutdown(server_ssl); +#ifndef DISABLE_OCSP sk_X509_pop_free(server_static_cbinfo->verify_stack, X509_free); + server_static_cbinfo->verify_stack = NULL; +#endif SSL_free(server_ssl); SSL_CTX_free(server_ctx); - server_static_cbinfo->verify_stack = NULL; server_ctx = NULL; server_ssl = NULL; tls_in.active = -1; @@ -2782,11 +2784,13 @@ if (shutdown) } } +#ifndef DISABLE_OCSP if (is_server) { sk_X509_pop_free(server_static_cbinfo->verify_stack, X509_free); server_static_cbinfo->verify_stack = NULL; } +#endif SSL_CTX_free(*ctxp); SSL_free(*sslp); commit e20e2d6d5e2dd50b29b785b3831be655138d046e Author: Jeremy Harris Date: Mon Apr 23 11:26:52 2018 +0100 DKIM: enforce limit of 20 on received DKIM-Signature: headers. Bug 2269 (cherry picked from commit 64b67b658a37dd780cc1b2fd0ef87febe461a0ba) diff --git a/src/exim_monitor/em_globals.c b/src/exim_monitor/em_globals.c index 50da58c8..e0ed0244 100644 --- a/src/exim_monitor/em_globals.c +++ b/src/exim_monitor/em_globals.c @@ -139,7 +139,7 @@ uschar *dkim_signers = NULL; uschar *dkim_signing_domain = NULL; uschar *dkim_signing_selector = NULL; uschar *dkim_verify_signers = US"$dkim_signers"; -BOOL dkim_collect_input = FALSE; +unsigned dkim_collect_input = 0; BOOL dkim_disable_verify = FALSE; #endif diff --git a/src/src/dkim.c b/src/src/dkim.c index 59445a85..c86dd5f0 100644 --- a/src/src/dkim.c +++ b/src/src/dkim.c @@ -34,6 +34,8 @@ pdkim_signature *dkim_signatures = NULL; pdkim_signature *dkim_cur_sig = NULL; static const uschar * dkim_collect_error = NULL; +#define DKIM_MAX_SIGNATURES 20 + /*XXX the caller only uses the first record if we return multiple. @@ -115,7 +117,7 @@ if (dkim_verify_ctx) /* Create new context */ dkim_verify_ctx = pdkim_init_verify(&dkim_exim_query_dns_txt, dot_stuffing); -dkim_collect_input = !!dkim_verify_ctx; +dkim_collect_input = dkim_verify_ctx ? DKIM_MAX_SIGNATURES : 0; dkim_collect_error = NULL; /* Start feed up with any cached data */ @@ -137,7 +139,7 @@ if ( dkim_collect_input dkim_collect_error = pdkim_errstr(rc); log_write(0, LOG_MAIN, "DKIM: validation error: %.100s", dkim_collect_error); - dkim_collect_input = FALSE; + dkim_collect_input = 0; } store_pool = dkim_verify_oldpool; } @@ -309,7 +311,7 @@ if (dkim_collect_error) goto out; } -dkim_collect_input = FALSE; +dkim_collect_input = 0; /* Finish DKIM operation and fetch link to signatures chain */ diff --git a/src/src/globals.c b/src/src/globals.c index 5b480572..9b34da87 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -669,7 +669,7 @@ BOOL disable_ipv6 = FALSE; BOOL disable_logging = FALSE; #ifndef DISABLE_DKIM -BOOL dkim_collect_input = FALSE; +unsigned dkim_collect_input = 0; uschar *dkim_cur_signer = NULL; BOOL dkim_disable_verify = FALSE; int dkim_key_length = 0; diff --git a/src/src/globals.h b/src/src/globals.h index 2915513f..a8835ed3 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -392,7 +392,7 @@ extern BOOL disable_ipv6; /* Don't do any IPv6 things */ extern BOOL disable_logging; /* Disables log writing when TRUE */ #ifndef DISABLE_DKIM -extern BOOL dkim_collect_input; /* Runtime flag that tracks wether SMTP input is fed to DKIM validation */ +extern unsigned dkim_collect_input; /* Runtime count of dkim signtures; tracks wether SMTP input is fed to DKIM validation */ extern uschar *dkim_cur_signer; /* Expansion variable, holds the current "signer" domain or identity during a acl_smtp_dkim run */ extern BOOL dkim_disable_verify; /* Set via ACL control statement. When set, DKIM verification is disabled for the current message */ extern int dkim_key_length; /* Expansion variable, length of signing key in bits */ diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index 5688c78a..594af03c 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -192,6 +192,7 @@ switch(status) case PDKIM_ERR_RSA_SIGNING: return US"SIGNING"; case PDKIM_ERR_LONG_LINE: return US"LONG_LINE"; case PDKIM_ERR_BUFFER_TOO_SMALL: return US"BUFFER_TOO_SMALL"; + case PDKIM_ERR_EXCESS_SIGS: return US"EXCESS_SIGS"; case PDKIM_SIGN_PRIVKEY_WRAP: return US"PRIVKEY_WRAP"; case PDKIM_SIGN_PRIVKEY_B64D: return US"PRIVKEY_B64D"; default: return US"(unknown)"; @@ -1008,6 +1009,13 @@ else while (last_sig->next) last_sig = last_sig->next; last_sig->next = sig; } + + if (--dkim_collect_input == 0) + { + ctx->headers = pdkim_prepend_stringlist(ctx->headers, ctx->cur_header->s); + ctx->cur_header->s[ctx->cur_header->ptr = 0] = '\0'; + return PDKIM_ERR_EXCESS_SIGS; + } } /* all headers are stored for signature verification */ @@ -1033,7 +1041,7 @@ int p, rc; if (!data) pdkim_body_complete(ctx); -else for (p = 0; p 0) @@ -606,7 +606,7 @@ for(;;) receive_ungetc = lwr_receive_ungetc; #ifndef DISABLE_DKIM dkim_save = dkim_collect_input; - dkim_collect_input = FALSE; + dkim_collect_input = 0; #endif /* Unless PIPELINING was offered, there should be no next command @@ -2041,7 +2041,8 @@ dnslist_domain = dnslist_matched = NULL; dkim_cur_signer = dkim_signers = dkim_signing_domain = dkim_signing_selector = NULL; dkim_cur_signer = dkim_signers = dkim_signing_domain = dkim_signing_selector = NULL; -dkim_disable_verify = dkim_collect_input = FALSE; +dkim_disable_verify = FALSE; +dkim_collect_input = 0; dkim_verify_overall = dkim_verify_status = dkim_verify_reason = NULL; dkim_key_length = 0; dkim_verify_signers = US"$dkim_signers"; diff --git a/src/src/spool_in.c b/src/src/spool_in.c index e34b5417..f0275bb7 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -261,7 +261,7 @@ bmi_verdicts = NULL; #ifndef DISABLE_DKIM dkim_signers = NULL; dkim_disable_verify = FALSE; -dkim_collect_input = FALSE; +dkim_collect_input = 0; #endif #ifdef SUPPORT_TLS commit 56b566daa255a8c3ebf59e6b71ba9576f21a9b2c Author: Jeremy Harris Date: Sat May 5 22:47:58 2018 +0100 Cutthrough: fix race resulting in duplicate-delivery. Bug 2273 Cherry-picked from: cfbb0d24e8 diff --git a/src/src/receive.c b/src/src/receive.c index 3b215f23..e7dc910b 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -4228,22 +4228,30 @@ if (deliver_freeze && freeze_tell != NULL && freeze_tell[0] != 0) /* Either a message has been successfully received and written to the two spool files, or an error in writing the spool has occurred for an SMTP message, or -an SMTP message has been rejected for policy reasons. (For a non-SMTP message -we will have already given up because there's no point in carrying on!) In -either event, we must now close (and thereby unlock) the data file. In the -successful case, this leaves the message on the spool, ready for delivery. In -the error case, the spool file will be deleted. Then tidy up store, interact -with an SMTP call if necessary, and return. +an SMTP message has been rejected for policy reasons, or a message was passed on +by cutthrough delivery. (For a non-SMTP message we will have already given up +because there's no point in carrying on!) For non-cutthrough we must now close +(and thereby unlock) the data file. In the successful case, this leaves the +message on the spool, ready for delivery. In the error case, the spool file will +be deleted. Then tidy up store, interact with an SMTP call if necessary, and +return. + +For cutthrough we hold the data file locked until we have deleted it, otherwise +a queue-runner could grab it in the window. A fflush() was done earlier in the expectation that any write errors on the data file will be flushed(!) out thereby. Nevertheless, it is theoretically possible for fclose() to fail - but what to do? What has happened to the lock -if this happens? */ +if this happens? We can at least log it; if it is observed on some platform +then we can think about properly declaring the message not-received. */ TIDYUP: -process_info[process_info_len] = 0; /* Remove message id */ -if (data_file != NULL) (void)fclose(data_file); /* Frees the lock */ +process_info[process_info_len] = 0; /* Remove message id */ +if (data_file && cutthrough_done == NOT_TRIED) + if (fclose(data_file)) /* Frees the lock */ + log_write(0, LOG_MAIN|LOG_PANIC, + "spoolfile error on close: %s", strerror(errno)); /* Now reset signal handlers to their defaults */ @@ -4330,6 +4338,8 @@ if (smtp_input) } if (cutthrough_done != NOT_TRIED) { + if (data_file) + (void) fclose(data_file); /* Frees the lock; do not care if error */ message_id[0] = 0; /* Prevent a delivery from starting */ cutthrough.delivery = cutthrough.callout_hold_only = FALSE; cutthrough.defer_pass = FALSE; commit d4de30edf98c3ea326f6f949a9bf934d17d8412e Author: Jeremy Harris Date: Fri May 11 16:26:17 2018 +0100 ARC: fix crash on signing with missing key file Cherry-picked from: 97e939dfe2 diff --git a/src/src/arc.c b/src/src/arc.c index 557ea851..a8562a72 100644 --- a/src/src/arc.c +++ b/src/src/arc.c @@ -1632,7 +1632,9 @@ g = arc_sign_append_ams(g, &arc_sign_ctx, instance, identity, selector, including self (but with an empty b= in self) */ -g = arc_sign_prepend_as(g, &arc_sign_ctx, instance, identity, selector, &ar, privkey); +if (g) + g = arc_sign_prepend_as(g, &arc_sign_ctx, instance, identity, selector, &ar, + privkey); /* Finally, append the dkim headers and return the lot. */ commit 9bd1e34631cc3d6f2b79b546a50c64ed6c5a48b6 Author: Jeremy Harris Date: Fri May 11 18:02:29 2018 +0100 Content scanning: Fix locking on message spool files. Bug 2275 (cherry picked from commit 1bd642c265dae5643f16d023879043b7576f66a9) diff --git a/src/src/globals.c b/src/src/globals.c index 9b34da87..45f49c85 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -1392,6 +1392,7 @@ uschar *spf_smtp_comment = NULL; #endif BOOL split_spool_directory = FALSE; +FILE *spool_data_file = NULL; uschar *spool_directory = US SPOOL_DIRECTORY "\0<--------------Space to patch spool_directory->"; BOOL spool_file_wireformat = FALSE; diff --git a/src/src/globals.h b/src/src/globals.h index a8835ed3..77b742d8 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -890,6 +890,7 @@ extern BOOL spf_result_guessed; /* spf result is of best-guess operation extern uschar *spf_smtp_comment; /* spf comment to include in SMTP reply */ #endif 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_file_wireformat; /* current -D file has CRLF rather than NL */ extern BOOL spool_wireformat; /* can write wireformat -D files */ diff --git a/src/src/receive.c b/src/src/receive.c index e7dc910b..f6db46c4 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -22,7 +22,6 @@ extern int dcc_ok; * Local static variables * *************************************************/ -static FILE *data_file = NULL; static int data_fd = -1; static uschar *spool_name = US""; @@ -341,10 +340,10 @@ if (spool_name[0] != '\0') /* Now close the file if it is open, either as a fd or a stream. */ -if (data_file) +if (spool_data_file) { - (void)fclose(data_file); - data_file = NULL; + (void)fclose(spool_data_file); + spool_data_file = NULL; } else if (data_fd >= 0) { @@ -1445,10 +1444,12 @@ if (rc == DISCARD) { recipients_count = 0; *blackholed_by_ptr = US"MIME ACL"; + cancel_cutthrough_connection(TRUE, US"mime acl discard"); } else if (rc != OK) { Uunlink(spool_name); + cancel_cutthrough_connection(TRUE, US"mime acl not ok"); unspool_mbox(); #ifdef EXPERIMENTAL_DCC dcc_ok = 0; @@ -1706,7 +1707,7 @@ header names list to be the normal list. Indicate there is no data file open yet, initialize the size and warning count, and deal with no size limit. */ message_id[0] = 0; -data_file = NULL; +spool_data_file = NULL; data_fd = -1; spool_name = US""; message_size = 0; @@ -3048,7 +3049,7 @@ the first line of the file (containing the message ID) because otherwise there are problems when Exim is run under Cygwin (I'm told). See comments in spool_in.c, where the same locking is done. */ -data_file = fdopen(data_fd, "w+"); +spool_data_file = fdopen(data_fd, "w+"); lock_data.l_type = F_WRLCK; lock_data.l_whence = SEEK_SET; lock_data.l_start = 0; @@ -3065,31 +3066,32 @@ data line (which was read as a header but then turned out not to have the right format); write it (remembering that it might contain binary zeros). The result of fwrite() isn't inspected; instead we call ferror() below. */ -fprintf(data_file, "%s-D\n", message_id); +fprintf(spool_data_file, "%s-D\n", message_id); if (next) { uschar *s = next->text; int len = next->slen; - len = fwrite(s, 1, len, data_file); len = len; /* compiler quietening */ - body_linecount++; /* Assumes only 1 line */ + if (fwrite(s, 1, len, spool_data_file) == len) /* "if" for compiler quietening */ + body_linecount++; /* Assumes only 1 line */ } /* Note that we might already be at end of file, or the logical end of file (indicated by '.'), or might have encountered an error while writing the message id or "next" line. */ -if (!ferror(data_file) && !(receive_feof)() && message_ended != END_DOT) +if (!ferror(spool_data_file) && !(receive_feof)() && message_ended != END_DOT) { if (smtp_input) { message_ended = chunking_state <= CHUNKING_OFFERED - ? read_message_data_smtp(data_file) + ? read_message_data_smtp(spool_data_file) : spool_wireformat - ? read_message_bdat_smtp_wire(data_file) - : read_message_bdat_smtp(data_file); + ? read_message_bdat_smtp_wire(spool_data_file) + : read_message_bdat_smtp(spool_data_file); receive_linecount++; /* The terminating "." line */ } - else message_ended = read_message_data(data_file); + else + message_ended = read_message_data(spool_data_file); receive_linecount += body_linecount; /* For BSMTP errors mainly */ message_linecount += body_linecount; @@ -3136,10 +3138,10 @@ if (!ferror(data_file) && !(receive_feof)() && message_ended != END_DOT) } else { - fseek(data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); give_local_error(ERRMESS_TOOBIG, string_sprintf("message too big (max=%d)", thismessage_size_limit), - US"message rejected: ", error_rc, data_file, header_list); + US"message rejected: ", error_rc, spool_data_file, header_list); /* Does not return */ } break; @@ -3169,8 +3171,8 @@ we can then give up. Note that for SMTP input we must swallow the remainder of the input in cases of output errors, since the far end doesn't expect to see anything until the terminating dot line is sent. */ -if (fflush(data_file) == EOF || ferror(data_file) || - EXIMfsync(fileno(data_file)) < 0 || (receive_ferror)()) +if (fflush(spool_data_file) == EOF || ferror(spool_data_file) || + EXIMfsync(fileno(spool_data_file)) < 0 || (receive_ferror)()) { uschar *msg_errno = US strerror(errno); BOOL input_error = (receive_ferror)() != 0; @@ -3198,8 +3200,8 @@ if (fflush(data_file) == EOF || ferror(data_file) || else { - fseek(data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); - give_local_error(ERRMESS_IOERR, msg, US"", error_rc, data_file, + fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + give_local_error(ERRMESS_IOERR, msg, US"", error_rc, spool_data_file, header_list); /* Does not return */ } @@ -3240,7 +3242,7 @@ if (extract_recip && (bad_addresses || recipients_count == 0)) } } - fseek(data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); /* If configured to send errors to the sender, but this fails, force a failure error code. We use a special one for no recipients so that it @@ -3254,7 +3256,7 @@ if (extract_recip && (bad_addresses || recipients_count == 0)) (bad_addresses == NULL)? (extracted_ignored? ERRMESS_IGADDRESS : ERRMESS_NOADDRESS) : (recipients_list == NULL)? ERRMESS_BADNOADDRESS : ERRMESS_BADADDRESS, - bad_addresses, header_list, data_file, FALSE)) + bad_addresses, header_list, spool_data_file, FALSE)) error_rc = (bad_addresses == NULL)? EXIT_NORECIPIENTS : EXIT_FAILURE; } else @@ -3282,7 +3284,7 @@ if (extract_recip && (bad_addresses || recipients_count == 0)) if (recipients_count == 0 || error_handling == ERRORS_STDERR) { Uunlink(spool_name); - (void)fclose(data_file); + (void)fclose(spool_data_file); exim_exit(error_rc, US"receiving"); } } @@ -3607,9 +3609,9 @@ else } else { - fseek(data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); give_local_error(ERRMESS_LOCAL_ACL, user_msg, - US"message rejected by non-SMTP ACL: ", error_rc, data_file, + US"message rejected by non-SMTP ACL: ", error_rc, spool_data_file, header_list); /* Does not return */ } @@ -3807,9 +3809,9 @@ else } else { - fseek(data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); give_local_error(ERRMESS_LOCAL_SCAN, errmsg, - US"message rejected by local scan code: ", error_rc, data_file, + US"message rejected by local scan code: ", error_rc, spool_data_file, header_list); /* Does not return */ } @@ -3882,8 +3884,8 @@ else } else { - fseek(data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); - give_local_error(ERRMESS_IOERR, errmsg, US"", error_rc, data_file, + fseek(spool_data_file, (long int)SPOOL_DATA_START_OFFSET, SEEK_SET); + give_local_error(ERRMESS_IOERR, errmsg, US"", error_rc, spool_data_file, header_list); /* Does not return */ } @@ -3909,7 +3911,7 @@ that is in the file, but we do add one extra for the notional blank line that precedes the data. This total differs from message_size in that it include the added Received: header and any other headers that got created locally. */ -fflush(data_file); +fflush(spool_data_file); fstat(data_fd, &statbuf); msg_size += statbuf.st_size - SPOOL_DATA_START_OFFSET + 1; @@ -4248,10 +4250,13 @@ then we can think about properly declaring the message not-received. */ TIDYUP: process_info[process_info_len] = 0; /* Remove message id */ -if (data_file && cutthrough_done == NOT_TRIED) - if (fclose(data_file)) /* Frees the lock */ +if (spool_data_file && cutthrough_done == NOT_TRIED) + { + if (fclose(spool_data_file)) /* Frees the lock */ log_write(0, LOG_MAIN|LOG_PANIC, "spoolfile error on close: %s", strerror(errno)); + spool_data_file = NULL; + } /* Now reset signal handlers to their defaults */ @@ -4338,8 +4343,11 @@ if (smtp_input) } if (cutthrough_done != NOT_TRIED) { - if (data_file) - (void) fclose(data_file); /* Frees the lock; do not care if error */ + if (spool_data_file) + { + (void) fclose(spool_data_file); /* Frees the lock; do not care if error */ + spool_data_file = NULL; + } message_id[0] = 0; /* Prevent a delivery from starting */ cutthrough.delivery = cutthrough.callout_hold_only = FALSE; cutthrough.defer_pass = FALSE; diff --git a/src/src/spool_mbox.c b/src/src/spool_mbox.c index 749484f2..05f90a81 100644 --- a/src/src/spool_mbox.c +++ b/src/src/spool_mbox.c @@ -35,9 +35,7 @@ uschar message_subdir[2]; uschar buffer[16384]; uschar *temp_string; uschar *mbox_path; -FILE *mbox_file = NULL; -FILE *data_file = NULL; -FILE *yield = NULL; +FILE *mbox_file = NULL, *l_data_file = NULL, *yield = NULL; header_line *my_headerlist; struct stat statbuf; int i, j; @@ -108,21 +106,25 @@ if (!spool_mbox_ok) goto OUT; } - /* copy body file */ - if (!source_file_override) + /* Copy body file. If the main receive still has it open then it is holding + a lock, and we must not close it (which releases the lock), so just use the + global file handle. */ + if (source_file_override) + l_data_file = Ufopen(source_file_override, "rb"); + else if (spool_data_file) + l_data_file = spool_data_file; + else { message_subdir[1] = '\0'; for (i = 0; i < 2; i++) { message_subdir[0] = split_spool_directory == (i == 0) ? message_id[5] : 0; temp_string = spool_fname(US"input", message_subdir, message_id, US"-D"); - if ((data_file = Ufopen(temp_string, "rb"))) break; + if ((l_data_file = Ufopen(temp_string, "rb"))) break; } } - else - data_file = Ufopen(source_file_override, "rb"); - if (!data_file) + if (!l_data_file) { log_write(0, LOG_MAIN|LOG_PANIC, "Could not open datafile for message %s", message_id); @@ -131,7 +133,7 @@ if (!spool_mbox_ok) /* The code used to use this line, but it doesn't work in Cygwin. - (void)fread(data_buffer, 1, 18, data_file); + (void)fread(data_buffer, 1, 18, l_data_file); What's happening is that spool_mbox used to use an fread to jump over the file header. That fails under Cygwin because the header is locked, but @@ -139,23 +141,23 @@ if (!spool_mbox_ok) explicitly, because the one in the file is parted of the locked area. */ if (!source_file_override) - (void)fseek(data_file, SPOOL_DATA_START_OFFSET, SEEK_SET); + (void)fseek(l_data_file, SPOOL_DATA_START_OFFSET, SEEK_SET); do { uschar * s; if (!spool_file_wireformat || source_file_override) - j = fread(buffer, 1, sizeof(buffer), data_file); + j = fread(buffer, 1, sizeof(buffer), l_data_file); else /* needs CRLF -> NL */ - if ((s = US fgets(CS buffer, sizeof(buffer), data_file))) + if ((s = US fgets(CS buffer, sizeof(buffer), l_data_file))) { uschar * p = s + Ustrlen(s) - 1; if (*p == '\n' && p[-1] == '\r') *--p = '\n'; else if (*p == '\r') - ungetc(*p--, data_file); + ungetc(*p--, l_data_file); j = p - buffer; } @@ -190,7 +192,7 @@ else *mbox_file_size = statbuf.st_size; OUT: -if (data_file) (void)fclose(data_file); +if (l_data_file && !spool_data_file) (void)fclose(l_data_file); if (mbox_file) (void)fclose(mbox_file); store_reset(reset_point); return yield; commit 3b9730e3deaf4eb03a99977d830da446dcc85cfb Author: Jeremy Harris Date: Thu May 24 16:31:27 2018 +0100 Use serial number 1 for self-generated selfsigned certificate Broken-by: 23bb69826c (cherry picked from commit 1613fd68b5931757016c3c25fdc3b0f37827e7f1) diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 35816cd6..08c1d939 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -790,7 +790,7 @@ if ((rc = gnutls_x509_privkey_generate(pkey, GNUTLS_PK_RSA, goto err; where = US"configuring cert"; -now = 0; +now = 1; if ( (rc = gnutls_x509_crt_set_version(cert, 3)) || (rc = gnutls_x509_crt_set_serial(cert, &now, sizeof(now))) || (rc = gnutls_x509_crt_set_activation_time(cert, now = time(NULL))) diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index cefa94fe..068a0d87 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -1000,7 +1000,7 @@ if (!EVP_PKEY_assign_RSA(pkey, rsa)) goto err; X509_set_version(x509, 2); /* N+1 - version 3 */ -ASN1_INTEGER_set(X509_get_serialNumber(x509), 0); +ASN1_INTEGER_set(X509_get_serialNumber(x509), 1); X509_gmtime_adj(X509_get_notBefore(x509), 0); X509_gmtime_adj(X509_get_notAfter(x509), (long)60 * 60); /* 1 hour */ X509_set_pubkey(x509, pkey); commit d5bd2a53d8a226793321b0c3011092ab97e63902 Author: Jeremy Harris Date: Wed Jun 6 11:19:23 2018 +0100 Fix logging of cmdline args when starting in an unlinked cwd. Bug 2274 diff --git a/src/src/exim.c b/src/src/exim.c index d740814f..93a65dbc 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -4083,14 +4083,23 @@ a debugging feature for finding out what arguments certain MUAs actually use. Don't attempt it if logging is disabled, or if listing variables or if verifying/testing addresses or expansions. */ -if (((debug_selector & D_any) != 0 || LOGGING(arguments)) - && really_exim && !list_options && !checking) +if ( (debug_selector & D_any || LOGGING(arguments)) + && really_exim && !list_options && !checking) { int i; uschar *p = big_buffer; Ustrcpy(p, "cwd= (failed)"); - Ustrncpy(p + 4, initial_cwd, big_buffer_size-5); + if (!initial_cwd) + p += 13; + else + { + Ustrncpy(p + 4, initial_cwd, big_buffer_size-5); + p += 4 + Ustrlen(initial_cwd); + /* in case p is near the end and we don't provide enough space for + * string_format to be willing to write. */ + *p = '\0'; + } while (*p) p++; (void)string_format(p, big_buffer_size - (p - big_buffer), " %d args:", argc); commit ebf2c3666fd2e9bbb208dbeb297a901ba287c72c Author: Jeremy Harris Date: Thu Jun 7 16:24:31 2018 +0100 ARC: Fix signing for case when DKIM signing failed diff --git a/src/src/arc.c b/src/src/arc.c index a8562a72..c2e7e508 100644 --- a/src/src/arc.c +++ b/src/src/arc.c @@ -1638,7 +1638,7 @@ if (g) /* Finally, append the dkim headers and return the lot. */ -g = string_catn(g, sigheaders->s, sigheaders->ptr); +if (sigheaders) g = string_catn(g, sigheaders->s, sigheaders->ptr); (void) string_from_gstring(g); gstring_reset_unused(g); return g; commit e9f733e658a1605edca1bd6b689232e18c8f53c6 Author: Jeremy Harris Date: Thu Jun 21 17:03:38 2018 +0100 DKIM: Fix signing for body lines starting with a pair of dots. Bug 2284 Broken-by: 42055a3385 diff --git a/src/src/dkim_transport.c b/src/src/dkim_transport.c index 11458680..13db0f2f 100644 --- a/src/src/dkim_transport.c +++ b/src/src/dkim_transport.c @@ -155,7 +155,10 @@ if (!rc) return FALSE; arc_sign_init(); #endif -dkim->dot_stuffed = !!(save_options & topt_end_dot); +/* The dotstuffed status of the datafile depends on whether it was stored +in wireformat. */ + +dkim->dot_stuffed = spool_file_wireformat; if (!(dkim_signature = dkim_exim_sign(deliver_datafile, SPOOL_DATA_START_OFFSET, hdrs, dkim, &errstr))) if (!(rc = dkt_sign_fail(dkim, &errno))) @@ -271,7 +274,9 @@ if (!rc) arc_sign_init(); #endif -/* Feed the file to the goats^W DKIM lib */ +/* Feed the file to the goats^W DKIM lib. At this point the dotstuffed +status of the file depends on the output of transport_write_message() just +above, which should be the result of the end_dot flag in tctx->options. */ dkim->dot_stuffed = !!(options & topt_end_dot); if (!(dkim_signature = dkim_exim_sign(dkim_fd, 0, NULL, dkim, &errstr))) commit 64469eb46f042bea1c12996d69c5fedd0bf44510 Author: Jeremy Harris Date: Mon Jun 25 12:08:37 2018 +0100 ARC: Fix verification to do AS checks in reverse order Broken from the original introduction (617d39327e) diff --git a/src/src/arc.c b/src/src/arc.c index c2e7e508..3bde81e2 100644 --- a/src/src/arc.c +++ b/src/src/arc.c @@ -966,16 +966,13 @@ return NULL; static const uschar * arc_verify_seals(arc_ctx * ctx) { -arc_set * as = ctx->arcset_chain; +arc_set * as = ctx->arcset_chain_last; if (!as) return US"none"; -while (as) - { - if (arc_seal_verify(ctx, as)) return US"fail"; - as = as->next; - } +for ( ; as; as = as->prev) if (arc_seal_verify(ctx, as)) return US"fail"; + DEBUG(D_acl) debug_printf("ARC: AS vfy overall pass\n"); return NULL; } commit 9dbbd9a5b838b85c2db09eaee9f26ef25b7c4ae4 Author: Jeremy Harris Date: Wed Jul 18 22:16:38 2018 +0100 I18N: Fix protocol recorded for a multi-SMTPUTF8-message connection. Bug 2287 (cherry picked from commit 946515bfe62796f6c0d6554e9e1e227f33253e7c) diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index e4d02cea..b1f442c8 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -4670,13 +4670,15 @@ while (done <= 0) case ENV_MAIL_OPT_UTF8: if (smtputf8_advertised) { - int old_pool = store_pool; - DEBUG(D_receive) debug_printf("smtputf8 requested\n"); message_smtputf8 = allow_utf8_domains = TRUE; - store_pool = POOL_PERM; - received_protocol = string_sprintf("utf8%s", received_protocol); - store_pool = old_pool; + if (Ustrncmp(received_protocol, US"utf8", 4) != 0) + { + int old_pool = store_pool; + store_pool = POOL_PERM; + received_protocol = string_sprintf("utf8%s", received_protocol); + store_pool = old_pool; + } } break; #endif commit 5c47e2f6e765a761ab9875583fede2e58ccdfa2e Author: Jeremy Harris Date: Fri Jul 20 16:19:34 2018 +0100 Fix non-EVENTS build Broken-by: c4b57fddca (cherry picked from commit 8d3dc2397dd769bf4654b0678be8d2acf0956ddd) diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 2dfb5b73..b9382ad1 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -1623,9 +1623,11 @@ if (!continue_hostname) string_sprintf("DANE error: tlsa lookup %s", rc == DEFER ? "DEFER" : "FAIL"), rc, FALSE); +# ifndef DISABLE_EVENT (void) event_raise(sx->tblock->event_action, US"dane:fail", sx->dane_required ? US"dane-required" : US"dnssec-invalid"); +# endif return rc; } } @@ -1634,8 +1636,10 @@ if (!continue_hostname) set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER, string_sprintf("DANE error: %s lookup not DNSSEC", sx->host->name), FAIL, FALSE); +# ifndef DISABLE_EVENT (void) event_raise(sx->tblock->event_action, US"dane:fail", US"dane-required"); +# endif return FAIL; } } @@ -1974,8 +1978,10 @@ if ( smtp_peer_options & OPTION_TLS log_write(0, LOG_MAIN, "DANE attempt failed; TLS connection to %s [%s]: %s", sx->host->name, sx->host->address, errstr); +# ifndef DISABLE_EVENT (void) event_raise(sx->tblock->event_action, US"dane:fail", US"validation-failure"); /* could do with better detail */ +# endif } # endif @@ -2070,7 +2076,7 @@ else if ( sx->smtps message = string_sprintf("a TLS session is required, but %s", smtp_peer_options & OPTION_TLS ? "an attempt to start TLS failed" : "the server did not offer TLS support"); -# ifdef SUPPORT_DANE +# if defined(SUPPORT_DANE) && !defined(DISABLE_EVENT) if (sx->dane) (void) event_raise(sx->tblock->event_action, US"dane:fail", smtp_peer_options & OPTION_TLS commit 7d366ef6bafb7ea0eb4632febe39781d359274d5 Author: Jeremy Harris Date: Sat Aug 11 14:56:59 2018 +0100 Fix cutthrough delivery for more than one iteration of address redirection. Bug 2296 (cherry picked from commit d16931c81f3e500fa6eafe5ec1c5d8e7db63e65a and commit 8a6b4e02cd384193a5566e8601bdd97249b22284) diff --git a/src/src/verify.c b/src/src/verify.c index ffa6a14f..3e7cf300 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -1061,6 +1061,8 @@ no_conn: && !sx.lmtp ) { + address_item * parent, * caddr; + HDEBUG(D_acl|D_v) debug_printf_indent("holding verify callout open for %s\n", cutthrough.delivery ? "cutthrough delivery" : "potential further verifies and delivery"); @@ -1082,12 +1084,16 @@ no_conn: cutthrough.host.address = string_copy(host->address); store_pool = oldpool; } - cutthrough.addr = *addr; /* Save the address_item for later logging */ + + /* Save the address_item and parent chain for later logging */ + cutthrough.addr = *addr; cutthrough.addr.next = NULL; cutthrough.addr.host_used = &cutthrough.host; - if (addr->parent) - *(cutthrough.addr.parent = store_get(sizeof(address_item))) = - *addr->parent; + for (caddr = &cutthrough.addr, parent = addr->parent; + parent; + caddr = caddr->parent, parent = parent->parent) + *(caddr->parent = store_get(sizeof(address_item))) = *parent; + ctblock.buffer = ctbuffer; ctblock.buffersize = sizeof(ctbuffer); ctblock.ptr = ctbuffer; @@ -1919,6 +1925,9 @@ while (addr_new) #endif rc = do_callout(addr, host_list, &tf, callout, callout_overall, callout_connect, options, se_mailfrom, pm_mailfrom); +#ifdef SUPPORT_TLS + deliver_set_expansions(NULL); +#endif } } else