Skip to content

qmail dkim.8

Manvendra Bhangui edited this page Feb 13, 2023 · 13 revisions

NAME

qmail-dkim - sign/verify using DKIM (SSP/ADSP optionally) and queue a mail message for delivery

SYNOPSIS

qmail-dkim

DESCRIPTION

qmail-dkim has the same interface as qmail-queue except that it inserts an appropriate DKIM header (rfc4871, rfc6376) before it queues the message. To invoke qmail-dkim, set QMAILQUEUE to point to qmail-dkim in the environment when you send or receive email.

qmail-dkim will call qmail-multi. To invoke an executable other than qmail-multi set DKIMQUEUE=bin/qmail-queue for example.

qmail-dkim supports RSA-SHA1, RSA-SHA256 and ED25519-SHA256 (rfc8463) encryption methods for signing and verification. By default RSA-SHA256 is used.

qmail-dkim supports DKIM signing and verification and can optionally use Sender Signing Practice (SSP) or Author Domain Signing Practice. qmail-dkim uses libdkim and OpenSSL libraries for signing and verification.

To sign a message, set the DKIMSIGN environment variable to the pathname of the private key that will be used to sign the message. You can run dknewkey(8) to create a private key. Any % character in the environment variable are removed and replaced by the domain name in the From: header. The selector (s=) will be taken from the basename of the file. If, after substituting the %, that file does not exist, the % character will be removed to check if the file exists. In addition to DKIMSIGN environment variable, you can have the domain and the pathname of the private key in the control file dkimkeys which has the following format

domain:private_key_path:envstr

Here envstr is a string of environment variable additions or removals. e.g.

QREGEX=1,DKIMSIGNOPTIONS=-z 4

If an entry for domain exists in dkimkeys, the value of
DKIMSIGN environment variable is ignored.

If a private key file does not exist and does not have a % character, the
message will be rejected with error 35.

In the absense of DKSIGN and DKVERIFY environment variable, qmail-dkim will
sign the message if RELAYCLIENT or AUTHINFO environment variable is set. It
will verify the message if RELAYCLIENT or AUTHINFO environment variable is
not set. Even if DKVERIFY is set, you can disable dkim verification, if
RELAYCLIENT or AUTHINFO is set, by setting RELAYCLIENT_NODKVERIFY
environment variable.

You can set various DKIM options in getopt style, by setting the
environment variable DKIMSIGNOPTIONS

c <canonicalization> r for relaxed [DEFAULT], s - simple,
                     t relaxed/simple, u - simple/relaxed
l                    include body length tag
q                    include query method tag;
t                    include a timestamp tag
h                    include copied headers. This adds the z= tag
                     containing a copy of the message's original
                     headers.
i <identity>         the identity, if not provided it will not be included
x <expire_time>      the expire time in seconds since epoch
                     ( DEFAULT = current time + 604800)
                     if set to - then it will not be included
z <hash>             1 for RSA-SHA1, 2 for RSA-SHA256, 3 for
                     RSA-SHA1+RSA-SHA256, 4 for Ed25519-SHA256

DKIMSIGNOPTIONS="-z 2 -c r -q"
Use RSA SHA256 hash, set relaxed canonicalization and include query method
tag

Apart from setting DKIMSIGNOPTIONS, you can set the identity and the expire time by setting DKIMIDENTITY and DKIMEXPIRE respectively. DKIMIDENTITY takes precedence over -i option specified in DKIMSIGNOPTIONS. Similarly, DKIMEXPIRE takes precedence over -x option specifed in R DKIMSIGNOPTIONS. qmail-dkim uses the domain found in the Return-Path, Sender, From headers to set the domain tag. If not it uses the BOUNCEDOMAIN environment variable. BOUNCEDOMAIN can be set to an email address or a domain (without the at sign).

As a default qmail-dkim inserts RSA-SHA256 DKIM-Signature and expects the private key file to be a RSA private key. This can be changed by setting DKIMOPTIONS="-z 4" to insert ed25519 signature. qmail-dkim can insert an additional DKIM-signature if DKIMSIGNEXTRA is set. This is useful for inserting both RSA and ED25519 signature when signing. As an example you can have DKIMSIGN pointing to a RSA private key, DKIMSIGNOPTIONS having "-z 2" along with other options if any. Additionally set DKIMSIGNEXTRA pointing to an ed25519 private key and set DKIMSIGNOPTIONSEXTRA to have "-z 4" along with other options if any.

To verify a message, set the DKIMVERIFY environment variable to a desired set of letters. Precisely, if you want a libdkim return status to generate an error, include that letter, where A is the first return status (DKIM_SUCCESS), B is the second (DKIM_FINISHED_BODY), etc. The letter should be uppercase if you want a permanent error to be returned, and lowercase if you want a temporary error to be returned (exit code 88). If you omit the letter, qmail-dkim will not issue any error inspite of DKIM verification failure. It will return success and the email will get delivered.

