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