Consulting djbware Publications

5. Authentication & Authorization

SMTP was designed as a Host-to-Host protocol with no authentication in mind. In fact, the protocol lacks any kind of credentials which are important today. Many criticism has been raised regarding this fact, but let's try to straighten out the basics.

In general -- regarding email -- it is possible to target anybody. Once you've got an email address as recipient, you are subject of solicitated an unsolicitated emails. You need to control it. However, regarding the sender two scenarios exist:

The permission to send by user is covered by SMTP Authentication. The permission to send per domain can an verified against the sender's IP address of the sending host by means of available SPF records on cryptographic signatures within the email - known as DKIM - and verified by means of the deployed public keys for the domain in the DNS.

Mechanism Credentials Purpose RFC Section
SMTP Authentication ... to send emails by (authenticated) user outside the origin domain allow unrestricted relaying RFC 4954 5.6, 5.9
X.509 Client Certificate ... to send emails by (trusted) user outside the origin domain allow unrestricted relaying [None; proprietary] 5.5, 5.9
Sending Domain Authorization (SPF) ... to receive emails from specific IP addresses granted by DNS SPF records prevent email fraud RFC 4408 5.10
DomainKeys Identified Mails (DKIM) ... to receive emails for individual cryptographically signed mails verified by the origin domain's public key issued in the DNS guarantee email integrity and qualify sending MTA RFC 6376 and RFC 8463 5.12

s/qmail supports both SMTP Authentication and SPF Authorization together with X.509 client certificates for ESMTP(S) and QMTPS. DKIM signing and verifying for SMTP emails is possible starting with s/qmail release 4.2.


Quick links:

5.1 Qualification and Credentials

Every 'Internet citizen' has to be addressed by a valid email address. An email address consists of a local part - the 'mailbox' name and the domain part - telling to which domain the email has to be forwarded to: typically sender@origindomain.

The 'domain part' is subject of the DNS. A valid SMTP recipient domain needs to have a SMTP host to be responsible and to be able to receive emails for this domain.

The 'local part' simply tells, that the user has a valid 'account' for this domain.

First: Sending an email with SMTP envelope address <sender@origindomain> implies, that originator sender posses a reachable mailbox at origindomain -- which might be subject of a test.

Second: Sending an email to <recipient@targetdomain> will only be successful if recipient has a mailbox at targetdomain -- which might be verified in the SMTP dialogue.

The situation can be modelled in the following way:

Figure: The three different scopes of SMTP qualifications: (1) Regarding Sender (Auth), (2) Regarding Domain (DNS), (3) Regarding Recipient (Mailbox).

Thus, we can identify three scopes -- or domains -- to testify the required qualifications:

  1. The sending MTA has to testify the credentials of sender. Details are discussed in this chapter.
  2. Within the Message Transfer System (MTS) the MTAs have to obligation to check whether both sending and receiving MTA do have qualified entries in the DNS In general, different qualification (and anti-qualification - like RBLs) schemes do exist. SPF (Sender Policy Framework) is covered in this chapter.
  3. Receiving an email for recipient@targetdomain will only be possible if recipient posses a Mailbox (or has at least an Alias entry).

In short:

