commit 173bd1c8f9cf83ad8c0e61a9e32678e7e371d41d Author: Jeremy Harris Date: Tue Jun 2 14:50:31 2020 +0100 Taint: fix pam expansion condition. Bug 2587 (cherry picked from commit f7f933a199be8bb7362c715e0040545b514cddca) diff --git a/src/src/auths/call_pam.c b/src/src/auths/call_pam.c index 2959cbbf3..80bb23ec3 100644 --- a/src/src/auths/call_pam.c +++ b/src/src/auths/call_pam.c @@ -83,8 +83,7 @@ for (int i = 0; i < num_msg; i++) { 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 @@ pam_arg_ended = FALSE; 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 */ commit 63652bbaf66c4bdb388b08fdf3eb8ab1e4d91475 Author: Jeremy Harris Date: Tue Jun 2 15:03:36 2020 +0100 Taint: fix listcount expansion operator. Bug 2586 (cherry picked from commit 44644c2e404a3ea0191db0b0458e86924fb240bb) diff --git a/src/src/expand.c b/src/src/expand.c index 26f7f10ac..6ed22c14d 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -7208,9 +7208,8 @@ while (*s != 0) { 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; } commit aabe0ebe82297d7dec3abdfff9c3b1edc34fd8ab Author: Patrick Boutilier Date: Tue Jun 2 15:16:10 2020 +0100 Docs: fix mistaken variable name (cherry picked from commit eb55cb1d2c5552209e24345e9d21f83ec1eaccf6) diff --git a/src/README.UPDATING b/src/README.UPDATING index a0afa8df0..708027f2c 100644 --- a/src/README.UPDATING +++ b/src/README.UPDATING @@ -31,9 +31,9 @@ Exim version 4.94 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 commit 623f07cfdcaca96274ca765d0fcf0761bdf7151b Author: Jeremy Harris Date: Wed Jun 3 11:40:17 2020 +0100 Taint: fix multiple ACL actions to properly manage tainted argument data (cherry picked from commit 12b7f811de4a540d0724585aecfa33b5881e2a30) diff --git a/src/src/acl.c b/src/src/acl.c index c1d60bbd9..8619cd5ef 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -3349,11 +3349,11 @@ for (; cb; cb = cb->next) { /* 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 */ } @@ -3514,7 +3514,7 @@ for (; cb; cb = cb->next) 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 +3567,7 @@ for (; cb; cb = cb->next) { /* 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 +3672,11 @@ for (; cb; cb = cb->next) { /* 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 */ } commit 0e8319c3edebfec2158fbaa4898af27cb3225c99 Author: Jeremy Harris Date: Thu Jun 4 15:28:15 2020 +0100 Fix -bi. Bug 2590 Actual fix from pierre.labastie@neuf.fr ; additional coding and testcase bu jgh Broken-by: bdcc6f2bd5 (Cherry-picked from: 0e0e171628) diff --git a/src/src/exim.c b/src/src/exim.c index a60488e95..6143fe989 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -2148,7 +2148,7 @@ on the second character (the one after '-'), to save some effort. */ 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; commit 701af1005a6effaac5ce249f1c2086dc6c0c2a7f Author: Jeremy Harris Date: Mon Jun 8 13:00:55 2020 +0100 Filters: fix "vacation" in Exim filter. Bug 2593 Broken-by: cfb9cf20cb (4.90) (cherry picked from commit 59eee1bc902f106d20f507ba16f37cb8ab5a5e8d) diff --git a/src/src/transports/autoreply.c b/src/src/transports/autoreply.c index 4c2c08b70..865abbf4f 100644 --- a/src/src/transports/autoreply.c +++ b/src/src/transports/autoreply.c @@ -474,10 +474,10 @@ if (oncelog && *oncelog && to) 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) { commit 3fe5ec41e81831028c992f77a15292872fbbac75 Author: Jeremy Harris Date: Thu Jun 11 20:45:05 2020 +0100 TLS: use RFC 6125 rules for certifucate name checks when CNAMES are present. Bug 2594 (cherry picked from commit 0851a3bbf4667081d47f5d85b6b3a5cb33cbdba6) diff --git a/src/src/host.c b/src/src/host.c index 0e0e0130b..817d4446c 100644 --- a/src/src/host.c +++ b/src/src/host.c @@ -1950,6 +1950,13 @@ BOOL temp_error = FALSE; int af; #endif +#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 some circumstances when the get..byname() function actually calls the DNS. */ @@ -2117,6 +2124,9 @@ for (int i = 1; i <= times; { 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; @@ -2260,6 +2270,13 @@ BOOL v6_find_again = FALSE; 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. */ diff --git a/src/src/structs.h b/src/src/structs.h index c6700d513..206237f04 100644 --- a/src/src/structs.h +++ b/src/src/structs.h @@ -80,14 +80,17 @@ typedef enum {DS_UNK=-1, DS_NO, DS_YES} dnssec_status_t; 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 --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 24114f05e..875c82efa 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -2601,9 +2601,9 @@ if (verify_check_given_host(CUSS &ob->tls_verify_cert_hostnames, host) == OK) { 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", diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index 8c9d8aa69..a62322928 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -372,10 +372,10 @@ typedef struct ocsp_resp { } 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 @@ typedef struct tls_ext_ctx_cb { } 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 @@ if (verify_check_given_host(CUSS &ob->tls_verify_cert_hostnames, host) == OK) { 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); commit 94d719d803caf2c0c902dceeb787795eac11a63b Author: Jeremy Harris Date: Fri Jun 12 00:46:34 2020 +0100 Taint: fix radius expansion condition (cherry picked from commit f91219c114a3d95792d052555664a5a7a3984a8d) diff --git a/src/src/auths/call_radius.c b/src/src/auths/call_radius.c index cc269dcd5..9d10b34c6 100644 --- a/src/src/auths/call_radius.c +++ b/src/src/auths/call_radius.c @@ -96,8 +96,7 @@ int sep = 0; #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); commit c165e95889471bc1a644104dd9a6129c47c56c09 Author: Jeremy Harris Date: Fri Jun 12 20:43:43 2020 +0100 smtp_accept_map_per_host: call search_tidyup in fail path. Bug 2597 (cherry-picked from: d3a538c8fe) diff --git a/src/src/daemon.c b/src/src/daemon.c index 2bed143a1..9d491593f 100644 --- a/src/src/daemon.c +++ b/src/src/daemon.c @@ -336,6 +336,7 @@ if ((max_for_this_host > 0) && 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; } } commit ecf1e77accda6355ebb745a0a03e97ba7eb298b2 Author: Jeremy Harris Date: Sun Jun 14 22:14:11 2020 +0100 Taint: fix verify. Bug 2598 (cherry-picked from 2b60ac1021 and 9eed571fd7) diff --git a/src/src/acl.c b/src/src/acl.c index 8619cd5ef..11d1fd028 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -1767,7 +1767,7 @@ switch(vp->value) /* 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 @@ while ((ss = string_nextinlist(&list, &sep, big_buffer, big_buffer_size))) 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; commit 5c608b75d5bd734ddca41e4468fb22544ef96265 Author: Jeremy Harris Date: Sat Jun 20 00:54:05 2020 +0100 Fix string_copy() macro to not multiple-eval args. Bug 2603 Broken-by: a76d120aed (cherry picked from commit 80c2ec2e47c556daff00c79ee068ce68f25fd264) diff --git a/src/src/functions.h b/src/src/functions.h index 0028deb0d..0050cdeeb 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -767,9 +767,9 @@ string_copy_trc(const uschar * s, const char * func, int line) /* 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__) /************************************************* commit cdee8a5f76cc013de5622112cd04e42d0dcf333b Author: Jeremy Harris Date: Mon Jun 22 17:27:18 2020 +0100 Cutthrough: handle request when a callout-hold is active. Bug 2604 (cherry picked from commit 99bfcf2b678e7bd8125a7eb44409e46549bfc111) diff --git a/src/src/acl.c b/src/src/acl.c index 11d1fd028..62cb68561 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -3264,37 +3264,41 @@ for (; cb; cb = cb->next) 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); } diff --git a/src/src/verify.c b/src/src/verify.c index fba1f6e9e..5f4181de9 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -875,12 +875,12 @@ tls_retry_connection: 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; commit 777ee8ae75277c05fb72cc94f568ba4d2bfe15a6 Author: Jeremy Harris Date: Thu Jun 25 11:16:54 2020 +0100 Lookups: Fix "subdir" filter on a dsearch. (cherry picked from commit e0e21929b7426b9b5bbf5e3747797043801b1151) diff --git a/src/src/lookups/dsearch.c b/src/src/lookups/dsearch.c index 455273fb1..501293ac0 100644 --- a/src/src/lookups/dsearch.c +++ b/src/src/lookups/dsearch.c @@ -125,8 +125,7 @@ if ( Ulstat(filename, &statbuf) >= 0 && 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 @@ if ( Ulstat(filename, &statbuf) >= 0 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; } commit 2be77199fc9009ab796ad2d67eed20d8da4773c7 Author: Jeremy Harris Date: Sun Jun 28 15:24:21 2020 +0100 Sqlite: fix segfault on bad/missing sqlite_dbfile. Bug 2606 (cherry picked from commit 3d0472791a0928963a3f8184fe28479e80d1a47d) diff --git a/src/src/lookups/sqlite.c b/src/src/lookups/sqlite.c index dc4439153..1638ea401 100644 --- a/src/src/lookups/sqlite.c +++ b/src/src/lookups/sqlite.c @@ -24,16 +24,23 @@ sqlite_open(const uschar * filename, uschar ** errmsg) 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; } commit 5f3e2ac9f39db5c8ef5a408929c8a5aba957b20f Author: Jeremy Harris Date: Mon Jun 29 17:26:36 2020 +0100 Taint: fix ACL "spam" condition, to permit tainted name arguments. Cherry-picked from: 62b2ccce05 diff --git a/src/src/spam.c b/src/src/spam.c index 5eff1ad5c..63ced4f65 100644 --- a/src/src/spam.c +++ b/src/src/spam.c @@ -190,7 +190,6 @@ spam(const uschar **listptr) 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 @@ spamd_address_container * sd; 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; commit cafcab0848e35047a93a8920b5105baf55f8e702 Author: Jeremy Harris Date: Tue Jun 30 21:16:42 2020 +0100 Fix message-reception clock usage. Bug 2615 Broken-by: 6906c131d1 (4.94) (cherry picked from commit c9bce82e3064126be34d85280d0a7fbf65b3abec) diff --git a/src/src/exim.c b/src/src/exim.c index 6143fe989..dde991065 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -384,14 +384,20 @@ return 0; *************************************************/ #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 @@ offset_ts.tv_nsec += 1000*1000*1000; #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,28 +457,9 @@ exim_wait_tick(struct timeval * tgt_tv, int resolution) struct timeval now_tv; long int now_true_usec; -#ifdef _POSIX_MONOTONIC_CLOCK -struct timespec now_ts; - -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; - } +exim_gettime(&now_tv); +now_true_usec = now_tv.tv_usec; +now_tv.tv_usec = (now_true_usec/resolution) * resolution; while (exim_tvcmp(&now_tv, tgt_tv) <= 0) { @@ -1728,7 +1738,7 @@ make quite sure. */ 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(); diff --git a/src/src/functions.h b/src/src/functions.h index 0050cdeeb..8b17531ed 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -234,6 +234,7 @@ extern void msg_event_raise(const uschar *, const address_item *); 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_gettimg(struct timeval *); extern void exim_nullstd(void); extern void exim_setugid(uid_t, gid_t, BOOL, uschar *); extern void exim_underbar_exit(int) NORETURN; diff --git a/src/src/receive.c b/src/src/receive.c index 0fbd35f82..0db897e9e 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -1784,7 +1784,7 @@ if (sender_host_address) dmarc_init(); /* initialize libopendmarc */ 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 commit 436c0d208b49421d3d126f7c284bc3130b50ff14 Author: Jeremy Harris Date: Fri Jul 3 20:35:58 2020 +0100 typoes diff --git a/src/src/functions.h b/src/src/functions.h index 8b17531ed..afe9f1bf4 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -234,7 +234,7 @@ extern void msg_event_raise(const uschar *, const address_item *); 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_gettimg(struct timeval *); +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; commit de99158d2927863a6826c9f5f2c6fa4547326e75 Author: Guillaume Outters Date: Mon Jul 6 22:31:51 2020 +0100 Fix DKIM signing to always ;-terminate. Bug 2295 (cherry picked from commit 65fe780259d0009354b5dfc9a4f1b48ad6513db2) diff --git a/src/src/pdkim/pdkim.c b/src/src/pdkim/pdkim.c index 7712409b5..3a6ca4e91 100644 --- a/src/src/pdkim/pdkim.c +++ b/src/src/pdkim/pdkim.c @@ -1111,14 +1111,14 @@ return string_catn(str, US"\r\n\t", 3); /* * 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 @@ return string_catn(str, US"\r\n\t", 3); * 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,92 +1135,64 @@ static gstring * 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; - } - -l = (pad?1:0) + (intro?Ustrlen(intro):0); +/* If we can fit at least the pad at the end of current line, do it now. +Otherwise, wrap if there is a pad. */ -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) - { - str = string_catn(str, US" ", 1); - *col += 1; - pad = NULL; /* only want this once */ - l--; - } - - if (intro) - { - size_t sl = Ustrlen(intro); - - str = string_catn(str, intro, sl); - *col += sl; - l -= sl; - intro = NULL; /* only want this once */ - } - - if (payload) +if (pad) + if (*col + 1 <= 78) { - 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; + str = string_catn(str, pad, 1); + (*col)++; + pad = NULL; + padded = 1; } + else + str = pdkim_hdr_cont(str, col); - /* the while precondition tells us it didn't fit. */ - str = pdkim_hdr_cont(str, col); - } +/* 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. */ -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; } commit 86ba540670517109809d93cd73716d320a6a0923 Author: Jeremy Harris Date: Thu Jul 9 15:30:55 2020 +0100 Fix taint trap in parse_fix_phrase(). Bug 2617 (cherry picked from commit 3c90bbcdc7cf73298156f7bcd5f5e750e7814e72) diff --git a/src/src/acl.c b/src/src/acl.c index 62cb68561..105b1b473 100644 --- a/src/src/acl.c +++ b/src/src/acl.c @@ -3199,8 +3199,7 @@ for (; cb; cb = cb->next) { 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; diff --git a/src/src/exim.c b/src/src/exim.c index dde991065..ac0ff5523 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -4769,8 +4769,7 @@ if (originator_login == NULL || f.running_in_test_harness) /* 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 --git a/src/src/expand.c b/src/src/expand.c index 6ed22c14d..791222324 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -7571,13 +7571,10 @@ while (*s != 0) 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 --git a/src/src/functions.h b/src/src/functions.h index afe9f1bf4..f4d1622dc 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -366,9 +366,9 @@ extern int parse_forward_list(uschar *, int, address_item **, uschar **, 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 diff --git a/src/src/parse.c b/src/src/parse.c index e3b471f1a..3ea758ac9 100644 --- a/src/src/parse.c +++ b/src/src/parse.c @@ -843,8 +843,7 @@ return NULL; /* 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 @@ Arguments: 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 @@ August 2000: Additional code added: 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 @@ encoding for the whole thing. */ 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 @@ while (s < end) } *t = 0; +store_release_above(t+1); return yield; } @@ -2102,7 +2077,6 @@ int main(void) { int start, end, domain; uschar buffer[1024]; -uschar outbuff[1024]; big_buffer = store_malloc(big_buffer_size); @@ -2115,8 +2089,7 @@ while (Ufgets(buffer, sizeof(buffer), stdin) != NULL) { 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 --git a/src/src/rewrite.c b/src/src/rewrite.c index f942bec05..7bff8a273 100644 --- a/src/src/rewrite.c +++ b/src/src/rewrite.c @@ -292,16 +292,11 @@ for (rewrite_rule * rule = rewrite_rules; 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 --git a/src/src/sieve.c b/src/src/sieve.c index 18aa3e609..2038f336b 100644 --- a/src/src/sieve.c +++ b/src/src/sieve.c @@ -3087,11 +3087,8 @@ while (*filter->pc) 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 @@ while (*filter->pc) 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 @@ while (*filter->pc) if (exec) { address_item *addr; - uschar *buffer; - int buffer_capacity; md5 base; uschar digest[16]; uschar hexdigest[33]; @@ -3342,11 +3334,8 @@ while (*filter->pc) 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; commit 9681a2140b43cfc028e61b4e7ffb13539cecffe5 Author: Jeremy Harris Date: Mon Jul 13 13:46:14 2020 +0100 Taint: fix ACL "spam" condition, to permit tainted name arguments Follow-on from: 62b2ccce05 (cherry picked from commit 532800c8bf0e4bc2c27739477e70e0d7eef7df21) diff --git a/src/src/spam.c b/src/src/spam.c index 63ced4f65..340f8b92f 100644 --- a/src/src/spam.c +++ b/src/src/spam.c @@ -18,7 +18,7 @@ uschar spam_score_int_buffer[16]; 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; @@ -393,13 +393,12 @@ if (sd->is_rspamd) } 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) @@ -630,7 +629,7 @@ if (spamd_address_work != spamd_address) 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 commit 81cc39a7f5c17099f93b5c611bde5f58daaab71b Author: Heiko Schlittermann (HS12-RIPE) Date: Thu Jul 16 23:45:55 2020 +0200 Fix debug_print_socket() debug_print_socket() crashed on AF_UNIX sockets diff --git a/src/src/debug.c b/src/src/debug.c index 3a7d6a6f5..acc723a29 100644 --- a/src/src/debug.c +++ b/src/src/debug.c @@ -328,20 +328,21 @@ if (fstat(fd, &s) == 0 && (s.st_mode & S_IFMT) == S_IFSOCK) 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_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; @@ -352,22 +353,25 @@ if (fstat(fd, &s) == 0 && (s.st_mode & S_IFMT) == S_IFSOCK) 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); break; commit 73b748711caf8a4b18dd1c0d7c662b5d57798dfe Author: Heiko Schlittermann (HS12-RIPE) Date: Thu Jul 16 23:53:27 2020 +0200 debug_print_socket(): output formatting diff --git a/src/src/debug.c b/src/src/debug.c index acc723a29..6d6132e39 100644 --- a/src/src/debug.c +++ b/src/src/debug.c @@ -338,7 +338,7 @@ if (fstat(fd, &s) == 0 && (s.st_mode & S_IFMT) == S_IFSOCK) 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)); alen = sizeof(*sinp); @@ -349,7 +349,7 @@ if (fstat(fd, &s) == 0 && (s.st_mode & S_IFMT) == S_IFSOCK) 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)); @@ -373,7 +373,7 @@ if (fstat(fd, &s) == 0 && (s.st_mode & S_IFMT) == S_IFSOCK) 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) @@ -388,7 +388,7 @@ if (fstat(fd, &s) == 0 && (s.st_mode & S_IFMT) == S_IFSOCK) { 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 commit d08a4ba2c5216195c107b123842da17bc1b0559f Author: Jeremy Harris Date: Sun Aug 9 01:38:00 2020 +0100 Fix spelling of local_part_data in docs and debug output (cherry picked from commit ccec2d82e2fda6d764f6cd1a9dd21c4f6285b614) diff --git a/src/src/routers/rf_queue_add.c b/src/src/routers/rf_queue_add.c index 938eee30a..4dab60dfe 100644 --- a/src/src/routers/rf_queue_add.c +++ b/src/src/routers/rf_queue_add.c @@ -98,7 +98,7 @@ DEBUG(D_route) " 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); } commit 18a352ae8a799bb7be3a03b14bbf3ce9e0ba4945 Author: Jeremy Harris Date: Fri Aug 14 13:09:53 2020 +0100 Fix ${readsocket } eol-replacement. Bug 2630 (cherry picked from commit 7f83b348ccf4cd815e9758ab9ca1012e66324e9d) diff --git a/src/src/expand.c b/src/src/expand.c index 791222324..bb9fd79ef 100644 --- a/src/src/expand.c +++ b/src/src/expand.c @@ -4920,7 +4920,7 @@ while (*s != 0) { 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 +5334,14 @@ while (*s != 0) 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 */ diff --git a/src/src/functions.h b/src/src/functions.h index f4d1622dc..51bb17a09 100644 --- a/src/src/functions.h +++ b/src/src/functions.h @@ -529,7 +529,7 @@ extern int string_is_ip_address(const uschar *, int *); #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 diff --git a/src/src/lookups/readsock.c b/src/src/lookups/readsock.c index c2088b7a5..cfc9b4ad8 100644 --- a/src/src/lookups/readsock.c +++ b/src/src/lookups/readsock.c @@ -186,7 +186,9 @@ FILE * fp; 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 @@ if (opts) for (uschar * s; s = string_nextinlist(&opts, &sep, NULL, 0); ) 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 --git a/src/src/macros.h b/src/src/macros.h index a507bbf83..2378773cb 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -41,9 +41,11 @@ manipulate them. */ /* 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 diff --git a/src/src/readconf.c b/src/src/readconf.c index 0d0769c88..948fa2403 100644 --- a/src/src/readconf.c +++ b/src/src/readconf.c @@ -1546,7 +1546,7 @@ if (flags & opt_fn_print) { 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 @@ switch(ol->type & opt_mask) 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 --git a/src/src/string.c b/src/src/string.c index 5acee1b00..f91a6a428 100644 --- a/src/src/string.c +++ b/src/src/string.c @@ -281,17 +281,17 @@ return ch; /* 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 @@ uschar *ss, *tt; 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 @@ if (nonprintcount == 0) return s; /* 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 @@ else 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); } commit d2671b04d025dee3b8311d2d83e0a0342c670f52 Author: Gavan Date: Fri Aug 21 15:46:01 2020 +0100 Taint: fix off-by-one in is_tainted(). Bug 2634 (cherry picked from commit e0ae68c8ee6788508da4989ee0d6fcbaf40c7b97) diff --git a/src/src/store.c b/src/src/store.c index c460ba383..7d08c9804 100644 --- a/src/src/store.c +++ b/src/src/store.c @@ -188,14 +188,14 @@ for (int pool = POOL_TAINT_BASE; pool < nelem(chainbase); pool++) 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; } commit 4970c58b394eb0778542c97e3f86b565f9e3daa7 Author: Jeremy Harris Date: Mon Aug 24 20:14:34 2020 +0100 Build: ifdef guard for EXPERIMENTAL_QUEUEFILE (cherry picked from commit 1f5d0a9551205febf6729c7ee36c27626a76b4a4) diff --git a/src/src/transports/queuefile.c b/src/src/transports/queuefile.c index 21ed3527f..97218548b 100644 --- a/src/src/transports/queuefile.c +++ b/src/src/transports/queuefile.c @@ -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 @@ return FALSE; } #endif /*!MACRO_PREDEF*/ +#endif /*EXPERIMENTAL_QUEUEFILE*/ commit d8e99d6047e709b35eabb1395c2046100d1a1dda Author: Jeremy Harris Date: Wed Aug 19 21:09:04 2020 +0100 DANE: force SNI to use $domain. Bug 2265 Note: this is not a complete fix for the issue (cherry picked from commit 7044dd8fd62e215572ecf5a2c7f1bb9581cf6628) diff --git a/src/src/receive.c b/src/src/receive.c index 0db897e9e..ec90e93cd 100644 --- a/src/src/receive.c +++ b/src/src/receive.c @@ -4004,7 +4004,7 @@ if (LOGGING(tls_certificate_verified) && tls_in.cipher) 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 --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 526164c46..a13f0ed63 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -1811,7 +1811,7 @@ if (LOGGING(tls_certificate_verified) && tls_in.cipher) 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 diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index 875c82efa..a34633390 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -2863,7 +2863,7 @@ DEBUG(D_tls) debug_printf("initialising GnuTLS as a client on fd %d\n", cctx->so /* 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) { diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c index a62322928..054b23d0c 100644 --- a/src/src/tls-openssl.c +++ b/src/src/tls-openssl.c @@ -3197,6 +3197,7 @@ tlsp->tlsa_usage = 0; #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 --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 8492a7f25..28dd8ff24 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -2020,6 +2020,7 @@ if (!continue_hostname) { case OK: sx->conn_args.dane = TRUE; ob->tls_tempfail_tryclear = FALSE; + ob->tls_sni = sx->addrlist->domain; break; case FAIL_FORCED: break; default: set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER, commit a505cf777f90755bce69ab53a899b284a304127b Author: Jeremy Harris Date: Sun Aug 23 15:32:48 2020 +0100 DANE: Fix 2-rcpt message, diff domins case. Bug 2265 (cherry picked from commit 99350dede64ad634300ddf15d0d97a81fd75d330) diff --git a/src/src/debug.c b/src/src/debug.c index 6d6132e39..819e83331 100644 --- a/src/src/debug.c +++ b/src/src/debug.c @@ -30,7 +30,16 @@ const uschar * rc_names[] = { /* Mostly for debug output */ [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", }; diff --git a/src/src/deliver.c b/src/src/deliver.c index 40db50084..f5e28941f 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -460,6 +460,9 @@ TRUE if the lists refer to the same hosts in the same order, except that 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 diff --git a/src/src/macros.h b/src/src/macros.h index 2378773cb..6fd5db94c 100644 --- a/src/src/macros.h +++ b/src/src/macros.h @@ -304,6 +304,7 @@ Use rc_names[] for debug strings. */ #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 */ diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 28dd8ff24..6ca4552a6 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -2019,11 +2019,12 @@ if (!continue_hostname) 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_sni = sx->addrlist->domain; + 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, + default: + set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER, string_sprintf("DANE error: tlsa lookup %s", rc_to_string(rc)), rc, FALSE, &sx->delivery_start); @@ -3430,6 +3431,7 @@ BOOL pass_message = FALSE; uschar *message = NULL; uschar new_message_id[MESSAGE_ID_LENGTH + 1]; smtp_context * sx = store_get(sizeof(*sx), TRUE); /* tainted, for the data buffers */ +BOOL dane_held; suppress_tls = suppress_tls; /* stop compiler warning when no TLS support */ *message_defer = FALSE; @@ -3446,13 +3448,36 @@ sx->conn_args.tblock = tblock; 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 */ +DANE_DOMAINS: +dane_held = FALSE; + +/* 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; + } + +/*XXX*/ +/* 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; + } } /* If there is a filter command specified for this transport, we can now @@ -4203,7 +4228,7 @@ if (sx->completed_addr && sx->ok && sx->send_quit) if (sx->first_addr != NULL) /* More addresses still to be sent */ - { /* in this run of the transport */ + { /* on this connection */ continue_sequence++; /* Causes * in logging */ pipelining_active = sx->pipelining_used; /* was cleared at DATA */ goto SEND_MESSAGE; @@ -4235,7 +4260,7 @@ if (sx->completed_addr && sx->ok && sx->send_quit) '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 { @@ -4255,7 +4280,7 @@ if (sx->completed_addr && sx->ok && sx->send_quit) 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. */ @@ -4295,7 +4320,7 @@ propagate it from the initial sx->cctx.sock = -1; continue_transport = NULL; continue_hostname = NULL; - return yield; + goto TIDYUP; } log_write(0, LOG_PANIC_DIE, "fork failed"); } @@ -4370,9 +4395,35 @@ if (sx->send_quit) (void) event_raise(tblock->event_action, US"tcp:close", NULL); #endif +/*XXX*/ +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; + } + continue_transport = NULL; continue_hostname = NULL; return yield; + +TIDYUP: +if (dane_held) for (address_item * a = sx->addrlist->next; a; a = a->next) + if (a->transport_return == DANE) + a->transport_return = PENDING_DEFER; +return yield; } diff --git a/src/src/verify.c b/src/src/verify.c index 5f4181de9..43343a646 100644 --- a/src/src/verify.c +++ b/src/src/verify.c @@ -674,7 +674,7 @@ coding means skipping this whole loop and doing the append separately. */ 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; commit 7dad62cced33eebd10d13fd1f6cd59696175fa44 Author: Jeremy Harris Date: Sun Aug 23 17:27:30 2020 +0100 Fix non-DANE build (cherry picked from commit 79b19a30d9fc64a7b7f70928cdefe4f51064280b) diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 6ca4552a6..d63379e37 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -2023,8 +2023,7 @@ if (!continue_hostname) ob->tls_sni = sx->first_addr->domain; /* force SNI */ break; case FAIL_FORCED: break; - default: - set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER, + default: set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER, string_sprintf("DANE error: tlsa lookup %s", rc_to_string(rc)), rc, FALSE, &sx->delivery_start); @@ -3431,7 +3430,9 @@ BOOL pass_message = FALSE; uschar *message = NULL; uschar new_message_id[MESSAGE_ID_LENGTH + 1]; smtp_context * sx = store_get(sizeof(*sx), TRUE); /* tainted, for the data buffers */ +#if !defined(DISABLE_TLS) && defined(SUPPORT_DANE) BOOL dane_held; +#endif suppress_tls = suppress_tls; /* stop compiler warning when no TLS support */ *message_defer = FALSE; @@ -3448,8 +3449,10 @@ sx->conn_args.tblock = tblock; gettimeofday(&sx->delivery_start, NULL); sx->sync_addr = sx->first_addr = addrlist; +#if !defined(DISABLE_TLS) && defined(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. */ @@ -3461,7 +3464,7 @@ if ((rc = smtp_setup_conn(sx, suppress_tls)) != OK) goto TIDYUP; } -/*XXX*/ +#if !defined(DISABLE_TLS) && defined(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. */ @@ -3479,6 +3482,7 @@ if (sx->conn_args.dane) 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. */ @@ -4395,7 +4399,7 @@ if (sx->send_quit) (void) event_raise(tblock->event_action, US"tcp:close", NULL); #endif -/*XXX*/ +#if !defined(DISABLE_TLS) && defined(SUPPORT_DANE) if (dane_held) { sx->first_addr = NULL; @@ -4414,15 +4418,18 @@ if (dane_held) } goto DANE_DOMAINS; } +#endif continue_transport = NULL; continue_hostname = NULL; return yield; TIDYUP: +#if !defined(DISABLE_TLS) && defined(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; } commit 569be4bc51fd4806edcf6b3abcf550dbbba90df5 Author: Jeremy Harris Date: Wed Aug 26 23:43:54 2020 +0100 DANE: Fix 2 messages from queue case (cherry picked from commit b6054898ace169a0e5143117397a4f666a5e7283) diff --git a/src/src/deliver.c b/src/src/deliver.c index f5e28941f..8f21c607e 100644 --- a/src/src/deliver.c +++ b/src/src/deliver.c @@ -1195,7 +1195,7 @@ else 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 @@ -4275,6 +4275,10 @@ for (int delivery_count = 0; addr_remote; delivery_count++) } } +/*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, @@ -4353,6 +4357,11 @@ for (int delivery_count = 0; addr_remote; delivery_count++) /************************************************************************/ +/*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 @@ -4499,6 +4508,7 @@ for (int delivery_count = 0; addr_remote; delivery_count++) 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 diff --git a/src/src/exim.c b/src/src/exim.c index ac0ff5523..630ac4038 100644 --- a/src/src/exim.c +++ b/src/src/exim.c @@ -2806,10 +2806,22 @@ on the second character (the one after '-'), to save some effort. */ 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); diff --git a/src/src/globals.c b/src/src/globals.c index fc3086f72..c34ac9ddd 100644 --- a/src/src/globals.c +++ b/src/src/globals.c @@ -729,6 +729,8 @@ uid_t config_uid = 0; 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 --git a/src/src/globals.h b/src/src/globals.h index c80c8532f..a4c1143b7 100644 --- a/src/src/globals.h +++ b/src/src/globals.h @@ -425,6 +425,8 @@ extern uschar *config_main_filename; /* File name actually used */ 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 --git a/src/src/spool_in.c b/src/src/spool_in.c index a0147d5ee..1b4cefdb2 100644 --- a/src/src/spool_in.c +++ b/src/src/spool_in.c @@ -55,7 +55,7 @@ for (int i = 0; i < 2; i++) 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 @@ -367,7 +367,7 @@ for (int n = 0; n < 2; n++) 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 @@ if (f.running_in_test_harness) #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 @@ -715,7 +715,7 @@ host_build_sender_fullhost(); #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 +743,7 @@ if (sscanf(CS big_buffer, "%d", &rcount) != 1 || rcount > 16384) 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 +814,7 @@ for (recipients_count = 0; recipients_count < rcount; recipients_count++) { 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 +829,7 @@ for (recipients_count = 0; recipients_count < rcount; recipients_count++) 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 +842,7 @@ for (recipients_count = 0; recipients_count < rcount; recipients_count++) 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 +878,13 @@ for (recipients_count = 0; recipients_count < rcount; recipients_count++) } #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 +956,7 @@ line count by adding the body linecount to the header linecount. Close the file 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 --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c index a34633390..dafe1be0c 100644 --- a/src/src/tls-gnu.c +++ b/src/src/tls-gnu.c @@ -545,7 +545,10 @@ else /* 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 */ { @@ -2890,6 +2893,7 @@ if (!cipher_list) cipher_list, &state, tlsp, errstr) != OK) return FALSE; + #ifdef MEASURE_TIMING report_time_since(&t0, US"client tls_init (delta)"); #endif diff --git a/src/src/transport.c b/src/src/transport.c index 2d8426f29..b1cda55fd 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -1661,6 +1661,7 @@ DEBUG(D_transport) 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 (connection_max_messages >= 0) local_message_max = connection_max_messages; 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 @@ if (host_record->count > WAIT_NAME_MAX) 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 @@ while (1) 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 @@ while (1) { 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 @@ if (host_length > 0) } 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 @@ void 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,12 @@ if (smtp_peer_options & OPTION_TLS) 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++] = tls_out.dane_verified ? US"-MCr" : US"-MCs"; + argv[i++] = tls_out.sni; + } } else argv[i++] = US"-MCT"; diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index d63379e37..7fc2a48bb 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -1620,8 +1620,8 @@ return FALSE; typedef struct smtp_compare_s { - uschar *current_sender_address; - struct transport_instance *tblock; + uschar * current_sender_address; + struct transport_instance * tblock; } smtp_compare_t; @@ -1991,6 +1991,74 @@ if (sx->smtps) } #endif +#ifdef SUPPORT_DANE +/* If we have a proxied TLS connection, check usability for this message */ + +if (continue_hostname && continue_proxy_cipher) + { + int rc; + const uschar * sni = US""; + + /* Check if the message will be DANE-verified; if so force its SNI */ + + 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; + } + + /* If the SNI 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); + + 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 + { + 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); + tls_out.dane_verified = FALSE; + 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 + + /* 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. */ @@ -3430,7 +3498,7 @@ BOOL pass_message = FALSE; uschar *message = NULL; uschar new_message_id[MESSAGE_ID_LENGTH + 1]; smtp_context * sx = store_get(sizeof(*sx), TRUE); /* tainted, for the data buffers */ -#if !defined(DISABLE_TLS) && defined(SUPPORT_DANE) +#ifdef SUPPORT_DANE BOOL dane_held; #endif @@ -3449,7 +3517,7 @@ sx->conn_args.tblock = tblock; gettimeofday(&sx->delivery_start, NULL); sx->sync_addr = sx->first_addr = addrlist; -#if !defined(DISABLE_TLS) && defined(SUPPORT_DANE) +#ifdef SUPPORT_DANE DANE_DOMAINS: dane_held = FALSE; #endif @@ -3464,7 +3532,7 @@ if ((rc = smtp_setup_conn(sx, suppress_tls)) != OK) goto TIDYUP; } -#if !defined(DISABLE_TLS) && defined(SUPPORT_DANE) +#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. */ @@ -4184,8 +4252,8 @@ if (sx->completed_addr && sx->ok && sx->send_quit) 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 @@ -4232,7 +4300,7 @@ if (sx->completed_addr && sx->ok && sx->send_quit) if (sx->first_addr != NULL) /* More addresses still to be sent */ - { /* on this connection */ + { /* for this message */ continue_sequence++; /* Causes * in logging */ pipelining_active = sx->pipelining_used; /* was cleared at DATA */ goto SEND_MESSAGE; @@ -4256,6 +4324,7 @@ if (sx->completed_addr && sx->ok && sx->send_quit) 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) @@ -4399,7 +4468,7 @@ if (sx->send_quit) (void) event_raise(tblock->event_action, US"tcp:close", NULL); #endif -#if !defined(DISABLE_TLS) && defined(SUPPORT_DANE) +#ifdef SUPPORT_DANE if (dane_held) { sx->first_addr = NULL; @@ -4425,7 +4494,7 @@ continue_hostname = NULL; return yield; TIDYUP: -#if !defined(DISABLE_TLS) && defined(SUPPORT_DANE) +#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; diff --git a/src/src/transports/smtp.h b/src/src/transports/smtp.h index 6e63a002d..213bca1a8 100644 --- a/src/src/transports/smtp.h +++ b/src/src/transports/smtp.h @@ -87,7 +87,7 @@ typedef struct { # 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; commit 6742503035564e057edc7d3eba88b108f7d3d74c Author: Jeremy Harris Date: Wed Aug 26 23:59:28 2020 +0100 Fix non-DANE build (cherry picked from commit 651946cbf8e3849687332049730e5fa23d42b4b7) diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c index 86c3e4127..c4c409677 100644 --- a/src/src/smtp_out.c +++ b/src/src/smtp_out.c @@ -375,7 +375,7 @@ smtp_port_for_connect(host_item * host, int port) { 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 --git a/src/src/transport.c b/src/src/transport.c index b1cda55fd..609fd128c 100644 --- a/src/src/transport.c +++ b/src/src/transport.c @@ -1907,7 +1907,11 @@ if (smtp_peer_options & OPTION_TLS) if (tls_out.sni) { - argv[i++] = tls_out.dane_verified ? US"-MCr" : US"-MCs"; + argv[i++] = +#ifdef SUPPORT_DANE + tls_out.dane_verified ? US"-MCr" : +#endif + US"-MCs"; argv[i++] = tls_out.sni; } } diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c index 7fc2a48bb..77335af09 100644 --- a/src/src/transports/smtp.c +++ b/src/src/transports/smtp.c @@ -1991,7 +1991,6 @@ if (sx->smtps) } #endif -#ifdef SUPPORT_DANE /* If we have a proxied TLS connection, check usability for this message */ if (continue_hostname && continue_proxy_cipher) @@ -1999,8 +1998,10 @@ 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 @@ -2024,15 +2025,17 @@ if (continue_hostname && continue_proxy_cipher) # endif return rc; } +#endif - /* If the SNI required for the new message differs from the existing conn - drop the connection to force a new one. */ + /* 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) { @@ -2040,6 +2043,10 @@ if (continue_hostname && continue_proxy_cipher) 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) @@ -2048,7 +2055,6 @@ if (continue_hostname && continue_proxy_cipher) HDEBUG(D_transport|D_acl|D_v) debug_printf_indent(" SMTP>> QUIT\n"); write(0, "QUIT\r\n", 6); close(0); - tls_out.dane_verified = FALSE; continue_hostname = continue_proxy_cipher = NULL; f.continue_more = FALSE; continue_sequence = 1; /* Unfortunately, this process cannot affect success log @@ -2056,7 +2062,6 @@ if (continue_hostname && continue_proxy_cipher) back through reporting pipe. */ } } -#endif /* Make a connection to the host if this isn't a continued delivery, and handle