DomainKeys

Introduction

DomainKeys is an authentication framework which stores public-keys in DNS and digitally signs emails on a domain basis.

Overview

A domain owner generates a private/public key-pairs that will be used to sign messages originating from that domain. The public-key is placed in DNS as a TXT record. The private-key is kept on the mail server which sends email for the domain.

When an email is submitted by an authorized user of that domain, dk-milter uses the private-key to digitally sign the email associated with the sending domain. The DomainKey-Signature header and signature is added to the email and the message is sent.

When a message is received with a DomainKey-Signature header, dk-milter extracts the signature and the sender's domain from the email. It does a DNS lookup on the TXT record to fetch the public-key for the sender's domain. Using the public-key, dk-milter verifies whether the signature of the email is valid. An Authentication-Results: domainkeys=pass header is inserted in the email if the verification is successful.

Selectors

Selectors allows a domain to have more than one public-key in DNS. This allows you to administer and change the public-keys advertized in DNS. If the selector is test and the domain is example.com, the public-key will be retrieved from test._domainkey.example.com. The name before _domainkey is the selector.

Dk-milter uses main as the default selector.

Canonicalization

Mail servers sometimes modify email in transit. This can invalidate the domainkeys signature. dk-milter supports two canonicalization algorithms. The simple algorithm tolerates almost no modification. The nofws algorithm tolerates common modifications such as white-space replacement and header line re-wrapping.

The default canonicalization used by dk-milter is simple.

Installation

The following installation guide is based upon sendmail 8.13. The DomainKeys feature is implemented through a milter. The milter does not support any sendmail version prior to 8.13.

Prerequisites

You should be able to modify the DNS records for your domain.

You should have a OpenSSL library installed.

You should have sendmail 8.13 installed.

Installing the DomainKeys milter

