[exim-cvs] Taint: reject or log more tainted list metadata e…

Top Page
Delete this message
Reply to this message
Author: Exim Git Commits Mailing List
Date:  
To: exim-cvs
Subject: [exim-cvs] Taint: reject or log more tainted list metadata elements
Gitweb: https://git.exim.org/exim.git/commitdiff/982854f86c4acc7779b6b65094ba557a9fcd50d6
Commit:     982854f86c4acc7779b6b65094ba557a9fcd50d6
Parent:     9d66ba85a9646c0b63c54acf69e186f0e785855d
Author:     Jeremy Harris <jgh146exb@???>
AuthorDate: Tue Nov 19 18:30:03 2024 +0000
Committer:  Jeremy Harris <jgh146exb@???>
CommitDate: Tue Nov 19 20:06:08 2024 +0000

    Taint: reject or log more tainted list metadata elements
---
 doc/doc-docbook/spec.xfpt |  27 ++++++++++
 doc/doc-txt/ChangeLog     |  13 ++++-
 src/src/expand.c          |   2 +-
 src/src/match.c           |  38 ++++++++++++--
 src/src/string.c          |   2 +-
 test/confs/2202           |   9 ++--
 test/stderr/0632          |   4 --
 test/stderr/2202          | 126 ++++++++++++++++++++++++----------------------
 test/stderr/5410          |   6 ---
 test/stderr/5420          |   9 ++--
 10 files changed, 147 insertions(+), 89 deletions(-)

diff --git a/doc/doc-docbook/spec.xfpt b/doc/doc-docbook/spec.xfpt
index ae30cb886..c8f765905 100644
--- a/doc/doc-docbook/spec.xfpt
+++ b/doc/doc-docbook/spec.xfpt
@@ -8635,6 +8635,11 @@ domainlist  dom2 = !a.b : *.b
 where &'x.y'& does not match. It's best to avoid negation altogether in
 referenced lists if you can.
 
+.new
+The list item which references a named list (&"+<listname>"&)
+may not be tainted.
+.wen
+
 .cindex "hiding named list values"
 .cindex "named lists" "hiding value of"
 Some named list definitions may contain sensitive data, for example, passwords for
@@ -8739,6 +8744,9 @@ possible to use the same configuration file on several different hosts that
 differ only in their names.
 
 The value for a match will be the primary host name.
+.new
+The pattern may not be tainted.
+.wen
 
 
 .next
@@ -8754,6 +8762,9 @@ In today's Internet, the use of domain literals is controversial;
 see the &%allow_domain_literals%& main option.
 
 The value for a match will be the string &`@[]`&.
+.new
+The pattern may not be tainted.
+.wen
 
 
 .next
@@ -8770,6 +8781,10 @@ local host, and the second only when no primary MX target is the local host,
 but a secondary MX target is. &"Primary"& means an MX record with the lowest
 preference value &-- there may of course be more than one of them.
 
+.new
+The pattern may not be tainted.
+.wen
+
 The MX lookup that takes place when matching a pattern of this type is
 performed with the resolver options for widening names turned off. Thus, for
 example, a single-component domain will &'not'& be expanded by adding the
@@ -9605,6 +9620,12 @@ lower case. However, although independent matches on the domain alone are still
 performed caselessly, regular expressions that match against an entire address
 become case-sensitive after &"+caseful"& has been seen.
 
+.new
+This string may not be tainted.
+To do caseful matching on list elements whic are tainted,
+place them in a named list.
+.wen
+
 
 
 .section "Local part lists" "SECTlocparlis"
@@ -9622,6 +9643,12 @@ matching in the local part list, but not elsewhere in the router. If
 &%caseful_local_part%& is set true in a router, matching in the &%local_parts%&
 option is case-sensitive from the start.
 
+.new
+This string may not be tainted.
+To do caseful matching on list elements whic are tainted,
+place them in a named list.
+.wen
+
 If a local part list is indirected to a file (see section &<<SECTfilnamlis>>&),
 comments are handled in the same way as address lists &-- they are recognized
 only if the # is preceded by white space or the start of the line.
diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog
index c9f7a4375..37cc3b77d 100644
--- a/doc/doc-txt/ChangeLog
+++ b/doc/doc-txt/ChangeLog
@@ -67,7 +67,18 @@ JH/14 Bug 3116: Fix crash in dkim signing.  On kernels supporting immutable
       memory segments, a write was done into one when a constant string was
       configured for a transport's dkim private key.
 
