Debian upgrade to exim4 4.94 brings new headache: tainted variables.

I have several virtual domains handled by my exim and hence I use variable expansion to obtain DKIM domain, selector and key. In my 000_localmacros is:

DKIM_CANON = relaxed
# Get the domain from the outgoing mail.
DKIM_DOMAIN = ${sg{${lc:${domain_data:$h_from:}}}{^www\.}{}}

DKIM_SELECTOR = ${lookup{DKIM_DOMAIN}lsearch{/etc/exim4/dkim/selector.map}}

# The file is based on the outgoing domain-name in the from-header.
DKIM_FILE = /etc/exim4/dkim/DKIM_DOMAIN/DKIM_SELECTOR/private.key

# If key exists then use it, if not don't.

(these variables are then used in Debian's default files transport/30_exim4-config_remote_smtp).

Sadly this generates:

2021-09-10 07:13:27.625 [28932] 1mOYqk-0007WN-8p Warning: Tainted filename '/etc/exim4/dkim/example.com/n201711/private.key'

Note that I did replace $domain already with $domain_data as suggested by the documentation but it does not help.

How can I fix this so I can remove my allow_insecure_tainted_data = yes statement?

2 Answers 2


Love your opening line.

I had a similar problem, I was using the following macro in my main config section:

DKIM_DOMAIN = ${lc:${domain_data:$h_from:}}
DKIM_SELECTOR = ${lookup{$dkim_domain} lsearch {/etc/exim4/dkim/tags} {$value} {defaultdkimtag}}

Along with a /etc/exim4/dkim/tags file that looks like:

mydomain.org: sometag
myotherdomain.org: 12345

But the DKIM_DOMAIN result is 'tainted' because $h_from is set by the (potential) attacker. To untaint it you have to replace it with a known-safe lookup.

The way I did it was:

DKIM_DOMAIN = ${lookup{${lc:${domain:$h_from:}}} lsearch,ret=key {/etc/exim4/dkim/tags}}
  • From the (tainted) $h_from...
  • Extract the domain using ${domain:...}. This will still be tainted.
  • Force it to lowercase ${lc:...}. Just 'cos. Still tainted
  • Look up the lowercased tainted domain using lsearch in the tags file. Here I use the ret=key option, which means that the returned value from the lookup will be "replaced with a non-tainted version of the lookup key"
  • So it is now untainted, and therefore does not taint subsequent use in other macros etc.

Your config was

DKIM_DOMAIN = ${sg{${lc:${domain_data:$h_from:}}}{^www\.}{}}

It looks like ${domain_data:$h_from:} is wrong - ${domain_data:...} is not listed as a string expansion. $domain_data is a valid variable, but that's not what you're trying to do there - I think, like me, you're trying to extract the domain from $h_from. So here I think you should be using ${domain:$h_from:} (I'm not sure what the colon at the end is, either, but I'll leave that to you)

So then you'd have:

DKIM_DOMAIN = ${sg{${lc:${domain:$h_from:}}}{^www\.}{}}

But that's still going to be tainted because you're allowing any externally provided value to be used in a filepath.

So I would suggest the following solution:

FROM_DOMAIN_WITHOUT_WWW = ${sg{${lc:${domain:$h_from:}}}{^www\.}{}}
DKIM_DOMAIN = ${lookup{FROM_DOMAIN_WITHOUT_WWW} lsearch,ret=key {/etc/exim4/dkim/selector.map}}

Or, if you don't like the extra macro:

DKIM_DOMAIN = ${lookup{${sg{${lc:${domain:$h_from:}}}{^www\.}{}}} lsearch,ret=key {/etc/exim4/dkim/selector.map}}

I have found a solution but I am not sure if it works with nested directories.

I just changed the DKIM_PRIVATE_KEY to:

DKIM_PRIVATE_KEY = ${lookup {DKIM_SELECTOR.DKIM_DOMAIN.key} dsearch,ret=full {/etc/exim4/dkim}}

and instead store the keys in a flat directory as selector.domainname.key.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .