4. Recipients Verification
qmail-smtpd accepts messages if the SMTP domain part of Recipient address ("Rcpt to: <recip@domain>") matches an entry in control/rcpthosts or control/morercpthosts.cdb. The existence of a mailbox/maildir for the corresponding SMTP Recipient is checked later in the delivery chain. In case no Mailbox/Maildir exists, the message is bounced back to the SMTP Sender ("Mail From: <send@example.com>").
For normal SMTP mail traffic that's fine as long as the rate of undeliverable messages don't exceed 10% and the Sender is 'legitmate'; ie. exists. Today's situation is different: Spam and Virus attacks with forged/faked Sender addresses to a bunch of random Recipient addresses yield an undeliverable rate up to 90%. Worse, the generated bounces will never reach the Sender and a double-bounce is eventually send to the postmaster.
Quick links:
- 4.1 Introduction to the Recipients mechanism
- 4.2 Setting up control/recipients
- 4.3 Generating a cdb with Recipient addresses
- 4.4 Included PAMs
- 4.5 Smart Rejections/Tarpitting Reloaded
4.1 Introduction to the Recipients mechanism
qmail-smtpd is aware of acceptable recipients, which are fetched from an external source (Recipients mechanism). Which source to query depends on the domain-part of the recipient address.
- The recipients are kept either in 'fastforward' compatible cdbs for quick lookup during the SMTP session (including users/assign.cdb), or
- are available by means of a 'checkpassword' compatible Pluggable Authentication Module (PAM).
The recipients check is done only in a none-RELAYCLIENT case and after control/rcpthosts, control/morercpthosts.cdb have been successfully consulted.
4.1.1 VERP Support
The Recipients mechanism supports natively Qmail's address extensions (VERP). This is in particular required fo EZMLM addreses.
Consider a recipient address like 'foo@mydomain.com. A VERP address would like 'foo-bar@mydomain.com. In order to accept emails for plain and VERP addresses you have to include:
- foo@mydomain.com and
- foo-@mydomain.com
as recipient addresses. The dash '-' tells the recipients extension to accept all VERP addreses. Of course, generic full qualified mail addreses with a dash a recognized as well.
The 'dash' character need to be substituted with the one defined in conf-break during setup.
4.1.2 SRS Support
In case SRS is used, bounced SRS messages need to be considered as well. For each domain (say: mydomain.com) - to be subject of SRS - include a generic entry for the recipients extension:
- If 'domain.com' is a domain for SRS re-writing,
- Include 'SRS0+@mdomain.com' here, in order to receive bounces.
Result: The recipients extension will recognise this address to be acceptable. It should be noted, that the extension character needs to comply to your settings.
4.1.3 Customization
The Recipients mechanism needs no customization except for the following circumstances:
- Compile-time options:
- Your Qmail address extension string is not '-'. This can be modified in conf-break.
- If in case in a PAM lookup the PAM returns with a '111', the email will rejected for temporary reasons. If however, you disable #define PAM111421 in qmail-smtpd.c, the email will be accepted in case of a PAM failure.
- Run-time options:
- Since some MTAs (namely Exchange) interprete a SMTP 450 reply to try to deliver the email even more frequently, the default behavior of qmail-smtpd is now to issue a 550 SMTP reply resulting in a "550, sorry no mailbox by that name (#5.7.1)" message. The advantage is, that a potential Sender receives a quick bounce with the propper reason, instead a deferred one. The disadvantage is, that this might be used for address harvesting
- However, setting the environment variable RECIPIENTS450 in case the Recipients check is negative, qmail-smtpd issues the error: "450 sorry, mailbox currently unavailable (#4.2.1)".
In any other case, a SMTP temporary failure protocol error is issued to the client saying:
- "550 sorry, no mailbox by that name (#5.7.1)" unless
- the environment variable RECIPIENTS450 is specified and qmail-smtpd issues the rejection message "450 sorry, mailbox currently not available (#4.2.1)"
4.1.4 Operation and Results
With the Recipients mechanism qmail-smtpd will act for none-RELAYCLIENTs as follows
- a) The domain part of the Recipient address is checked against rcpthosts/morercpthosts.cdb and accepted if either it matches or the domain is not provided.
-
b) the Recipient address is checked against all readable
sources as listed in control/recipients and accepted
if
- either the entire address matches,
- a VERP-part of the address matches,
- the domain part of address is wildcarded or,
- for the unqualified address an entry exist in the form 'user@localhost', or
- perhaps, the fall-through statement '!*' is finally provided.
4.2 Setting up control/recipients
The recipients lookup is triggered by the recipient domain, thus is domain-specific. You can specify which lookup is performed per domain within control/recipients. Consider the following:
- An entry 'example.com' is used to match 'example.com' and
in addition all subdomain addresses '*.example.com';
depending of s course control/rcpthosts. - An entry '@example.com' serves as exact match for the domain address.
- The entry '*' will match all domains for the respective lookup.
- Reversely, domains flagged as '!domain.com' are not queried and all recipients for this domain are accepted.
The Recipients mechanism can be used in a 'fail-closed' or
'fail-open' mode for the domains included in control/recipients.
Typically the recipient check is done 'fail-closed', thus if all
queries are negative, the incoming email with this recipient address
will be rejected.
A 'fail-open' behaviour can be achieved adding '!*' as last statement in control/recipients. Thus, emails for domains not listed in control/recipients will finally be accepted.
Method | Operation |
Legacy | Put 'recipients.cdb' into control/recipients. This is a backward compatible mode |
Per Domain cdbs | Put 'example.com:example.cdb' in control/recipients and you advise the Recipients mechanism to do a per-domain lookup |
Global cdbs | Use '*:users/recipients.cdb' in control/recipients. This is equivalent to (1.) |
Local Unix accounts | Use '@mydomain.com:=' in control/recipients and the file users/assign.cdb is consulted for local users at this domain. The wildcard '*' for the domain is accepted as well. |
Per Virtual Domain | Put 'example.com|bin/qmail-vmailuser' into control/recipients and the Recipients mechanism will use qmail-vmailuser after the "|" to check the existence of the provided RCPT TO for this particular Domain |
Global Virtual Domain | Put '*|bin/qmail-vmailuser' into control/recipients and the Recipients mechanism will use use qmail-vmailuser after the "|" to check the existence of the provided RCPT TO |
LDAP PAM | Put 'example.com|ldap_pam.pl <ldaphost>' into control/recipients and the Recipients mechanism will use the program defined after the "|" to check the existence of the provided RCPT TO for this particular Domain |
Gobal LDAP PAM | Put '*|ldapam myldapserver' into control/recipients and you delegate the entire verification of the RCPT TO to the program in charge |
Wildcarded domain | Prepend the domain name with a '!' and emails for this domain will be entirely accepted: '!localhost' |
Pass-Thru for unlisted domains | Use '!*' as last statement in control/recipients |
Lines in control/recipients starting with a '#' are not evaluated, thus are treated as comment lines.
4.3 Generating a cdb with Recipient addresses
Build a list of recipients (with full qualified addresses):
- Use 'qmail-pwd2recipients' to build this list for local system users.
- Use 'qmail-alias2recipients' to build this list for qmail alias users (ie. postmaster, root).
- Use 'qmail-users2recipients' to build this list for qmail users (as per users/assign).
- You can use 'qmail-vpopmail2recipients' for vpopmail users.
Verify that list to be found under users/recipients.
If you have a different Qmail home directory, modify the above scripts.
You may need to change "localhost" in the above scripts to the real hostname.
- Run qmail-recipients
to transform that list into a cdb:
- users/recipients.cdb
- After the successful generation of the recipients.cdb
- you can rename it to your taste.
- Edit control/recipients and
- include users/recipients.cdb therein.
- If you have 'fastforward' cdbs (those which are generated
by 'setforward')
- you have to place the output somewhere in a subdirectory under Qmail's home directory and include those into control/recipients.
- At that time, your control/recipients file may look like:
mydomain.com:control/mydomain.cdb users/recipients.cdb etc/fastforward.cdb - You can add an arbitary number of cdbs to control/recipients.
- Any change regarding control/recipients and/or the content of the cdbs is effective on the fly.
- If you run EZMLM, you have to set up a list of recipient
addresses for all your mailing lists.
- Simply put the full qualified list name into recipients;
- the qmail-smtpd Recipients mechanism supports EZMLM VERPs usage.
- You may need to adjust the provided scripts qmail-pwd2recipients, qmail-users2recipients, and qmail-alias2recipient to your need; these are samples.
4.4 Checkpassword compatible PAMs for Recipients
Recipient's PAM mechanism allows to include any Checkpassword compliant modules. Currently, I ship which s/qmail the following native PAMs:
- qmail-smtpam to query a remote MTA.
- qmail-vmailuser to verify the existence of virtual domain users.
4.4.1 Requirements for a Checkpassword compatible PAM
The checkpassword API is defined in:
http://cr.yp.to/checkpwd/interface.html
and typically consists of the string:
username\0password\0timestamp\0otherdata\0
written to file descriptor 3 (FD 3) to be read by the checkpassword compatible PAM.
For email address (recipient) verification, we replace
username\0
with
email-address\0
ie.
recipient@domain.tld\0
- The PAM fetches this information and checks for its existance
in any external resource, for example a LDAP
directory or a SQL database. - The PAM returns a '0' in case of successful verification, otherwise a '1'; and perhaps a '111' in case of problems.
- recipient's checkpassword API allows to enter up to five additional arguments; which are specific to the PAM.
The attached PERL ldap_mail.pl serves as a sample.
Note: The PAM has to be either in the Unix $PATH or explicitely defined in control/recipients.
4.4.2 Calling a PAM from the recipients file
The general syntax invoking a PAM from control/recipients follows the principals invoking a cdb. However, the triggering token is a '|' and the PAM may be accompanied by at most five (5) arguments separated by white spaces:
- The first line tells qmail-smtpd to invoke the standard ldap_pam.pl
in case the recipients's domain part matches exactly 'mydomain.com' and
tries to verify the local part of the recipient address from the LDAP directory server
named 'ldap.mydomain.com' while doing a simple bind with DN 'admin' and password 'secret'.
Note: Usually the DN is more complicated and in particular a certain search-base is necessary. - The second line tries to connect to an Exchange server by means of the qmail-smtpam at 'exchange.customer.com' in case a subset of the recipients' remote part matches 'customer.com'. This includes in particular a potential user named 'postmaster@exchange.customer.com'.
- It should be noted, that in both cases above, several PAMs and perhaps several target systems might be defined for the lookup of the very same domain. In particular for case two, several Exchange servers can be specified as redundancy. In case the first one is not responsive, the next one is tried.
- The third line tries to find any other recipients targeting 'global.net' in the 'global' directory server at 'ldap.global.net' using a customized LDAP PAM named 'ldapam'.
- Finally, the last line '!*' tells qmail-smtpd to accept any other recipients for domains not been explicitly listed before.
4.5 Included PAMs
s/qmail comes with a variety of PAMs available. Given a busy system, the lookup of a user database maybe costly. In case of a lexical or dictionary user attack this impacts the system. Here, any method with least system impact is preferable. In general, this is achieved by a 'C' module.
4.5.1 qmail-smtpam
The additional module qmail-smtpam can be considered as stripped-down version of qmail-remote providing the first steps of the email dialoge using the standard Helo message, a Nullsender Mail From: address, and finally the provided the Rcpt To: address available on FD 3.
Once it receives the SMTP return code, it exits either with '0' in case of a '250' positive reply, a '1' in case of a failure, or a '111' realizing communication problems.
qmail-smtpam thus uses a standard SMTP dialoge without advanced features, though complying to RFC 821 while using the canonical name for host to connect to.
4.5.2 qmail-vmailuser
The qmail-vmailuser is the 'swiss knife' to allow RECIPIENT validation upon SMTP recipt.
qmail-vmailuser is SUID'ed by root to be able to provide email 'mailbox' verification; however you may lower those permissions.
qmail-vmailuser has the following working behavior:
- qmail-vmailuser is typically called by the RECIPIENTS mechanism from SQMAIL/control/recipients.
- Given the file SQMAIL/control/virtualusers it checks for exisiting 'mailboxes'
- for a standards 'virtual user' in s/qmail, or a
- vpopmail's user mailbox, or a
- vmailmgr's user mailbox (or any).
If such a mailbox is recognized, it will deliver the email; otherwise will refuse delivery.
qmail-vmailuser has internal knowlegde about the delivery to those virtual mail managers and thus needs typically no customization apart from the path of the home directories.
4.5.3 The ldap_pam.pl
The ldap_pam.pl is a PERL script using the PERL library 'Net::LDAP' from http://www.cpan.org while providing simple and TLS encrypted (strong) binds to an LDAP directory server.
Syntax of the ldap_pam.pl:
ldpap_pam.pl -h:host:port -d:DN -w:password -b:base -s:scope -c:certificate -l(ogging) -ll
- The bind is realized for host.
- The default port is 389 in
case of a simple bind and 636 once a certificate is provided (strong bind).
Other ports can be specified. - In case of a simple bind, the Distinguished Name DN of a (technical) LDAP user and its password has to be provided.
- For for the strong bind, additionally a certificate needs to be presented.
The file certificate includes both the X.509 certificate and the un-encrypted keyfile.
Note: This feature is untested and needs certainly refinements. It should be considered, that any TLS encrypted connection to the LDAP directory is time- and computing intensive. By definition, the email addresses stored in the LDAP are public information.
The ldap_pam.pl shall be considered as prototype only. Due to the large variety of LDAP servers and possibilities to store the information into and to fetch it from the directory, there is the need for specific customization. Logging might be activated in two steps.
Fetching the RCPT TO: information from the LDAP directory requires some customization for the LDAP server:
- credentials provided by its password -- needs to have (reed) access for all relevant entries with the LDAP attribute mail while using the search path base with the provided scope.
- In case other attributes may be retrieved, those can be specified in the PAM.
- Take care about multi-attribute and attributes including multi-recipient addresses and case-sensitivity (of the local part of the mail address).
Note: This PERL module is outdated and need refactoring. A generic qmail-ldapam is in preparation.
4.6 Smart Rejections/Tarpitting Reloaded
Defining a TARPITCOUNT can be used to terminate the SMTP session if the number of invalid Recipients ("Rcpt to:") exceeds the TARPITCOUNT. Unlike the typical tarpitting mechanism, this is a hard limit (Smart Rejection).
- All invalid recipient addresses will be rejected.
- However, the client can continue with the DATA command and
- finally emails received to valid Recipients encountered before will be accepted and queued.
However, defining a TARPITCOUNT together with a TARPITDELAY < 0 acts as 'tarpitting reloaded':
- All invalid RECIPIENT addresses will be rejected in the first place.
- If the TARPITCOUNT limt has been exaggerated, the DATA command will not be honored.
- Thus emails from notorious Sender will not be accepted.
- This information will survive SMTP Resets.