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
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
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
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
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"
&%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.
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
-----------------
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 ? "|" : " ", "\\__");
}
+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 *
*************************************************/
if (cb->flags & MCS_AT_SPECIAL && pattern[0] == '@')
{
+ if (is_tainted_metadata(pattern))
+ return DEFER;
+
if (pattern[1] == 0)
{
pattern = primary_hostname;
{
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;
{
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;
else if (type == MCL_HOST && *ss == '+')
{
+ if (is_tainted_metadata(ss)) goto BAD_TAINT;
if (Ustrcmp(ss, "+include_unknown") == 0)
{
include_unknown = TRUE;
}
/* 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 == '!')
{
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\"",
/* Something deferred */
+BAD_TAINT:
DEFER_RETURN:
HDEBUG(D_any)
{
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)
{
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
.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
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>)↩
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░{↩
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>)↩
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░{↩
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@cioce.test.again.dns> rejected RCPT <a@b>
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:
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
├─────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>)↩
├─────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░{↩
├─────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>)↩
├─────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░{↩
├─────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>)↩
├─────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░{↩
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
├─────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>)↩
├─────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░{↩
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
├─────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>)↩
├─────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░{↩
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
├─────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>)↩
├─────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░{↩