qmail and DKIM

DomainKeys Identified Mail (DKIM) is a method for validating the identity associated with a message using using public-key cryptography and key server technology. DKIM is an enhanced version of Yahoo's Domain Keys and Cisco's Identified Internet Mail methods. As of February 2007 an IETF draft was accepted as "Proposed standard", it may take some more time till this will be a standard but the current version is stable ( according to dkim.org ).

Implementations of DKIM in email servers is not available as much as DomainKeys.
For qmail there is a patch that uses libdomainkeys to implement a replacement for qmail-queue that will verify and sign messages but this only works for DomainKeys not DKIM. I have developed a solution for qmail to be able to verify and sign messages with DKIM.

My solution was inspired by a post on the qmail list http://article.gmane.org/gmane.mail.qmail.general/52049 that used two shell scripts one as a qmail-queue wrapper for verifying the messages and one as a qmail-remote wrapper for signing the messages. Both scripts call dktest, a test program that comes with libdomainkeys, to do the signing and verification.

Libdkim is a portable implementation of the DKIM drafts and it comes with a test program, libdkimtest somehow similar to dktest so I'm using this to do the signing and verification.

Although libdkim developers claim it is portable it needed some patching to make it compile on Linux, mainly some macro definitions that are only available in windows header files. I found those definitions in a file macros.h from mono and I added it to libdkim's source and included it in dkim.h .

I also added some small features and enhancements like a help / usage message for libdkimtest, the ability to specify the domain and selector in the command line. I removed some hard coded defaults that blocked options specified on the command line and I made it be able to process messages that had bare LF as line terminators instead of CR/LF.

The shell scripts that used dktest also needed some small modifications because libdkimtest does not read the message on it's standard input and it does not output the message to the standard output.

Here is the shell script that does the verification:

  1. #!/bin/sh
  2.  
  3. [ "$DKIMQUEUE" ] || DKIMQUEUE="/var/qmail/bin/qmail-dk"
  4.  
  5. if printenv | grep -q '^DKIMVERIFY=' ; then
  6.  
  7. tmp=`mktemp /tmp/dkim.verify.XXXXXXXXXXXXXXX`
  8. tmp2=`mktemp /tmp/dkim.verify.XXXXXXXXXXXXXXX`
  9. cat - > "$tmp"
  10.  
  11. /usr/local/bin/libdkimtest -v "$tmp" > "$tmp2" 2> /dev/null
  12.  
  13. (
  14. if [ "$(cat "$tmp2"|grep "Success")" != "" ] ; then
  15. echo "DKIM-Status: good"
  16. else
  17. if [ "$(cat "$tmp2"|grep "Fail")" != "" ] ; then
  18. echo "DKIM-Status: failed"
  19. fi
  20. fi
  21. ) | /bin/cat - "$tmp" | $DKIMQUEUE
  22. retval=$?
  23. rm "$tmp" "$tmp2"
  24. exit $retval
  25. else
  26. exec $DKIMQUEUE
  27. fi

This should replace qmail-queue and to make it verify the messages you have to set the DKIMVERIFY environmental variable. This script calls qmail-dk because I want to also verify the DomainKeys signature.

and here is the script that should replace qmail-remote:

  1. #!/usr/local/bin/bash
  2.  
  3. [ "$DKSIGN" ] || DKSIGN="/etc/domainkeys/%/default"
  4. [ "$DKREMOTE" ] || DKREMOTE="/var/qmail/bin/qmail-remote.orig"
  5.  
  6. if [[ $DKSIGN == *%* ]] ; then
  7. DOMAIN=${2##*@}
  8. DKSIGN="${DKSIGN%%%*}${DOMAIN}${DKSIGN#*%}"
  9. fi
  10. if [ -f "$DKSIGN" ] ; then
  11. tmp=`mktemp -t dkim.sign.XXXXXXXXXXXXXXX`
  12. tmp2=`mktemp -t dkim.sign.XXXXXXXXXXXXXXX`
  13.  
  14. /bin/cat - > "$tmp"
  15. /usr/local/bin/libdkimtest -ydefault -s "$tmp" "$DKSIGN" "$tmp2" 2> /dev/null
  16.  
  17. (/bin/cat "$tmp2" |tr -d "\\015") | "$DKREMOTE" "$@"
  18. retval=$?
  19. rm "$tmp" "$tmp2"
  20. exit $retval
  21. else
  22. exec "$DKREMOTE" "$@"
  23. fi

copy qmail-remote to qmail-remote.orig and then name this script qmail-remote

get libdkim, unzip it, get these patches : libdkim linux compile patch and libdkim extra options patch

and apply then like this

  1. cd libdkim
  2. patch -p1 < libdkim.patch
  3. patch -p1 < libdkim2.patch
  4.  
  5. cd src
  6. make LINUX=y
  7. cp libdkimtest /usr/local/bin
  8.  

If you had qmail-dk working before then the scripts will just use the private keys in /etc/domainkeys/{domain_name}/default just like qmail-dk .

Update:

because some people had troubles applying the patches to newer versions of libdkim I have added the patched source code here: libdkim-patched.tar.gz

It would still be a good idea to patch the newer versions and I might do that when I find some time.

101 thoughts on “qmail and DKIM

  1. The signatures are different by what they sign and how they sign it.
    This probably has something to do with the versions of libdkim that you’re using and you can probably control it through some parameter to libdkimtest.

    I wouldn’t worry about removing extra signatures but if your server signs a lot of messages this might put too much load on the server.

  2. hm I see, I currently can’t test this anymore right now because someone started sending mail through the server again when I was suppose tohave exclusive access to it. so they sent out a few tens of thousands of emaisl with invalid dkim signatures haha

    anyway until I can look at this again do you have any what causes this?

    this is from dkim-test@altn.com

    dkim=neutral header.d=xh.com (b=Hr6JR1QX2y; 1:-2:DKIM_BAD_SYNTAX); header.d=xh.com (b=QrgP7FPews; 2:-2:DKIM_BAD_SYNTAX); header.d=xh.com (b=NgpRYX/i8V; 3:-2:DKIM_BAD_SYNTAX);

    I am setting domain with -d flag to libdkimtest, what should be the format of this argument if not domain.com? or is this some other problem …

    using libdkim 1.0.21 patched with your extra options patch

    1. I don’t know exactly what the d flag does. It might not work as it should.
      As you can see I don’t set any domain , I let libdkimtest determine the domain from the From: or Sender: headers in the message

  3. I finally got this going but I had to
    1) revert to the patched version of libdkim on here vs patching the newest version
    2) in the bash script set the -i (email address) and -d (domain) switches manually which in my case is ok since only one email address is sending.

Leave a Reply