Re: [exim] "creative" policy for DKIM checking (57.2)

Top Page
Delete this message
Reply to this message
Author: Michael J. Tubby B.Sc. MIET
Date:  
To: exim-users
Subject: Re: [exim] "creative" policy for DKIM checking (57.2)


On 1/1/2017 8:32 AM, Andrew C Aitchison wrote:
> On Sat, 31 Dec 2016, Ian Zimmerman wrote:
>
>> So, that section of the Spec says:
>>
>> Current DKIM verifiers may want to explicitly call the ACL for known
>> domains or identities. This would be achieved as follows:
>>
>> dkim_verify_signers = paypal.com:ebay.com:$dkim_signers
>>
>> This would result in acl_smtp_dkim always being called for "paypal.com"
>> and "ebay.com", plus all domains and identities that have signatures in
>> the message. You can also be more creative in constructing your
>> policy. For example:
>>
>> dkim_verify_signers = $sender_address_domain:$dkim_signers
>>
>> But I cannot see the point of doing either of these things.
>>
>> Either one of the prepended domains has a valid signature in the
>> message, or none has. One way or the other, the final result of running
>> acl_smtp_dkim for _all_ members of dkim_verify_signers is the same as if
>> nothing were prepended.
>>
>> At least if the only decision I want to make is "one of
>> dkim_verify_signers has a valid sig" versus "any other outcome".
>> If I were to consider invalid and failing sigs things would be
>> different.
>> Is _that_ why this "creative" hook exists?
>
> I think the docs were written before we knew how often things broke DKIM,
> and almost certainly before DMARC.
>
> IIUC the idea was that that writer/postmaster *knows* that paypal and
> ebay
> always sign with dkim, so an unsigned message allegedly from those hosts
> must be spam.
>
>




This is how we do it on our production servers:

1. Each relay host has a local copy of a MySQL database which is
replicated from a central master

2. One of the tables is called "dkim_known_signers" which contains a
list of domains for which we force verification (gmail.com, paypal.com,
et. al)

3. The global section of Exim config contains:

             DKIM_KNOWN_SIGNERS = select domain from dkim_known_signers 
where active=1;
             domainlist dkim_known_signers = ${lookup 
mysql{DKIM_KNOWN_SIGNERS}{${sg{$value}{\\n}{ : }} }}


     as one of the domain lists.


4. We also support whitelisting for "known broken senders"

5. Our DKIM ACL is quite large and not terribly intuiative as there is
some negative-logic in here:


###
### ack_check_dkim: this ACL is used for checking DKIM
###

#
# acl_m2 set to zero on start for normal/full checks, set to 1 if
white-listed
#

acl_check_dkim:

         #
         # start of DKIM debug message and clear macro
         #
         warn    set acl_m2 = 0
                 logwrite = DKIM START: domain=$sender_address_domain 
possible_signer=$dkim_cur_signer status=$dkim_verify_status ${if 
def:dkim_verify_reason {(reason=$dkim_verify_reason) }}


         #
         # strict checking on known signers...
         #
         deny    sender_domains = +dkim_known_signers
#               dkim_signers = +dkim_known_signers
                 dkim_status = none:invalid:fail
                 message = Message from $sender_address_domain (known 
signer) with invalid or missing signature
                 logwrite = DKIM DENY: Rejected $sender_address_domain 
is known signer (in database) but has invalid/missing signature


         accept  sender_domains = +dkim_known_signers
#               dkim_signers = +dkim_known_signers
                 dkim_status = pass
                 logwrite = DKIM PASS: Accepted $sender_address_domain 
is known signer and has good signature
                 add_header = :after_received:X-DKIM-Result: 
Domain=$sender_address_domain Result=Good and Known Domain


         #
         # ignore noise where we have no signature
         #
         accept  dkim_status = none
#               logwrite = DKIM SKIP: Skipping DKIM checks - no 
signature for: $dkim_cur_signer


         #
         # 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
                 set acl_m2 = 1


         #
         # 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 