-JH/15 Disallow tainted change-of-separator on lists
+JH/15 Disallow tainted metadata in lists.
+      - Change-of-separator prefixes are handled specially when they are
+      explicit text; only the remainder of the list is expanded. A change-of-
+      separator resulting from expansion will not take effect if tainted.
+      - Elements starting with a plus-sign (named-list inclusion,
+      case-interpretation etc) and (hostlist) @[] (et al) are not handled
+      specially and are still operative at this time - but warnings are logged;
+      if any of these are needed in a list with a tainted element (which taints
+      the entire list at string-expansion time) then a named-list can be used
+      for that element.
+      - Exclamation-marks ("!" signifying negation) are not checked for taint
+      at this time.
 
 Exim version 4.98
 -----------------
diff --git a/src/src/expand.c b/src/src/expand.c
index 052c059e8..3e8f73d89 100644
--- a/src/src/expand.c
+++ b/src/src/expand.c
@@ -4548,7 +4548,7 @@ for (int fill = 11 - Ustrlen(what); fill > 0; fill--)
   debug_printf("%V", "-");
 
 debug_printf("%s: %.*W\n", what, nchar, value);
-if (is_tainted(value))
+if (nchar > 0 && is_tainted(value))
   debug_printf_indent("%V          %V(tainted)\n",
     flags & ESI_SKIPPING ? "|" : " ", "\\__");
 }