Download the source code for the sendmail DomainKeys milter from the sendmail.net website.

  • Extract the files from the downloaded dk-milter file
  • Edit the dk-filter/Makefile.m4 as follows:

    include(confBUILDTOOLSDIR`/M4/switch.m4')
    define(`confMT', `TRUE')
    define(`confREQUIRE_LIBSM', `true')

    APPENDDEF(`confLIBS', `-lssl -lcrypto')
    dnl Enable and edit these paths as needed:
    dnl APPENDDEF(`confINCDIRS', `-I/usr/local/ssl/include ')
    dnl APPENDDEF(`confLIBDIRS', `-L/usr/local/ssl/lib ')

    dnl To use the asynchronous resolver library provided, enable this line:
    dnl bldPUSH_SMLIB(`ar')

    bldPUSH_SMLIB(`dk')
    APPENDDEF(`confINCDIRS', `-I../libdk/ ')

    bldPUSH_SMLIB(`sm')

    dnl Enable these next line if needed to specify the locations of libmilter.a
    dnl and the libmilter include files:
    dnl APPENDDEF(`confINCDIRS', `-I/usr/local/sendmail/include')
    dnl APPENDDEF(`confLIBDIRS', `-L/usr/local/sendmail/lib')

    dnl Enable for IPv6 support:
    dnl APPENDDEF(`confENVDEF', `-DNETINET6 ')

    dnl APPENDDEF(`confENVDEF', `-D_FFR_EXTERNAL_IGNORE_LIST ')
    dnl APPENDDEF(`confENVDEF', `-D_FFR_FLUSH_HEADERS ')
    dnl APPENDDEF(`confENVDEF', `-D_FFR_MACRO_LIST ')
    APPENDDEF(`confENVDEF', `-D_FFR_MULTIPLE_KEYS ')
    dnl APPENDDEF(`confENVDEF', `-D_FFR_REPORTINFO ')
    dnl APPENDDEF(`confENVDEF', `-D_FFR_REQUIRED_HEADERS ')
    dnl APPENDDEF(`confENVDEF', `-D_FFR_SELECT_CANONICALIZATION ')

    dnl If you do POP before SMTP:
    dnl APPENDDEF(`confENVDEF', `-DPOPAUTH ')
    dnl APPENDDEF(`confINCDIRS', `-I/usr/local/BerkeleyDB/include')
    dnl APPENDDEF(`confLIBDIRS', `-L/usr/local/BerkeleyDB/lib')
    dnl PREPENDDEF(`confLIBS', `-ldb ')

    bldPRODUCT_START(`executable', `dk-filter')
    define(`bldSOURCES', `dk-filter.c util.c ')
    PREPENDDEF(`confLIBS', `-lmilter ')
    bldPRODUCT_END

    bldPRODUCT_START(`manpage', `dk-filter')
    define(`bldSOURCES', `dk-filter.8')
    bldPRODUCT_EN

     

    If the OpenSSL library and include files are installed in a different location, adjust the confINCDIRS and confLIBDIRS settings to point to the directories.

  • If your OpenSSL files are installed in a different directory, edit the the confINCDIRS setting in libdk/Makefile.m4, removing the dnl at the beginning of the line, to point to the directory where the OpenSSL include files are:

    include(confBUILDTOOLSDIR`/M4/switch.m4')

    define(`confREQUIRE_LIBSM', `true')

    dnl Enable these lines to make use of the provided asynchronous resolver:
    dnl bldPUSH_SMLIB(`ar')
    dnl APPENDDEF(`confENVDEF', `-DUSE_ARLIB ')
    dnl APPENDDEF(`confINCDIRS', `-I../libar/ ')

    dnl Enable and edit this as appropriate for your system:
    dnl APPENDDEF(`confINCDIRS', `-I/usr/local/ssl/include ')

    define(`confMT', `true')

    bldPRODUCT_START(`library', `libdk')
    define(`bldSOURCES', `dk.c rfc2822.c util.c ')
    bldPRODUCT_END

    bldFINISH

     

  • Change the current directory to directory where dk-milter was extracted.
  • Enter the following command:

    sh Build

  • If there are no errors, you may continue with the installation.
  • Enter the following command:

    sh Build install

Generating a public and private key

DomainKeys requires a public and private key. The private key should be saved in a safe location on your server. The public key will used in the DNS TXT record for DomainKeys.

  • Enter the following command to generate your private key:

    openssl genrsa -out rsa.private 768

  • Enter the following command to generate your public key:
  • openssl rsa -in rsa.private -out rsa.public -pubout -outform PEM

  • Move your private key to the domainkeys directory and rename it to mail.key.pem using the following command:

    mv rsa.private /var/db/domainkeys/mail.key.pem

    mail is the selector name in our example.

DNS TXT record for DomainKeys

Create a DNS TXT record for selector._domainkey.example.com as follows:

mail._domainkey.example.com. IN TXT "k=rsa; t=y; p=MEwwPQRJKoZIhvcNADAQCQADOwAwOAIxANPpYHdE2tevfEpvL1Tk2dDYv0pF28/f5MxU83x/0b sn4R4p7waPaz1IbOGs/6bm5QIDAQAB"

The string after p= is the base64 encoding of your public key.

If the rsa.public file which was generated contains

-----BEGIN PUBLIC KEY-----
MEwwPQRJKoZIhvcNADAQCQADOwAwOAIxANPpYHdE2tevfEpvL1Tk2dDYv0pF28/f 5MxU83x/0bsn4R4p7waPaz1IbOGs/6bm5QIDAQAB
-----END PUBLIC KEY-----

the base64 encoding is everything between the first ----- BEGIN PUBLIC KEY----- and -----END PUBLIC KEY----- lines. You should remove any spaces and newlines.

The t=y value pair means that the domain is using this key in test mode.

The sending domain policy is published using a DNS TXT record as follows:

_domainkey.example.com. IN TXT "t=y; o=~"

The t=y value pair means that the example.com domain is testing DomainKeys.

The o tag denotes the outbound signing policy. ~ (tilde) means that the example.com domain may sign some emails with DomainKeys.

Starting the DomainKeys milter

The milter runs as a daemon in the background. If you are starting the milter as root, you should create an unprivileged user for the milter.

  • Create a system user called domainkeys, for example. This user should not be assigned a login shell.
  • Enter the following command to start the DomainKeys milter:

    /usr/bin/dk-filter -l -p inet:8891@localhost -c simple -d example.com -s /var/db/domainkeys/mail.key.pem -S mail -u domainkeys -m MSA

    In the above example, all mail submitted through MSA (tcp port 587) with a sending domain of example.com will be signed by dk-filter using the simple canonicalization algorithm.

    The following example will sign mail for two domains:

    /usr/bin/dk-filter -l -p inet:8891@localhost -c simple -d example.com,example.net -s /var/db/domainkeys/mail.key.pem -S mail -u domainkeys -m MSA

Configuring sendmail with DomainKeys milter

  • Edit the .mc configuration file that was used to build the sendmail.cf and add the following entry to it

INPUT_MAIL_FILTER(`dk-filter', `S=inet:8891@localhost')

Using DomainKeys milter with SpamAssassin

Our AuthResults plugin can be used with SpamAssassin to evaluate the DomainKeys results.

Testing DomainKeys

You can perform a DomainKeys test by sending an email to autorespond+dk@dk.elandsys.com.

The email address specified as the (envelope) sender, and not the author (From:), will receive the reply. If you do not receive a reply, it is likely that your mail server rejected the mail sent by our autoresponder.

Below is an example of a signature header:

DomainKey-Signature: a=rsa-sha1; s=mail; d=example.com; c=simple; q=dns;
         b=Th2SZylksW2kVFD8lROlZLQwp1GmOgYzUlgxVDV/7lcSu0TxgjOkFxAfJFV2NEm8

If the verification is successful, you should see the following header in your email:

Authentication-Results: mail.example.net; domainkeys=pass

A DomainKeys test for canonicalization nofws can be performed by sending an email to autorespond+dk-nofws@dk.elandsys.com. Send an email to autorespond+dk-simple@dk.elandsys.com to test simple canonicalization.

DomainKeys signature verification failures

DomainKeys uses the email headers and body to generate a signature. If the subject line is rewritten or text is appended to the message body after it has been signed, the domainkeys verification fails.

References

Domain-based Email Authentication Using Public-Keys Advertised in the DNS (DomainKeys) - RFC 4870

DK-filter manual

NAME
dk-filter - DomainKeys filter for sendmail

SYNOPSIS
dk-filter -p socketspec [-a peerlist] [-A] [-b modes] [-c canon] [-C
config] [-d domains] [-D] [-f] [-i ilist] [-I eilist] [-h] [-H] [-k]
[-l] [-m mtas] [-M macro[=value][,...]] [-o hdrlist] [-P pidfile] [-q]
[-R] [-s keyfile] [-S selector] [-T secs] [-u userid] [-U popdb] [-V]

DESCRIPTION
dk-filter implements Yahoo!, Inc.'s DomainKeys draft standard for sign-
ing and verifying e-mail messages on a per-domain basis.

Details regarding the protocol and other issues related to the draft
standard can be found at http://antispam.yahoo.com/domainkeys.

OPTIONS
-a peerlist
Identifies a file of "peers" which identifies clients whose con-
nections should be accepted without processing by this filter.
The peerlist should contain on each line a hostname, domain name
(e.g. ".example.com"), IP address, an IPv6 address (including an
IPv4 mapped address), or a CIDR-style IP specification (e.g.
"192.168.1.0/24").

-A Automatically re-start on failures. Use with caution; if the
filter fails instantly after it starts, this can cause a tight
fork(2) loop.

-b modes
Selects operating modes. modes is a concatenation of characters
which indicate which mode(s) of operation are desired. Valid
modes are s (signer) and v (verifier). The default is sv.

-c canon
Selects the canonicalization method to be used when signing mes-
sages. When verifying, the message's DomainKey-Signature:
header specifies the canonicalization method. The recognized
values are nofws and simple as defined by the DomainKeys draft.
The default is simple.

-C config
Configuration control. See the CONFIGURATION section for
details.

-d domain [,...]
A comma-separated list of domains whose mail should be signed by
this filter. Mail from other domains will be verified rather
than being signed.

If the value of this parameter starts with a "/" character, it
is assumed to be a filename from which the domain list will be
read, one per line, with "#" characters indicating the beginning
of a comment.

In either case, the domain name(s) may contain the special char-
acter "*" which is treated as a wildcard character matching zero
or more characters in a domain name.

-D Sign subdomains of those listed by the -d option as well as the
actual domains.

-f Normally dk-filter forks and exits immediately, leaving the ser-
vice running in the background. This flag suppresses that be-
haviour so that it runs in the foreground.

-h Causes dk-filter to add a header indicating the presence of this
filter in the path of the message from injection to delivery.
The product's name, version, and the job ID are included in the
header's contents.

-H Includes on DomainKey signatures the list of headers that were
included in the signature. This makes the signature header
larger by explicitly listing the included headers, but this also
allows verifying agents to ignore headers that were added in
transit.

-i ilist
Identifies a file of internal hosts whose mail should be signed
rather than verified. Entries in this file follow the same form
as those of the -a option above. If not specified, the default
of "127.0.0.1" is applied.

-I eilist
Identifies a file of "external" hosts which may send mail
through the server as one of the signing domains without creden-
tials as such; basically suppresses the "external host (host-
name) tried to send mail as (domain)" log messages. Entries in
this file follow the same form as those of the -a option above.

-k Causes -s to be interpreted as the location of a key list, which
is a file listing rules for signing with multiple keys. The key
list should contain a set of lines of the form sender-pat-
tern:keypath where sender-pattern is a pattern to match against
message senders (with the special character "*" interpreted as
"zero or more characters"), and keypath is the path to the PEM-
formatted private key to be used for signing messages which
match the sender-pattern. The selector used in the signature
will be the filename portion of keypath.

-l Log via calls to syslog(3) any interesting activity.

-m mta [,...]
A comma-separated list of MTA names (a la the sendmail(8) Dae-
monPortOptions Name parameter) whose mail should be signed by
this filter. There is no default.

-M macro[=value][,...]
Defines a set of MTA-provided macros which should be checked to
see if the sender has been determined to be a local user and
therefore whether or not the message should be signed; if a
value is specified, the value of the macro must match the value
specified (matching is case-insensitive), otherwise the macro
must be defined but may contain any value. The list is empty by
default.

-o header [,...]
A comma-separated list of headers which should not be signed.
Ignored when verifying.

-p socketspec
Specifies the socket that should be established by the filter to
receive connections from sendmail(8) in order to provide ser-
vice. socketspec is in one of two forms: local:path which cre-
ates a UNIX domain socket at the specified path, or
inet:port[@host] which creates a TCP socket on the specified
port. If the host is not given as either a hostname or an IP
address, the socket will be listening on all interfaces. This
option is mandatory.

-P pidfile
Writes the process ID of the filter, once started, to the file-
name given.

-q Requests that messages which fail verification be quarantined by
the MTA. (Requires a sufficiently recent version of the milter
library.)

-R When a signature verification fails and the signing site adver-
tises a reporting address (i.e. r=user@host in its policy
record), send a structured report to that address containing
details needed to reproduce the problem.

-s keyfile
Gives the location of a PEM-formatted private key to be used for
message signing.

-S selector
Defines the name of the selector to be used when signing mes-
sages. See the DomainKeys specification for details.

-T secs
Sets the DNS timeout in seconds. A value of 0 causes an infi-
nite wait. The default is 5. Ignored if not using the asyn-
chronous resolver package. See also the NOTES section below.

-u userid
Attempts to be come the specified userid before starting opera-
tions.

-U popdb
Requests that the filter consult a POP authentication database
for IP addresses that should be allowed for signing. The filter
must be specially compiled to enable this feature, since it adds
a library dependency.

-V Print the version number and exit without doing anything else.

CONFIGURATION
The value of the -C switch is a comma-separated list of settings of the
form result=action which defines what the filter should do with mes-
sages that produce certain results. Each result and each action has a
full name and an abbreviated name. Either is accepted. Below, the
abbreviated name appears in parentheses.

results
badsignature (bad) the signature found in the message did not
verify successfully against the message; dnserror (dns) an error
was encountered attempting to retrieve a public key from the
nameserver; internal (int) an internal error occurred; nosigna-
ture (no) no signature was present on the message; signature-
missing (miss) no signature was present on the message which
claims to sign all messages.

action accept (a) accept the message; discard (d) discard the message;
tempfail (t) temp-fail the message; reject (r) reject the mes-
sage.

In the interests of minimal initial impact, the defaults for badsigna-
ture, nosignature and signaturemissing are all accept, and the default
for the others is tempfail.

OPERATION
A message will be verified unless it conforms to the signing criteria,
which are: (1) the domain on the From: address or Sender: address (if
present) must be listed by the -d command line switch, and (2) the
client connecting to the MTA must (a) have authenticated, or (b) be
listed in the file referenced by the -i command line switch (or be in
the default list for that option), or (c) be connected to daemon port
named by the -m command line switch.

When signing a message, a DomainKey-Signature: header will be prepended
to the message. The signature is computed using the private key pro-
vided. You must be running a version of sendmail(8) recent enough to
be able to do header prepend operations (8.13.0 or later).

When verifying a message, an Authentication-Results: header will be
prepended to indicate the presence of a signature and whether or not it
could be validated against the body of the message using the public key
advertised by the sender's nameserver. The value of this header can be
used by mail user agents to sort or discard messages that were not
signed or could not be verified.

ENVIRONMENT
The following environment variable(s) can be used to adjust the behav-
iour of this filter:

DK_TMPDIR
The directory to use when creating temporary files. The default
is /var/tmp.

NOTES
When using DNS timeouts (see the -T option above), be sure not to use a
timeout that is larger than the timeout being used for interaction
between sendmail and the filter. Otherwise, the MTA could abort a mes-
sage while waiting for a reply from the filter, which in turn is still
waiting for a DNS reply.

VERSION
This man page covers version 1.0.1 of dk-filter.

Feedback

If you have any comments, you can contact the author here

 

http://www.elandsys.com/resources/sendmail/domainkeys.html