The complete set of letters with the corresponding return status is given below

A - DKIM_SUCCESS                        - Function executed successfully
B - DKIM_FINISHED_BODY                  - process result: no more message
                                          body is needed
C - DKIM_PARTIAL_SUCCESS                - verify result: at least one
                                          but not all signatures verified
D - DKIM_NEUTRAL                        - verify result: no signatures
                                          verified but message is
                                          not suspicious
E - DKIM_SUCCESS_BUT_EXTRA              - signature result: signature
                                          verified but it did not
                                          include all of the body
F - DKIM_3PS_SIGNATURE                  - 3rd-party signature
G - DKIM_FAIL                           - Function failed to execute
H - DKIM_BAD_SYNTAX                     - signature error: DKIM-Signature
                                          could not parse or has bad
                                          tags/values
I - DKIM_SIGNATURE_BAD                  - signature error: RSA verify
                                          failed
J - DKIM_SIGNATURE_BAD_BUT_TESTING      - signature error: RSA verify
                                          failed but testing
K - DKIM_SIGNATURE_EXPIRED              - signature error: x= is old
L - DKIM_SELECTOR_INVALID               - signature error: selector doesn't
                                          parse or contains invalid values
M - DKIM_SELECTOR_GRANULARITY_MISMATCH  - signature error: selector
                                          g= doesn't match i=
N - DKIM_SELECTOR_KEY_REVOKED           - signature error: selector
                                          p= empty
O - DKIM_SELECTOR_DOMAIN_NAME_TOO_LONG  - signature error: selector domain
                                          name too long to request
P - DKIM_SELECTOR_DNS_TEMP_FAILURE      - signature error: temporary dns
                                          failure requesting selector
Q - DKIM_SELECTOR_DNS_PERM_FAILURE      - signature error: permanent dns
                                          failure requesting selector
R - DKIM_SELECTOR_PUBLIC_KEY_INVALID    - signature error: selector
                                          p= value invalid or wrong format
S - DKIM_NO_SIGNATURES                  - no signatures
T - DKIM_NO_VALID_SIGNATURES            - no valid signatures
U - DKIM_BODY_HASH_MISMATCH             - sigature verify error: message
                                          body does not hash to bh value
V - DKIM_SELECTOR_ALGORITHM_MISMATCH    - signature error: selector
                                          h= doesn't match signature a=
W - DKIM_STAT_INCOMPAT                  - signature error: incompatible v=
X - DKIM_UNSIGNED_FROM                  - signature error: not found
                                          message From headers in signature

For example, if you want to permanently reject messages that have a signature that is expired, include the letter 'K' in the DKIMVERIFY environment variable. A conservative set of letters is FGHIKLMNOQRTUVWjp. Reject permanently 3PS, FAILURE, SYNTAX, SIGNATURE_BAD, SIGNATURE_EXPIRED, SELECTOR_INVALID, GRANULARITY_MISMATCH, SELECTOR_KEY_REVOKED, DOMAIN_NAME_TOO_LONG, SELECTOR_PUBLIC_KEY_INVALID, NO_VALID_SIGNATURES and BODY_HASH_MISMATCH errors, and temporarily SIGNATURE_BAD_BUT_TESTING and DNS_TEMP_FAILURE. Add in S if you want to reject messages that do not have a DKIM signature. You can use the control files signaturedomains and nosignaturedomains (See Below) to further fine tune the action to be taken when a mail arrives with no DKIM signature. Note that qmail-dkim always inserts the DKIM-Status header, so that messages can be rejected later at delivery time, or in the mail reader. In that case you may set DKIMVERIFY to an empty string. If you want to check all message's From header in signature set the UNSIGNED_FROM environment variable to an empty string. If you want to check messages without signed subject header, set UNSIGNED_SUBJECT environment variable. If you want to honor body lengh tag (l=), set HONOR_BODYLENGTHTAG environment variable.

qmail-dkim supports signing practice which can be additonall checked when a signature verifcation fails -

SSP - Sender Signing Practice

and

ADSP - Author Domain Signing Practice.

