qmail

Related links:

All qmail/mess822-related patches and scripts here are in the public domain.

The qmail-realrcptto patch

The qmail-realrcptto patch copies logic from qmail-send, qmail-lspawn, qmail-getpw, and qmail-local into qmail-smtpd and qmail-qmtpd, so that if a local delivery (i.e., one for a domain in /var/qmail/control/locals or virtualdomains) would eventually bounce due to a missing .qmail file, then that recipient address is rejected during the SMTP or QMTP protocol conversation. There are other qmail patches around that do similar jobs (badrcptto, etc.); the focus of this patch is to get this functionality with no additional administrative effort, rather than qmail-smtpd's running speed. You just set up your .qmail files, as you would have to do anyway, and the rest is automatic, though slower than a CDB lookup.

Addresses which use the default delivery instructions are never rejected by this patch, because they would never be bounced due to the lack of a .qmail file. If you're not sure whether this patch will reject mail for certain addresses on your system, you can check using the qtraceaddr script below. If it reports “bounce message due to lack of a .qmail file”, then qmail-realrcptto will reject mail for that address; otherwise mail will be accpeted.

This makes less work for the qmail machine, since these undeliverable addresses are rejected before ever entering the qmail queue (but it makes more work - to perform the check itself - in cases that would not bounce). Joe-job double bounces are also eliminated.

Note that with this patch, qmail-smtpd and qmail-qmtpd will always use the latest versions of the control files envnoathost, locals, percenthack, and virtualdomains, while qmail-send will use its cached copy in memory until it receives SIGHUP or is restarted. (Even after SIGHUP, qmail-send still uses the old envnoathost and percenthack.) So after control files are changed, and before qmail-send is notified, it is possible that a message recipient might be accepted through the network even though it would bounce later, or a message recipient might be rejected through the network even though the delivery would succeed.

If QMAILRRTDENYALL=1 is set in the environment for qmail-smptd and qmail-qmtpd, then each individual recipient address will be accepted, but the whole message will be rejected, to stop attackers from probing for valid addresses. It's still possible to probe by sending empty, single-recipient messages, and then sending the real message with all the recipients that weren't rejected.

This patch is less effective when there are .qmail-default files, or when qmaild does not have sufficient filesystem permissions to stat() users' .qmail files, since in those cases, qmail-smtpd cannot know that the delivery would later bounce due to the lack of a .qmail file. For example, I'm told that this patch is not useful for virtual domains managed with vpopmail, since an applicable .qmail-default file always exists. (I'm not familiar with vpopmail myself.) The patch may still be useful for other local or virtual domains on the same server, if they are not managed by vpopmail.

The qmail-branch patch

The qmail-branch patch enhances the .qmail delivery instruction language, narrowing the gap between qmail-local and procmail. If you have a sequence of lines like:

    ?label command arg ...
    ...
    :label

qmail-local will deliver the message to the command just as it does for a |command line, and if the command exits with status 99, qmail-local will skip down to the :label line; delivery instructions in the intervening lines are ignored. If the command exits with status other than 99, the result is the same as with a |command line. :label lines are otherwise ignored, just like #comment lines.

A label is a (possibly empty) sequence of non-space, non-tab, nonzero bytes. Text following a label on a ":" line is ignored. If there is no command on a ?label line, it's an unconditional jump. If a command exits 99 and the corresponding label is not found, all following delivery instructions are skipped (as with |command). There are no backward jumps.

This makes the .qmail language a little more useful, IMO, but not enough to cause trouble. :) (You get if-then-else, but no loops.) The syntax is a little ugly, but it gets the job done. The same functionality is already available with |command lines, but then you need multiple .qmail files, which exposes extra addresses to outside senders, so it gets a little more complicated.

A .qmail file using this feature might look like:

# Sort out mail from Sue.
?test [ "$SENDER" = sue@somewhere.org ] || exit 99
/home/b1ff/mail/sue/
# Skip all further processing.
?done
:test

# Is this a copy of a mailing list message?
?test iftocc `cat lists` || exit 99
# Bounce this copy.
|cat duplicate; exit 100
:test

# Deliver to the default mailbox.
/home/b1ff/mail/main/

Another way of tackling this problem is to do all deliveries for a given address from a single shell script, and then to deliver to just that shell script in the .qmail file. This requires manually rewinding the standard input between deliveries; you can do this with seek0. This is more error-prone, but faster, since a shell is spawned only once per message. I might write a wrapper for qmail-local that checks for such a delivery script called .qmailx adjacent to .qmail, executes it without forking if it exists, and executes qmail-local without forking otherwise.

The qtraceaddr script

The qtraceaddr Perl script illustrates how qmail decides how to deliver messages for any addresses given on the command line. If your qmail installation used non-default values for conf-qmail, conf-break, or the alias user, you can specify them in the $QMAIL, $QMAILBREAK, and $QMAILALIAS environment variables. For example:

# env QMAILBREAK=+ /path/to/qtraceaddr address@example.org

The mess822-nonroot patch

The mess822-nonroot patch prevents mess822 from installing its own leapsecs.dat file in /etc. This is useful for installing mess822 as a non-root user or when the existing /etc/leapsecs.dat file is more up-to-date than the one in mess822.