commit 173bd1c8f9cf83ad8c0e61a9e32678e7e371d41d
Author: Jeremy Harris <jgh146exb@wizmail.org>
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 <jgh146exb@wizmail.org>
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 <boutilpj@ednet.ns.ca>
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 <jgh146exb@wizmail.org>
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 <jgh146exb@wizmail.org>
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 <jgh146exb@wizmail.org>
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 <jgh146exb@wizmail.org>
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 <jgh146exb@wizmail.org>
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 <jgh146exb@wizmail.org>
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 <jgh146exb@wizmail.org>
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 <jgh146exb@wizmail.org>
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 <jgh146exb@wizmail.org>
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 <jgh146exb@wizmail.org>
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 <jgh146exb@wizmail.org>
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 <jgh146exb@wizmail.org>
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 <jgh146exb@wizmail.org>
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 <jgh146exb@wizmail.org>
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 <guillaume-exim@outters.eu>
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 <jgh146exb@wizmail.org>
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 <jgh146exb@wizmail.org>
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) <hs@schlittermann.de>
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) <hs@schlittermann.de>
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