When a signature fails to verify for a message, you can use SSP/ADSP to determine if the message is suspicious or not. To verify a message against SSP/ADSP, set the DKIMPRACTICE environment variable to the desired set of letters allowed for DKIMVERIFY environment variable. SSP/ADSP should be used only when signature verification fails. SSP/ADSP should be invoked only when libdkim returns the error codes (F,G,H,I,J,K,L,M,N,P,Q,R,S,T,U,V,W,X) for signature verification. In case you want to test against SSP/ADSP only for DKIM_NO_SIGNATURE and DKIM_NO_VALID_SIGNATURE set the environment variable DKIMPRACTICE="ST". If you want automatic behaviour, set DKIMPRACTICE to an empty string. In this case ADSP/SSP will be used when return code matches "FGHIJKLMNPQRSTUVWX". qmail-dkim uses ADSP as the default signing practice. You can override this by setting the SIGN_PRACTICE to ssp, adsp, local (lowercase). if you set SIGN_PRACTICE to local**,** qmail-dkim will check the domain against the control file signaturedomains (See Below). If the domain is found listed in signaturedomains, qmail-dkim will bypass ADSP/SSP and return DKIM_FAIL if signature fails to verify. Setting SIGN_PRACTICE to anything else will cause qmail-dkim to disable Signing Practice.

If ADSP or SSP is checked, qmail-dkim will insert the X-DKIM-ADSP or X-DKIM-SSP header as given below

A - DKIM_SUCCESS             - Message passes ADSP test
B - DKIM_ADSP_UNKNOWN        - some messages may be signed
C - DKIM_ADSP_ALL            - All message are signed with author signature 
D - DKIM_ADSP_DISCARDABLE    - messages which fail verification are
                               Discardable
E - DKIM_ADSP_SCOPE          - domain is out of scope
F - DKIM_ADSP_TEMPFAIL       - Temporary Error 

or

A - DKIM_SUCCESS             - Message passes ADSP test
B - DKIM_SSP_UNKNOWN         - some messages may be signed
C - DKIM_SSP_ALL             - All message are signed with author signature 
D - DKIM_SSP_STRICT          - messages which fail verification are
                               Discardable
E - DKIM_SSP_SCOPE           - domain is out of scope
F - DKIM_SSP_TEMPFAIL        - Temporary Error 

You can have a control file signaturedomains containing a list of domains which you know are sure to sign messages using DKIM. If a message comes from a domain listed in Rsignaturedomains, and the signature fails verification (any of DKIM failure status), qmail-dkim will bypass ADSP/SSP checks and return DKIM_FAIL. The name of this control file can be overriden by the environment variable SIGNATUREDOMAINS.

You can have a control file nosignaturedomains containing a list of domains which you know are sure not to sign messages using DKIM. If a message comes from a domain listed in nosignaturedomains, and does not have a DKIM-Signature header, qmail-dkim will bypass ADSP/SSP checks and return DKIM_NEUTRAL. The wildcard entry '*' in this file, will result in all mails which do not have a signature to pass DKIM test (unless the domain is listed in the control file signaturedomains). The name of this control file can be overriden by the environment variable NOSIGNATUREDOMAINS.

qmail-dkim will use the environment variable SELECTOR_DATA instead of dns for the public key text record. This can be used to test signatures without deploying the public key in dns.

Typically, you would sign messages generated on-host by setting DKIMSIGN in the environment before running an qmail-smtpd(8) or sendmail(1) / qmail-inject(8). DKIMSIGN will be carried through qmail-smtpd or through qmail's sendmail emulation through qmail-inject to qmail-dkim. You would also set it for qmail-smtpd at the same time RELAYCLIENT is set, most often in the tcpserver cdb file. If a host is authorized to relay, you probably want to sign messages sent by that host. DKIMVERIFY should be set for all other hosts.

If neither DKIMSIGN nor DKIMVERIFY are set, then DKIMSIGN will be set to /etc/indimail/control/domainkeys/%/default. The % will be replaced by the domain in the From: header. If such a file does not exist, then it will be set to /etc/indimail/control/domainkeys/default. If such a private key exists, it will be used to sign the domain. You can also set DKIMKEY to chose a key different from /etc/indimail/control/domainkeys/%/default. DKIMKEY can also have % character that will be replaced by the domain in the From: header. If the private key does not exist, qmail-dkim will exit with return code 35.

By default qmail-dkim will use all of the headers when signing a message.

NOTES

If the environment variable CONTROLDIR is set, qmail-dkim uses that instead of /etc/indimail/control to read control files and the private key.

EXIT CODES

qmail-dkim returns the same exit codes as qmail-queue with these additions:

35
The private key file does not exist.

57
Trouble waiting for qmail-queue to exit.

58
Unable to vfork.

59
Unable to create a pipe to qmail-queue.

88
For custom errors. Additionally diagnostic information of the error will be written to descriptor 2.

SEE ALSO

addresses(5), envelopes(5), qmail-header(5), dknewkey(8), dkim(8), qmail-inject(8), qmail-qmqpc(8), qmail-queue(8), qmail-send(8), qmail-smtpd(8)

Clone this wiki locally