Why this patch

I wanted authenticated SMTP submission without patching ofmipd(8) or qmail-smtpd(8). Reasons:

  1. I haven’t been able to run the latest TLS patch because it conflicts with the popular SMTP AUTH patches, the last time someone published a merged patch was 2007, and one of my goals in life is to spend as little time as possible hand-merging patches, especially if they’re security-sensitive.
  2. I haven’t been able to run the 2007 TLS merged patch because that version doesn’t interoperate with Gmail (and probably other sites).
  3. Now that I think about it, the popular SMTP AUTH patches never felt truly qmail-ish.
  4. Thinking about it some more, yikes: SMTP AUTH can be abused to dictionary-attack the root password — and if I were running POP3, guessing right would run qmail-pop3d as root!
  5. With a more qmail-ish design, I could improve security and get new user-controlled features.

How and why acceptutils is different:

Without this patch

POP3 runs as the authenticated user, even if that happens to be root. SMTP AUTH runs with hardcoded privileges, needs checkpassword marked setuid-root in order to invoke it strangely, and is implemented by a patch to ofmipd or qmail-smtpd.

With this patch

POP3 runs as the authenticated user, who will never be root. SMTP AUTH works the same way, invokes checkpassword as intended, and does not require any changes to ofmipd or qmail-smtpd.

If someone manages to guess the root password, they won’t know they did: it looks exactly like any other failed login.

Both services run green thanks to these new programs:

  • reup runs a program repeatedly until it succeeds.
  • authup offers SMTP or POP3 authentication and calls checkpassword.
  • checknotroot refuses to run as UID 0.
  • fixsmtpio filters SMTP I/O and exit status to suit authup.

Install

  1. Extract a fresh copy of netqmail into “qmail-acceptutils”.
  2. Apply this patch (not yet released) there.
  3. Copy over conf-* from your main qmail source tree.

Then simply:

# make acceptutils
# cp reup authup checknotroot fixsmtpio /var/qmail/bin
# cp reup.8 authup.8 checknotroot.8 fixsmtpio.8 /var/qmail/man/man8

(You can also try to merge this patch into your main qmail source tree. But since it only adds new programs, why bother?)

Example: authenticated submission service

To accept messages via SMTP AUTH on localhost:26:

#!/bin/sh

exec tcpserver 127.0.0.1 26 \
    reup -t 5               \
    authup smtp             \
    checkpassword           \
    checknotroot            \
    fixsmtpio               \
    env RELAYCLIENT="" ofmipd

and put these rules in control/fixsmtpio.

# if client closes the connection, tell authup to be happy
AUTHUP_USER:clienteof::*:0

# if server greets us unhappily, notify authup
AUTHUP_USER:greeting::4*:14
AUTHUP_USER:greeting::5*:15

# if server times out, hide message (authup has its own)
AUTHUP_USER:timeout::*:16:

# always (authenticated or not) replace greeting
:greeting::2*::&fixsmtpio

# always replace greeting in HELO/EHLO
:helo::2*::&fixsmtpio
:ehlo::2*::&fixsmtpio

# always prepend acceptutils link to HELP message
:help::*::&fixsmtpio

# always replace greeting in QUIT
:quit::2*::&fixsmtpio

# don't advertise AUTH or STARTTLS
AUTHUP_USER:ehlo::250?AUTH*::
AUTHUP_USER:ehlo::250?STARTTLS::

# don't allow AUTH or STARTTLS
AUTHUP_USER:auth:NOOP :*::502 unimplemented (#5.5.1)
AUTHUP_USER:starttls:NOOP :*::502 unimplemented (#5.5.1)

Note that stunnel (or similar) is needed to encrypt the service and make it available over the network.

Note that authenticated submission must be on a separate port (or host) from incoming SMTP. If you’re currently using port 25 for both, consider moving submission to port 587, or wait for a future version of acceptutils.

Example: retire previous SMTP AUTH patch

When you’re satisfied with acceptutils, at leisure, remove your previous SMTP AUTH patch and chmod -s checkpassword. Once you’re running an unpatched ofmipd, remove env RELAYCLIENT="".

(Exception: if you’re relying on AUTH support in qmail-remote, don’t remove your previous patch.)

New user-controlled features

Sender address rewriting

Since authenticated submission runs as you, messages you send can be modified according to your settings.

For example, since checkpassword sets $HOME, you can give ofmipd your own CDB of rules for rewriting envelope senders and From: headers.

Here’s an ofmipd-with-user-cdb wrapper to do this:

#!/bin/sh

ofmipd_arg=""
user_cdb="$HOME/.ofmipd/rules.cdb"
[ -f "${user_cdb}" ] && ofmipd_arg="${user_cdb}"

exec ofmipd "${ofmipd_arg}"

Stateful filtering

Since authenticated submission and incoming delivery both run as you, messages you send can influence what happens to messages you receive.

For example, filtering your submitted messages through pymsgauth-filter inserts a token into the headers and records it in $HOME, where pymsgauth-confirm can find and match the token in incoming messages.

Here’s how to post to DJB’s mailing lists from any AUTH-capable mail client without ever seeing a qsecretary challenge again:

  1. Apply the QMAILQUEUE patch (available here for ofmipd, included with netqmail for qmail-smtpd).
  2. Install qmail-qfilter.
  3. Install rejectutils.
  4. Install pymsgauth with the pymsgauth-filter patch.
  5. Configure $HOME/.pymsgauth/pymsgauthrc and the relevant .qmail file.
  6. Add pymsgauth-filter to control/ofmipfilters.
  7. Set QMAILQUEUE="qmail-qfilter-ofmipd-queue" and PYMSGAUTH_TOLERATE_UNCONFIGURED=1 in your service’s environment.

Security

Please consider carefully the risks and mitigations and decide for yourself whether acceptutils might safely improve your SMTP AUTH submission service.

Possible future directions

Here are some ideas for the future of acceptutils.

Get this patch

When it’s ready, I’ll announce it on the qmail list.

Improve this patch

If you see a simpler way to do it, I’d love to know.