Skip to content

qmail dkim.8

Manvendra Bhangui edited this page Feb 25, 2024 · 13 revisions

NAME

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

SYNOPSIS

qmail-dkim [ qqf [arg1] [arg2] [...] ]

DESCRIPTION

qmail-dkim(8) is a DKIM signing or DKIM verification program. It reads descriptor 0 for a RFC-5322 message and can either print a DKIM-Signature header or it can verify a DKIM signed RFC-5322 message and print a DKIM-Status header. qmail-dkim prints the original message after the DKIM-Signature or DKIM-Status header. The output of DKIM-Signature, DKIM-Status and the message is on a pipe to qmail-queue. If DKIMQUEUE is defined, the DKIM-Signature/DKIM-Status header and message will be written to the program defined by DKIMQUEUE. You can use /bin/cat for DKIMQUEUE to have a signed message printed on descriptor 1. qmail-dkim is a qmail-queue frontend and can be called by many indimail-mta programs by defining QMAILQUEUE as /usr/sbin/qmail-dkim. You can also use it by setting QMAILQUEUE as below. Here the message will pass through qmail-dkim followed by qmail-spamfilter) and finally qmail-queue, which will deposit the mail in the queue.

QMAILQUEUE="/usr/sbin/qmail-dkim /usr/sbin/qmail-spamfilter"

You could also set DKIMQUEUE as below to have qmail-dkim call qmail-spamfilter

DKIMQUEUE=/usr/sbin/qmail-spamfilter

If you want qmail-dkim to call something other than qmail-queue, pass it in the command line arguments to qmail-dkim (first example above) or set the DKIMQUEUE or QUEUEPROG environment variable (second example above). Command line arguments takes precedence over environment variable. Both the examples above will make qmail-dkim call qmail-spamfilter instead of qmail-queue.

qmail-dkim supports RSA-SHA1, RSA-SHA256 and ED25519-SHA256 (RFC-8463) encryption methods for signing and verification. By default RSA-SHA256 is used. qmail-dkim 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. You can disable dkimkeys by setting NODKIMKEYS environment variable.

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

If RELAYCLIENT or AUTHINFO environment variable is set, qmail-dkim will sign the message even if DKIMSIGN and DKIMVERIFY environment variables are not set. Even if DKIMVERIFY is set, you can disable DKIM verification by setting RELAYCLIENT_NODKIMVERIFY environment variable.

If RELAYCLIENT or AUTHINFO environment variable is not set, qmail-dkim will verify the message.

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, X-Bounced-Address headers to set the domain tag. If not it uses the value in bouncehost control file. If bouncehost is missing, me is used. You can use BOUNCEDOMAIN environment variable to override the value in the control file. 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
Y - DKIM_BAD_IDENTITY                   - signature error: invalid identity
                                          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 FGHIKLMNORTUVWjp. 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, BODY_HASH_MISMATCH, SELECTOR_ALGORITHM_MISMATCH, TAT_INCOMPAT 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 or "p" to issue temporary error for temporary DNS resolution failure. 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 overridden 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 overridden 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. You an exclude headers from gettng signed by setting a colon separated list of headers in EXCLUDE_DKIMSIGN environment variable.

qmail-dkim sets an alarm of 86400 seconds to quit if it doesn't complete. This can be changed by setting DEATH environment variable. See the man pages for qmail-queue(8) and indimail-mta-internals(7) for more details.

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, any error will be written to descriptor 2 or the value of the environment variable ERROR_FD, if set.

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), indimail-mta-internals(7),

Clone this wiki locally