Next: Sockmaps, Previous: SPF Functions, Up: Library [Contents][Index]
DKIM or DomainKeys Identified Mail is an email authentication method that allows recipients to verify if an email was authorized by the owner of the domain that email claims to originate from. It does so by adding a digital signature which is verified using a public key published as a DNS TXT record. For technical details about DKIM, please refer to RFC 6376 (http://tools.ietf.org/html/rfc6376).
MFL provides functions for DKIM signing and verification.
Verifies the message msg (a message descriptor, obtained from
a call to current_message
, mailbox_get_message
,
message_from_stream
or a similar function).
Return value (constants defined in the ‘status’ module):
The message contains one or more ‘DKIM-Signature’ headers and one of them verified successfully.
The message contains one or more ‘DKIM-Signature’ headers, all of which failed to verify.
The message was not signed using DKIM, or the DNS query to obtain the public key failed, or an internal software error occurred during verification.
The following two global variables are always set upon return from this
function: dkim_explanation
and dkim_explanation_code
.
These can be used to clarify the verification result to the end user.
The variable dkim_signing_algorithm
is initialized with the
name of the algorithm used to sign the message.
Upon successful return, the variable dkim_verified_signature
is
set to the value of the successfully verified DKIM signature.
Name of the algorithm used to sign the message (either ‘rsa-sha1’ or ‘rsa-sha256’). If the algorithm was not specified (e.g. the signature is malformed), this variable is assigned an empty value.
An explanatory message clarifying the verification result.
A numeric code corresponding to the ‘dkim_explanation’ string. Its possible values are defined in ‘status.mfl’:
‘DKIM verification passed’
‘DKIM verification passed’
‘No DKIM signature’
‘internal error’
‘signature syntax error’
‘signature is missing required tag’
According to the DKIM specification, required tags are: a=
,
b=
, bh=
, d=
, h=
, s=
, v=
.
‘domain mismatch’
The domain part of the i=
tag does not match and is not a
subdomain of the domain listed in the d=
tag.
‘incompatible version’
Incompatible DKIM version listed in the v=
tag.
‘unsupported signing algorithm’
Either the a=
tag of the DKIM signature contains an unsupported
algorithm (currently supported algorithms are: ‘rsa-sha1’ and
‘rsa-sha256’) or this algorithm, while being supported by
mailfromd
, is not listed in the h=
tag of the public
DKIM key.
‘unsupported query method’
The q=
tag of the public DKIM key contains something other than
‘dns/txt’.
‘From field not signed’
‘signature expired’
‘public key unavailable’
‘public key not found’
‘key syntax error’
‘key revoked’
‘body hash did not verify’
‘can't decode b= tag’
Base64 decoding of the b=
tag failed.
‘signature did not verify’
‘unsupported public key type’
The k=
tag of the public DKIM signature contains a value, other
than ‘rsa’.
Upon successful return from the dkim_verify
function, this
variable holds the value of the successfully verified DKIM header.
This value is unfolded and all whitespace is removed from it.
An example of using the ‘dkim_verify’ function:
require status require dkim prog eom do string result switch dkim_verify(current_message()) do case DKIM_VERIFY_OK: set result "pass; verified for " . dkim_verified_signature_tag('i') case DKIM_VERIFY_PERMFAIL: set result "fail (%dkim_explanation)" case DKIM_VERIFY_TEMPFAIL: set result "neutral" done header_add("X-Verification-Result", "dkim=%result") done
The ‘dkim’ module defines convenience functions for manipulating with DKIM signatures:
Extracts the value of the tag tag from the DKIM signature sig. Signature must be normalized by performing the header unwrapping and removing whitespace characters.
If the tag was not found, returns empty string, unless tag is one of the tags listed in the table below. If any of these tags are absent, the following values are returned instead:
Tag | Default value |
---|---|
c | ‘simple/simple’ |
q | ‘dns/txt’ |
i | ‘@’ + the value of the ‘d’ tag. |
Returns the value of tag tag from the ‘dkim_verified_signature’ variable.
This function is available only in the eom
handler.
Signs the current message. Notice, that no other modification should be attempted on the message after calling this function. Doing so would make the signature invalid.
Mandatory arguments:
Name of the domain claiming responsibility for an introduction of a message into the mail stream. It is also known as the signing domain identifier (SDID).
The selector name. This value, along with d identifies the location of the DKIM public key necessary for verifying the message. The public key is stored in the DNS TXT record for
s._domainkey.d
Name of the disk file that keeps the private key for signing the message. The file must be in PKCS#1 or PKCS#8 format (PEM formatted).
Optional arguments:
Canonicalization algorithm for message headers. Valid values are: ‘simple’ and ‘relaxed’. ‘simple’ is the default.
Canonicalization algorithm for message body. Valid and default values are the same as for ch.
A colon-separated list of header field names that identify the header fields that must be signed. Optional whitespace is allowed at either side of each colon separator. Header names are case-insensitive. This list must contain at least the ‘From’ header.
It may contain names of headers that are not present in the message being signed. This provides a way to explicitly assert the absence of a header field. For example, if headers contained ‘X-Mailer’ and that header is not present in the message being signed, but is added by a third party later, the signature verification will fail.
Similarly, listing a header field name once more than the actual number of its occurrences in a message allows you to prevent any further additions. For example, if there is a single ‘Comments’ header field at the time of signing, putting ‘Comments:Comments:’ in the headers parameter is sufficient to prevent any surplus ‘Comments’ headers from being added later on.
Multiple instances of the same header name are allowed. They mean that multiple occurrences of the corresponding header field will be included in the header hash. When such multiple header occurrences are referenced, they will be presented to the hashing algorithm in the reverse order. E.g. if the header list contained ‘Received:Received’) and the current message contained three ‘Received’ headers:
Received: A Received: B Received: C
then these headers will be signed in the following order:
Received: C Received: B
The default value for this parameter, split over multiple lines for readability, is as follows:
Signing algorithm: either ‘rsa-sha256’ or ‘rsa-sha1’. Default is ‘rsa-sha256’.
An example of using this function:
precious string domain "example.org" precious string selector "s2048" prog eom do dkim_sign("example.org", "s2048", "/etc/pem/my-private.pem", "relaxed", "relaxed", "from:to:subject") done
• Setting up a DKIM record |
When sending a signed message, it is critical that no other
modifications be applied to the message after it has been signed.
Unfortunately, it is not always the case when mailfromd
is
used with Sendmail
. Before sending the message over SMTP,
Sendmail
reformats the headers that contain a list of email
addresses, by applying to them a procedure called in its parlance
commaization. The following headers are modified:
Apparently-To
, Bcc
, Cc
,
Disposition-Notification-To
, Errors-To
, From
,
Reply-To
, Resent-Bcc
, Resent-Cc
,
Resent-From
, Resent-Reply-To
, Resent-Sender
,
Resent-To
, Sender
, To
. Thus, if your
dkim_sign
includes any of these in the signature (which is the
default) and some of them happen to be formatted other way than the
one Sendmail
prefers, the DKIM signature would not verify on
the recipient side. To prevent this from happening, dkim_sign
mimics the Sendmail behavior and reformats those headers before
signing the message. This should ensure that the message signed and
the message actually sent are the same. This default behavior is
controlled by the following global variable:
“Commaize” the address headers (see the list above) of the message the same way Sendmail does, and then sign the resulting message.
The default value is 1 (true
). You can set it to 0
(false
) if this behavior is not what you want (e.g. if you are
using postfix
or some other MTA).
The functions header_add
and header_insert
(see Header modification functions) as well as the add
action (see header manipulation) cannot interact properly with
dkim_sign
due to the shortcomings of the Milter API. If any of
these was called, dkim_sign
will throw the e_badmmq
exception with the diagnostics following diagnostics:
MMQ incompatible with dkim_sign: op on h, value v
where op is the operation code (‘ADD HEADER’ or ‘INSERT HEADER’), h is the header name and v is its value.
The following example shows one graceful way of handling such exception:
prog eom do try do dkim_sign("example.org", "s2048", "/etc/pem/my-private.pem") done catch e_badmmq do # Purge the message modification queue mmq_purge() # and retry dkim_sign("example.org", "s2048", "/etc/pem/my-private.pem") done done
See Message modification queue, for a discussion of the message modification queue.
Next: Sockmaps, Previous: SPF Functions, Up: Library [Contents][Index]