Skip to content

Instantly share code, notes, and snippets.

@mrballcb
Created April 24, 2014 14:44
Show Gist options
  • Save mrballcb/11257290 to your computer and use it in GitHub Desktop.
Save mrballcb/11257290 to your computer and use it in GitHub Desktop.
DKIM headers in Exim
acl_check_dkim:
# Skip this whole acl if header.d contains an @ sign because exim is
# breaking down the header.i part (which usually is an email address)
# bit by bit, working towards just the domain name.
accept condition = ${if match{$dkim_cur_signer}{\N@\N}}
accept dkim_status = none
sender_domains = KNOWN_DKIM_SIGNERS
dkim_signers = KNOWN_DKIM_SIGNERS
condition = ${if eqi{$sender_address_domain}{$dkim_cur_signer} {yes}{no}}
log_message = Possible DKIM Forgery: Unsigned message from $sender_address_domain
add_header = :at_start:X-DKIM: Exim $version_number on $primary_hostname (no dkim signature for required domain: $dkim_cur_signer)
accept dkim_status = none
!sender_domains = KNOWN_DKIM_SIGNERS
!dkim_signers = KNOWN_DKIM_SIGNERS
set acl_m_dkim_hdr = 1
add_header = :at_start:X-DKIM: Exim $version_number on $primary_hostname (no dkim signature for $dkim_cur_signer)
warn condition = ${if eq {$acl_m_dkim_hdr}{1} {no}{yes}}
set acl_m_dkim_hdr = 1
add_header = :at_start:X-DKIM: Exim $version_number on $primary_hostname
accept dkim_status = pass
add_header = :at_start:Authentication-Results: $primary_hostname; dkim=$dkim_verify_status header.d=$dkim_cur_signer header.i=$dkim_identity header.s=$dkim_selector
warn dkim_status = invalid : fail
add_header = :at_start:Authentication-Results: $primary_hostname; dkim=$dkim_verify_status header.d=$dkim_cur_signer header.i=$dkim_identity header.s=$dkim_selector reason="$dkim_verify_reason"
deny dkim_status = fail
sender_domains = KNOWN_DKIM_SIGNERS
dkim_signers = KNOWN_DKIM_SIGNERS
condition = ${if eq {$dkim_key_testing}{1} {no}{yes}}
message = Rejected: $dkim_verify_reason
accept
@pilchita
Copy link

excelent.! i need add the spf result in the authentication header, any ideas?

@Athanasius
Copy link

Not all of this works, but only in the sense that, as per:

The ACL test specified by acl_smtp_dkim happens after a message has been received, and is executed for each DKIM signature found in a message.
https://www.exim.org/exim-html-current/doc/html/spec_html/ch-access_control_lists.html

this ACL won't even be called unless there is a DKIM signature in the email. This those two accept dkim_status = none sections are moot.

@Athanasius
Copy link

Athanasius commented Aug 7, 2022

this ACL won't even be called unless there is a DKIM signature in the email. This those two accept dkim_status = none sections are moot.

Ah, don't mind me, now I've found the documentation bit about setting dkim_verify_signers for that.

@Athanasius
Copy link

For what it's worth, if like me you want to use a file for the 'known signing domains' then:

DKIM_KNOWN_SIGNERS_FILE=/etc/exim4/dkim_known_signers
# Use a DKIM ACL
acl_smtp_dkim = acl_check_dkim
dkim_verify_signers = $sender_address_domain:$dkim_signers

...

acl_check_dkim:
        # Skip this whole acl if header.d contains an @ sign because exim is
        # breaking down the header.i part (which usually is an email address)
        # bit by bit, working towards just the domain name.
        accept  condition      = ${if match{$dkim_cur_signer}{\N@\N}}

        accept  dkim_status = none
                sender_domains = ${lookup {$sender_address_domain} lsearch,ret=full {DKIM_KNOWN_SIGNERS_FILE} {$sender_address_domain}}
                dkim_signers   = ${lookup {$sender_address_domain} lsearch,ret=full {DKIM_KNOWN_SIGNERS_FILE} {$sender_address_domain}}
                condition      = ${if eqi{$sender_address_domain}{$dkim_cur_signer} {yes}{no}}
                log_message    = Possible DKIM Forgery: Unsigned message from $sender_address_domain
                add_header     = :at_start:X-DKIM: Exim $version_number on $primary_hostname (no dkim signature for required domain: $dkim_cur_signer)

        accept  dkim_status    = none
                !sender_domains = ${lookup {$sender_address_domain} lsearch,ret=full {DKIM_KNOWN_SIGNERS_FILE} {$sender_address_domain}}
                !dkim_signers   = ${lookup {$sender_address_domain} lsearch,ret=full {DKIM_KNOWN_SIGNERS_FILE} {$sender_address_domain}}
                set acl_m_dkim_hdr = 1
                add_header     = :at_start:X-DKIM: Exim $version_number on $primary_hostname (no dkim signature for $dkim_cur_signer)

        warn    condition      = ${if eq {$acl_m_dkim_hdr}{1} {no}{yes}}
                set acl_m_dkim_hdr = 1
                add_header     = :at_start:X-DKIM: Exim $version_number on $primary_hostname

        accept dkim_status     = invalid : pass
                add_header     = :at_start:Authentication-Results: $primary_hostname; dkim=$dkim_verify_status header.d=$dkim_cur_signer header.i=$dkim_identity header.s=$dkim_selector

        accept dkim_status     = invalid : fail
                add_header     = :at_start:Authentication-Results: $primary_hostname; dkim=$dkim_verify_status header.d=$dkim_cur_signer header.i=$dkim_identity header.s=$dkim_selector reason="$dkim_verify_reason"

        accept

You can't just reference the file in dkim_verify_signers , because zero expansion is done on that, ever. Try to set it to a file and you'll end up with $dkim_cur_signer in the ACL literally being that value, i.e. the name of the file. So, work around this by just triggering the ACL for all SMTP-injected email using dkim_verify_signers = $sender_address_domain:$dkim_signers, and then do lookups on the file in the ACL.

The exact form of the ${lookup ...} is so that you can have a file that contains, other than comments, literally just a domain per line.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment