All,
I haven't put in much of an appearance here for a couple of years, but a
long time ago Peter Bowyer and myself built a mail relay system, its
been running Exim 4.63 for years on Fedora 6 and I've just come to do a
major upgrade on it and replace it with Exim 4.77 and Ubuntu 12.4 LTS
server.
We had experimental DomainKeys on the old system and I've replaced it
with Exim's in-built DKIM and I'm having some trouble getting my head
around the way it works, or more precisely, the way it confuses me...
What I am trying to do:
a) accept messages that are correctly signed
b) reject (deny) messages that are incorrectly signed
c) defer for messages that are signed but we can't verify them for
a transient reason (eg. can't reach their DNS)
d) accept all messages that have no signature
e) maintain sufficient information in the logs so that I can
understand what is going on
What I have:
* Pentuim 4 2.8GHz server, 1Gb RAM, 250Gb HDD
* Ubuntu 12.4 LTS Server (Beta2)
* Exim-4.77 built from source with experimental SPF, SRS, DKIM,
MySQL and a few other bits and pieces
* latest ClamAV, SpamAssassin, etc.
Here is my current config pertaining to the DKIM parts:
# mysql table with things like paypal.com, ebay.com, google.com etc.
SELECT_DKIM_KNOWN_SIGNERS = select domain from dkim_known_signers where
active=1;
DKIM_KNOWN_SIGNERS = ${lookup
mysql{SELECT_DKIM_KNOWN_SIGNERS}{${sg{$value}{\\n}{ : }} }}
# list of signers we should attempt to verify
dkim_verify_signers = $dkim_signers : $sender_address_domain :
DKIM_KNOWN_SIGNERS
acl_check_dkim:
#
# debugging
#
warn logwrite = DKIM TEST: domain=$sender_address_domain
possible_signer=$dkim_cur_signer status=$dkim_verify_status
reason=$dkim_verify_reason
#
# skip DKIM if domain whitelisted for DKIM, i.e. known good
domain that has broken DKIM
#
accept sender_domains = +dkim_whitelist_domains
logwrite = DKIM SKIP: Skipping DKIM checks for
whitelisted domain: $sender_address_domain
#
# skip DKIM checks on hosts we relay for
#
accept hosts = +relay_from_hosts
logwrite = DKIM SKIP: Skipping DKIM checks for relay
host: $sender_fullhost
#
# skip DKIM checks on authenticated hosts (that we also relay for)
#
accept authenticated = *
logwrite = DKIM SKIP: Skipping DKIM checks for
authenticated host: $sender_fullhost
#
# defer when message not testable, e.g. can't get public key, etc.
#
defer dkim_status = invalid
message = Message from $sender_address_domain cannot be
verified
logwrite = DKIM DEFER: domain=$sender_address_domain
#
# deny when message fails signature test
#
deny dkim_status = fail
message = Message from $sender_address_domain has
invalid DKIM signature
logwrite = DKIM DENY: domain=$sender_address_domain
#
# accept the message (correctly signed)
#
accept
sender_domains = $sender_address_domain
dkim_signers = $sender_address_domain
dkim_status = pass
logwrite = DKIM MATCH: domain=$sender_address_domain
signer=$dkim_cur_signer status=$dkim_verify_status
add_header = :after_received:X-DKIM-Result:
domain=$sender_address_domain result=signature ok
#
# accept anything else
#
accept
# debugging
logwrite = DKIM DEFAULT: Accept at end of ACL:
domain=$sender_address_domain
I see some discrepancies between documented and actual behaviour (and
what I expected or would like to see :-)
Exim's documentation (Chapter 54 section 2) states: "Verification of
DKIM signatures in incoming email is implemented via the acl_smtp_dkim
ACL. By default, this ACL is called once for each syntactically(!)
correct signature in the incoming message."
If I leave $dkim_verify_signers set to default ($dkim_signers) and I
receive a signed mail message then I see what I would expect:
2012-04-14 07:18:07 1SIwJD-0000k1-3k DKIM: d=mail.dealcloud.co.uk s=key1
c=relaxed/relaxed a=rsa-sha1 i=deals@??? [verification
succeeded]
2012-04-14 07:18:07 1SIwJD-0000k1-3k DKIM START:
domain=xpressus.emarsys.net signer=mail.dealcloud.co.uk status=pass reason=
2012-04-14 07:18:07 1SIwJD-0000k1-3k DKIM ACCEPT:
domain=xpressus.emarsys.net signer=mail.dealcloud.co.uk status=pass
2012-04-14 07:18:07 1SIwJD-0000k1-3k DKIM START:
domain=xpressus.emarsys.net signer=deals@???
status=pass reason=
2012-04-14 07:18:07 1SIwJD-0000k1-3k DKIM ACCEPT:
domain=xpressus.emarsys.net signer=deals@??? status=pass
the above example appears to have two signatures and the ACL is called
twice, both signers pass and on both occasions the ACL returns accept
before reaching the bottom.
However, if I set $dkim_verify_signers to the compound list
(dkim_verify_signers = $dkim_signers : $sender_address_domain :
DKIM_KNOWN_SIGNERS) then the DKIM ACL gets called even when Exim has not
generated its initial "DKIM: d= s= c= a= i= [status]" message (that
it generates when it detects a signature) - here it is doing it:
2012-04-14 21:08:51 CONNECT: New connection from 194.25.134.18:38422 -> 25
2012-04-14 21:08:53 CONNECT: Accepting connection from: 194.25.134.18 as
not blocked by any DNSRBL
2012-04-14 21:08:53 1SJ9HB-0003S3-CZ DKIM TEST: domain=t-online.de
possible_signer=t-online.de status=none reason=
2012-04-14 21:08:53 1SJ9HB-0003S3-CZ DKIM DEFAULT: Accecpt at end of
ACL: domain=t-online.de
2012-04-14 21:08:53 1SJ9HB-0003S3-CZ DKIM TEST: domain=t-online.de
possible_signer=ebay.com status=none reason=
2012-04-14 21:08:53 1SJ9HB-0003S3-CZ DKIM DEFAULT: Accecpt at end of
ACL: domain=t-online.de
2012-04-14 21:08:53 1SJ9HB-0003S3-CZ DKIM TEST: domain=t-online.de
possible_signer=paypal.com status=none reason=
2012-04-14 21:08:53 1SJ9HB-0003S3-CZ DKIM DEFAULT: Accecpt at end of
ACL: domain=t-online.de
2012-04-14 21:08:53 1SJ9HB-0003S3-CZ DKIM TEST: domain=t-online.de
possible_signer=paypal.co.uk status=none reason=
2012-04-14 21:08:53 1SJ9HB-0003S3-CZ DKIM DEFAULT: Accecpt at end of
ACL: domain=t-online.de
2012-04-14 21:08:53 1SJ9HB-0003S3-CZ DKIM TEST: domain=t-online.de
possible_signer=ebay.co.uk status=none reason=
2012-04-14 21:08:53 1SJ9HB-0003S3-CZ DKIM DEFAULT: Accecpt at end of
ACL: domain=t-online.de
2012-04-14 21:08:53 1SJ9HB-0003S3-CZ DKIM TEST: domain=t-online.de
possible_signer=yahoo.com status=none reason=
2012-04-14 21:08:53 1SJ9HB-0003S3-CZ DKIM DEFAULT: Accecpt at end of
ACL: domain=t-online.de
2012-04-14 21:08:53 1SJ9HB-0003S3-CZ DKIM TEST: domain=t-online.de
possible_signer=yahoo.co.uk status=none reason=
2012-04-14 21:08:53 1SJ9HB-0003S3-CZ DKIM DEFAULT: Accecpt at end of
ACL: domain=t-online.de
2012-04-14 21:08:53 1SJ9HB-0003S3-CZ DKIM TEST: domain=t-online.de
possible_signer=gmail.com status=none reason=
2012-04-14 21:08:53 1SJ9HB-0003S3-CZ DKIM DEFAULT: Accecpt at end of
ACL: domain=t-online.de
2012-04-14 21:08:53 1SJ9HB-0003S3-CZ DKIM TEST: domain=t-online.de
possible_signer=googlemail.com status=none reason=
2012-04-14 21:08:53 1SJ9HB-0003S3-CZ DKIM DEFAULT: Accecpt at end of
ACL: domain=t-online.de
2012-04-14 21:08:53 1SJ9HB-0003S3-CZ DKIM TEST: domain=t-online.de
possible_signer=google.com status=none reason=
2012-04-14 21:08:53 1SJ9HB-0003S3-CZ DKIM DEFAULT: Accecpt at end of
ACL: domain=t-online.de
2012-04-14 21:08:53 1SJ9HB-0003S3-CZ MIME: Checking status: 1
so, it iterates over all of the entries in $dkim_verify_signers and
*not* all of the signatures in the email (since there are none) - is
this not a bug - I can't see the point of the ACL being called for the
contents of $dkim_verify_signers ?
Next, for the testing that I think that I want to do I'm trying to find
the first definite 'accept' (good signature) or 'deny' (bad signature)
and bail out of the ACL checking for the message at that point. I can't
see the point in carrying on calling the ACL when we know we want to
bail out - I think what is needed is a bit like a "no_more" when
routing, for example:
deny dkim_status = fail
message = Message from $sender_address_domain has
invalid DKIM signature
logwrite = DKIM DENY: domain=$sender_address_domain
no_more // return 'deny' and exit the DKIM ACL
altogether
One of the conditions that I'm not sure how we resolve (or even it it
ever happens) is what we do if there are two signatures on an incoming
message and one is good (pass) and one is bad (fail) - but I guess this
is down to the ACL creator ;-)
Mike