diff --git a/src/src/match.c b/src/src/match.c
index 5670388ea..636ccc203 100644
--- a/src/src/match.c
+++ b/src/src/match.c
@@ -36,6 +36,16 @@ typedef struct check_address_block {
 
 
 
+static BOOL
+is_tainted_metadata(const uschar * s)
+{
+/* Not enforcing for now, only logging; will enforce in a future release */
+if (is_tainted(s))
+  log_write(0, LOG_MAIN|LOG_PANIC,
+        "attempt to use tainted list metadata %s", s);
+return FALSE;
+}
+
 /*************************************************
 *           Generalized string match             *
 *************************************************/
@@ -170,6 +180,9 @@ just fall through - the match will fail. */
 
 if (cb->flags & MCS_AT_SPECIAL && pattern[0] == '@')
   {
+  if (is_tainted_metadata(pattern))
+    return DEFER;
+
   if (pattern[1] == 0)
     {
     pattern = primary_hostname;
@@ -572,10 +585,12 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0)))
     {
     if (Ustrcmp(ss, "+caseful") == 0)
       {
-      check_address_block *cb = (check_address_block *)arg;
-      uschar *at = Ustrrchr(cb->origaddress, '@');
+      check_address_block * cb = (check_address_block *)arg;
+      uschar * at;
+
+      if (is_tainted_metadata(ss)) goto BAD_TAINT;
 
-      if (at)
+      if ((at = Ustrrchr(cb->origaddress, '@')))
         Ustrncpy(cb->address, cb->origaddress, at - cb->origaddress);
       cb->flags &= ~MCS_CASELESS;
       continue;
@@ -588,7 +603,8 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0)))
     {
     if (Ustrcmp(ss, "+caseful") == 0)
       {
-      check_string_block *cb = (check_string_block *)arg;
+      check_string_block * cb = (check_string_block *)arg;
+      if (is_tainted_metadata(ss)) goto BAD_TAINT;
       Ustrcpy(US cb->subject, cb->origsubject);
       cb->flags &= ~MCS_CASELESS;
       continue;
@@ -601,6 +617,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0)))
 
   else if (type == MCL_HOST && *ss == '+')
     {
+    if (is_tainted_metadata(ss)) goto BAD_TAINT;
     if (Ustrcmp(ss, "+include_unknown") == 0)
       {
       include_unknown = TRUE;
@@ -628,7 +645,16 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0)))
     }
 
   /* Starting with ! specifies a negative item. It is theoretically possible
-  for a local part to start with !. In that case, a regex has to be used. */
+  for a local part to start with !. In that case, a regex has to be used.
+
+  XXX It would be good to disallow a tainted ! here, but the sequence
+  "! $tainted_var" is liable to be frequently used, and requiring a
+  named-list as a workaround would mean a lot of churn. Unfortunately,
+  some attacker can feed "!badthing" into a variable that some overworked
+  admin has used in a list...
+  Maybe we could intro another meta prefix char, which does not negate the
+  element match result (but still protects against a ! in $tainted_var) ?
+  Of course, this would still require churn in configs. */
 
   if (*ss == '!')
     {
@@ -775,6 +801,7 @@ while ((sss = string_nextinlist(&list, &sep, NULL, 0)))
     HDEBUG(D_lists)
       { debug_printf_indent(" start sublist %s\n", ss+1); expand_level += 2; }
 
+    if (is_tainted_metadata(ss)) goto BAD_TAINT;
     if (!(t = tree_search(*anchorptr, ss+1)))
       {
       log_write(0, LOG_MAIN|LOG_PANIC, "unknown named%s list \"%s\"",
@@ -963,6 +990,7 @@ return yield == OK ? FAIL : OK;
  
 /* Something deferred */
 
+BAD_TAINT:
 DEFER_RETURN:
   HDEBUG(D_any)
     {
diff --git a/src/src/string.c b/src/src/string.c
index b370cfacc..4c582c65d 100644
--- a/src/src/string.c
+++ b/src/src/string.c
@@ -952,7 +952,7 @@ if (!*s) return NULL;
 sep_is_special = iscntrl(sep);
 
 /* Handle the case when a buffer is provided. */
-/*XXX need to also deal with qouted-requirements mismatch */
+/*XXX need to also deal with quoted-requirements mismatch */
 
 if (buffer)
   {
diff --git a/test/confs/2202 b/test/confs/2202
index 64c638d9a..7a1ddcbc8 100644
--- a/test/confs/2202
+++ b/test/confs/2202
@@ -10,6 +10,10 @@ acl_smtp_vrfy = vrfy
 acl_smtp_rcpt = rcpt
 disable_ipv6
 
+# need to use this sublist due to taint
+hostlist goodhosts = *.$sender_address_domain : $sender_address_domain : \
+              ${lookup dnsdb{>:defer_never,mxh=$sender_address_domain}}
+
 .ifdef DNS_RECURSE
 hosts_treat_as_local =        test.again.dns
 domainlist try_again_dns_list =    @mx_any
@@ -29,9 +33,6 @@ vrfy:
 .endif
 
 rcpt:
-  accept hosts = +ignore_unknown : \
-    *.$sender_address_domain : \
-    $sender_address_domain : \
-    ${lookup dnsdb{>:defer_never,mxh=$sender_address_domain}}
+  accept hosts = +ignore_unknown : +goodhosts
 
 # End
diff --git a/test/stderr/0632 b/test/stderr/0632
index 8855f4e38..339bca7b6 100644
--- a/test/stderr/0632
+++ b/test/stderr/0632
@@ -377,7 +377,6 @@ p1235   ├───expanded: ░($tls_in_ver)
 p1235   ├─────result:  ◀skipped▶
 p1235   ╰───skipping: result is not used
 p1235  ├───item-res: 
-p1235             ╰──(tainted)
 p1235  ├considering: ${if░def:tls_in_cipher_std░{░tls░$tls_in_cipher_std↩
 p1235  ␉}}(Exim░$version_number)↩
 p1235  ␉${if░def:sender_address░{(envelope-from░<$sender_address>)↩
@@ -414,7 +413,6 @@ p1235   ␉
 p1235   ├─────result:  ◀skipped▶
 p1235   ╰───skipping: result is not used
 p1235  ├───item-res: 
-p1235             ╰──(tainted)
 p1235  ├considering: (Exim░$version_number)↩
 p1235  ␉${if░def:sender_address░{(envelope-from░<$sender_address>)↩
 p1235  ␉}}id░$message_exim_id${if░def:received_for░{↩
@@ -854,7 +852,6 @@ p1236   ├───expanded: ░($tls_in_ver)
 p1236   ├─────result:  ◀skipped▶
 p1236   ╰───skipping: result is not used
 p1236  ├───item-res: 
-p1236             ╰──(tainted)
 p1236  ├considering: ${if░def:tls_in_cipher_std░{░tls░$tls_in_cipher_std↩
 p1236  ␉}}(Exim░$version_number)↩
 p1236  ␉${if░def:sender_address░{(envelope-from░<$sender_address>)↩
@@ -891,7 +888,6 @@ p1236   ␉
 p1236   ├─────result:  ◀skipped▶
 p1236   ╰───skipping: result is not used
 p1236  ├───item-res: 
-p1236             ╰──(tainted)
 p1236  ├considering: (Exim░$version_number)↩
 p1236  ␉${if░def:sender_address░{(envelope-from░<$sender_address>)↩
 p1236  ␉}}id░$message_exim_id${if░def:received_for░{↩
diff --git a/test/stderr/2202 b/test/stderr/2202
index 12b9bbf5d..d6e38f3a8 100644
--- a/test/stderr/2202
+++ b/test/stderr/2202
@@ -38,69 +38,73 @@ log directory space = nnnnnK inodes = nnnnn check_space = 10240K inodes = 100

SMTP>> 250 OK

 SMTP<< rcpt to:<a@b>
 using ACL "rcpt"
-processing ACL rcpt "accept" (TESTSUITE/test-config 35)
-check hosts = +ignore_unknown : *.$sender_address_domain : $sender_address_domain : ${lookup dnsdb{>:defer_never,mxh=$sender_address_domain}}
- search_open: dnsdb "NULL"
- search_find: file="NULL"
-   key=">:defer_never,mxh=cioce.test.again.dns" partial=-1 affix=NULL starflags=0 opts=NULL
- LRU list:
- internal_search_find: file="NULL"
-   type=dnsdb key=">:defer_never,mxh=cioce.test.again.dns" opts=NULL
- database lookup required for >:defer_never,mxh=cioce.test.again.dns
-                              (tainted)
- dnsdb key: cioce.test.again.dns
- DNS lookup of cioce.test.again.dns (MX) using fakens
- DNS lookup of cioce.test.again.dns (MX) gave TRY_AGAIN
- cioce.test.again.dns in dns_again_means_nonexist?
-  list element: *
-  cioce.test.again.dns in dns_again_means_nonexist? yes (matched "*")
- cioce.test.again.dns is in dns_again_means_nonexist: returning DNS_NOMATCH
- DNS: couldn't fake dnsa len
- DNS: no SOA record found for neg-TTL
-  writing neg-cache entry for cioce.test.again.dns-MX-xxxx, ttl -1
- creating new cache entry
- lookup failed
-host in "+ignore_unknown : *.cioce.test.again.dns : cioce.test.again.dns : "?
+processing ACL rcpt "accept" (TESTSUITE/test-config 36)
+check hosts = +ignore_unknown : +goodhosts
+host in "+ignore_unknown : +goodhosts"?
  list element: +ignore_unknown
- list element: *.cioce.test.again.dns
- sender host name required, to match against *.cioce.test.again.dns
-  looking up host name for ip4.ip4.ip4.ip4
-   DNS lookup of ip4-reverse.in-addr.arpa (PTR) using fakens
-   DNS lookup of ip4-reverse.in-addr.arpa (PTR) succeeded
-   Reverse DNS security status: unverified
-   IP address lookup yielded "the.local.host.name"
-   check dnssec require list
-   ╎the.local.host.name not in empty list (option unset? cannot trace name)
-   check dnssec request list
-   ╎the.local.host.name not in empty list (option unset? cannot trace name)
-   DNS lookup of the.local.host.name (A) using fakens
-   DNS lookup of the.local.host.name (A) succeeded
-   local host found for non-MX address
-   the.local.host.name ip4.ip4.ip4.ip4 mx=-1 sort=xx 
-   checking addresses for the.local.host.name
-   Forward DNS security status: unverified
+ list element: +goodhosts
+  start sublist goodhosts
+   ╎search_open: dnsdb "NULL"
+   ╎search_find: file="NULL"
+   ╎  key=">:defer_never,mxh=cioce.test.again.dns" partial=-1 affix=NULL starflags=0 opts=NULL
+   ╎LRU list:
+   ╎internal_search_find: file="NULL"
+   ╎  type=dnsdb key=">:defer_never,mxh=cioce.test.again.dns" opts=NULL
+   ╎database lookup required for >:defer_never,mxh=cioce.test.again.dns
+   ╎                             (tainted)
+   ╎dnsdb key: cioce.test.again.dns
+   ╎DNS lookup of cioce.test.again.dns (MX) using fakens
+   ╎DNS lookup of cioce.test.again.dns (MX) gave TRY_AGAIN
+   ╎cioce.test.again.dns in dns_again_means_nonexist?
+   ╎ list element: *
+   ╎ cioce.test.again.dns in dns_again_means_nonexist? yes (matched "*")
+   ╎cioce.test.again.dns is in dns_again_means_nonexist: returning DNS_NOMATCH
+   ╎DNS: couldn't fake dnsa len
+   ╎DNS: no SOA record found for neg-TTL
+   ╎ writing neg-cache entry for cioce.test.again.dns-MX-xxxx, ttl -1
+   ╎creating new cache entry
+   ╎lookup failed
+  host in "*.cioce.test.again.dns : cioce.test.again.dns : "?
+   ╎list element: *.cioce.test.again.dns
+   ╎sender host name required, to match against *.cioce.test.again.dns
+   ╎ looking up host name for ip4.ip4.ip4.ip4
+   ╎  DNS lookup of ip4-reverse.in-addr.arpa (PTR) using fakens
+   ╎  DNS lookup of ip4-reverse.in-addr.arpa (PTR) succeeded
+   ╎  Reverse DNS security status: unverified
+   ╎  IP address lookup yielded "the.local.host.name"
+   ╎  check dnssec require list
+   ╎   the.local.host.name not in empty list (option unset? cannot trace name)
+   ╎  check dnssec request list
+   ╎   the.local.host.name not in empty list (option unset? cannot trace name)
+   ╎  DNS lookup of the.local.host.name (A) using fakens
+   ╎  DNS lookup of the.local.host.name (A) succeeded
+   ╎  local host found for non-MX address
+   ╎  the.local.host.name ip4.ip4.ip4.ip4 mx=-1 sort=-151 
+   ╎  checking addresses for the.local.host.name
+   ╎  Forward DNS security status: unverified
   ip4.ip4.ip4.ip4 OK
- sender_fullhost = the.local.host.name (test) [ip4.ip4.ip4.ip4]
- sender_rcvhost = the.local.host.name ([ip4.ip4.ip4.ip4] helo=test)
- list element: cioce.test.again.dns
- using host_fake_gethostbyname for cioce.test.again.dns (IPv4)
- DNS lookup of cioce.test.again.dns (A) using fakens
- DNS lookup of cioce.test.again.dns (A) gave TRY_AGAIN
- cioce.test.again.dns in dns_again_means_nonexist?
-  list element: *
-  cioce.test.again.dns in dns_again_means_nonexist? yes (matched "*")
- cioce.test.again.dns is in dns_again_means_nonexist: returning DNS_NOMATCH
- DNS: couldn't fake dnsa len
- DNS: no SOA record found for neg-TTL
-  writing neg-cache entry for cioce.test.again.dns-A-xxxx, ttl -1
- host_fake_gethostbyname(af=inet) returned 1 (HOST_NOT_FOUND)
- no IP address found for host cioce.test.again.dns (during SMTP connection from the.local.host.name (test) [ip4.ip4.ip4.ip4])
+   ╎sender_fullhost = the.local.host.name (test) [ip4.ip4.ip4.ip4]
+   ╎sender_rcvhost = the.local.host.name ([ip4.ip4.ip4.ip4] helo=test)
+   ╎list element: cioce.test.again.dns
+   ╎using host_fake_gethostbyname for cioce.test.again.dns (IPv4)
+   ╎DNS lookup of cioce.test.again.dns (A) using fakens
+   ╎DNS lookup of cioce.test.again.dns (A) gave TRY_AGAIN
+   ╎cioce.test.again.dns in dns_again_means_nonexist?
+   ╎ list element: *
+   ╎ cioce.test.again.dns in dns_again_means_nonexist? yes (matched "*")
+   ╎cioce.test.again.dns is in dns_again_means_nonexist: returning DNS_NOMATCH
+   ╎DNS: couldn't fake dnsa len
+   ╎DNS: no SOA record found for neg-TTL
+   ╎ writing neg-cache entry for cioce.test.again.dns-A-xxxx, ttl -1
+   ╎host_fake_gethostbyname(af=inet) returned 1 (HOST_NOT_FOUND)
+   ╎no IP address found for host cioce.test.again.dns (during SMTP connection from the.local.host.name (test) [ip4.ip4.ip4.ip4])
 LOG: host_lookup_failed MAIN
   no IP address found for host cioce.test.again.dns (during SMTP connection from the.local.host.name (test) [ip4.ip4.ip4.ip4])
- failed to find IP address for cioce.test.again.dns: item ignored by +ignore_unknown
-host in "+ignore_unknown : *.cioce.test.again.dns : cioce.test.again.dns : "? no (end of list)
-accept: condition test failed in ACL rcpt
-end of ACL rcpt: implicit DENY
+   ╎host in "*.cioce.test.again.dns : cioce.test.again.dns : "? no (failed to find IP address for cioce.test.again.dns)
+   end sublist goodhosts
+  host in "+ignore_unknown : +goodhosts"? no (end of list)
+ accept: condition test failed in ACL rcpt
+ end of ACL rcpt: implicit DENY

SMTP>> 550 Administrative prohibition

LOG: MAIN REJECT
H=the.local.host.name (test) [ip4.ip4.ip4.ip4] F=<xx@???> rejected RCPT <a@b>
@@ -108,7 +112,7 @@ SMTP<< quit
SMTP>> 221 myhost.test.ex closing connection

LOG: smtp_connection MAIN
SMTP connection from the.local.host.name (test) [ip4.ip4.ip4.ip4] D=qqs closed by QUIT
-search_tidyup called
+ search_tidyup called
>>>>>>>>>>>>>>>> Exim pid=p1234 (fresh-exec) terminating with rc=0 >>>>>>>>>>>>>>>>

 Exim version x.yz ....
 Hints DB:
@@ -140,7 +144,7 @@ host in smtp_accept_max_nonmail_hosts?
  list element: *
   host in smtp_accept_max_nonmail_hosts? yes (matched "*")
 using ACL "vrfy"
-processing ACL vrfy "warn" (TESTSUITE/test-config 28)
+processing ACL vrfy "warn" (TESTSUITE/test-config 32)
 check domains = +try_again_dns_list
 test.again.dns in "+try_again_dns_list"?
  list element: +try_again_dns_list
diff --git a/test/stderr/5410 b/test/stderr/5410
index dce73fa67..bd1e5d26f 100644
--- a/test/stderr/5410
+++ b/test/stderr/5410
@@ -531,7 +531,6 @@ try option received_header_text
   ├─────result:  ◀skipped▶
   ╰───skipping: result is not used
  ├───item-res: 
-            ╰──(tainted)
  ├considering: ${if░def:tls_in_cipher_std░{░tls░$tls_in_cipher_std↩
  ␉}}(Exim░$version_number)↩
  ␉${if░def:sender_address░{(envelope-from░<$sender_address>)↩
@@ -568,7 +567,6 @@ try option received_header_text
   ├─────result:  ◀skipped▶
   ╰───skipping: result is not used
  ├───item-res: 
-            ╰──(tainted)
  ├considering: (Exim░$version_number)↩
  ␉${if░def:sender_address░{(envelope-from░<$sender_address>)↩
  ␉}}id░$message_exim_id${if░def:received_for░{↩
@@ -1158,7 +1156,6 @@ try option received_header_text
   ├─────result:  ◀skipped▶
   ╰───skipping: result is not used
  ├───item-res: 
-            ╰──(tainted)
  ├considering: ${if░def:tls_in_cipher_std░{░tls░$tls_in_cipher_std↩
  ␉}}(Exim░$version_number)↩
  ␉${if░def:sender_address░{(envelope-from░<$sender_address>)↩
@@ -1195,7 +1192,6 @@ try option received_header_text
   ├─────result:  ◀skipped▶
   ╰───skipping: result is not used
  ├───item-res: 
-            ╰──(tainted)
  ├considering: (Exim░$version_number)↩
  ␉${if░def:sender_address░{(envelope-from░<$sender_address>)↩
  ␉}}id░$message_exim_id${if░def:received_for░{↩
@@ -1785,7 +1781,6 @@ try option received_header_text
   ├─────result:  ◀skipped▶
   ╰───skipping: result is not used
  ├───item-res: 
-            ╰──(tainted)
  ├considering: ${if░def:tls_in_cipher_std░{░tls░$tls_in_cipher_std↩
  ␉}}(Exim░$version_number)↩
  ␉${if░def:sender_address░{(envelope-from░<$sender_address>)↩
@@ -1822,7 +1817,6 @@ try option received_header_text
   ├─────result:  ◀skipped▶
   ╰───skipping: result is not used
  ├───item-res: 
-            ╰──(tainted)
  ├considering: (Exim░$version_number)↩
  ␉${if░def:sender_address░{(envelope-from░<$sender_address>)↩
  ␉}}id░$message_exim_id${if░def:received_for░{↩
diff --git a/test/stderr/5420 b/test/stderr/5420
index b96d91def..6fad569ad 100644
--- a/test/stderr/5420
+++ b/test/stderr/5420
@@ -31,6 +31,7 @@ try option acl_smtp_helo
  list element: *
   in limits_advertise_hosts? yes (matched "*")
  in dsn_advertise_hosts? no (option unset)
+try option acl_smtp_atrn
 try option acl_smtp_etrn
 try option acl_smtp_vrfy
 try option acl_smtp_expn
@@ -530,7 +531,6 @@ try option received_header_text
   ├─────result:  ◀skipped▶
   ╰───skipping: result is not used
  ├───item-res: 
-            ╰──(tainted)
  ├considering: ${if░def:tls_in_cipher_std░{░tls░$tls_in_cipher_std↩
  ␉}}(Exim░$version_number)↩
  ␉${if░def:sender_address░{(envelope-from░<$sender_address>)↩
@@ -567,7 +567,6 @@ try option received_header_text
   ├─────result:  ◀skipped▶
   ╰───skipping: result is not used
  ├───item-res: 
-            ╰──(tainted)
  ├considering: (Exim░$version_number)↩
  ␉${if░def:sender_address░{(envelope-from░<$sender_address>)↩
  ␉}}id░$message_exim_id${if░def:received_for░{↩
@@ -708,6 +707,7 @@ try option acl_smtp_helo
  list element: *
   in limits_advertise_hosts? yes (matched "*")
  in dsn_advertise_hosts? no (option unset)
+try option acl_smtp_atrn
 try option acl_smtp_etrn
 try option acl_smtp_vrfy
 try option acl_smtp_expn
@@ -1156,7 +1156,6 @@ try option received_header_text
   ├─────result:  ◀skipped▶
   ╰───skipping: result is not used
  ├───item-res: 
-            ╰──(tainted)
  ├considering: ${if░def:tls_in_cipher_std░{░tls░$tls_in_cipher_std↩
  ␉}}(Exim░$version_number)↩
  ␉${if░def:sender_address░{(envelope-from░<$sender_address>)↩
@@ -1193,7 +1192,6 @@ try option received_header_text
   ├─────result:  ◀skipped▶
   ╰───skipping: result is not used
  ├───item-res: 
-            ╰──(tainted)
  ├considering: (Exim░$version_number)↩
  ␉${if░def:sender_address░{(envelope-from░<$sender_address>)↩
  ␉}}id░$message_exim_id${if░def:received_for░{↩
@@ -1334,6 +1332,7 @@ try option acl_smtp_helo
  list element: *
   in limits_advertise_hosts? yes (matched "*")
  in dsn_advertise_hosts? no (option unset)
+try option acl_smtp_atrn
 try option acl_smtp_etrn
 try option acl_smtp_vrfy
 try option acl_smtp_expn
@@ -1782,7 +1781,6 @@ try option received_header_text
   ├─────result:  ◀skipped▶
   ╰───skipping: result is not used
  ├───item-res: 
-            ╰──(tainted)
  ├considering: ${if░def:tls_in_cipher_std░{░tls░$tls_in_cipher_std↩
  ␉}}(Exim░$version_number)↩
  ␉${if░def:sender_address░{(envelope-from░<$sender_address>)↩
@@ -1819,7 +1817,6 @@ try option received_header_text
   ├─────result:  ◀skipped▶
   ╰───skipping: result is not used
  ├───item-res: 
-            ╰──(tainted)
  ├considering: (Exim░$version_number)↩
  ␉${if░def:sender_address░{(envelope-from░<$sender_address>)↩
  ␉}}id░$message_exim_id${if░def:received_for░{↩


--
## subscription configuration (requires account):
## https://lists.exim.org/mailman3/postorius/lists/exim-cvs.lists.exim.org/
## unsubscribe (doesn't require an account):
## exim-cvs-unsubscribe@???
## Exim details at http://www.exim.org/
## Please use the Wiki with this list - http://wiki.exim.org/