diff -urN ../exim-4.94.orig/OS/os.h-GNU ./OS/os.h-GNU --- ../exim-4.94.orig/OS/os.h-GNU 2020-05-30 23:35:38.000000000 +0300 +++ ./OS/os.h-GNU 2021-03-16 17:15:23.180086000 +0200 @@ -1,6 +1,8 @@ /* Exim: OS-specific C header file for GNU/Hurd */ /* Copyright (c) The Exim Maintainers 2020 */ +#include + #define CRYPT_H #define GLIBC_IP_OPTIONS #define HAVE_BSD_GETLOADAVG @@ -24,5 +26,9 @@ /* setgroups(0, NULL) succeeds, and drops the gid group as well as any supplementary groups*/ #define OS_SETGROUPS_ZERO_DROPS_ALL + +#if _POSIX_C_SOURCE >= 200809L || _ATFILE_SOURCE +# define EXIM_HAVE_OPENAT +#endif /* End */ diff -urN ../exim-4.94.orig/README.UPDATING ./README.UPDATING --- ../exim-4.94.orig/README.UPDATING 2020-05-30 23:35:38.000000000 +0300 +++ ./README.UPDATING 2021-03-16 17:15:23.186998000 +0200 @@ -31,9 +31,9 @@ Some Transports now refuse to use tainted data in constructing their delivery location; this WILL BREAK configurations which are not updated accordingly. -In particular: any Transport use of $local_user which has been relying upon +In particular: any Transport use of $local_part which has been relying upon check_local_user far away in the Router to make it safe, should be updated to -replace $local_user with $local_part_data. +replace $local_part with $local_part_data. Attempting to remove, in router or transport, a header name that ends with an asterisk (which is a standards-legal name) will now result in all headers diff -urN ../exim-4.94.orig/exim_monitor/em_globals.c ./exim_monitor/em_globals.c --- ../exim-4.94.orig/exim_monitor/em_globals.c 2020-05-30 23:35:38.000000000 +0300 +++ ./exim_monitor/em_globals.c 2021-03-16 17:15:23.187873000 +0200 @@ -205,6 +205,7 @@ uschar *sender_fullhost = NULL; uschar *sender_helo_name = NULL; uschar *sender_host_address = NULL; +uschar *sender_host_auth_pubname = NULL; uschar *sender_host_authenticated = NULL; uschar *sender_host_name = NULL; int sender_host_port = 0; diff -urN ../exim-4.94.orig/exim_monitor/em_menu.c ./exim_monitor/em_menu.c --- ../exim-4.94.orig/exim_monitor/em_menu.c 2020-05-30 23:35:38.000000000 +0300 +++ ./exim_monitor/em_menu.c 2021-03-16 17:15:23.189241000 +0200 @@ -670,7 +670,7 @@ sprintf(CS big_buffer, "%s/input/%s", spool_directory, buffer); if (Ustat(big_buffer, &statbuf) == 0) text_showf(text, "Format error in spool file %s: size=%lu\n", buffer, - (ulong)statbuf.st_size); + (unsigned long)statbuf.st_size); else text_showf(text, "Format error in spool file %s\n", buffer); } else text_showf(text, "Read error for spool file %s\n", buffer); diff -urN ../exim-4.94.orig/src/EDITME ./src/EDITME --- ../exim-4.94.orig/src/EDITME 2020-05-30 23:35:38.000000000 +0300 +++ ./src/EDITME 2021-03-16 17:15:23.192689000 +0200 @@ -564,9 +564,9 @@ # DISABLE_EVENT=yes -# Uncomment this line to include support for early pipelining, per +# Uncomment this line to remove support for early pipelining, per # https://datatracker.ietf.org/doc/draft-harris-early-pipe/ -# SUPPORT_PIPE_CONNECT=yes +# DISABLE_PIPE_CONNECT=yes #------------------------------------------------------------------------------ diff -urN ../exim-4.94.orig/src/acl.c ./src/acl.c --- ../exim-4.94.orig/src/acl.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/acl.c 2021-03-16 17:15:23.193951000 +0200 @@ -1767,7 +1767,7 @@ /* Remaining items are optional; they apply to sender and recipient verification, including "header sender" verification. */ -while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))) +while ((ss = string_nextinlist(&list, &sep, NULL, 0))) { if (strcmpic(ss, US"defer_ok") == 0) defer_ok = TRUE; else if (strcmpic(ss, US"no_details") == 0) no_details = TRUE; @@ -1804,7 +1804,7 @@ uschar * opt; while (isspace(*sublist)) sublist++; - while ((opt = string_nextinlist(&sublist, &optsep, buffer, sizeof(buffer)))) + while ((opt = string_nextinlist(&sublist, &optsep, NULL, 0))) { callout_opt_t * op; double period = 1.0F; @@ -3199,8 +3199,7 @@ { const uschar *pp = p + 6; while (*pp) pp++; - submission_name = string_copy(parse_fix_phrase(p+6, pp-p-6, - big_buffer, big_buffer_size)); + submission_name = parse_fix_phrase(p+6, pp-p-6); p = pp; } else break; @@ -3264,37 +3263,41 @@ the case where both sides handle prdr and this-node prdr acl is "accept" */ ignored = US"PRDR active"; + else if (f.deliver_freeze) + ignored = US"frozen"; + else if (f.queue_only_policy) + ignored = US"queue-only"; + else if (fake_response == FAIL) + ignored = US"fakereject"; + else if (rcpt_count != 1) + ignored = US"nonfirst rcpt"; + else if (cutthrough.delivery) + ignored = US"repeated"; + else if (cutthrough.callout_hold_only) + { + DEBUG(D_acl) + debug_printf_indent(" cutthrough request upgrades callout hold\n"); + cutthrough.callout_hold_only = FALSE; + cutthrough.delivery = TRUE; /* control accepted */ + } else { - if (f.deliver_freeze) - ignored = US"frozen"; - else if (f.queue_only_policy) - ignored = US"queue-only"; - else if (fake_response == FAIL) - ignored = US"fakereject"; - else + cutthrough.delivery = TRUE; /* control accepted */ + while (*p == '/') { - if (rcpt_count == 1) + const uschar * pp = p+1; + if (Ustrncmp(pp, "defer=", 6) == 0) { - cutthrough.delivery = TRUE; /* control accepted */ - while (*p == '/') - { - const uschar * pp = p+1; - if (Ustrncmp(pp, "defer=", 6) == 0) - { - pp += 6; - if (Ustrncmp(pp, "pass", 4) == 0) cutthrough.defer_pass = TRUE; - /* else if (Ustrncmp(pp, "spool") == 0) ; default */ - } - else - while (*pp && *pp != '/') pp++; - p = pp; - } + pp += 6; + if (Ustrncmp(pp, "pass", 4) == 0) cutthrough.defer_pass = TRUE; + /* else if (Ustrncmp(pp, "spool") == 0) ; default */ } else - ignored = US"nonfirst rcpt"; + while (*pp && *pp != '/') pp++; + p = pp; } } + DEBUG(D_acl) if (ignored) debug_printf(" cutthrough request ignored on %s item\n", ignored); } @@ -3349,11 +3352,11 @@ { /* Separate the regular expression and any optional parameters. */ const uschar * list = arg; - uschar *ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size); + uschar *ss = string_nextinlist(&list, &sep, NULL, 0); /* Run the dcc backend. */ rc = dcc_process(&ss); /* Modify return code based upon the existence of options. */ - while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))) + while ((ss = string_nextinlist(&list, &sep, NULL, 0))) if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER) rc = FAIL; /* FAIL so that the message is passed to the next ACL */ } @@ -3435,14 +3438,14 @@ case ACLC_DKIM_SIGNER: if (dkim_cur_signer) rc = match_isinlist(dkim_cur_signer, - &arg,0,NULL,NULL,MCL_STRING,TRUE,NULL); + &arg, 0, NULL, NULL, MCL_STRING, TRUE, NULL); else rc = FAIL; break; case ACLC_DKIM_STATUS: rc = match_isinlist(dkim_verify_status, - &arg,0,NULL,NULL,MCL_STRING,TRUE,NULL); + &arg, 0, NULL, NULL, MCL_STRING, TRUE, NULL); break; #endif @@ -3454,7 +3457,7 @@ /* used long way of dmarc_exim_expand_query() in case we need more * view into the process in the future. */ rc = match_isinlist(dmarc_exim_expand_query(DMARC_VERIFY_STATUS), - &arg,0,NULL,NULL,MCL_STRING,TRUE,NULL); + &arg, 0, NULL, NULL, MCL_STRING, TRUE, NULL); break; #endif @@ -3514,7 +3517,7 @@ int sep = 0; const uschar *s = arg; uschar * ss; - while ((ss = string_nextinlist(&s, &sep, big_buffer, big_buffer_size))) + while ((ss = string_nextinlist(&s, &sep, NULL, 0))) { if (Ustrcmp(ss, "main") == 0) logbits |= LOG_MAIN; else if (Ustrcmp(ss, "panic") == 0) logbits |= LOG_PANIC; @@ -3567,7 +3570,7 @@ { /* Separate the regular expression and any optional parameters. */ const uschar * list = arg; - uschar * ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size); + uschar * ss = string_nextinlist(&list, &sep, NULL, 0); uschar * opt; BOOL defer_ok = FALSE; int timeout = 0; @@ -3672,11 +3675,11 @@ { /* Separate the regular expression and any optional parameters. */ const uschar * list = arg; - uschar *ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size); + uschar *ss = string_nextinlist(&list, &sep, NULL, 0); rc = spam(CUSS &ss); /* Modify return code based upon the existence of options. */ - while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))) + while ((ss = string_nextinlist(&list, &sep, NULL, 0))) if (strcmpic(ss, US"defer_ok") == 0 && rc == DEFER) rc = FAIL; /* FAIL so that the message is passed to the next ACL */ } diff -urN ../exim-4.94.orig/src/auths/call_pam.c ./src/auths/call_pam.c --- ../exim-4.94.orig/src/auths/call_pam.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/auths/call_pam.c 2021-03-16 17:15:23.195695000 +0200 @@ -83,8 +83,7 @@ { case PAM_PROMPT_ECHO_ON: case PAM_PROMPT_ECHO_OFF: - arg = string_nextinlist(&pam_args, &sep, big_buffer, big_buffer_size); - if (!arg) + if (!(arg = string_nextinlist(&pam_args, &sep, NULL, 0))) { arg = US""; pam_arg_ended = TRUE; @@ -155,7 +154,7 @@ fail. PAM doesn't support authentication with an empty user (it prompts for it, causing a potential mis-interpretation). */ -user = string_nextinlist(&pam_args, &sep, big_buffer, big_buffer_size); +user = string_nextinlist(&pam_args, &sep, NULL, 0); if (user == NULL || user[0] == 0) return FAIL; /* Start off PAM interaction */ diff -urN ../exim-4.94.orig/src/auths/call_radius.c ./src/auths/call_radius.c --- ../exim-4.94.orig/src/auths/call_radius.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/auths/call_radius.c 2021-03-16 17:15:23.195945000 +0200 @@ -96,8 +96,7 @@ #endif -user = string_nextinlist(&radius_args, &sep, big_buffer, big_buffer_size); -if (!user) user = US""; +if (!(user = string_nextinlist(&radius_args, &sep, NULL, 0))) user = US""; DEBUG(D_auth) debug_printf("Running RADIUS authentication for user \"%s\" " "and \"%s\"\n", user, radius_args); diff -urN ../exim-4.94.orig/src/auths/get_data.c ./src/auths/get_data.c --- ../exim-4.94.orig/src/auths/get_data.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/auths/get_data.c 2021-03-16 17:15:23.197329000 +0200 @@ -168,14 +168,20 @@ len = Ustrlen(ss); /* The character ^ is used as an escape for a binary zero character, which is -needed for the PLAIN mechanism. It must be doubled if really needed. */ +needed for the PLAIN mechanism. It must be doubled if really needed. +The parsing ambiguity of ^^^ is taken as ^^ -> ^ ; ^ -> NUL - and there is +no way to get a leading ^ after a NUL. We would need to intro new syntax to +support that (probably preferring to take a more-standard exim list as a source +and concat the elements with intervening NULs. Either a magic marker on the +source string for client_send, or a new option). */ + for (int i = 0; i < len; i++) if (ss[i] == '^') if (ss[i+1] != '^') ss[i] = 0; else - if (--len > ++i) memmove(ss + i, ss + i + 1, len - i); + if (--len > i+1) memmove(ss + i + 1, ss + i + 2, len - i); /* The first string is attached to the AUTH command; others are sent unembellished. */ diff -urN ../exim-4.94.orig/src/daemon.c ./src/daemon.c --- ../exim-4.94.orig/src/daemon.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/daemon.c 2021-03-16 17:15:23.203260000 +0200 @@ -130,11 +130,30 @@ /************************************************* *************************************************/ +#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS static void +unlink_notifier_socket(void) +{ +uschar * s = expand_string(notifier_socket); +DEBUG(D_any) debug_printf("unlinking notifier socket %s\n", s); +Uunlink(s); +} +#endif + + +static void close_daemon_sockets(int daemon_notifier_fd, int * listen_sockets, int listen_socket_count) { -if (daemon_notifier_fd >= 0) (void) close(daemon_notifier_fd); +if (daemon_notifier_fd >= 0) + { + (void) close(daemon_notifier_fd); + daemon_notifier_fd = -1; +#ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS + unlink_notifier_socket(); +#endif + } + for (int i = 0; i < listen_socket_count; i++) (void) close(listen_sockets[i]); } @@ -336,6 +355,7 @@ log_write(L_connection_reject, LOG_MAIN, "Connection from %s refused: too many connections " "from that IP address", whofrom->s); + search_tidyup(); goto ERROR_RETURN; } } @@ -971,11 +991,7 @@ close(daemon_notifier_fd); daemon_notifier_fd = -1; #ifndef EXIM_HAVE_ABSTRACT_UNIX_SOCKETS - { - uschar * s = expand_string(notifier_socket); - DEBUG(D_any) debug_printf("unlinking notifier socket %s\n", s); - Uunlink(s); - } + unlink_notifier_socket(); #endif } diff -urN ../exim-4.94.orig/src/debug.c ./src/debug.c --- ../exim-4.94.orig/src/debug.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/debug.c 2021-03-16 17:15:23.205408000 +0200 @@ -30,10 +30,19 @@ [UNEXPECTED] = US"UNEXPECTED", [CANCELLED] = US"CANCELLED", [FAIL_SEND] = US"FAIL_SEND", - [FAIL_DROP] = US"FAIL_DROP" + [FAIL_DROP] = US"FAIL_DROP", + [DANE] = US"DANE", }; +const uschar * dns_rc_names[] = { + [DNS_SUCCEED] = US"DNS_SUCCEED", + [DNS_NOMATCH] = US"DNS_NOMATCH", + [DNS_NODATA] = US"DNS_NODATA", + [DNS_AGAIN] = US"DNS_AGAIN", + [DNS_FAIL] = US"DNS_FAIL", +}; + /************************************************* * Print tree * *************************************************/ @@ -328,48 +337,52 @@ gstring * g = NULL; int val; socklen_t vlen = sizeof(val); - struct sockaddr a; + struct sockaddr_storage a; socklen_t alen = sizeof(a); struct sockaddr_in * sinp = (struct sockaddr_in *)&a; struct sockaddr_in6 * sin6p = (struct sockaddr_in6 *)&a; - struct sockaddr_un * sa_unp ; (struct sockaddr_un *)&a; + struct sockaddr_un * sunp = (struct sockaddr_un *)&a; - if (getsockname(fd, &a, &alen) == 0) - switch (sinp->sin_family) + if (getsockname(fd, (struct sockaddr*)&a, &alen) == 0) + switch (a.ss_family) { case AF_INET: - g = string_cat(g, US" domain AF_INET"); + g = string_cat(g, US"domain AF_INET"); g = string_fmt_append(g, " lcl [%s]:%u", inet_ntoa(sinp->sin_addr), ntohs(sinp->sin_port)); - if (getpeername(fd, &a, &alen) == 0) + alen = sizeof(*sinp); + if (getpeername(fd, sinp, &alen) == 0) g = string_fmt_append(g, " rmt [%s]:%u", inet_ntoa(sinp->sin_addr), ntohs(sinp->sin_port)); break; case AF_INET6: { uschar buf[46]; - g = string_cat(g, US" domain AF_INET6"); + g = string_cat(g, US"domain AF_INET6"); g = string_fmt_append(g, " lcl [%s]:%u", inet_ntop(AF_INET6, &sin6p->sin6_addr, CS buf, sizeof(buf)), ntohs(sin6p->sin6_port)); - if (getpeername(fd, &a, &alen) == 0) + alen = sizeof(*sin6p); + if (getpeername(fd, sin6p, &alen) == 0) g = string_fmt_append(g, " rmt [%s]:%u", inet_ntop(AF_INET6, &sin6p->sin6_addr, CS buf, sizeof(buf)), ntohs(sin6p->sin6_port)); break; } case AF_UNIX: - g = string_cat(g, US" domain AF_UNIX"); - g = string_fmt_append(g, " lcl %s%s", - sa_unp->sun_path[0] ? US"" : US"@", - sa_unp->sun_path[0] ? sa_unp->sun_path : sa_unp->sun_path+1); - if (getpeername(fd, &a, &alen) == 0) - g = string_fmt_append(g, " rmt %s%s", - sa_unp->sun_path[0] ? US"" : US"@", - sa_unp->sun_path[0] ? sa_unp->sun_path : sa_unp->sun_path+1); - break; + g = string_cat(g, US"domain AF_UNIX"); + if (alen > sizeof(sa_family_t)) /* not unix(7) "unnamed socket" */ + g = string_fmt_append(g, " lcl %s%s", + sunp->sun_path[0] ? US"" : US"@", + sunp->sun_path[0] ? sunp->sun_path : sunp->sun_path+1); + alen = sizeof(*sunp); + if (getpeername(fd, sunp, &alen) == 0) + g = string_fmt_append(g, " rmt %s%s", + sunp->sun_path[0] ? US"" : US"@", + sunp->sun_path[0] ? sunp->sun_path : sunp->sun_path+1); + break; default: - g = string_fmt_append(g, " domain %u", sinp->sin_family); + g = string_fmt_append(g, "domain %u", sinp->sin_family); break; } if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &val, &vlen) == 0) @@ -384,7 +397,7 @@ { struct protoent * p = getprotobynumber(val); g = p - ? string_fmt_append(g, " proto %s\n", p->p_name) + ? string_fmt_append(g, " proto %s", p->p_name) : string_fmt_append(g, " proto %d", val); } #endif diff -urN ../exim-4.94.orig/src/deliver.c ./src/deliver.c --- ../exim-4.94.orig/src/deliver.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/deliver.c 2021-03-16 17:15:23.208609000 +0200 @@ -460,6 +460,9 @@ This enables Exim to use a single SMTP transaction for sending to two entirely different domains that happen to end up pointing at the same hosts. +We do not try to batch up different A-record host names that refer to the +same IP. + Arguments: one points to the first host list two points to the second host list @@ -1192,7 +1195,7 @@ if (addr->host_used) { g = d_hostlog(g, addr); - if (continue_sequence > 1) + if (continue_sequence > 1) /*XXX this is wrong for a dropped proxyconn. Would have to pass back from transport */ g = string_catn(g, US"*", 1); #ifndef DISABLE_EVENT @@ -4272,6 +4275,10 @@ } } +/*XXX need to defeat this when DANE is used - but we don't know that yet. +So look out for the place it gets used. +*/ + /* Get the flag which specifies whether the transport can handle different domains that nevertheless resolve to the same set of hosts. If it needs expanding, get variables set: $address_data, $domain_data, $localpart_data, @@ -4350,6 +4357,11 @@ /************************************************************************/ +/*XXX don't know yet if DANE will be used. So tpt will have to +check at the point if gets next addr from list, and skip/defer any +nonmatch domains +*/ + /* Pick off all addresses which have the same transport, errors address, destination, and extra headers. In some cases they point to the same host list, but we also need to check for identical host lists generated from @@ -4496,6 +4508,7 @@ if (continue_transport) { BOOL ok = Ustrcmp(continue_transport, tp->name) == 0; +/*XXX do we need to check for a DANEd conn vs. a change of domain? */ /* If the transport is about to override the host list do not check it here but take the cost of running the transport process to discover @@ -7066,10 +7079,20 @@ /* If this is a run to continue deliveries to an external channel that is -already set up, defer any local deliveries. */ +already set up, defer any local deliveries. -if (continue_transport) +jgh 2020/12/20: I don't see why; locals should be quick. +The defer goes back to version 1.62 in 1997. A local being still deliverable +during a continued run might result from something like a defer during the +original delivery, eg. in a DB lookup. Unlikely but possible. + +To avoid delaying a local when combined with a callout-hold for a remote +delivery, test continue_sequence rather than continue_transport. */ + +if (continue_sequence > 1 && addr_local) { + DEBUG(D_deliver|D_retry|D_route) + debug_printf("deferring local deliveries due to continued-transport\n"); if (addr_defer) { address_item *addr = addr_defer; diff -urN ../exim-4.94.orig/src/dns.c ./src/dns.c --- ../exim-4.94.orig/src/dns.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/dns.c 2021-03-16 17:15:23.210566000 +0200 @@ -516,7 +516,7 @@ || !(trusted = expand_string(dns_trust_aa)) || !*trusted || !(auth_name = dns_extract_auth_name(dnsa)) - || OK != match_isinlist(auth_name, &trusted, 0, NULL, NULL, + || OK != match_isinlist(auth_name, &trusted, 0, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) ) return FALSE; @@ -908,8 +908,8 @@ #ifndef STAND_ALONE save_domain = deliver_domain; deliver_domain = string_copy(name); /* set $domain */ - rc = match_isinlist(name, (const uschar **)&dns_again_means_nonexist, 0, NULL, NULL, - MCL_DOMAIN, TRUE, NULL); + rc = match_isinlist(name, CUSS &dns_again_means_nonexist, 0, + &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL); deliver_domain = save_domain; if (rc != OK) { diff -urN ../exim-4.94.orig/src/exim.c ./src/exim.c --- ../exim-4.94.orig/src/exim.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/exim.c 2021-03-16 17:15:23.213550000 +0200 @@ -384,14 +384,20 @@ *************************************************/ #ifdef _POSIX_MONOTONIC_CLOCK -/* Amount CLOCK_MONOTONIC is behind realtime, at startup. */ +# ifdef CLOCK_BOOTTIME +# define EXIM_CLOCKTYPE CLOCK_BOOTTIME +# else +# define EXIM_CLOCKTYPE CLOCK_MONOTONIC +# endif + +/* Amount EXIM_CLOCK is behind realtime, at startup. */ static struct timespec offset_ts; static void exim_clock_init(void) { struct timeval tv; -if (clock_gettime(CLOCK_MONOTONIC, &offset_ts) != 0) return; +if (clock_gettime(EXIM_CLOCKTYPE, &offset_ts) != 0) return; (void)gettimeofday(&tv, NULL); offset_ts.tv_sec = tv.tv_sec - offset_ts.tv_sec; offset_ts.tv_nsec = tv.tv_usec * 1000 - offset_ts.tv_nsec; @@ -402,6 +408,29 @@ #endif +void +exim_gettime(struct timeval * tv) +{ +#ifdef _POSIX_MONOTONIC_CLOCK +struct timespec now_ts; + +if (clock_gettime(EXIM_CLOCKTYPE, &now_ts) == 0) + { + now_ts.tv_sec += offset_ts.tv_sec; + if ((now_ts.tv_nsec += offset_ts.tv_nsec) >= 1000*1000*1000) + { + now_ts.tv_sec++; + now_ts.tv_nsec -= 1000*1000*1000; + } + tv->tv_sec = now_ts.tv_sec; + tv->tv_usec = now_ts.tv_nsec / 1000; + } +else +#endif + (void)gettimeofday(tv, NULL); +} + + /* Exim uses a time + a pid to generate a unique identifier in two places: its message IDs, and in file names for maildir deliveries. Because some OS now re-use pids within the same second, sub-second times are now being used. @@ -428,29 +457,10 @@ struct timeval now_tv; long int now_true_usec; -#ifdef _POSIX_MONOTONIC_CLOCK -struct timespec now_ts; +exim_gettime(&now_tv); +now_true_usec = now_tv.tv_usec; +now_tv.tv_usec = (now_true_usec/resolution) * resolution; -if (clock_gettime(CLOCK_MONOTONIC, &now_ts) == 0) - { - now_ts.tv_sec += offset_ts.tv_sec; - if ((now_ts.tv_nsec += offset_ts.tv_nsec) >= 1000*1000*1000) - { - now_ts.tv_sec++; - now_ts.tv_nsec -= 1000*1000*1000; - } - now_tv.tv_sec = now_ts.tv_sec; - now_true_usec = (now_ts.tv_nsec / (resolution * 1000)) * resolution; - now_tv.tv_usec = now_true_usec; - } -else -#endif - { - (void)gettimeofday(&now_tv, NULL); - now_true_usec = now_tv.tv_usec; - now_tv.tv_usec = (now_true_usec/resolution) * resolution; - } - while (exim_tvcmp(&now_tv, tgt_tv) <= 0) { struct itimerval itval; @@ -1728,7 +1738,7 @@ setlocale(LC_ALL, "C"); -/* Get the offset between CLOCK_MONOTONIC and wallclock */ +/* Get the offset between CLOCK_MONOTONIC/CLOCK_BOOTTIME and wallclock */ #ifdef _POSIX_MONOTONIC_CLOCK exim_clock_init(); @@ -2148,7 +2158,7 @@ concept of *the* alias file, but since Sun's YP make script calls sendmail this way, some support must be provided. */ case 'i': - if (!*++argrest) bi_option = TRUE; + if (!*argrest) bi_option = TRUE; else badarg = TRUE; break; @@ -2796,10 +2806,22 @@ case 'S': smtp_peer_options |= OPTION_SIZE; break; #ifndef DISABLE_TLS + /* -MCs: used with -MCt; SNI was sent */ + /* -MCr: ditto, DANE */ + + case 'r': + case 's': if (++i < argc) + { + continue_proxy_sni = string_copy_taint(argv[i], TRUE); + if (argrest[1] == 'r') continue_proxy_dane = TRUE; + } + else badarg = TRUE; + break; + /* -MCt: similar to -MCT below but the connection is still open via a proxy process which handles the TLS context and coding. Require three arguments for the proxied local address and port, - and the TLS cipher. */ + and the TLS cipher. */ case 't': if (++i < argc) sending_ip_address = string_copy_taint(argv[i], TRUE); @@ -4759,8 +4781,7 @@ /* Ensure that the user name is in a suitable form for use as a "phrase" in an RFC822 address.*/ -originator_name = string_copy(parse_fix_phrase(originator_name, - Ustrlen(originator_name), big_buffer, big_buffer_size)); +originator_name = parse_fix_phrase(originator_name, Ustrlen(originator_name)); /* If a message is created by this call of Exim, the uid/gid of its originator are those of the caller. These values are overridden if an existing message is diff -urN ../exim-4.94.orig/src/exim_dbutil.c ./src/exim_dbutil.c --- ../exim-4.94.orig/src/exim_dbutil.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/exim_dbutil.c 2021-03-16 17:15:23.214423000 +0200 @@ -391,7 +391,7 @@ Arguments: dbblock a pointer to an open database block key the key of the record to be read - length where to put the length (or NULL if length not wanted) + length where to put the length (or NULL if length not wanted). Includes overhead. Returns: a pointer to the retrieved record, or NULL if the record is not found @@ -419,7 +419,7 @@ yield = store_get(EXIM_DATUM_SIZE(result_datum), TRUE); memcpy(yield, EXIM_DATUM_DATA(result_datum), EXIM_DATUM_SIZE(result_datum)); -if (length != NULL) *length = EXIM_DATUM_SIZE(result_datum); +if (length) *length = EXIM_DATUM_SIZE(result_datum); EXIM_DATUM_FREE(result_datum); /* Some DBM libs require freeing */ return yield; @@ -620,6 +620,7 @@ t = wait->text; name[MESSAGE_ID_LENGTH] = 0; + /* Leave corrupt records alone */ if (wait->count > WAIT_NAME_MAX) { fprintf(stderr, @@ -1222,7 +1223,7 @@ /* A continuation record may have been deleted or renamed already, so non-existence is not serious. */ - if (value == NULL) continue; + if (!value) continue; /* Delete if too old */ @@ -1243,10 +1244,31 @@ /* Leave corrupt records alone */ + if (wait->time_stamp > time(NULL)) + { + printf("**** Data for '%s' corrupted\n time in future: %s\n", + key, print_time(((dbdata_generic *)value)->time_stamp)); + continue; + } if (wait->count > WAIT_NAME_MAX) { - printf("**** Data for %s corrupted\n count=%d=0x%x max=%d\n", + printf("**** Data for '%s' corrupted\n count=%d=0x%x max=%d\n", key, wait->count, wait->count, WAIT_NAME_MAX); + continue; + } + if (wait->sequence > WAIT_CONT_MAX) + { + printf("**** Data for '%s' corrupted\n sequence=%d=0x%x max=%d\n", + key, wait->sequence, wait->sequence, WAIT_CONT_MAX); + continue; + } + + /* Record over 1 year old; just remove it */ + + if (wait->time_stamp < time(NULL) - 365*24*60*60) + { + dbfn_delete(dbm, key); + printf("deleted %s (too old)\n", key); continue; } diff -urN ../exim-4.94.orig/src/expand.c ./src/expand.c --- ../exim-4.94.orig/src/expand.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/expand.c 2021-03-16 17:15:23.220390000 +0200 @@ -1298,15 +1298,16 @@ { const uschar * tlist = list; int sep = 0; -uschar dummy; +/* Tainted mem for the throwaway element copies */ +uschar * dummy = store_get(2, TRUE); if (field < 0) { - for (field++; string_nextinlist(&tlist, &sep, &dummy, 1); ) field++; + for (field++; string_nextinlist(&tlist, &sep, dummy, 1); ) field++; sep = 0; } if (field == 0) return NULL; -while (--field > 0 && (string_nextinlist(&list, &sep, &dummy, 1))) ; +while (--field > 0 && (string_nextinlist(&list, &sep, dummy, 1))) ; return string_nextinlist(&list, &sep, NULL, 0); } @@ -4920,7 +4921,7 @@ { expand_string_message = string_sprintf("lookup of \"%s\" gave DEFER: %s", - string_printing2(key, FALSE), search_error_message); + string_printing2(key, SP_TAB), search_error_message); goto EXPAND_FAILED; } if (expand_setup > 0) expand_nmax = expand_setup; @@ -5334,11 +5335,14 @@ while ((item = string_nextinlist(&list, &sep, NULL, 0))) g = string_append_listele(g, ',', item); - /* possibly plus an EOL string */ + /* possibly plus an EOL string. Process with escapes, to protect + from list-processing. The only current user of eol= in search + options is the readsock expansion. */ + if (sub_arg[3] && *sub_arg[3]) g = string_append_listele(g, ',', - string_sprintf("eol=%s", sub_arg[3])); - + string_sprintf("eol=%s", + string_printing2(sub_arg[3], SP_TAB|SP_SPACE))); } /* Gat a (possibly cached) handle for the connection */ @@ -7208,9 +7212,8 @@ { int cnt = 0; int sep = 0; - uschar buffer[256]; - while (string_nextinlist(CUSS &sub, &sep, buffer, sizeof(buffer))) cnt++; + while (string_nextinlist(CUSS &sub, &sep, NULL, 0)) cnt++; yield = string_fmt_append(yield, "%d", cnt); continue; } @@ -7572,13 +7575,10 @@ prescribed by the RFC, if there are characters that need to be encoded */ case EOP_RFC2047: - { - uschar buffer[2048]; yield = string_cat(yield, parse_quote_2047(sub, Ustrlen(sub), headers_charset, - buffer, sizeof(buffer), FALSE)); + FALSE)); continue; - } /* RFC 2047 decode */ diff -urN ../exim-4.94.orig/src/functions.h ./src/functions.h --- ../exim-4.94.orig/src/functions.h 2020-05-30 23:35:38.000000000 +0300 +++ ./src/functions.h 2021-03-16 17:15:23.221737000 +0200 @@ -234,6 +234,7 @@ extern int exim_chown_failure(int, const uschar*, uid_t, gid_t); extern const uschar * exim_errstr(int); extern void exim_exit(int) NORETURN; +extern void exim_gettime(struct timeval *); extern void exim_nullstd(void); extern void exim_setugid(uid_t, gid_t, BOOL, uschar *); extern void exim_underbar_exit(int) NORETURN; @@ -365,9 +366,9 @@ const uschar *, uschar *, error_block **); extern uschar *parse_find_address_end(uschar *, BOOL); extern uschar *parse_find_at(uschar *); -extern const uschar *parse_fix_phrase(const uschar *, int, uschar *, int); +extern const uschar *parse_fix_phrase(const uschar *, int); extern uschar *parse_message_id(uschar *, uschar **, uschar **); -extern const uschar *parse_quote_2047(const uschar *, int, uschar *, uschar *, int, BOOL); +extern const uschar *parse_quote_2047(const uschar *, int, uschar *, BOOL); extern uschar *parse_date_time(uschar *str, time_t *t); extern int vaguely_random_number(int); #ifndef DISABLE_TLS @@ -528,7 +529,7 @@ #ifdef SUPPORT_I18N extern BOOL string_is_utf8(const uschar *); #endif -extern const uschar *string_printing2(const uschar *, BOOL); +extern const uschar *string_printing2(const uschar *, int); extern uschar *string_split_message(uschar *); extern uschar *string_unprinting(uschar *); #ifdef SUPPORT_I18N @@ -767,9 +768,9 @@ /* Simple string-copy functions maintaining the taint */ #define string_copyn(s, len) \ - string_copyn_taint_trc((s), (len), is_tainted(s), __FUNCTION__, __LINE__) + string_copyn_trc((s), (len), __FUNCTION__, __LINE__) #define string_copy(s) \ - string_copy_taint_trc((s), is_tainted(s), __FUNCTION__, __LINE__) + string_copy_trc((s), __FUNCTION__, __LINE__) /************************************************* diff -urN ../exim-4.94.orig/src/globals.c ./src/globals.c --- ../exim-4.94.orig/src/globals.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/globals.c 2021-03-16 17:15:23.222449000 +0200 @@ -729,6 +729,8 @@ int connection_max_messages= -1; uschar *continue_proxy_cipher = NULL; +BOOL continue_proxy_dane = FALSE; +uschar *continue_proxy_sni = NULL; uschar *continue_hostname = NULL; uschar *continue_host_address = NULL; int continue_sequence = 1; diff -urN ../exim-4.94.orig/src/globals.h ./src/globals.h --- ../exim-4.94.orig/src/globals.h 2020-05-30 23:35:38.000000000 +0300 +++ ./src/globals.h 2021-03-16 17:15:23.223113000 +0200 @@ -425,6 +425,8 @@ extern uschar *config_main_directory; /* Directory where the main config file was found */ extern uid_t config_uid; /* Additional owner */ extern uschar *continue_proxy_cipher; /* TLS cipher for proxied continued delivery */ +extern BOOL continue_proxy_dane; /* proxied conn is DANE */ +extern uschar *continue_proxy_sni; /* proxied conn SNI */ extern uschar *continue_hostname; /* Host for continued delivery */ extern uschar *continue_host_address; /* IP address for ditto */ extern int continue_sequence; /* Sequence num for continued delivery */ diff -urN ../exim-4.94.orig/src/host.c ./src/host.c --- ../exim-4.94.orig/src/host.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/host.c 2021-03-16 17:15:23.224812000 +0200 @@ -1946,8 +1946,13 @@ int yield, times; host_item *last = NULL; BOOL temp_error = FALSE; -#if HAVE_IPV6 int af; + +#ifndef DISABLE_TLS +/* Copy the host name at this point to the value which is used for +TLS certificate name checking, before anything modifies it. */ + +host->certname = host->name; #endif /* Make sure DNS options are set as required. This appears to be necessary in @@ -1967,10 +1972,10 @@ #ifdef STAND_ALONE if (disable_ipv6) #else - if (disable_ipv6 || - (dns_ipv4_lookup != NULL && - match_isinlist(host->name, CUSS &dns_ipv4_lookup, 0, NULL, NULL, - MCL_DOMAIN, TRUE, NULL) == OK)) + if ( disable_ipv6 + || dns_ipv4_lookup + && match_isinlist(host->name, CUSS &dns_ipv4_lookup, 0, + &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK) #endif { af = AF_INET; times = 1; } @@ -1980,7 +1985,7 @@ /* No IPv6 support */ #else /* HAVE_IPV6 */ - times = 1; + af = AF_INET; times = 1; #endif /* HAVE_IPV6 */ /* Initialize the flag that gets set for DNS syntax check errors, so that the @@ -2022,7 +2027,7 @@ #else /* not HAVE_IPV6 */ if (f.running_in_test_harness) - hostdata = host_fake_gethostbyname(host->name, AF_INET, &error_num); + hostdata = host_fake_gethostbyname(host->name, af, &error_num); else { hostdata = gethostbyname(CS host->name); @@ -2117,6 +2122,9 @@ { host_item *next = store_get(sizeof(host_item), FALSE); next->name = host->name; +#ifndef DISABLE_TLS + next->certname = host->certname; +#endif next->mx = host->mx; next->address = text_address; next->port = PORT_NONE; @@ -2192,8 +2200,8 @@ int rc; const uschar *save = deliver_domain; deliver_domain = host->name; /* set $domain */ - rc = match_isinlist(host->name, CUSS &dns_again_means_nonexist, 0, NULL, NULL, - MCL_DOMAIN, TRUE, NULL); + rc = match_isinlist(host->name, CUSS &dns_again_means_nonexist, 0, + &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL); deliver_domain = save; if (rc == OK) { @@ -2260,6 +2268,13 @@ BOOL dnssec_fail = FALSE; int i; +#ifndef DISABLE_TLS +/* Copy the host name at this point to the value which is used for +TLS certificate name checking, before any CNAME-following modifies it. */ + +host->certname = host->name; +#endif + /* If allow_ip is set, a name which is an IP address returns that value as its address. This is used for MX records when allow_mx_to_ip is set, for those sites that feel they have to flaunt the RFC rules. */ @@ -2286,9 +2301,9 @@ #ifndef STAND_ALONE if ( disable_ipv6 || !(whichrrs & HOST_FIND_BY_AAAA) - || (dns_ipv4_lookup - && match_isinlist(host->name, CUSS &dns_ipv4_lookup, 0, NULL, NULL, - MCL_DOMAIN, TRUE, NULL) == OK) + || dns_ipv4_lookup + && match_isinlist(host->name, CUSS &dns_ipv4_lookup, 0, + &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK ) i = 0; /* look up A records only */ else @@ -2546,12 +2561,12 @@ dns_answer * dnsa = store_get_dns_answer(); dns_scan dnss; BOOL dnssec_require = dnssec_d - && match_isinlist(host->name, CUSS &dnssec_d->require, - 0, NULL, NULL, MCL_DOMAIN, TRUE, NULL) == OK; + && match_isinlist(host->name, CUSS &dnssec_d->require, + 0, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK; BOOL dnssec_request = dnssec_require - || ( dnssec_d - && match_isinlist(host->name, CUSS &dnssec_d->request, - 0, NULL, NULL, MCL_DOMAIN, TRUE, NULL) == OK); + || ( dnssec_d + && match_isinlist(host->name, CUSS &dnssec_d->request, + 0, &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK); dnssec_status_t dnssec; /* Set the default fully qualified name to the incoming name, initialize the @@ -2616,10 +2631,10 @@ } if (rc == DNS_FAIL || rc == DNS_AGAIN) { - #ifndef STAND_ALONE - if (match_isinlist(host->name, CUSS &srv_fail_domains, 0, NULL, NULL, - MCL_DOMAIN, TRUE, NULL) != OK) - #endif +#ifndef STAND_ALONE + if (match_isinlist(host->name, CUSS &srv_fail_domains, 0, + &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) != OK) +#endif { yield = HOST_FIND_AGAIN; goto out; } DEBUG(D_host_lookup) debug_printf("DNS_%s treated as DNS_NODATA " "(domain in srv_fail_domains)\n", (rc == DNS_FAIL)? "FAIL":"AGAIN"); @@ -2668,8 +2683,8 @@ DEBUG(D_host_lookup) debug_printf("dnssec fail on MX for %.256s", host->name); #ifndef STAND_ALONE - if (match_isinlist(host->name, CUSS &mx_fail_domains, 0, NULL, NULL, - MCL_DOMAIN, TRUE, NULL) != OK) + if (match_isinlist(host->name, CUSS &mx_fail_domains, 0, + &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) != OK) { yield = HOST_FIND_SECURITY; goto out; } #endif rc = DNS_FAIL; @@ -2678,8 +2693,8 @@ case DNS_FAIL: case DNS_AGAIN: #ifndef STAND_ALONE - if (match_isinlist(host->name, CUSS &mx_fail_domains, 0, NULL, NULL, - MCL_DOMAIN, TRUE, NULL) != OK) + if (match_isinlist(host->name, CUSS &mx_fail_domains, 0, + &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) != OK) #endif { yield = HOST_FIND_AGAIN; goto out; } DEBUG(D_host_lookup) debug_printf("DNS_%s treated as DNS_NODATA " diff -urN ../exim-4.94.orig/src/lookups/dsearch.c ./src/lookups/dsearch.c --- ../exim-4.94.orig/src/lookups/dsearch.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/lookups/dsearch.c 2021-03-16 17:15:23.227279000 +0200 @@ -125,8 +125,7 @@ && S_ISDIR(statbuf.st_mode) && ( flags & FILTER_DIR || keystring[0] != '.' - || keystring[1] != '.' - || keystring[1] && keystring[2] + || keystring[1] && keystring[1] != '.' ) ) ) ) { /* Since the filename exists in the filesystem, we can return a @@ -135,10 +134,10 @@ return OK; } -if (errno == ENOENT) return FAIL; +if (errno == ENOENT || errno == 0) return FAIL; save_errno = errno; -*errmsg = string_sprintf("%s: lstat failed", filename); +*errmsg = string_sprintf("%s: lstat: %s", filename, strerror(errno)); errno = save_errno; return DEFER; } diff -urN ../exim-4.94.orig/src/lookups/ldap.c ./src/lookups/ldap.c --- ../exim-4.94.orig/src/lookups/ldap.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/lookups/ldap.c 2021-03-16 17:15:23.228282000 +0200 @@ -1093,7 +1093,6 @@ uschar *local_servers = NULL; uschar *server; const uschar *list; -uschar buffer[512]; while (isspace(*url)) url++; @@ -1254,7 +1253,7 @@ /* Loop through the default servers until OK or FAIL. Use local_servers list * if defined in the lookup, otherwise use the global default list */ list = !local_servers ? eldap_default_servers : local_servers; -while ((server = string_nextinlist(&list, &sep, buffer, sizeof(buffer)))) +while ((server = string_nextinlist(&list, &sep, NULL, 0))) { int rc; int port = 0; diff -urN ../exim-4.94.orig/src/lookups/readsock.c ./src/lookups/readsock.c --- ../exim-4.94.orig/src/lookups/readsock.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/lookups/readsock.c 2021-03-16 17:15:23.230520000 +0200 @@ -186,7 +186,9 @@ gstring * yield; int ret = DEFER; -DEBUG(D_lookup) debug_printf_indent("readsock: file=\"%s\" key=\"%s\" len=%d opts=\"%s\"\n", filename, keystring, length, opts); +DEBUG(D_lookup) + debug_printf_indent("readsock: file=\"%s\" key=\"%s\" len=%d opts=\"%s\"\n", + filename, keystring, length, opts); /* Parse options */ @@ -200,7 +202,7 @@ lf.do_tls = TRUE; #endif else if (Ustrncmp(s, "eol=", 4) == 0) - eol = s + 4; + eol = string_unprinting(s + 4); else if (Ustrcmp(s, "cache=yes") == 0) lf.cache = TRUE; else if (Ustrcmp(s, "send=no") == 0) diff -urN ../exim-4.94.orig/src/lookups/sqlite.c ./src/lookups/sqlite.c --- ../exim-4.94.orig/src/lookups/sqlite.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/lookups/sqlite.c 2021-03-16 17:15:23.231052000 +0200 @@ -24,16 +24,23 @@ sqlite3 *db = NULL; int ret; -if (!filename || !*filename) filename = sqlite_dbfile; -if (*filename != '/') +if (!filename || !*filename) + { + DEBUG(D_lookup) debug_printf_indent("Using sqlite_dbfile: %s\n", sqlite_dbfile); + filename = sqlite_dbfile; + } +if (!filename || *filename != '/') *errmsg = US"absolute file name expected for \"sqlite\" lookup"; else if ((ret = sqlite3_open(CCS filename, &db)) != 0) { *errmsg = (void *)sqlite3_errmsg(db); + sqlite3_close(db); + db = NULL; DEBUG(D_lookup) debug_printf_indent("Error opening database: %s\n", *errmsg); } -sqlite3_busy_timeout(db, 1000 * sqlite_lock_timeout); +if (db) + sqlite3_busy_timeout(db, 1000 * sqlite_lock_timeout); return db; } diff -urN ../exim-4.94.orig/src/macros.h ./src/macros.h --- ../exim-4.94.orig/src/macros.h 2020-05-30 23:35:38.000000000 +0300 +++ ./src/macros.h 2021-03-16 17:15:23.232088000 +0200 @@ -41,9 +41,11 @@ /* For almost all calls to convert things to printing characters, we want to -allow tabs. A macro just makes life a bit easier. */ +allow tabs & spaces. A macro just makes life a bit easier. */ -#define string_printing(s) string_printing2((s), TRUE) +#define string_printing(s) string_printing2((s), 0) +#define SP_TAB BIT(0) +#define SP_SPACE BIT(1) /* We need a special return code for "no recipients and failed to send an error @@ -189,9 +191,10 @@ #define SPOOL_NAME_LENGTH (MESSAGE_ID_LENGTH+2) /* The maximum number of message ids to store in a waiting database -record. */ +record, and the max number of continuation records allowed. */ #define WAIT_NAME_MAX 50 +#define WAIT_CONT_MAX 1000 /* Wait this long before determining that a Proxy Protocol configured host isn't speaking the protocol, and so is disallowed. Can be moved to @@ -302,6 +305,7 @@ #define CANCELLED 13 /* Authentication cancelled */ #define FAIL_SEND 14 /* send() failed in authenticator */ #define FAIL_DROP 15 /* Fail and drop connection (used in ACL) */ +#define DANE 16 /* Deferred for domain mismatch (used in transport) */ /* Returns from the deliver_message() function */ @@ -1067,8 +1071,8 @@ #define AUTHS_REGEX US"\\n250[\\s\\-]AUTH\\s+([\\-\\w \\t]+)(?:\\n|$)" -#define EARLY_PIPE_FEATURE_NAME "X_PIPE_CONNECT" -#define EARLY_PIPE_FEATURE_LEN 14 +#define EARLY_PIPE_FEATURE_NAME "PIPE_CONNECT" +#define EARLY_PIPE_FEATURE_LEN 12 /* Flags for auth_client_item() */ diff -urN ../exim-4.94.orig/src/match.c ./src/match.c --- ../exim-4.94.orig/src/match.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/match.c 2021-03-16 17:15:23.233172000 +0200 @@ -850,6 +850,11 @@ (void)fclose(f); HDEBUG(D_lists) debug_printf("%s %s (matched \"%s\" in %s)\n", ot, yield == OK ? "yes" : "no", sss, filename); + + /* The "pattern" being matched came from the file; we use a stack-local. + Copy it to allocated memory now we know it matched. */ + + if (valueptr) *valueptr = string_copy(ss); return file_yield; case DEFER: @@ -1288,9 +1293,11 @@ provided that "caseless" is set. (It is FALSE for calls for matching rewriting patterns.) Otherwise just the domain is lower cases. A magic item "+caseful" in the list can be used to restore a caseful copy of the local part from the -original address. */ +original address. +Limit the subject address size to avoid mem-exhastion attacks. The size chosen +is historical (we used to use big_buffer her). */ -if ((len = Ustrlen(address)) > 255) len = 255; +if ((len = Ustrlen(address)) > BIG_BUFFER_SIZE) len = BIG_BUFFER_SIZE; ab.address = string_copyn(address, len); for (uschar * p = ab.address + len - 1; p >= ab.address; p--) diff -urN ../exim-4.94.orig/src/parse.c ./src/parse.c --- ../exim-4.94.orig/src/parse.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/parse.c 2021-03-16 17:15:23.235544000 +0200 @@ -843,8 +843,7 @@ /* This function is used for quoting text in headers according to RFC 2047. If the only characters that strictly need quoting are spaces, we return the -original string, unmodified. If a quoted string is too long for the buffer, it -is truncated. (This shouldn't happen: this is normally handling short strings.) +original string, unmodified. Hmmph. As always, things get perverted for other uses. This function was originally for the "phrase" part of addresses. Now it is being used for much @@ -856,77 +855,57 @@ chars len the length of the string charset the name of the character set; NULL => iso-8859-1 - buffer the buffer to put the answer in - buffer_size the size of the buffer fold if TRUE, a newline is inserted before the separating space when more than one encoded-word is generated Returns: pointer to the original string, if no quoting needed, or - pointer to buffer containing the quoted string, or - a pointer to "String too long" if the buffer can't even hold - the introduction + pointer to allocated memory containing the quoted string */ const uschar * -parse_quote_2047(const uschar *string, int len, uschar *charset, uschar *buffer, - int buffer_size, BOOL fold) +parse_quote_2047(const uschar *string, int len, uschar *charset, BOOL fold) { -const uschar *s = string; -uschar *p, *t; -int hlen; +const uschar * s = string; +int hlen, l; BOOL coded = FALSE; BOOL first_byte = FALSE; +gstring * g = + string_fmt_append(NULL, "=?%s?Q?", charset ? charset : US"iso-8859-1"); -if (!charset) charset = US"iso-8859-1"; +hlen = l = g->ptr; -/* We don't expect this to fail! */ - -if (!string_format(buffer, buffer_size, "=?%s?Q?", charset)) - return US"String too long"; - -hlen = Ustrlen(buffer); -t = buffer + hlen; -p = buffer; - -for (; len > 0; len--) +for (s = string; len > 0; s++, len--) { - int ch = *s++; - if (t > buffer + buffer_size - hlen - 8) break; + int ch = *s; - if ((t - p > 67) && !first_byte) + if (g->ptr - l > 67 && !first_byte) { - *t++ = '?'; - *t++ = '='; - if (fold) *t++ = '\n'; - *t++ = ' '; - p = t; - Ustrncpy(p, buffer, hlen); - t += hlen; + g = fold ? string_catn(g, US"?=\n ", 4) : string_catn(g, US"?= ", 3); + l = g->ptr; + g = string_catn(g, g->s, hlen); } - if (ch < 33 || ch > 126 || - Ustrchr("?=()<>@,;:\\\".[]_", ch) != NULL) + if ( ch < 33 || ch > 126 + || Ustrchr("?=()<>@,;:\\\".[]_", ch) != NULL) { if (ch == ' ') { - *t++ = '_'; + g = string_catn(g, US"_", 1); first_byte = FALSE; } else { - t += sprintf(CS t, "=%02X", ch); + g = string_fmt_append(g, "=%02X", ch); coded = TRUE; first_byte = !first_byte; } } - else { *t++ = ch; first_byte = FALSE; } + else + { g = string_catn(g, s, 1); first_byte = FALSE; } } -*t++ = '?'; -*t++ = '='; -*t = 0; - -return coded ? buffer : string; +g = string_catn(g, US"?=", 2); +return coded ? string_from_gstring(g) : string; } @@ -969,32 +948,25 @@ We *could* use this for all cases, getting rid of the messy original code, but leave it for now. It would complicate simple cases like "John Q. Smith". -The result is passed back in the buffer; it is usually going to be added to -some other string. In order to be sure there is going to be no overflow, -restrict the length of the input to 1/4 of the buffer size - this allows for -every single character to be quoted or encoded without overflowing, and that -wouldn't happen because of amalgamation. If the phrase is too long, return a -fixed string. +The result is passed back in allocated memory. Arguments: phrase an RFC822 phrase len the length of the phrase - buffer a buffer to put the result in - buffer_size the size of the buffer Returns: the fixed RFC822 phrase */ const uschar * -parse_fix_phrase(const uschar *phrase, int len, uschar *buffer, int buffer_size) +parse_fix_phrase(const uschar *phrase, int len) { int ch, i; BOOL quoted = FALSE; const uschar *s, *end; +uschar * buffer; uschar *t, *yield; while (len > 0 && isspace(*phrase)) { phrase++; len--; } -if (len > buffer_size/4) return US"Name too long"; /* See if there are any non-printing characters, and if so, use the RFC 2047 encoding for the whole thing. */ @@ -1002,11 +974,13 @@ for (i = 0, s = phrase; i < len; i++, s++) if ((*s < 32 && *s != '\t') || *s > 126) break; -if (i < len) return parse_quote_2047(phrase, len, headers_charset, buffer, - buffer_size, FALSE); +if (i < len) + return parse_quote_2047(phrase, len, headers_charset, FALSE); /* No non-printers; use the RFC 822 quoting rules */ +buffer = store_get(len*4, is_tainted(phrase)); + s = phrase; end = s + len; yield = t = buffer + 1; @@ -1173,6 +1147,7 @@ } *t = 0; +store_release_above(t+1); return yield; } @@ -2102,7 +2077,6 @@ { int start, end, domain; uschar buffer[1024]; -uschar outbuff[1024]; big_buffer = store_malloc(big_buffer_size); @@ -2115,8 +2089,7 @@ { buffer[Ustrlen(buffer)-1] = 0; if (buffer[0] == 0) break; - printf("%s\n", CS parse_fix_phrase(buffer, Ustrlen(buffer), outbuff, - sizeof(outbuff))); + printf("%s\n", CS parse_fix_phrase(buffer, Ustrlen(buffer))); } printf("Testing parse_extract_address without group syntax and without UTF-8\n"); diff -urN ../exim-4.94.orig/src/pdkim/pdkim.c ./src/pdkim/pdkim.c --- ../exim-4.94.orig/src/pdkim/pdkim.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/pdkim/pdkim.c 2021-03-16 17:15:23.236565000 +0200 @@ -1111,14 +1111,14 @@ /* * RFC 5322 specifies that header line length SHOULD be no more than 78 - * lets make it so! * pdkim_headcat * - * returns uschar * (not nul-terminated) + * Returns gstring (not nul-terminated) appending to one supplied * * col: this int holds and receives column number (octets since last '\n') * str: partial string to append to - * pad: padding, split line or space after before or after eg: ";" + * pad: padding, split line or space after before or after eg: ";". + * Only the initial charater is used. * intro: - must join to payload eg "h=", usually the tag name * payload: eg base64 data - long data can be split arbitrarily. * @@ -1127,7 +1127,7 @@ * pairs and inside long values. it also always spaces or breaks after the * "pad" * - * no guarantees are made for output given out-of range input. like tag + * No guarantees are made for output given out-of range input. like tag * names longer than 78, or bogus col. Input is assumed to be free of line breaks. */ @@ -1135,93 +1135,65 @@ pdkim_headcat(int * col, gstring * str, const uschar * pad, const uschar * intro, const uschar * payload) { -size_t l; +int len, chomp, padded = 0; -if (pad) - { - l = Ustrlen(pad); - if (*col + l > 78) - str = pdkim_hdr_cont(str, col); - str = string_catn(str, pad, l); - *col += l; - } +/* If we can fit at least the pad at the end of current line, do it now. +Otherwise, wrap if there is a pad. */ -l = (pad?1:0) + (intro?Ustrlen(intro):0); - -if (*col + l > 78) - { /*can't fit intro - start a new line to make room.*/ - str = pdkim_hdr_cont(str, col); - l = intro?Ustrlen(intro):0; - } - -l += payload ? Ustrlen(payload):0 ; - -while (l>77) - { /* this fragment will not fit on a single line */ - if (pad) +if (pad) + if (*col + 1 <= 78) { - str = string_catn(str, US" ", 1); - *col += 1; - pad = NULL; /* only want this once */ - l--; + str = string_catn(str, pad, 1); + (*col)++; + pad = NULL; + padded = 1; } + else + str = pdkim_hdr_cont(str, col); - if (intro) - { - size_t sl = Ustrlen(intro); +/* Special case: if the whole addition does not fit at the end of the current +line, but could fit on a new line, wrap to give it its full, dedicated line. */ - str = string_catn(str, intro, sl); - *col += sl; - l -= sl; - intro = NULL; /* only want this once */ - } - - if (payload) - { - size_t sl = Ustrlen(payload); - size_t chomp = *col+sl < 77 ? sl : 78-*col; - - str = string_catn(str, payload, chomp); - *col += chomp; - payload += chomp; - l -= chomp-1; - } - - /* the while precondition tells us it didn't fit. */ - str = pdkim_hdr_cont(str, col); - } - -if (*col + l > 78) +len = (pad ? 2 : padded) + + (intro ? Ustrlen(intro) : 0) + + (payload ? Ustrlen(payload) : 0); +if (len <= 77 && *col+len > 78) { str = pdkim_hdr_cont(str, col); - pad = NULL; + padded = 0; } +/* Either we already dealt with the pad or we know there is room */ + if (pad) { + str = string_catn(str, pad, 1); str = string_catn(str, US" ", 1); - *col += 1; - pad = NULL; + *col += 2; } - -if (intro) +else if (padded && *col < 78) { - size_t sl = Ustrlen(intro); - - str = string_catn(str, intro, sl); - *col += sl; - l -= sl; - intro = NULL; + str = string_catn(str, US" ", 1); + (*col)++; } -if (payload) - { - size_t sl = Ustrlen(payload); +/* Call recursively with intro as payload: it gets the same, special treatment +(that is, not split if < 78). */ - str = string_catn(str, payload, sl); - *col += sl; - } +if (intro) + str = pdkim_headcat(col, str, NULL, NULL, intro); +if (payload) + for (len = Ustrlen(payload); len; len -= chomp) + { + if (*col >= 78) + str = pdkim_hdr_cont(str, col); + chomp = *col+len > 78 ? 78 - *col : len; + str = string_catn(str, payload, chomp); + *col += chomp; + payload += chomp; + } + return str; } @@ -1352,7 +1324,8 @@ int excess = p->key.len - 32; if (excess > 0) { - DEBUG(D_acl) debug_printf("DKIM: unexpected pubkey len %lu\n", p->key.len); + DEBUG(D_acl) + debug_printf("DKIM: unexpected pubkey len %lu\n", (unsigned long) p->key.len); p->key.data += excess; p->key.len = 32; } } diff -urN ../exim-4.94.orig/src/readconf.c ./src/readconf.c --- ../exim-4.94.orig/src/readconf.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/readconf.c 2021-03-16 17:15:23.239875000 +0200 @@ -1546,7 +1546,7 @@ { if (flags & opt_fn_print_label) printf("%s = ", name); printf("%s\n", smtp_receive_timeout_s - ? string_printing2(smtp_receive_timeout_s, FALSE) + ? string_printing2(smtp_receive_timeout_s, SP_TAB) : readconf_printtime(smtp_receive_timeout)); } else if (*str == '$') @@ -2463,7 +2463,7 @@ case opt_rewrite: /* Show the text value */ s = *(USS value); if (!no_labels) printf("%s = ", name); - printf("%s\n", s ? string_printing2(s, FALSE) : US""); + printf("%s\n", s ? string_printing2(s, SP_TAB) : US""); break; case opt_int: diff -urN ../exim-4.94.orig/src/receive.c ./src/receive.c --- ../exim-4.94.orig/src/receive.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/receive.c 2021-03-16 17:15:23.241296000 +0200 @@ -1784,7 +1784,7 @@ ids, and fractions of a second are required. See the comments that precede the message id creation below. */ -(void)gettimeofday(&message_id_tv, NULL); +exim_gettime(&message_id_tv); /* For other uses of the received time we can operate with granularity of one second, and for that we use the global variable received_time. This is for @@ -4004,7 +4004,7 @@ if (LOGGING(tls_peerdn) && tls_in.peerdn) g = string_append(g, 3, US" DN=\"", string_printing(tls_in.peerdn), US"\""); if (LOGGING(tls_sni) && tls_in.sni) - g = string_append(g, 3, US" SNI=\"", string_printing(tls_in.sni), US"\""); + g = string_append(g, 2, US" SNI=", string_printing2(tls_in.sni, SP_TAB|SP_SPACE)); #endif if (sender_host_authenticated) diff -urN ../exim-4.94.orig/src/rewrite.c ./src/rewrite.c --- ../exim-4.94.orig/src/rewrite.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/rewrite.c 2021-03-16 17:15:23.242215000 +0200 @@ -292,16 +292,11 @@ uschar *p1 = new + start - 1; uschar *p2 = new + end + 1; const uschar *pf1, *pf2; - uschar buff1[256], buff2[256]; while (p1 > new && p1[-1] == ' ') p1--; - pf1 = parse_fix_phrase(new, p1 - new, buff1, sizeof(buff1)); + pf1 = parse_fix_phrase(new, p1 - new); while (*p2 == ' ') p2++; - pf2 = parse_fix_phrase(p2, Ustrlen(p2), buff2, sizeof(buff2)); - - /* Note that pf1 and pf2 are NOT necessarily buff1 and buff2. For - a non-RFC 2047 phrase that does not need to be RFC 2822 quoted, they - will be buff1+1 and buff2+1. */ + pf2 = parse_fix_phrase(p2, Ustrlen(p2)); start = Ustrlen(pf1) + start + new - p1; end = start + Ustrlen(newparsed); diff -urN ../exim-4.94.orig/src/routers/rf_queue_add.c ./src/routers/rf_queue_add.c --- ../exim-4.94.orig/src/routers/rf_queue_add.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/routers/rf_queue_add.c 2021-03-16 17:15:23.246636000 +0200 @@ -98,7 +98,7 @@ " errors_to=%s\n", addr->transport ? addr->transport->name : US"", addr->local_part, addr->domain, addr->prop.errors_address); - debug_printf(" domain_data=%s localpart_data=%s\n", addr->prop.domain_data, + debug_printf(" domain_data=%s local_part_data=%s\n", addr->prop.domain_data, addr->prop.localpart_data); } diff -urN ../exim-4.94.orig/src/sieve.c ./src/sieve.c --- ../exim-4.94.orig/src/sieve.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/sieve.c 2021-03-16 17:15:23.248973000 +0200 @@ -3087,11 +3087,8 @@ if ((pid = child_open_exim2(&fd, envelope_from, envelope_from, US"sieve-notify")) >= 1) { - FILE *f; - uschar *buffer; - int buffer_capacity; + FILE * f = fdopen(fd, "wb"); - f = fdopen(fd, "wb"); fprintf(f,"From: %s\n", from.length == -1 ? expand_string(US"$local_part_prefix$local_part$local_part_suffix@$domain") : from.character); @@ -3104,12 +3101,9 @@ message.character=US"Notification"; message.length=Ustrlen(message.character); } - /* Allocation is larger than necessary, but enough even for split MIME words */ - buffer_capacity = 32 + 4*message.length; - buffer=store_get(buffer_capacity, TRUE); if (message.length != -1) fprintf(f, "Subject: %s\n", parse_quote_2047(message.character, - message.length, US"utf-8", buffer, buffer_capacity, TRUE)); + message.length, US"utf-8", TRUE)); fprintf(f,"\n"); if (body.length>0) fprintf(f,"%s\n",body.character); fflush(f); @@ -3263,8 +3257,6 @@ if (exec) { address_item *addr; - uschar *buffer; - int buffer_capacity; md5 base; uschar digest[16]; uschar hexdigest[33]; @@ -3342,11 +3334,8 @@ addr->reply->from = expand_string(US"$local_part@$domain"); else addr->reply->from = from.character; - /* Allocation is larger than necessary, but enough even for split MIME words */ - buffer_capacity=32+4*subject.length; - buffer = store_get(buffer_capacity, is_tainted(subject.character)); /* deconst cast safe as we pass in a non-const item */ - addr->reply->subject = US parse_quote_2047(subject.character, subject.length, US"utf-8", buffer, buffer_capacity, TRUE); + addr->reply->subject = US parse_quote_2047(subject.character, subject.length, US"utf-8", TRUE); addr->reply->oncelog = string_from_gstring(once); addr->reply->once_repeat=days*86400; diff -urN ../exim-4.94.orig/src/smtp_in.c ./src/smtp_in.c --- ../exim-4.94.orig/src/smtp_in.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/smtp_in.c 2021-03-16 17:15:23.250970000 +0200 @@ -1811,7 +1811,7 @@ if (LOGGING(tls_peerdn) && tls_in.peerdn) g = string_append(g, 3, US" DN=\"", string_printing(tls_in.peerdn), US"\""); if (LOGGING(tls_sni) && tls_in.sni) - g = string_append(g, 3, US" SNI=\"", string_printing(tls_in.sni), US"\""); + g = string_append(g, 2, US" SNI=", string_printing2(tls_in.sni, SP_TAB|SP_SPACE)); return g; } #endif @@ -5935,12 +5935,14 @@ g = string_append(g, 2, US";\n\tauth=pass (", sender_host_auth_pubname); -if (Ustrcmp(sender_host_auth_pubname, "tls") != 0) - g = string_append(g, 2, US") smtp.auth=", authenticated_id); -else if (authenticated_id) - g = string_append(g, 2, US") x509.auth=", authenticated_id); +if (Ustrcmp(sender_host_auth_pubname, "tls") == 0) + g = authenticated_id + ? string_append(g, 2, US") x509.auth=", authenticated_id) + : string_cat(g, US") reason=x509.auth"); else - g = string_catn(g, US") reason=x509.auth", 17); + g = authenticated_id + ? string_append(g, 2, US") smtp.auth=", authenticated_id) + : string_cat(g, US", no id saved)"); if (authenticated_sender) g = string_append(g, 2, US" smtp.mailfrom=", authenticated_sender); diff -urN ../exim-4.94.orig/src/smtp_out.c ./src/smtp_out.c --- ../exim-4.94.orig/src/smtp_out.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/smtp_out.c 2021-03-16 17:15:23.251272000 +0200 @@ -375,7 +375,7 @@ { if (host->port != PORT_NONE) { - HDEBUG(D_transport|D_acl|D_v) + HDEBUG(D_transport|D_acl|D_v) if (port != host->port) debug_printf_indent("Transport port=%d replaced by host-specific port=%d\n", port, host->port); port = host->port; diff -urN ../exim-4.94.orig/src/spam.c ./src/spam.c --- ../exim-4.94.orig/src/spam.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/spam.c 2021-03-16 17:15:23.251469000 +0200 @@ -18,7 +18,7 @@ uschar spam_bar_buffer[128]; uschar spam_action_buffer[32]; uschar spam_report_buffer[32600]; -uschar prev_user_name[128] = ""; +uschar * prev_user_name = NULL; int spam_ok = 0; int spam_rc = 0; uschar *prev_spamd_address_work = NULL; @@ -174,7 +174,7 @@ { sd = spamds[i]; if (!sd->is_failed && sd->priority == pri) - if ((rnd -= sd->weight) <= 0) + if ((rnd -= sd->weight) < 0) return i; } @@ -190,7 +190,6 @@ int sep = 0; const uschar *list = *listptr; uschar *user_name; -uschar user_name_buffer[128]; unsigned long mbox_size; FILE *mbox_file; client_conn_ctx spamd_cctx = {.sock = -1}; @@ -218,9 +217,7 @@ result = 0; /* find the username from the option list */ -if ((user_name = string_nextinlist(&list, &sep, - user_name_buffer, - sizeof(user_name_buffer))) == NULL) +if (!(user_name = string_nextinlist(&list, &sep, NULL, 0))) { /* no username given, this means no scanning should be done */ return FAIL; @@ -396,13 +393,12 @@ } else { /* spamassassin variant */ - (void)string_format(spamd_buffer, - sizeof(spamd_buffer), - "REPORT SPAMC/1.2\r\nUser: %s\r\nContent-length: %ld\r\n\r\n", - user_name, - mbox_size); + int n; + uschar * s = string_sprintf( + "REPORT SPAMC/1.2\r\nUser: %s\r\nContent-length: %ld\r\n\r\n%n", + user_name, mbox_size, &n); /* send our request */ - wrote = send(spamd_cctx.sock, spamd_buffer, Ustrlen(spamd_buffer), 0); + wrote = send(spamd_cctx.sock, s, n, 0); } if (wrote == -1) @@ -633,7 +629,7 @@ prev_spamd_address_work = string_copy(spamd_address_work); /* remember user name and "been here" for it */ -Ustrcpy(prev_user_name, user_name); +prev_user_name = user_name; spam_ok = 1; return override diff -urN ../exim-4.94.orig/src/spool_in.c ./src/spool_in.c --- ../exim-4.94.orig/src/spool_in.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/spool_in.c 2021-03-16 17:15:23.252169000 +0200 @@ -55,7 +55,7 @@ set_subdir_str(message_subdir, id, i); fname = spool_fname(US"input", message_subdir, id, US"-D"); - DEBUG(D_deliver) debug_printf("Trying spool file %s\n", fname); + DEBUG(D_deliver) debug_printf_indent("Trying spool file %s\n", fname); /* We protect against symlink attacks both in not propagating the * file-descriptor to other processes as we exec, and also ensuring that we @@ -253,7 +253,7 @@ sender_host_address = NULL; sender_host_name = NULL; sender_host_port = 0; -sender_host_authenticated = NULL; +sender_host_authenticated = sender_host_auth_pubname = NULL; sender_ident = NULL; f.sender_local = FALSE; f.sender_set_untrusted = FALSE; @@ -367,7 +367,7 @@ errno = 0; #ifndef COMPILE_UTILITY -DEBUG(D_deliver) debug_printf("reading spool file %s\n", name); +DEBUG(D_deliver) debug_printf_indent("reading spool file %s\n", name); #endif /* COMPILE_UTILITY */ /* The first line of a spool file contains the message id followed by -H (i.e. @@ -430,7 +430,7 @@ #endif #ifndef COMPILE_UTILITY -DEBUG(D_deliver) debug_printf("user=%s uid=%ld gid=%ld sender=%s\n", +DEBUG(D_deliver) debug_printf_indent("user=%s uid=%ld gid=%ld sender=%s\n", originator_login, (long int)originator_uid, (long int)originator_gid, sender_address); #endif @@ -580,6 +580,8 @@ host_lookup_deferred = TRUE; else if (Ustrcmp(p, "ost_lookup_failed") == 0) host_lookup_failed = TRUE; + else if (Ustrncmp(p, "ost_auth_pubname", 16) == 0) + sender_host_auth_pubname = string_copy_taint(var + 18, tainted); else if (Ustrncmp(p, "ost_auth", 8) == 0) sender_host_authenticated = string_copy_taint(var + 10, tainted); else if (Ustrncmp(p, "ost_name", 8) == 0) @@ -715,7 +717,7 @@ #ifndef COMPILE_UTILITY DEBUG(D_deliver) - debug_printf("sender_local=%d ident=%s\n", f.sender_local, + debug_printf_indent("sender_local=%d ident=%s\n", f.sender_local, sender_ident ? sender_ident : US"unset"); #endif /* COMPILE_UTILITY */ @@ -743,7 +745,7 @@ goto SPOOL_FORMAT_ERROR; #ifndef COMPILE_UTILITY -DEBUG(D_deliver) debug_printf("recipients_count=%d\n", rcount); +DEBUG(D_deliver) debug_printf_indent("recipients_count=%d\n", rcount); #endif /* COMPILE_UTILITY */ recipients_list_max = rcount; @@ -814,7 +816,7 @@ { int dummy; #if !defined (COMPILE_UTILITY) - DEBUG(D_deliver) debug_printf("**** SPOOL_IN - Exim 3 spool file\n"); + DEBUG(D_deliver) debug_printf_indent("**** SPOOL_IN - Exim 3 spool file\n"); #endif while (isdigit(*(--p)) || *p == ','); if (*p == ' ') @@ -829,7 +831,7 @@ else if (*p == ' ') { #if !defined (COMPILE_UTILITY) - DEBUG(D_deliver) debug_printf("**** SPOOL_IN - early Exim 4 spool file\n"); + DEBUG(D_deliver) debug_printf_indent("**** SPOOL_IN - early Exim 4 spool file\n"); #endif *p++ = 0; (void)sscanf(CS p, "%d", &pno); @@ -842,7 +844,7 @@ int flags; #if !defined (COMPILE_UTILITY) - DEBUG(D_deliver) debug_printf("**** SPOOL_IN - Exim standard format spoolfile\n"); + DEBUG(D_deliver) debug_printf_indent("**** SPOOL_IN - Exim standard format spoolfile\n"); #endif (void)sscanf(CS p+1, "%d", &flags); @@ -878,13 +880,13 @@ } #if !defined(COMPILE_UTILITY) else - { DEBUG(D_deliver) debug_printf("**** SPOOL_IN - No additional fields\n"); } + { DEBUG(D_deliver) debug_printf_indent("**** SPOOL_IN - No additional fields\n"); } if (orcpt || dsn_flags) - DEBUG(D_deliver) debug_printf("**** SPOOL_IN - address: <%s> orcpt: <%s> dsn_flags: 0x%x\n", + DEBUG(D_deliver) debug_printf_indent("**** SPOOL_IN - address: <%s> orcpt: <%s> dsn_flags: 0x%x\n", big_buffer, orcpt, dsn_flags); if (errors_to) - DEBUG(D_deliver) debug_printf("**** SPOOL_IN - address: <%s> errorsto: <%s>\n", + DEBUG(D_deliver) debug_printf_indent("**** SPOOL_IN - address: <%s> errorsto: <%s>\n", big_buffer, errors_to); #endif @@ -956,7 +958,7 @@ and give a positive response. */ #ifndef COMPILE_UTILITY -DEBUG(D_deliver) debug_printf("body_linecount=%d message_linecount=%d\n", +DEBUG(D_deliver) debug_printf_indent("body_linecount=%d message_linecount=%d\n", body_linecount, message_linecount); #endif /* COMPILE_UTILITY */ diff -urN ../exim-4.94.orig/src/spool_out.c ./src/spool_out.c --- ../exim-4.94.orig/src/spool_out.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/spool_out.c 2021-03-16 17:15:23.252585000 +0200 @@ -174,9 +174,11 @@ fprintf(fp, "-host_address %s.%d\n", sender_host_address, sender_host_port); if (sender_host_name) spool_var_write(fp, US"host_name", sender_host_name); - if (sender_host_authenticated) - spool_var_write(fp, US"host_auth", sender_host_authenticated); } +if (sender_host_authenticated) + spool_var_write(fp, US"host_auth", sender_host_authenticated); +if (sender_host_auth_pubname) + spool_var_write(fp, US"host_auth_pubname", sender_host_auth_pubname); /* Also about the interface a message came in on */ diff -urN ../exim-4.94.orig/src/store.c ./src/store.c --- ../exim-4.94.orig/src/store.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/store.c 2021-03-16 17:15:23.253618000 +0200 @@ -188,14 +188,14 @@ if ((b = current_block[pool])) { uschar * bc = US b + ALIGNED_SIZEOF_STOREBLOCK; - if (US p >= bc && US p <= bc + b->length) return TRUE; + if (US p >= bc && US p < bc + b->length) return TRUE; } for (int pool = POOL_TAINT_BASE; pool < nelem(chainbase); pool++) for (b = chainbase[pool]; b; b = b->next) { uschar * bc = US b + ALIGNED_SIZEOF_STOREBLOCK; - if (US p >= bc && US p <= bc + b->length) return TRUE; + if (US p >= bc && US p < bc + b->length) return TRUE; } return FALSE; } diff -urN ../exim-4.94.orig/src/string.c ./src/string.c --- ../exim-4.94.orig/src/string.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/string.c 2021-03-16 17:15:23.254522000 +0200 @@ -281,17 +281,17 @@ /* This function is called for critical strings. It checks for any non-printing characters, and if any are found, it makes a new copy of the string with suitable escape sequences. It is most often called by the -macro string_printing(), which sets allow_tab TRUE. +macro string_printing(), which sets flags to 0. Arguments: s the input string - allow_tab TRUE to allow tab as a printing character + flags Bit 0: convert tabs. Bit 1: convert spaces. Returns: string with non-printers encoded as printing sequences */ const uschar * -string_printing2(const uschar *s, BOOL allow_tab) +string_printing2(const uschar *s, int flags) { int nonprintcount = 0; int length = 0; @@ -301,7 +301,10 @@ while (*t != 0) { int c = *t++; - if (!mac_isprint(c) || (!allow_tab && c == '\t')) nonprintcount++; + if ( !mac_isprint(c) + || flags & SP_TAB && c == '\t' + || flags & SP_SPACE && c == ' ' + ) nonprintcount++; length++; } @@ -310,17 +313,19 @@ /* Get a new block of store guaranteed big enough to hold the expanded string. */ -ss = store_get(length + nonprintcount * 3 + 1, is_tainted(s)); +tt = ss = store_get(length + nonprintcount * 3 + 1, is_tainted(s)); /* Copy everything, escaping non printers. */ -t = s; -tt = ss; - -while (*t != 0) +for (t = s; *t; ) { int c = *t; - if (mac_isprint(c) && (allow_tab || c != '\t')) *tt++ = *t++; else + if ( mac_isprint(c) + && (!(flags & SP_TAB) || c != '\t') + && (!(flags & SP_SPACE) || c != ' ') + ) + *tt++ = *t++; + else { *tt++ = '\\'; switch (*t) @@ -947,7 +952,10 @@ s = ss; if (!*s || *++s != sep || sep_is_special) break; } - while (g->ptr > 0 && isspace(g->s[g->ptr-1])) g->ptr--; + /* while (g->ptr > 0 && isspace(g->s[g->ptr-1])) g->ptr--; */ + while ( g->ptr > 0 && isspace(g->s[g->ptr-1]) + && (g->ptr == 1 || g->s[g->ptr-2] != '\\') ) + g->ptr--; buffer = string_from_gstring(g); gstring_release_unused(g); } diff -urN ../exim-4.94.orig/src/structs.h ./src/structs.h --- ../exim-4.94.orig/src/structs.h 2020-05-30 23:35:38.000000000 +0300 +++ ./src/structs.h 2021-03-16 17:15:23.254949000 +0200 @@ -80,14 +80,17 @@ typedef struct host_item { struct host_item *next; - const uschar *name; /* Host name */ - const uschar *address; /* IP address in text form */ - int port; /* port value in host order (if SRV lookup) */ - int mx; /* MX value if found via MX records */ - int sort_key; /* MX*1000 plus random "fraction" */ - int status; /* Usable, unusable, or unknown */ - int why; /* Why host is unusable */ - int last_try; /* Time of last try if known */ + const uschar *name; /* Host name */ +#ifndef DISABLE_TLS + const uschar *certname; /* Name used for certificate checks */ +#endif + const uschar *address; /* IP address in text form */ + int port; /* port value in host order (if SRV lookup) */ + int mx; /* MX value if found via MX records */ + int sort_key; /* MX*1000 plus random "fraction" */ + int status; /* Usable, unusable, or unknown */ + int why; /* Why host is unusable */ + int last_try; /* Time of last try if known */ dnssec_status_t dnssec; } host_item; diff -urN ../exim-4.94.orig/src/tls-gnu.c ./src/tls-gnu.c --- ../exim-4.94.orig/src/tls-gnu.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/tls-gnu.c 2021-03-16 17:15:23.256265000 +0200 @@ -545,8 +545,11 @@ /* peercert is set in peer_status() */ tlsp->peerdn = state->peerdn; -tlsp->sni = state->received_sni; +/* do not corrupt sni sent by client; record sni rxd by server */ +if (!state->host) + tlsp->sni = state->received_sni; + /* record our certificate */ { const gnutls_datum_t * cert = gnutls_certificate_get_ours(state->session); @@ -2601,9 +2604,9 @@ { state->exp_tls_verify_cert_hostnames = #ifdef SUPPORT_I18N - string_domain_utf8_to_alabel(host->name, NULL); + string_domain_utf8_to_alabel(host->certname, NULL); #else - host->name; + host->certname; #endif DEBUG(D_tls) debug_printf("TLS: server cert verification includes hostname: \"%s\".\n", @@ -2863,7 +2866,7 @@ /* If dane is flagged, have either request or require dane for this host, and a TLSA record found. Therefore, dane verify required. Which implies cert must be requested and supplied, dane verify must pass, and cert verify irrelevant -(incl. hostnames), and (caller handled) require_tls */ +(incl. hostnames), and (caller handled) require_tls and sni=$domain */ if (conn_args->dane && ob->dane_require_tls_ciphers) { @@ -2890,6 +2893,7 @@ cipher_list, &state, tlsp, errstr) != OK) return FALSE; + #ifdef MEASURE_TIMING report_time_since(&t0, US"client tls_init (delta)"); #endif @@ -3158,6 +3162,7 @@ sigalrm_seen = FALSE; if (smtp_receive_timeout > 0) ALARM(smtp_receive_timeout); +errno = 0; do inbytes = gnutls_record_recv(state->session, state->xfer_buffer, MIN(ssl_xfer_buffer_size, lim)); @@ -3318,6 +3323,7 @@ debug_printf("Calling gnutls_record_recv(session=%p, buffer=%p, len=" SIZE_T_FMT ")\n", state->session, buff, len); +errno = 0; do inbytes = gnutls_record_recv(state->session, buff, len); while (inbytes == GNUTLS_E_AGAIN); @@ -3381,6 +3387,7 @@ DEBUG(D_tls) debug_printf("gnutls_record_send(session=%p, buffer=%p, left=" SIZE_T_FMT ")\n", state->session, buff, left); + errno = 0; do outbytes = gnutls_record_send(state->session, buff, left); while (outbytes == GNUTLS_E_AGAIN); diff -urN ../exim-4.94.orig/src/tls-openssl.c ./src/tls-openssl.c --- ../exim-4.94.orig/src/tls-openssl.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/tls-openssl.c 2021-03-16 17:15:23.257349000 +0200 @@ -372,10 +372,10 @@ } ocsp_resplist; typedef struct tls_ext_ctx_cb { - tls_support * tlsp; - uschar *certificate; - uschar *privatekey; - BOOL is_server; + tls_support * tlsp; + uschar * certificate; + uschar * privatekey; + BOOL is_server; #ifndef DISABLE_OCSP STACK_OF(X509) *verify_stack; /* chain for verifying the proof */ union { @@ -390,14 +390,14 @@ } client; } u_ocsp; #endif - uschar *dhparam; + uschar * dhparam; /* these are cached from first expand */ - uschar *server_cipher_list; + uschar * server_cipher_list; /* only passed down to tls_error: */ - host_item *host; + host_item * host; const uschar * verify_cert_hostnames; #ifndef DISABLE_EVENT - uschar * event_action; + uschar * event_action; #endif } tls_ext_ctx_cb; @@ -2915,9 +2915,9 @@ { cbinfo->verify_cert_hostnames = #ifdef SUPPORT_I18N - string_domain_utf8_to_alabel(host->name, NULL); + string_domain_utf8_to_alabel(host->certname, NULL); #else - host->name; + host->certname; #endif DEBUG(D_tls) debug_printf("Cert hostname to check: \"%s\"\n", cbinfo->verify_cert_hostnames); @@ -3197,6 +3197,7 @@ #ifndef DISABLE_OCSP { # ifdef SUPPORT_DANE + /*XXX this should be moved to caller, to be common across gnutls/openssl */ if ( conn_args->dane && ob->hosts_request_ocsp[0] == '*' && ob->hosts_request_ocsp[1] == '\0' diff -urN ../exim-4.94.orig/src/transport.c ./src/transport.c --- ../exim-4.94.orig/src/transport.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/transport.c 2021-03-16 17:15:23.258891000 +0200 @@ -1661,6 +1661,7 @@ debug_printf("transport_check_waiting entered\n"); debug_printf(" sequence=%d local_max=%d global_max=%d\n", continue_sequence, local_message_max, connection_max_messages); + acl_level++; } /* Do nothing if we have hit the maximum number that can be send down one @@ -1670,23 +1671,23 @@ if (local_message_max > 0 && continue_sequence >= local_message_max) { DEBUG(D_transport) - debug_printf("max messages for one connection reached: returning\n"); - return FALSE; + debug_printf_indent("max messages for one connection reached: returning\n"); + goto retfalse; } /* Open the waiting information database. */ if (!(dbm_file = dbfn_open(string_sprintf("wait-%.200s", transport_name), O_RDWR, &dbblock, TRUE, TRUE))) - return FALSE; + goto retfalse; /* See if there is a record for this host; if not, there's nothing to do. */ if (!(host_record = dbfn_read(dbm_file, hostname))) { dbfn_close(dbm_file); - DEBUG(D_transport) debug_printf("no messages waiting for %s\n", hostname); - return FALSE; + DEBUG(D_transport) debug_printf_indent("no messages waiting for %s\n", hostname); + goto retfalse; } /* If the data in the record looks corrupt, just log something and @@ -1697,7 +1698,7 @@ dbfn_close(dbm_file); log_write(0, LOG_MAIN|LOG_PANIC, "smtp-wait database entry for %s has bad " "count=%d (max=%d)", hostname, host_record->count, WAIT_NAME_MAX); - return FALSE; + goto retfalse; } /* Scan the message ids in the record from the end towards the beginning, @@ -1835,8 +1836,8 @@ if (host_length <= 0) { dbfn_close(dbm_file); - DEBUG(D_transport) debug_printf("waiting messages already delivered\n"); - return FALSE; + DEBUG(D_transport) debug_printf_indent("waiting messages already delivered\n"); + goto retfalse; } /* we were not able to find an acceptable message, nor was there a @@ -1847,7 +1848,7 @@ { Ustrcpy(new_message_id, message_id); dbfn_close(dbm_file); - return FALSE; + goto retfalse; } } /* we need to process a continuation record */ @@ -1865,7 +1866,12 @@ } dbfn_close(dbm_file); +DEBUG(D_transport) {acl_level--; debug_printf("transport_check_waiting: TRUE\n"); } return TRUE; + +retfalse: +DEBUG(D_transport) {acl_level--; debug_printf("transport_check_waiting: FALSE\n"); } +return FALSE; } /************************************************* @@ -1877,7 +1883,7 @@ transport_do_pass_socket(const uschar *transport_name, const uschar *hostname, const uschar *hostaddress, uschar *id, int socket_fd) { -int i = 20; +int i = 22; const uschar **argv; /* Set up the calling arguments; use the standard function for the basics, @@ -1898,6 +1904,16 @@ argv[i++] = sending_ip_address; argv[i++] = string_sprintf("%d", sending_port); argv[i++] = tls_out.active.sock >= 0 ? tls_out.cipher : continue_proxy_cipher; + + if (tls_out.sni) + { + argv[i++] = +#ifdef SUPPORT_DANE + tls_out.dane_verified ? US"-MCr" : +#endif + US"-MCs"; + argv[i++] = tls_out.sni; + } } else argv[i++] = US"-MCT"; diff -urN ../exim-4.94.orig/src/transports/appendfile.c ./src/transports/appendfile.c --- ../exim-4.94.orig/src/transports/appendfile.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/transports/appendfile.c 2021-03-16 17:15:23.260137000 +0200 @@ -1783,8 +1783,8 @@ if (statbuf.st_nlink != 1) { addr->basic_errno = ERRNO_NOTREGULAR; - addr->message = string_sprintf("mailbox %s%s has too many links (%d)", - filename, islink ? " (symlink)" : "", statbuf.st_nlink); + addr->message = string_sprintf("mailbox %s%s has too many links (%lu)", + filename, islink ? " (symlink)" : "", (unsigned long)statbuf.st_nlink); goto RETURN; } diff -urN ../exim-4.94.orig/src/transports/autoreply.c ./src/transports/autoreply.c --- ../exim-4.94.orig/src/transports/autoreply.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/transports/autoreply.c 2021-03-16 17:15:23.260532000 +0200 @@ -474,10 +474,10 @@ else { EXIM_DATUM key_datum, result_datum; - uschar * dirname = string_copy(oncelog); - uschar * s; + uschar * dirname, * s; - if ((s = Ustrrchr(dirname, '/'))) *s = '\0'; + dirname = (s = Ustrrchr(oncelog, '/')) + ? string_copyn(oncelog, s - oncelog) : NULL; EXIM_DBOPEN(oncelog, dirname, O_RDWR|O_CREAT, ob->mode, &dbm_file); if (!dbm_file) { diff -urN ../exim-4.94.orig/src/transports/queuefile.c ./src/transports/queuefile.c --- ../exim-4.94.orig/src/transports/queuefile.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/transports/queuefile.c 2021-03-16 17:15:23.261699000 +0200 @@ -8,7 +8,10 @@ /* See the file NOTICE for conditions of use and distribution. */ + #include "../exim.h" + +#ifdef EXPERIMENTAL_QUEUEFILE /* whole file */ #include "queuefile.h" /* Options specific to the appendfile transport. They must be in alphabetic @@ -276,3 +279,4 @@ } #endif /*!MACRO_PREDEF*/ +#endif /*EXPERIMENTAL_QUEUEFILE*/ diff -urN ../exim-4.94.orig/src/transports/smtp.c ./src/transports/smtp.c --- ../exim-4.94.orig/src/transports/smtp.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/transports/smtp.c 2021-03-16 17:15:23.263325000 +0200 @@ -1020,7 +1020,7 @@ (void) smtp_discard_responses(sx, sx->conn_args.ob, *countp); return rc; } -#endif +#endif /*!DISABLE_PIPE_CONNECT*/ /************************************************* @@ -1087,7 +1087,7 @@ { DEBUG(D_transport) debug_printf("%s expect mail\n", __FUNCTION__); count--; - sx->pending_MAIL = FALSE; + sx->pending_MAIL = sx->RCPT_452 = FALSE; if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2', ob->command_timeout)) { @@ -1227,7 +1227,7 @@ if (addr->more_errno >> 8 == 52 && yield & 3) { - if (!sx->RCPT_452) + if (!sx->RCPT_452) /* initialised at MAIL-ack above */ { DEBUG(D_transport) debug_printf("%s: seen first 452 too-many-rcpts\n", __FUNCTION__); @@ -1274,6 +1274,8 @@ } } } + if (count && !(addr = addr->next)) + return -2; } /* Loop for next RCPT response */ /* Update where to start at for the next block of responses, unless we @@ -1549,7 +1551,9 @@ if (require_auth == OK && !f.smtp_authenticated) { +#ifndef DISABLE_PIPE_CONNECT invalidate_ehlo_cache_entry(sx); +#endif set_errno_nohost(sx->addrlist, ERRNO_AUTHFAIL, string_sprintf("authentication required but %s", fail_reason), DEFER, FALSE, &sx->delivery_start); @@ -1620,8 +1624,8 @@ typedef struct smtp_compare_s { - uschar *current_sender_address; - struct transport_instance *tblock; + uschar * current_sender_address; + struct transport_instance * tblock; } smtp_compare_t; @@ -1989,8 +1993,81 @@ DEFER, FALSE, &sx->delivery_start); return ERROR; } -#endif +#else +/* If we have a proxied TLS connection, check usability for this message */ + +if (continue_hostname && continue_proxy_cipher) + { + int rc; + const uschar * sni = US""; + +# ifdef SUPPORT_DANE + /* Check if the message will be DANE-verified; if so force its SNI */ + + tls_out.dane_verified = FALSE; + smtp_port_for_connect(sx->conn_args.host, sx->port); + if ( sx->conn_args.host->dnssec == DS_YES + && ( sx->dane_required + || verify_check_given_host(CUSS &ob->hosts_try_dane, sx->conn_args.host) == OK + ) ) + switch (rc = tlsa_lookup(sx->conn_args.host, &sx->conn_args.tlsa_dnsa, sx->dane_required)) + { + case OK: sx->conn_args.dane = TRUE; + ob->tls_tempfail_tryclear = FALSE; /* force TLS */ + ob->tls_sni = sx->first_addr->domain; /* force SNI */ + break; + case FAIL_FORCED: break; + default: set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER, + string_sprintf("DANE error: tlsa lookup %s", + rc_to_string(rc)), + rc, FALSE, &sx->delivery_start); +# ifndef DISABLE_EVENT + (void) event_raise(sx->conn_args.tblock->event_action, + US"dane:fail", sx->dane_required + ? US"dane-required" : US"dnssec-invalid"); +# endif + return rc; + } +# endif + + /* If the SNI or the DANE status required for the new message differs from the + existing conn drop the connection to force a new one. */ + + if (ob->tls_sni && !(sni = expand_cstring(ob->tls_sni))) + log_write(0, LOG_MAIN|LOG_PANIC, + "<%s>: failed to expand transport's tls_sni value: %s", + sx->addrlist->address, expand_string_message); + +# ifdef SUPPORT_DANE + if ( (continue_proxy_sni ? (Ustrcmp(continue_proxy_sni, sni) == 0) : !*sni) + && continue_proxy_dane == sx->conn_args.dane) + { + tls_out.sni = US sni; + if ((tls_out.dane_verified = continue_proxy_dane)) + sx->conn_args.host->dnssec = DS_YES; + } +# else + if ((continue_proxy_sni ? (Ustrcmp(continue_proxy_sni, sni) == 0) : !*sni)) + tls_out.sni = US sni; +# endif + else + { + DEBUG(D_transport) + debug_printf("Closing proxied-TLS connection due to SNI mismatch\n"); + + HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP>> QUIT\n"); + write(0, "QUIT\r\n", 6); + close(0); + continue_hostname = continue_proxy_cipher = NULL; + f.continue_more = FALSE; + continue_sequence = 1; /* Unfortunately, this process cannot affect success log + which is done by delivery proc. Would have to pass this + back through reporting pipe. */ + } + } +#endif /*!DISABLE_TLS*/ + /* Make a connection to the host if this isn't a continued delivery, and handle the initial interaction and HELO/EHLO/LHLO. Connect timeout errors are handled specially so they can be identified for retries. */ @@ -2019,7 +2096,8 @@ switch (rc = tlsa_lookup(sx->conn_args.host, &sx->conn_args.tlsa_dnsa, sx->dane_required)) { case OK: sx->conn_args.dane = TRUE; - ob->tls_tempfail_tryclear = FALSE; + ob->tls_tempfail_tryclear = FALSE; /* force TLS */ + ob->tls_sni = sx->first_addr->domain; /* force SNI */ break; case FAIL_FORCED: break; default: set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER, @@ -3429,6 +3507,9 @@ uschar *message = NULL; uschar new_message_id[MESSAGE_ID_LENGTH + 1]; smtp_context * sx = store_get(sizeof(*sx), TRUE); /* tainted, for the data buffers */ +#ifdef SUPPORT_DANE +BOOL dane_held; +#endif suppress_tls = suppress_tls; /* stop compiler warning when no TLS support */ *message_defer = FALSE; @@ -3445,15 +3526,41 @@ gettimeofday(&sx->delivery_start, NULL); sx->sync_addr = sx->first_addr = addrlist; -/* Get the channel set up ready for a message (MAIL FROM being the next -SMTP command to send */ +#ifdef SUPPORT_DANE +DANE_DOMAINS: +dane_held = FALSE; +#endif +/* Get the channel set up ready for a message, MAIL FROM being the next +SMTP command to send. */ + if ((rc = smtp_setup_conn(sx, suppress_tls)) != OK) { timesince(&addrlist->delivery_time, &sx->delivery_start); - return rc; + yield = rc; + goto TIDYUP; } +#ifdef SUPPORT_DANE +/* If the connection used DANE, ignore for now any addresses with incompatible +domains. The SNI has to be the domain. Arrange a whole new TCP conn later, +just in case only TLS isn't enough. */ + +if (sx->conn_args.dane) + { + const uschar * dane_domain = sx->first_addr->domain; + + for (address_item * a = sx->first_addr->next; a; a = a->next) + if ( a->transport_return == PENDING_DEFER + && Ustrcmp(dane_domain, a->domain) != 0) + { + DEBUG(D_transport) debug_printf("DANE: holding %s for later\n", a->domain); + dane_held = TRUE; + a->transport_return = DANE; + } + } +#endif + /* If there is a filter command specified for this transport, we can now set it up. This cannot be done until the identity of the host is known. */ @@ -3819,9 +3926,10 @@ } /* Process all transported addresses - for LMTP or PRDR, read a status for - each one. */ + each one. We used to drop out at first_addr, until someone returned a 452 + followed by a 250... and we screwed up the accepted addresses. */ - for (address_item * addr = addrlist; addr != sx->first_addr; addr = addr->next) + for (address_item * addr = addrlist; addr; addr = addr->next) { if (addr->transport_return != PENDING_OK) continue; @@ -4154,8 +4262,8 @@ t_compare.tblock = tblock; t_compare.current_sender_address = sender_address; - if ( sx->first_addr != NULL - || f.continue_more + if ( sx->first_addr != NULL /* more addrs for this message */ + || f.continue_more /* more addrs for coninued-host */ || ( #ifndef DISABLE_TLS ( tls_out.active.sock < 0 && !continue_proxy_cipher @@ -4202,7 +4310,7 @@ if (sx->first_addr != NULL) /* More addresses still to be sent */ - { /* in this run of the transport */ + { /* for this message */ continue_sequence++; /* Causes * in logging */ pipelining_active = sx->pipelining_used; /* was cleared at DATA */ goto SEND_MESSAGE; @@ -4226,6 +4334,7 @@ tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_WAIT); sx->cctx.tls_ctx = NULL; + tls_out.active.sock = -1; smtp_peer_options = smtp_peer_options_wrap; sx->ok = !sx->smtps && smtp_write_command(sx, SCMD_FLUSH, "EHLO %s\r\n", sx->helo_data) @@ -4234,7 +4343,7 @@ '2', ob->command_timeout); if (sx->ok && f.continue_more) - return yield; /* More addresses for another run */ + goto TIDYUP; /* More addresses for another run */ } else { @@ -4254,7 +4363,7 @@ else #endif if (f.continue_more) - return yield; /* More addresses for another run */ + goto TIDYUP; /* More addresses for another run */ /* If the socket is successfully passed, we mustn't send QUIT (or indeed anything!) from here. */ @@ -4294,7 +4403,7 @@ sx->cctx.sock = -1; continue_transport = NULL; continue_hostname = NULL; - return yield; + goto TIDYUP; } log_write(0, LOG_PANIC_DIE, "fork failed"); } @@ -4369,8 +4478,37 @@ (void) event_raise(tblock->event_action, US"tcp:close", NULL); #endif +#ifdef SUPPORT_DANE +if (dane_held) + { + sx->first_addr = NULL; + for (address_item * a = sx->addrlist->next; a; a = a->next) + if (a->transport_return == DANE) + { + a->transport_return = PENDING_DEFER; + if (!sx->first_addr) + { + /* Remember the new start-point in the addrlist, for smtp_setup_conn() + to get the domain string for SNI */ + + sx->first_addr = a; + DEBUG(D_transport) debug_printf("DANE: go-around for %s\n", a->domain); + } + } + goto DANE_DOMAINS; + } +#endif + continue_transport = NULL; continue_hostname = NULL; +return yield; + +TIDYUP: +#ifdef SUPPORT_DANE +if (dane_held) for (address_item * a = sx->addrlist->next; a; a = a->next) + if (a->transport_return == DANE) + a->transport_return = PENDING_DEFER; +#endif return yield; } diff -urN ../exim-4.94.orig/src/transports/smtp.h ./src/transports/smtp.h --- ../exim-4.94.orig/src/transports/smtp.h 2020-05-30 23:35:38.000000000 +0300 +++ ./src/transports/smtp.h 2021-03-16 17:15:23.263458000 +0200 @@ -87,7 +87,7 @@ # ifdef EXPERIMENTAL_TLS_RESUME uschar *tls_resumption_hosts; # endif - uschar *tls_sni; + const uschar *tls_sni; uschar *tls_verify_certificates; int tls_dh_min_bits; BOOL tls_tempfail_tryclear; diff -urN ../exim-4.94.orig/src/transports/smtp_socks.c ./src/transports/smtp_socks.c --- ../exim-4.94.orig/src/transports/smtp_socks.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/transports/smtp_socks.c 2021-03-16 17:15:23.263601000 +0200 @@ -190,7 +190,7 @@ { sd = &proxies[i]; if (!sd->is_failed && sd->priority == pri) - if ((rnd -= sd->weight) <= 0) + if ((rnd -= sd->weight) < 0) return i; } diff -urN ../exim-4.94.orig/src/verify.c ./src/verify.c --- ../exim-4.94.orig/src/verify.c 2020-05-30 23:35:38.000000000 +0300 +++ ./src/verify.c 2021-03-16 17:15:23.267093000 +0200 @@ -674,7 +674,7 @@ if (!sx) sx = store_get(sizeof(*sx), TRUE); /* tainted buffers */ memset(sx, 0, sizeof(*sx)); - sx->addrlist = addr; + sx->addrlist = sx->first_addr = addr; sx->conn_args.host = host; sx->conn_args.host_af = host_af, sx->port = port; @@ -875,12 +875,12 @@ case PENDING_OK: done = TRUE; new_address_record.result = ccache_accept; break; - case FAIL: done = TRUE; + case FAIL: done = TRUE; yield = FAIL; *failure_ptr = US"recipient"; new_address_record.result = ccache_reject; break; - default: break; + default: break; } break; diff -urN ../exim-4.94.orig/src/version.h ./src/version.h --- ../exim-4.94.orig/src/version.h 2020-06-01 17:32:25.000000000 +0300 +++ ./src/version.h 2021-03-16 17:15:24.117397000 +0200 @@ -1,5 +1,5 @@ /* automatically generated file - see ../scripts/reversion */ -#define EXIM_RELEASE_VERSION "4.94" +#define EXIM_RELEASE_VERSION "4.94-62-037b68890" #ifdef EXIM_VARIANT_VERSION #define EXIM_VERSION_STR EXIM_RELEASE_VERSION "-" EXIM_VARIANT_VERSION #else diff -urN ../exim-4.94.orig/src/version.sh ./src/version.sh --- ../exim-4.94.orig/src/version.sh 2020-06-01 17:32:25.000000000 +0300 +++ ./src/version.sh 2021-03-16 17:15:24.117029000 +0200 @@ -1,3 +1,3 @@ # automatically generated file - see ../scripts/reversion -EXIM_RELEASE_VERSION="4.94" +EXIM_RELEASE_VERSION="4.94-62-037b68890" EXIM_COMPILE_NUMBER="1"