cannot obtain public key


         #
         # accept the message (correctly signed)
         #
         accept  dkim_status = pass
                 sender_domains = $sender_address_domain
                 dkim_signers = $sender_address_domain
                 logwrite = DKIM PASS: 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 the message EVEN IF the signature FAILS! due to white 
listing
         #
         accept  condition = ${if eq {$acl_m2}{1}}
                 dkim_status = fail
                 sender_domains = $sender_address_domain
                 dkim_signers = $sender_address_domain
                 logwrite = DKIM FAIL (WHITELISTED): 
domain=$sender_address_domain status=$dkim_verify_status - DKIM failed 
but message accepted
                 add_header = :after_received:X-DKIM-Result: 
Domain=$sender_address_domain Result=FAIL (but whitelisted)


         #
         # deny (strict) when message fails signature test *and* acl_m2 
= 0 (not whitelisted)
         #
         deny    condition = ${if eq {$acl_m2}{0}}
                 dkim_status = fail
                 sender_domains = $sender_address_domain
                 dkim_signers = $sender_address_domain
                 message = Message from has invalid DKIM signature
                 logwrite = DKIM FAIL (DENY): 
domain=$sender_address_domain - message rejected!


         #
         # accept anything else (should never get here)
         #
         accept  logwrite = DKIM DEFAULT: domain=$sender_address_domain 
- message accepted (at end of ACL)




What this does for us is:

     * force checking of known DKIM signers - reject email from known 
signers that doesn't have a signature


     * check signatures on all emails - when present


     * return a 421 (defer) for email where a DKIM signature is present 
but the public key cannot be fetched


     * allow for whitelisting of broken hosts


     * do something sane where there are two (or more) DKIM signatures 
that may be in conflict


     * provide lots of useful debug



What we find this that there are household names (like ASDA, Laithwaites
wines, and others that included a well known ISP) that can't do DKIM
properly - typically they sign outgoing emails and then forget to put
the public key in the DNS, have an out-of-date key in the DNS, are using
tge wrong selector or have a syntax error in the key data (doesn't parse).

As a result I have to refer them to the DKIM key checker at ProtoDave:

         https://protodave.com/




The following shell snippet will weed out the broken senders from my log
entries:

         grep DKIM mainlog | grep DEFER | cut -f6 -d ' ' | cut -f2 -d 
'=' | grep . | sort | uniq


today's "Rogues Gallery" includes the following:

         root@relay1:/etc/exim# grep DKIM /var/log/exim/mainlog | grep 
DEFER | cut -f6 -d ' ' | cut -f2 -d '=' | grep . | sort | uniq
         9bbj.com
         almin.co.zw
         ares.constituted300.site
         b.e.iwantoneofthose.com
         causeway.com
         centiplex.net
         dynamicplumbing.co.nz
         email.crewclothing.co.uk
         e.mail.halfords.com
         email.hotelderby.be
*eu-west-1.amazonses.com*
         financial-businessadvisory.com
         grnetwork.org
         hcqs.us
         hns.ma
         impactcorner.com
         kingster.edu.in
         kooshansr.ir
         lists.epuk.org
         mail157.atl21.rsgsv.net
         mail164.atl221.rsgsv.net
         mail237.atl61.mcsv.net
         mail43.atl161.mcsv.net
         manage.c21media.net
         mediamasher.com
         midaspack.com
         mmmail032.co.uk
         mol.go.th
         mstat220.co.uk
         nebulahub.com
*newsletter.laithwaites.co.uk*
*petsensedirect.co.uk*
         phliks.com
         reflexlearning.com
         s1.hansindia.co.in
         saibaque.com
         savtastar.co.il
         sendgrid.myfonts.com
         server.theprogressteam.com
         service.jimtrade.com
         surabaya.go.id
         thegfcc.org
         thprom.ru
         ukrs394972.pur3.net
         USACOLIS.biz
         useli.org
         yogaforny.com
         root@relay1:/etc/exim#


*sigh*



Mike