Thus, regarding the sending of email, we have two situations:

  1. Domain qualified email sender (ie. Web Shops' sending IP).
  2. User qualified email sender (based on userid/password or X.509 certs).

The question for the recipient of this email is: Can we differentiate between those sources?
The answer is: No. We neither can trust any information based on (2.) nor should discriminate emails from source (1.).

5.2 One-factor Authentication

An identity is a subject possessing distinguishable attributes; at least a (user)name to address the subject and a particular attribute, the password. The name is the public part of the identity while the password is used to authenticate the identity among others. We call this a 'One-factor' Authentication.

In this scheme, both the name (Userid) and the password are static and don't depend on the (current) authentication. If, however the current timestamp of the authentication process is used as salt (among other information, like the IP address), one may construct a One-Time Password (OTP) which is used and valid only for this authentication request.

The identity which wants to get authenticated is called the Supplicant; while the system, which has knowledge about the (set of) identities is the Identity Provider (IdP). There might be a third party involved, the Identity Broker which is typically available in terms of a Shibboleth service.

Other schemes depend not only on knowledge but rather include some additional 'hardware' which needs to be uniquely issued by the IdP: a token, an USB stick, a smartcard ... If both, this hardware piece needs to be available during authentication as well together with the password we consider this a Two-factor Authentication.

5.3 Identity Provider (IdP)

The Identity Provider (IdP) is a system able to store the attributes of an identity persistently and allowing a lookup. Typically, the IdP can be considered as Authenticator, a server.
The IdP not only holds the user database, but additionally is able to support different authentication mechanisms.

One important security issue for the IdP is, to protect the user database (and her in particular the passwords) thus an untrusted third party, even in case the user data are proliferated by mistake, is not able to reconstruct the users's passwords. The Unix shadow password and crypt facility are perfect means for those, since the allow (in the running) Unix system access only to a root-owned process and protect the passwords by individual salting.

Other IdPs may use a standard SQL database-access or employ LDAP lookup as back-end.

5.4 Authentication Supplicant qmail-authuser

During qmail's development, several authentication modules have been realized as Pluggable Authentication Module, a PAM. D.J. Bernsten has provided a versatile authentication framework, the checkpassword interface.

Within s/qmail, the qmail-authuser program is available, supporting a file-based authentication, providing access to the Unix user database, and including a call to Vopmail's vcheckpw and VmailMgr's checkvpw in one go.

qmail-authuser replaces:

fallowing the same call syntax.

Thus, qmail-authuser is sqmail's swiss knife for authentication purposes, superseding the older mechanisms; though they still can be used. It supports user, login, plain (which require a 'plaintext' password), and Challenge/Response (CR) authentication; however this depends on the back-end (the IdP) as well. qmail-authuser can be used for (E)SMTP and POP3 authentication.

qmail-authuser depends on a 'tokenized' password scheme in the user database which follows some ideas already outlined for the 'Recipient' extension. Let's explain that by example:

Userid Token Password Mechanism/Purpose
username(@domain) password Plaintext or CRAM/MD5 evaluation
username(@domain) % hashed password (MD5, SHA1, SHA256) Plaintext only evaluation
username(@domain) || * (any user) ? (not required) Plaintext only against Unix crypt
username(@domain) || * (any user) ! (not required) Plaintext only using Unix crypt + return environment settings for username
username(@domain) || @domain || @ (any user/any domain) + (not required) Plaintext or C/R evaluation; calling checkvpw as secondary PAM
username(@domain) || @domain || @ (any user/any domain) & (not required) Plaintext, calling vchkpw as secondary PAM
username(@domain) || @domain || @ (any user/any domain) = (not required) using Dovecot as IdP wrapper/backend for plaintext and C/R passwords

The 'token' is part of the password and removed prior of authentication and is used to define the authentication mechanism attached to particular userid. The userid may be a single username or may be constructed as username@domain where both parts are considered independently. The settings are tried in most significant order of the userid and in fall-thru mode for all methods. Thus, you can mix 'local' users with virtual domain users for the different virtual domain handlers.

5.4.1 Hashed Password Storage Methodes

qmail-authuser password evaluation and storages methods haven enhanced to support SHA1 and SHA256 alongside with MD5 hashes. Given, complex and unique passwords are used, this allows to protect the user's individual password even in case the server is compromised.

5.4.2 Salted Password Storage Methodes

This mechanism can (and will) be expanded for salted passwords as well, but requires the maintenance of a cdb and a bookkeeping UI module.

5.4.3 Using Dovecot as Authentication Wrapper

qmail-authuser copes now with Dovecot's authentication provisioning system directly, which requires to invoke a local domain socket within Dovecot; typically within the file 10-master.conf:

service auth { unix_listener /var/run/dovecot/auth-sqmail { mode = 0600 user = qmaild group = nofiles } }

The location and path of the domain socket can be chosen arbitrarily; however it is necessary to tell qmail-authuser where to find it:

... qmail-smtpd qmail-authuser -s /var/run/dovecot/auth-sqmail true

qmail-authuser will now call the doveadm module with the argument auth test -a and 'feeding' this socket with the provided authentication data given on FD 3. The additional argument 'true' is necessary for calls of qmail-authuser.

Note: Potentially, the following configuration is required in the file /etc/pam.d/dovecot:

auth required pam_unix.so nullok account required pam_unix.so

5.5 X.509 certificate based Authentication

Within s/qmail, qmail-smtpd and qmail-qmtpd support X.509 client certificate based authentication.

What does this mean?

qmail-smtpd and qmail-qmtpd recognize the presence of the X.509 client cert and will accept the MUA as relayclient. Further, the validated X.509 client cert and its DN will be included in the Received header of the email.

Note 1: qmail-smtpd can require the match of the email address in the cert's DN with the used SMTP originator address.

Note 2: SMTP Authentication needs to be enforced by means of the environment variable SMTPAUTH (see below).

5.5.1 sslserver settings

Using sslserver to allow X.509 client cert based authentication the following steps are necessary:

  1. For each User a X.509 client certificate (+ keyfile in PEM format) needs to generated which is digitally signed by a (local) CA. Those X.509 certs are only useful in your down administrative domain and are solely forseen for email authentication purpose. You may alternatively issue more generic X.509 client certs; however since the (private) keyfile to always bound to a MUA, this is not without risk. In addition, the keyfile can be password protected to prevent abuse.
  2. The signing CA X.509 root Cert (PEM format) has to included in sslserver key repository: CCAFILE=path_to_CA_signing_root_file(s). It is possible to provide several CA root Certs for different domains/customers (in one common file).
  3. sslserver has to be advised to request a client cert per connection. The (global) option sslserver -m can be used, or alternatively the environment variable CCAVERIFY="" is provided in sslserver cdb.

5.5.1 Additional qmail-smtpd settings

qmail-smtpd includes the authentication information in the the Received: header and displays the provided Distinguished Name of the user identity. The DN should include the email address of the sender.

In general, there is no connection between the DN and the ESMTP 'Return-Path' address, but this can enforced. For this purpose, the environment variable LOCALMFCHECK="?" can be set; typically as provided in the cdb. Now, only ESMTPA connections with matching conditions are granted.

5.5.2 qmail-smtpd logging with X.509 client certs

In case, the ESMTPA authentication was accepted or rejected, qmail-smtpd displays the DN as user identification in the logs following the token "?~". Further, the authentication method is given as cert.

5.6 qmail-smtpd Authentication

s/qmail includes a rich set of ESMTP Authentication capabilities for qmail-smtpd together with qmail-authuser. For some background, have a look at my SMTP Authentication tutorial; however, this may not be up to date.

Currently, the ESMTP auth mechanisms

are supported; but depend on the IdP.

5.6.1 qmail-smtpd Auth environment settings

The environment variable SMTPAUTH can be used to specify the type of SMTP Authentication. Your choices are:

SMTPAUTH Meaning
"" Left blank to allow Authentication types "PLAIN" and "LOGIN"
"+cram" Add "CRAM-MD5" support
"cram" Just (secure) "CRAM-MD5" support, no other types offered
"!" Enforcing SMTP Auth (of type "LOGIN" or "PLAIN")
"!cram" Enforcing SMTP Auth of type "CRAM-MD5"
"!+cram" Enforcing SMTP Auth of type "LOGIN", "PLAIN", or "CRAM-MD5"
"-" Disabling SMTP Auth (for a particular connection)

Unlike the Submission feature, these settings may be realized in the tcprules cdb per connection.

If not using qmail-smtpd in Submission mode on port 587 those settings are provided as variables in sslserver's cdb.

Note: It is required to install ucspi-tcp6 in order to generate the cdb by tcprules.

5.6.2 qmail-smtpd Auth logging

qmail-smtpd provides logging of accepted and failed authentication attempts.

The authentication mechanism together with the username is provided in the logs. Sample:

qmail-smtpd: pid 1167 Accept::AUTH::plain P:ESMTPSA S:1.2.3.4:example.com H:[127.0.0.1] F:me@example.com T:feh@fehcom.de ?~ 'userid'

5.7 qmail-pop3d Authentication

Authentication for Mailbox users is a 'must' and without authentication access to a Mailbox is not permitted. Along side with qmail-pop3d, qmail-popup is required to provide the userid, password, and the environmental settings.

5.7.1 qmail-pop3d Auth environment settings

Unlike qmail-smtpd, for qmail-popup only a slim choice of authentication mechanisms exists.

POP3AUTH Meaning
"apop" Enforcing Auth of type "APOP"
"+apop" Enabling Auth of type "APOP"

Note: For qmail-popup no particular environment variables are required to enable authentication.

5.8 qmail-qmtpd Authentication

qmail-qmtpd supports authentication solely based on X.509 client certificates employing sslserver. The procedure follows the one discussed in section 5.5, except that the validation of the email address against the provided QMTP originator address is not provided and no specific logging takes place.

5.9 qmail-remote Authentication

qmail-remote is not at personal (E)SMTP client but rather acts on behalf of the (domain) owner. Thus, qmail-remote supports ESMTP authentication under two distinct scenarios:

  • As ESMTP client submitting emails from a specific domain. For this purpose, the control file authusers can be employed.
  • As ESMTP deliverer for emails to a relayhost as specified by means of the legacy (but now extended) smptroutes control file.

5.9.1 qmail-remote User Authentication

In a setting where s/qmail has to serve multiple domains, you can set up qmail-remote to respect and to act according to the domain's preferences:

Scenario 1 (control/authusers):

Based on the entire SMTP sending address, the domain part; or perhaps in general, one can specify the ESMTP username, the password, and hostname and port to connect to for ESMTP authentication.

@example.com:relay.example.com|user|passwd info@example.com:relay.example.com;26|infouser|infopasswd :mailrelay.example.com|e=mc2|testpass

However, if s/qmail depends on some remore relayhosts irrespectively of the sender, the following setup will be beneficial:

Scenario 2 (control/smtproutes):

Based on destination, qmail-remote can be advised to send emails on behalf of a (commonly known) username and password to a specific relayhost on a given port.

:submission.myrelay.com;587|myuserid|mypasswd

Note: authsenders settings have precedence over smtproutes.

5.9.2 qmail-remote Client Certificate Authentication

qmail-remote supports (as client) X.509 certificate validation and complementing section 5.5 regarding qmail-smtpd.

Now, the control file domaincerts may be employed to provide domain based X.509 authentication. This requires to include a X.509 + keyfile for a specific domain.

qmail-remote domaincerts provides the following capabilities:

  • The PEM encoded X.509 certificate is given per sending domain (syntax):

    domain:certificate|keyfile|password

  • If domain equals '* this certificate is used as default for all connections.
  • The file certificate may include the private key, thus keyfile can be omitted.
  • The private key can be protected with a password.

Note: It is certainly a good idea to change the permission of this file only to be readable by qmail-remote running as user qmailr.

5.10 SPF Records and Domain Authorization

Starting with release 3.2 s/qmail supports the Sender Policy Framework SPF. SPF is mainly a mechanism to attach authorization to particular MTAs; their IP address respectively. Based on Jana Saout's code, s/qmail incorporates a native SPF parser with IPv6 capabilities.

SPF is rather complex and provides mechanisms for recursive lookups as given in the SPF record. Here is the layout of qmail-smtpd's SPF implementation:

Figure: qmail-smtpd's SPF modules.

5.10.1 Checking SPF records

SPF DNS records are common these days for the 'big players' in the Internet, like Gmail and others. However, the may possess a certain level of complexity to parse including text-semantic analysis and delegation to other SPF domain providers.

In order get used to SPF, spfquery may be used to see, how the SPF information retrieval is working. In general, an email supplier (using ESMTP) publishes for this domain a specific DNS TXT record which provides authorization for (mainly) IP addresses of MTAs of the domain example.com. This domain name is taken from the 'Mail From:' SMTP envelope address (in case of bounce, the EHLO/HELO string). The idea is taken from Hadmuth Danisch's RMX proposal, which was sacked in the IETF letter ballot and replaced with the much more complex SPF model.

Within my SPF implementation the entire information retrieval path is provided as output from spfquery.

Here is a (complicated) sample for a FAIL result:

/var/qmail/bin/spfquery 144.76.139.155 mx-2.btshosting.co.uk no-reply@eLIVE.co.uk -v result=fail SPF information evaluated: S=144.76.139.155 O=no-reply@eLIVE.co.uk C=eLIVE.co.uk H=mx-2.btshosting.co.uk I=spf.protection.outlook.com MIPv4=207.46.101.128 MIPv4=207.46.100.0 MIPv4=207.46.163.0 MIPv4=65.55.169.0 MIPv4=157.56.110.0 MIPv4=157.55.234.0 MIPv4=213.199.154.0 MIPv4=213.199.180.0 I=spfa.protection.outlook.com MIPv4=157.56.112.0 MIPv4=207.46.51.64 MIPv4=157.55.158.0 MIPv4=64.4.22.64 MIPv4=40.92.0.0 MIPv4=40.107.0.0 MIPv4=40.107.128.0 MIPv4=134.170.140.0 I=spfb.protection.outlook.com MIPv4=23.103.128.0 MIPv4=23.103.198.0 MIPv4=65.55.88.0 MIPv4=104.47.0.0 MIPv4=23.103.200.0 MIPv4=23.103.208.0 MIPv4=23.103.191.0 MIPv4=216.32.180.0 MIPv4=94.245.120.64 R:- R:- R:- R:- SPF results returned: See http://spf.pobox.com/why.html?sender=no-reply@eLIVE.co.uk&ip=144.76.139.155&receiver=localhost

Here, the 'M' denotes the SPF mechanism, 'I' means include. All presented IP addresses are checked against the sending MTA's one. CIDR evaluation is supported.

Once SPF records are published into the DNS, one may specify, if a particular MTA

  • is authorized (PASS '+'),
  • should be rejected (FAIL '-'),
  • is ambiguous (SOFTFAIL '~'), or
  • is unknown (NEUTRAL '?')

according to the SPF policy for sending email from example.com as provided as SMTP Return-Path envelope address.

s/qmail provides a complex SPF record parser and supporting all 'mechanisms' as defined in RFC 4408.

5.10.2 SPF evaluation in qmail-smtpd

SPF records requires intensive DNS lookups and this is done at the stage 'RCPT TO:' by qmail-smtpd, thus other anti-Spam means will work before.

Advise: Use a local DNSCache to speed up the lookup and reduce the load of any potential recursive DNS Resolver.

qmail-smtpd provides three tweaks for the SPF setup:

  1. The environment variable SPF is used as generic switch for the SPF evaluation. s/qmail follows Jana Saout's principals, as shown in the following table. However, no specific control file for qmail-smtpd is provided.
  2. A local policy (as last resort) can be specified in spflocalrules. The former spfguess is not supported anymore.
  3. More important, in case of an SPF Fail, the return code of qmail-smtpd can be customized to allow a site specific explanation. The default expression build in is:

    See http://%{d}/why.html?sender=%{s}&ip=%{i}&receiver=%{R}"

    This explanation uses the typical SPF macros, which are expanded at run-time.

SPF values to be specified for individual connections:

SPF Meaning
"0" Never do SPF lookups, don't create Received-SPF headers
"1" Only create Received-SPF headers, never block
"2" Use temporary errors when you have DNS lookup problems
"3" Reject mails when SPF resolves to fail (deny)
"4" Reject mails when SPF resolves to softfail
"5" Reject mails when SPF resolves to neutral
"6" Reject mails when SPF does not resolve to pass

SPF values 1 to 3 are 'save' and can be used in a standard environment. Setting SPF="1" raises the possibility to allow a Mail Delivery Agent (MDA) to inspect the Received-SPF header for spam and fraud detection.

While SPF record evalution can be triggered by a standard qmail-smtpd environment variable set in the run script, it is advisiable to perform a stronger SPF evaluation only for 'unknown' MTAs. In order to feed downstream Spam detection systems, I recommend:

  1. Within qmail-smtpd use SPF envaluation for all MTA (in the run script) defining "SPF=1" which means 'annotation' mode only.
  2. Within tcpserver/sslserver's cdb include "SPF=2/SPF=3" in the last generic 'allow' statement.

=:allow,SPF="2",GREETDELAY="80",HELOCHECK="A",MFDNSCHECK="",BADMIMETYPE="+",BADLOADERTYPE="M",QHPSI="clamdscan",TARPITCOUNT="1",TARPITDELAY="999" :allow,RBLSMTPD="-We don't accept mails from MX with missing DNS PTR or DNS A/AAAA record."

Note: In case auf email authentication, no SPF evaluation is used.

5.10.3 SPF Received Header

qmail-smtpd generates a standard Received-SPF header, in case

  • the provided SPF environment value is given and not null,
  • the domain to lookup provides SPF records to be evaluated.

Thus, the Received-SPF header is only present in case SPF information has been looked up successfully or a particular DNS failure hase been recognized. A standard PASS situation looks like this:

Received-SPF: pass (mout.gmx.net: domain of gmx.de designates 212.227.15.19 as permitted sender) receiver=mail.fehcom.de; client-ip=212.227.15.19 envelope-from=<FallenWarrior@gmx.de>

The implementation is able to include all cases of SPF results ('+' pass, '-' fail, '~' softfail, '?' neutral, 'o' none, 't' temperror, 'e' permerror); however depends on a feed of SPF variables.

The (global) variable spfinfo is populated as set of concatenated individual values in the following manner:

spfinfo="|I=identity|C=clientip|H=helo|E=envelopefrom|R=receiver|P=problem|M=mechanism|R:result"
  1. identity carries the information of the retrieval source (i.e. recipient address, Helo greeting ...).
  2. clientip is the IP of the sending host.
  3. helo is the Helo greeting of the sender; if this is omitted, qmail-smtpd uses the received E/HELO string.
  4. envelopefrom is the Return-Path from the envelope.
  5. receiver is the recipient MTA.
  6. problem includes a description of encountered errors.
  7. mechanism is the purported evaluation method.
  8. result is the result of the evaluation and is expressed in the standard tokens (i.e. '+', '-' ...).

Note 1: Except for result any of these variables may be empty/unsupplied.

Note 2: The provided information follows the guidelines in RFC 6376.

5.10.4 SPF Logging

qmail-smtpd displays the evaluated SPF information under two distinct conditions:

  1. PASS: In case, the SPF information matches the received criterions, irrespectively of the SPF settings (SPF > 0) the email is labeled als SPF accepted and additional receiving information is added.
  2. FAIL: In case SPF is setup to reject failed SPF evaluation, a Fail is indicated in the logs.

qmail-smtpd: pid 16659 Accept::SPF::Recipients_Cdb P:ESMTPS S:212.227.15.19:mout.gmx.net H:mout.gmx.net F:Warrior@gmx.de T:...@fehcom.de .... qmail-smtpd: pid 3192 Reject::SPF::Fail P:ESMTPS S:144.76.139.155:mx-2.btshosting.co.uk H:mx-2.btshosting.co.uk F:no-reply@eLIVE.co.uk T:mail@fehcom.de

5.11 Sender Rewriting Scheme SRS

Starting with s/qmail 4.0, the modules srsforward and srsreverse are available taking care of sending and receiving emails with a SRS SMTP envelope addresses, though without requiring to install libsrs2.

Both modules are used within a dot-qmail file and evaluate the control file srsdomains declaring domains to be treated with SRS addresses. A virtual user 'srs' included into the control file virtualdomains may take care of bounces with SRS addresses.

SRS takes care of problems in the SPF scheme:

If your MTA at mydomain.org receives emails from user@foo.org the original SMTP address is used unaltered while forwarding the mail. In case the domain foo.org has a SPF record set up and the receiving third party evaluates the SPF DNS TXT record of foo.org, presumable this forwarded email will not be accepted due to violation of the SPF policy at foo.org. With SRS, the forwarder (us at mydomain.org) can re-write the RCPT TO: addresss from foo.org with srsforward prior of forwarding the mail.

It should be noted, that SRS is not standarised. It is in fact a variant of VERP, comparable with BATV.

Assuming, your domain name is mydomain.org mails received from user@foo.org and is subject for srsforward, it will re-write the SMTP Return-Path address (RCPT TO:) in the following (minimal) way:

user@foo.org ==> SRS0+HHH=TT=foo.org=user@mydomain.org

SRS address ingredients:

  • HHH: These three letters perform a cryptographic hash.
  • TT: A timestamp, making the delivery unique and mitigates forgery of bounces.
  • '+': The chosen delimiting character given in the control file srsdomains; while '=' and '-' are allowed as well. '=' is the default delimiter and this character is alos used in the rest of the local part of the SRS address.

While srsforward and srsreverse are usually called from a dedicated user's dot-qmail file, rather s/qmail allows in addition to facilitate the alias user for this task.

5.11.1 Forwarding mails with SRS

Enabling SRS in s/qmail you can setup a control file srsdomains. Here, you define some recipient domain names which eventually will forward mails via SRS, including cryptographic material to be used and additional configuration parameters. Let's assume, we are a MTA at mydomain.org and additionally serve 2ndomain.com and badadmin.org. We may setup up this srsdomains file:

*:mysecret|-|srs. 2ndomain.com:secret1 secret2 secret3||srs.mydomain.org !badadmin.org:

This results in the following SRS settings:

  • The first line defines the default: SRS forwarding is enabled for all received domains, defining 'mysecret' as base for the hash 'HHH' with delimiter "'"and providing the RCPT TO: address as 'srs.mydomain.org': SRS0-HHH=TT=foo.org=user@users.mydomain.org
  • The second line defines a particular behaviour for '2ndomain.com' using three secrets, defaulting to the standard delimiter "=", and using srs.mydomain.org as RTCP TO: domain part: SRS0=HHH=TT=foo.org=user@srs.mydomain.org
  • The third line shows the possibility to disable particular domains acting as SRS forwarder.

SRS forwarding is possible for recipient domains provisioned in srsdomains with a dedicated secret. If for example foo.org would *not* be present in the srsdomains file given above, the default '*' would be used and any of the given 'secrets' would be valid.

The settings are passive unlike somebody uses srsforward explicitely in a dot-qmail file. Forwarding emails for domains including SRS enhanced SMTP (RCPT TO:) address is triggered upon (local) receipt by a dot-qmail file:

|srsforward example.com foobar.net ...

Typically, this is the user's dot-qmail file. However, it could be a 'virtual user' as well, in particular within a vpopmail or vmailmgr setup; thus applicable for Qmail toasters as well. As a result, the email would be forwarded with the SRS enhanced RCPT TO: SMTP envelope address to all domains given in the .qmail file.

It should be noted that the arguments for srsforward - the domain names - can be constructed on-the-fly potentially using the environment variables provided by qmail-local and described in qmail-command.

5.11.2 Receiving bounces with SRS addresses

Having set up SRS and using the scheme, this SRS-enabled domain is responsible for potential bounces, resulting in case the recipient mailbox is not reachable. This is, because the SRS host has substituted the orignal domain-part in the SMTP RCPT TO: envelope with its own.

Quote from libsrs2 regarding SRS reverse:

The return-path is used for bounce messages. If the target address is undeliverable, a bounce message is returned to the sender. Under classical forwarding, bounce messages are routed directly to the original sender. Under the new system, bounce messages are relayed back through the forwarding service, to the rewritten sender address. The forwarding service then unwraps the original sender and forwards the bounce.

To make it more explicit:

  1. With SRS enabled, a bounce mail (eg. if the recipient is unvaialable) will always target the forwarding MTA, since the SMTP 'RCPT TO:' has been rewritten and includes in the domain part now the forwarding host's domain name.
  2. The email needs to be received and thus a matching rcpthosts entry is required to be recognized by qmail-smtpd.
  3. The SRS address needs to be 'unwrap' and the bounce forwarded to the original sender, which is only possible in case of matching 'secrets'.
  4. The bounce sender in the SMTP MAIL FROM: needs to be chosen, but defaults to the NULL sender '<>'.

To provide such a service - here srsreverse - needs some care, because accepting unsolicited messages for third-party domains and forwarding those would result in an Open Relay. SRS copes with this, including a hash value and a time-stamp in the local part of the RCPT TO: address for SRS forwarded mails.

Here, we have two choices:

  • We use our MTA's standard domain name for SRS forwarding and bounce handling..
  • In a multi-tenant setup, we need to raise a subdomain to cope bounces received for any particular domain.

Given these circumstandes, handling of bounce mails with SRS envelope information (RCPT TO:) is simple:

|srsreverse

srsreverse reconstructs from the SMTP envelope address of the incoming bounce mail the forseen (remote) recipient while verifying the legitimity of the SRS information; thus checking whether the bounce mail address is in accord with the information provided in srsdomains.

5.11.3 A usable setting for SRS

SRS forwarding shall be an exception. Mostly it happens, when mails need to be dispersed without a mailing listing manager like ezmlm. Here, sending domains might have a SPF record set, and thus need to get treated by SRS.

The handling of bounces is considered to be third order. You might follow these rules:

  • Setting up ONE bounce policy for your MTA handling all SRS bounces: You need to setup a .qmail-default file (for the alias user) including the call of a srsreverse and handling bounces.
  • More clever, you can raise a SRS subdomain (reachable via DNS MX), which needs to be configured in rcpthosts and locals e.g. as srs.mydomain.org together with a specific dot-qmail file (again for the alias user) .qmail-srs-default following the previous recipe.
  • In an advanced multi-tenant environment, you can setup - aligned with your srsdomains definitions - dedicated dot-qmail files for virtual users together with entries in rcpthosts and virtualdomains.

5.11.4 Logging of SRS rewriting actions

Any delivery of srsforward and/or srsreverse will show up in the qmail-send log:

2020-02-25 14:01:34.029809500 delivery 2617: success: srsforward:_SRS0=shBI=4O=example.com=erwin@example.com/did_1+0+1/

5.12 DomainKeys Identified Mails

The task of DomainKeys Identified Mails (DKIM) is to provide authenticity and integrity for emails while adding a digital signature within the email header. This requires a cryptographic public/private key pair, a technical procedure, and a protocol which is defined in mainly RFC 6376 and RFC 8463. DKIM is only suited for SMTP and can not be used for QMTP, since this a byte and not a record-line transmission protocol.

The DKIM protocol includes three major steps:

  • The canonicalization of the email, based on the CRLF line format.
  • The signing procedure resulting in a DKIM header which includes the required information how the signature was generated, where to find the public key (in DNS), and the signature itself.
  • The verification operation while parsing the mail given by the protocol information, generating the signature material to be compared against the signature as unfolded by the public key.

This prodecure is unfortunately quite complex, since it includes several canonicalization schemes, two different hash functions (SHA1 and SHA256), and two signature mechanisms: RSA and Ed25516.

For s/qmail, I've refactored the public domain libdkim library from the ALT-N project, straightend the C++ code, removed obsolete implementation details and included Edwards ECC cryptography based on Ed25519.

The resulting module qmail-dkim is however API-compatible with libdkim given the fehQlibs are used additionally in order to provide DNS services.

In order to support DKIM signing and verification the following modules are now part of s/qmail:

  1. qmail-dkim the main module for signing and verification, which can be used stand-alone.
  2. qmail-dksign a stub-module called from qmail-rspawn, providing the input message at queue/dkim/X in order to sign it by qmail-dkim and storing it under queue/dkim/Y ready for qmail-remote to be send.
  3. Figure: The interrelationship among qmail-rspawn, qmail-dksign, qmail-dkim, and qmail-remote.
    Arrows with open triangles show the message, the filled ones the program flow.

  4. qmail-dkverify a is stub-module being responsibe for DKIM verification and called via the QMAILQUEUE="bin/qmail-dkverify" meachanism (QMAILQUEUE_EXTRA) invoked by qmail-smtpd. This will provide the message with enhanced CRs at queue/dkim/X to be verified by qmail-dkim. The CR stripped message is scheduled to qmail-queue.
  5. Figure: The interrelationship between qmail-smptd, qmail-dkverify, qmail-dkim, and qmail-queue.

qmail-dkim supports the following signature algorithms:

  1. RSA with SHA1
  2. RSA with SHA256
  3. RSA with SHA1 and SHA256
  4. Ed25519 (with SHA256)
  5. Ed25519 with RSA-SHA256

In order to operate and to provide the canonicalization, this version of s/qmail (4.2.x) raises an additional directory structure: queue/dkim/N/, where N is the argumente given in conf-split. These directories are thus staging directories for DKIM. This implies, that any DKIM processed email is read and written several times, which decreases performance. Due to the many operational steps and interdependencies (as seen in the figures above) a lot of concurrent file descripictors are used. This limit may be raised far above 1024 in order to achieve a high qmail-remote concurrency. Otherwise, the following message in qmail-sends's log may be seen:

delivery 1525: deferral: qmail-spawn:_Unable_to_create_pipe_(too_many_FDS)._(#4.3.0)/

Under Linux, you need to raise the available file descriptors. Typically the setting is done via /etc/security/limits.conf. Here, you need to include the following:

#<domain> <type> <item> <value> # qmails soft nofile 4096 qmailr soft nofile 4096

Now, you raise the FD limit to 4096 for both the users qmails and qmailr. Depending on your throughput, this number shall be even higher! In order to become effective, you need to restart qmail-send.

RSA SHA-256 and Ed25519 keys required:

  1. A RSA private and public key, where the public key is presented as DER-enhanced and BASE64 encoded key in the DNS as TXT record typically given as default._domainkey.example.com where default is called the 'selector'.
  2. An Ed25519 private and public key, where the public key is given as 'naked' BASE64 encoded key in the DNS with a distinct selector. Here, I have chosen the default name to be'eddy' resulting in an DNS name eddy._domainkey.example.com.

5.12.1 DKIM sign outgoing messages by qmail-dksign

qmail-dksign is a setuid module:

-rws--x--x 1 qmailq sqmail 42024 Jan 3 16:04 /var/qmail/bin/qmail-dksign

and is only invoked by qmail-rspawn if the control file

control/dkimdomains

is present. Use the file control/dkimdomains to instruct qmail-dkim how to DKIM sign an outgoing message and provides the following customization:

senddomain:[selector,selector2]|[SDID]|[AUID]|[expire]|[r|s|t|u]:[1|2|3|4]:[l]

Apart form the senddomain followed by a colon, all other parameters are optional. By default, a RSA signatature for SHA256 is generated.

  • selector is used to prepend the artificial DKIM domain name selector._domainkey.example.com, where example.com is the sending domain for RSA signatures.
    If not explicitely given, it defaults to 'default'.
  • selector2 is used for binding of the Ed25519 public key.
    If not explicitely given, it defaults to 'eddy'.
  • SDID (Signing Domain Identifier) defaults usually to senddomain but may be chosen to any other FQDN. This permits to use the same FQDN in the DNS for different sending domains.
  • AUID is the Agent/User Identifier and may contain additional information for some policy to be obeyed. Here, it has no specific meaning.
    In case AUID matches '~' (the 'tilde') automatically the provided 'Mail From:' address is substituded here.
  • expire is the validity period of the signature in seconds and default to 604800.
  • r|s|t|u specifies, how to canonicalize the message:
    • s simple/strict,
    • t relax on header, simple on body,
    • u simple on header, relax on body.
    • r is relax on header and body, the default here.
  • 1|2|3|4|5 is the signature algorithm:
    1. RSA-SHA1.
    2. RSA-SHA256 (default).
    3. RSA-SHA1 + RSA-SHA256.
    4. Ed25519.
    5. Ed25519 + RSA-SHA256.
  • l tells to include the length of the message in the DKIM header.

Note: selector and SDID can be chosen freely for any DKIM key but need to reflect the settings in the DNS for the domain name given of the DKIM TXT record.

5.12.2 DKIM key repository: ssl/domainkeys/fqdn

In s/qmail/ the X.509 cerificates, private keys, and other TLS materials are stored in the protected direcory /var/qmail/ssl/. This is enhanced now for the subdirectory domainkeys followed by the sending domain:

ssl/domainkeys/mydomain.net/default -> rsa.private_default /rsa.public_default ssl/domainkeys/example.com/default -> rsa.private_default /rsa.public_default /eddy -> ed25519.private_eddy /ed25519.public_eddy

In order to work, a private domainkey file needs to exist at:

ssl/domainkeys/mydomain/<selector> -> [rsa|ed25519].private_<selector>

This structure roughly invertes the DNS name given for the DKIM public key:

<selector>._domainkey.fqdn <-> domainkeys/fqdn/<selector>

Here, fqdn could be either the domain name or the hostname of the MTA.

  • The file name <selector> needs to coincide with its setting in control/dkimdomains for the given domain.
  • You can avoid the name in control/dkimdomains if the RSA <selector> defaults to 'default' or the Ed25519 <selector2> defaults to 'eddy'.
  • Several different <selector>s may exisit in the domain's subdirectory; but only the one explicitely (or implicitely) given by control/dkimdomains becomes active for signing.

5.12.3 Generating DKIM keys

Domainkeys are generated by mkdkimkey; a script available in s/qmail's installation diretory ./scripts.
In order to enable it, follow these steps:

  1. Within ./scripts call make. This will customise the shell script and creates mkdkimkey.
  2. Copy this script to some useful location; potentially the s/qmail bin directory for easy use.

Upon calling the script without arguments, you get the usage information:

./mkdkimkey Usage: mkdkimkey [-hpc] [-s selector] [-m modulus] domain -h (show this help and exit) -v (verbose output) -p (print TXT record for domain) -s <selector> (set the selector) -m (set RSA modulo size; default: 2048 bits) -c (generate Ed25519-sha256 keys) RSA key generation: ================== mkdkimkey -s rsa-selector domain Ed25519 key generation: ====================== mkdkimkey -c -s ed-selector domain Key activation: ============== The created private key 'rsa|ed25519.private_<selector>' is automatically symlinked to '<selector>'. If included in 'control/dkimdomains', this file name is picked up by qmail-dksign for signing outgoing mails. The default name for '<selector>' is 'default'. This is implicitely assumed, if no particular '<selector>' is given.

mkdkimkey will automatically generate the required keys in the target directory given by domain. Please obey the permissions of the keys. The domainkey private file is automatically raised to be readable only by qmail-dkim operating as qmailq user:

-rw------- 1 qmailq sqmail 887 Aug 1 18:54 rsa.private_default

  • The generated key files always carry the selector as appendix and thus the matching private and public key file can be identified unambiguously.
  • Existing key files with the same selector name, are not automatically overwritten.
  • In case of overwriting key files with the same selector name, the old ones are copied with extension .previous.
  • The generated private key file is automatically symlinked to its selector name.
  • Key generation can be done on-the-fly without side-effects.

5.12.4 Provisioning the DKIM public key in DNS

mkdkimkey will of course also generate the public key for your domain. You can always ask for a BIND compliant output to be used as DKIM TXT record for your DNS authoritive server:

./mkdkimkey -p example.com Domain's public key in '/var/qmail/ssl/domainkeys/example.com' used for TXT DNS record: default._domainkey.example.com. IN TXT "v=DKIM1; k=rsa; t=y; p=MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQC5Zlzxycz9o/jNCEgJMHHPCwIKlSQC5S+4WP/GedtQlWHpRQsZNcJbIWxTaMuVtxfvqFCK9o0aDIapAc3Rgx+9yon6bH7gR43Rf8/Qe4Ojm2aBfiOStemX5aKjmgIAXZXrK9lEwCoHGaNrHzP+q9arspFyBzKO7+BcRhFKDjxuQQIDAQAB" You need to publish this TXT record in the DNS before activating [rsa|ed25519].private_default -> default for signing.

It is important to understand, that only this step defines the binding between the domain name and the public key, which of course needs to be complemented by the private key.

Within the DKIM TXT record you need to specify which signature algorithm is used and you may include constraints on the hash function and on the usage. This is detailed in RFC 6376 Section 3.6. You may modify the generated DNS TXT record manually for your needs and deploy it afterwards in the DNS.

In case, you have a different DNS authoritative server (like tinydns), use their own generic format.

5.12.5 Chosing the DKIM signature procedure

qmail-dksign works multi-tenant, thus you can create individual (or none) keys per sending domain. Apart from that it allows parenting, thus a subdomain inherits the key of its parent, and also a default signing procedure. In any case: More specific settings have precedence over less specific ones.

In the file dkimdomains the following information can be given to be provisioned to qmail-dkim:

  1. The sending domain before the colon (:). An asterix (*) is used to command signing an email origininating from any domain (even forwarded) with your domainkey.
  2. The selector which is used to prepend the domainkey to allow key rollover. The default selector is default but can be customized. The private is always referenced as default irrespectable of your choice. Use selector as suffix for the private key.
  3. The SDID (Sending Domain Information Identitifer). This allows you to use ONE domainkey to be supplied for serveral domains.
  4. The AUID (Agent User Identity) used to delegate the DKIM signature to another domain.
  5. The Lifetime of the signature. A receiving MTA should not accept DKIM signed message where the lifetime between sending and receiving is expired.
  6. The Canonicalization of the email message (relaxed, simple, ...).
  7. The Signature Algorithm to be used (1 or 2 or combined -3- for RSA or 4 for Ed25519).
  8. Commands to include the Length of the signed message in the DKIM header (potentially required, if additional footers are added).

Individual domains can be excluded for signing. Here, you can indicate those with a prepending exclamation mark (!). Thus, control/dkimdomains allows you to provide a complete description of your DKIM settings per sending domains. However, there are the following drawbacks:

  • You can specify to use concurrent RSA/SHA1 and RSA/SHA256 signatures to be included in the email's DKIM headers (they use the same private key).
  • You always have to create a working symlink binding a domain in control/dkimdomains with the actual available DKIM private key under ssl/domainkeys/<domain>/signkey. Otherwise, qmail-rspawn will complain with the following error message:

    delivery 106: failure: Unable_to_run_qmail-remote./

5.12.6 DKIM Key-Rollover

It should be a standard procedure to generate new DKIM keys after a certain period, at least each year; depending on the signature strength.

In order to become effective, a new (public) domain key needs to be provisioned in the DNS some time before the private key is used for signing. The time span depends on the TTL of the DKIM key in DNS and should be at least a small multiple of it. Several DKIM DNS records with different selectors per domain may co-exist.

To allow a smooth transition, the old and the new public key need to be present in the DNS. A DKIM validating application, like qmail-dkverify will fetch both and try both for signature verification and succeed, if one key matches.

To support key-rollover, generate new DKIM keys. The new one shall be given a different selector comprising the DNS TXT record newselector._domainkey.example.com to be published in the DNS alongside with the old one. This old one should become retired and removed from the DNS in the next days, after the new selector was used for signing.

mkdimkkey generates new keys while appending the selector information in both the private and the public key. This prevents any ambiguity about the matching of private and public keys.

5.12.7 DKIM Key-Sharing

Another common usage allows to use the same public/private key pair for different domains. Here, you need to synchronize your local data as provisioned to qmail-dksign and the data in the DNS. The basic idea is, that all domains referring to the same public key in DNS share the same private key by a symlinking the source in ssl/domainkeys/.

  • If you have raised keys for mydomain.org and would like the use the some for foreign.net set up the link:
    ssl/domainkeys/foreign.net -> ssl/domainkey/myddomain.org.
  • Include an entry in control/dkimdomains:
    mydomain.org:
    foreign.net:|mydomain.org|

Thus, the DNS DKIM name - the SDID - to be looked up as provisioned in the DKIM header in the mail, points to an already existing DNS record while allowing you to save raising additional entries in the DNS and thus using the same selector, private and public key for several domains for DKIM signing and verification.

5.12.8 DKIM signature header

The information included in the DKIM header generated by qmail-dksign depends on you settings for the respective domain in control/dkimdomains. Let's give a sample using Ed25519 signatures:

Dkim-Signature: v=1; a=ed25519-sha256; c=relaxed/relaxed; d=example.com; s=default; x=1669738939; i=me@example.com; q=dns/txt; h=Received:Date:Message-ID: From:Subject:To; bh=47DEQpj8HBSa+/TImW+5JCeuQeRkm5NMpJWZG3hSuFU=; b=d7wDZJfNbYAS5vN0NbeqzChFm8gMUZkUjQp9ZR0RdEM2lDL69wfF0PWvrm4 Kui77o5jKO4AFHPPBJfvynQbqAA==

The relevant pieces here are:

  • d=exmple.com: The domain name for which the DNS DKIM lookup shall be raised for (SDID) enhanced with
  • s=default, the selector,
  • i=me@example.com the AUID (not used here),
  • q=dns/txt use a DNS query to fetch the public key,
  • h=Receveive:Date... the email headers to be included in the signing procedure,
  • b=d7... the hash of body (here: SHA256),
  • b=d7wD ... the calculated Ed25519 signature covering message headers and including the value of the body hash as well.

Note: Unlike RSA, Ed25519 does not allow to calculate the signature in a 'stream' format; rather it is done in one step only.

5.12.9 Verifying DKIM signed inbound messages by qmail-dkverify

The verification of DKIM signed email is possible upon receipt by qmail-dkverify used queue as plug-in called by qmail-smtpd while setting up: QMAILQUEUE="bin/qmail-dkverify" either in the start script or within the sslserver's cdb, which is the recommended way. This allows to fine-grain the DKIM verification and also to avoid 'false-negatives' for domains with a wrong DKIM DNS setup.

qmail-dkverify is again a setuid module

-rws--x--x 1 qmailq sqmail 39408 Jan 3 16:04 /var/qmail/bin/qmail-dkverify

and works usually in annotation mode only. In case a a DKIM public key could be fetched from the respective DNS DKIM TXT record and the verification was successful, this this shown in the received email header only:

Received: (qmail 16024 invoked from network); 25 Dec 2022 16:16:22 -0000 X-Authentication-Results: piplus.example.com; dkim=pass; bigchief.heaven.com Dkim-Signature: v=1; a=ed25519-sha256; c=simple/simple; d=piplus.example.com; s=eddy; l=10; x=1671984982; q=dns/txt; h=Received: Date:Message-ID:From:Subject:To; bh=bj74/rslR0bn/dJ9cdn2gpaxFeM o+U6NOpehHz71RU4=; b=gD5uwfw5O9fksp++qd6NAxUAqA0jJskdcO7tgMi8r8i aYzZ9EivB3/sKXNNMT2PNlV1Di2VyPb/pUsjLmCa4Dw== Received: (qmail 1708 invoked by uid 1002); 25 Dec 2022 16:16:22 -0000

The 'X-Authentication-Results' header is included showing the result 'pass', the sending MTA 'piplus.example.com', and the DKIM verifying MTA: 'bigchief.heaven.com'. This additional header is based on the evaluation of the DKIM header and the DNS DKIM TXT public key for the domain (and selector) indicated in the signature header below, In case of a failure, the reason for this is explained verbose in parenthesis.

As can be seen: The DKIM signature is completely voluntary. Thus it is only provisioned if all boundary conditions are met. Therefore, it ist not useful as a trigger/filter for mail abuse. 'False-negatives' are mainly due to mis-configuration or expired/revoked keys.

However, you can reject emails with failed DKIM signature setting additionally the environment variable DKIM=+ alongside with the QMAILQUEUE="bin/qmail-dkverify" setting.

5.12.10 Addendum: DKIM Settings in DNS TXT Records

A DKIM record in the DNS is a simple TXT record, however with a defined data structure (RFC 6376):

  • The data section consists of individual items.
  • Each item is introduced by single character (the tag) followed by an equal sign (=): The identifier.
  • Each item is terminated by a semi-colon ';' except for the last token, where this can be omitted.
  • White spaces in the data section are permitted, but should be avoided to shorten the DNS information.

DKIM items:

Identifier Item Mandatory
v= DKIMv1 yes
p= public key yes
k= Signature algorithm (rsa/ed25519) no
h= Hash algorithm (sha1/sha256) no
s= Service Type ('email' or '*') no
t= Test flag ('y'/yes or 's'/special) no