Next: , Previous: , Up: Library   [Contents][Index]


5.38 DKIM

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.

Built-in Function: number dkim_verify (number msg)

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):

dkim_verify status: DKIM_VERIFY_OK

The message contains one or more ‘DKIM-Signature’ headers and one of them verified successfully.

dkim_verify status: DKIM_VERIFY_PERMFAIL

The message contains one or more ‘DKIM-Signature’ headers, all of which failed to verify.

dkim_verify status: DKIM_VERIFY_TEMPFAIL

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.

Built-in variable: string dkim_signing_algorithm

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.

Built-in variable: string dkim_explanation

An explanatory message clarifying the verification result.

Built-in variable: number dkim_explanation_code

A numeric code corresponding to the ‘dkim_explanation’ string. Its possible values are defined in ‘status.mfl’:

DKIM explanation code: DKIM_EXPL_OK

DKIM verification passed

DKIM explanation code: DKIM_EXPL_OK

DKIM verification passed

DKIM explanation code: DKIM_EXPL_NO_SIG

No DKIM signature

DKIM explanation code: DKIM_EXPL_INTERNAL_ERROR

internal error

DKIM explanation code: DKIM_EXPL_SIG_SYNTAX

signature syntax error

DKIM explanation code: DKIM_EXPL_SIG_MISS

signature is missing required tag

According to the DKIM specification, required tags are: a=, b=, bh=, d=, h=, s=, v=.

DKIM explanation code: DKIM_EXPL_DOMAIN_MISMATCH

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.

DKIM explanation code: DKIM_EXPL_BAD_VERSION

incompatible version

Incompatible DKIM version listed in the v= tag.

DKIM explanation code: DKIM_EXPL_BAD_ALGORITHM

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.

DKIM explanation code: DKIM_EXPL_BAD_QUERY

unsupported query method

The q= tag of the public DKIM key contains something other than ‘dns/txt’.

DKIM explanation code: DKIM_EXPL_FROM

From field not signed

DKIM explanation code: DKIM_EXPL_EXPIRED

signature expired

DKIM explanation code: DKIM_EXPL_DNS_UNAVAIL

public key unavailable

DKIM explanation code: DKIM_EXPL_DNS_NOTFOUND

public key not found

DKIM explanation code: DKIM_EXPL_KEY_SYNTAX

key syntax error

DKIM explanation code: DKIM_EXPL_KEY_REVOKED

key revoked

DKIM explanation code: DKIM_EXPL_BAD_BODY

body hash did not verify

DKIM explanation code: DKIM_EXPL_BAD_BASE64

can't decode b= tag

Base64 decoding of the b= tag failed.

DKIM explanation code: DKIM_EXPL_BAD_SIG

signature did not verify

DKIM explanation code: DKIM_EXPL_BAD_KEY_TYPE

unsupported public key type

The k= tag of the public DKIM signature contains a value, other than ‘rsa’.

Built-in variable: string dkim_verified_signature

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:

Library Function: dkim_signature_tag (string sig, string tag)

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:

TagDefault value
csimple/simple
qdns/txt
i@’ + the value of the ‘d’ tag.
Library Function: string dkim_verified_signature_tag (string tag)

Returns the value of tag tag from the ‘dkim_verified_signature’ variable.

Built-in Function: void dkim_sign (string d, string s, string keyfile, [ string ch, string cb, string headers, string algo ])

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:

d

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).

s

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
keyfile

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:

ch

Canonicalization algorithm for message headers. Valid values are: ‘simple’ and ‘relaxed’. ‘simple’ is the default.

cb

Canonicalization algorithm for message body. Valid and default values are the same as for ch.

headers

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:

  • "From:From:"
  • "Reply-To:Reply-To:"
  • "Subject:Subject:"
  • "Date:Date:"
  • "To:"
  • "Cc:"
  • "Resent-Date:"
  • "Resent-From:"
  • "Resent-To:"
  • "Resent-Cc:"
  • "In-Reply-To:"
  • "References:"
  • "List-Id:"
  • "List-Help:"
  • "List-Unsubscribe:"
  • "List-Subscribe:"
  • "List-Post:"
  • "List-Owner:"
  • "List-Archive"
algo

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

Note on interaction of dkim_sign with Sendmail

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:

Built-in variable: number dkim_sendmail_commaize

“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).

Note on interaction of dkim_sign with MMQ

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: , Previous: , Up: Library   [Contents][Index]