437 lines
20 KiB
Text
437 lines
20 KiB
Text
Please note that this file is not called ``Internet Mail For Dummies.''
|
|
It _records_ my thoughts on various issues. It does not _explain_ them.
|
|
Paragraphs are not organized except by section. The required background
|
|
varies wildly from one paragraph to the next.
|
|
|
|
In this file, ``sendmail'' means Allman's creation; ``sendmail-clone''
|
|
means the program in this package.
|
|
|
|
|
|
1. Security
|
|
|
|
There are lots of interesting remote denial-of-service attacks on any
|
|
mail system. A long-term solution is to insist on prepayment for
|
|
unauthorized resource use. The tricky technical problem is to make the
|
|
prepayment enforcement mechanism cheaper than the expected cost of the
|
|
attacks. (For local denial-of-service attacks it's enough to be able to
|
|
figure out which user is responsible.)
|
|
|
|
qmail-send's log was originally designed for profiling. It subsequently
|
|
sprouted some tracing features. However, there's no way to verify
|
|
securely that a particular message came from a particular local user;
|
|
how do you know the recipient is telling you the truth about the
|
|
contents of the message? With QUEUE_EXTRA it'd be possible to record a
|
|
one-way hash of each outgoing message, but a user who wants to send
|
|
``bad'' mail can avoid qmail entirely.
|
|
|
|
I originally decided on security grounds not to put qmail advertisements
|
|
into SMTP responses: advertisements often act as version identifiers.
|
|
But this problem went away when I found a stable qmail URL.
|
|
|
|
As qmail grows in popularity, the mere knowledge that rcpthosts is so
|
|
easily available will deter people from setting up unauthorized MXs.
|
|
(I've never seen an unauthorized MX, but I can imagine that it would be
|
|
rather annoying.) Note that, unlike the bat book checkcompat() kludge,
|
|
rcpthosts doesn't interfere with mailing lists.
|
|
|
|
qmail-start doesn't bother with tty dissociation. On some old machines
|
|
this means that random people can send tty signals to the qmail daemons.
|
|
That's a security flaw in the job control subsystem, not in qmail.
|
|
|
|
The resolver library isn't too bloated (before 4.9.4, at least), but it
|
|
uses stdio, which _is_ bloated. Reading /etc/resolv.conf costs lots of
|
|
memory in each qmail-remote process. So it's tempting to incorporate a
|
|
smaller resolver library into qmail. (Bonus: I'd avoid system-specific
|
|
problems with old resolvers.) The problem is that I'd then be writing a
|
|
fundamentally insecure library. I'd no longer be able to blame the BIND
|
|
authors and vendors for the fact that attackers can easily use DNS to
|
|
steal mail. Possible solution: replace dns.c with something that passes
|
|
requests (reliably!) to a local daemon; call the original resolver
|
|
library from that daemon.
|
|
|
|
NFS is the primary enemy of security partitioning under UNIX. Here's the
|
|
story. Sun knew from the start that NFS was completely insecure. It
|
|
tried to hide that fact by disallowing root access over NFS. Intruders
|
|
nevertheless broke into system after system, first obtaining bin access
|
|
and then obtaining root access. Various people thus decided to compound
|
|
Sun's error and build a wall between root and all other users: if all
|
|
system files are owned by root, and if there are no security holes other
|
|
than NFS, someone who breaks in via NFS won't be able to wipe out the
|
|
operating system---he'll merely be able to wipe out all user files. This
|
|
clueless policy means that, for example, all the qmail users have to be
|
|
replaced by root. See what I mean by ``enemy''? ... Basic NFS comments:
|
|
Aside from the cryptographic problem of having hosts communicate
|
|
securely, it's obvious that there's an administrative problem of mapping
|
|
client uids to server uids. If a host is secure and under your control,
|
|
you shouldn't have to map anything. If a host is under someone else's
|
|
control, you'll want to map his uids to one local account; it's his
|
|
client's job to decide which of his users get to talk NFS in the first
|
|
place. Sun's original map---root to nobody, everyone else left alone---
|
|
is, as far as I can tell, always wrong.
|
|
|
|
|
|
2. Injecting mail locally (qmail-inject, sendmail-clone)
|
|
|
|
RFC 822 section 3.4.9 prohibits certain visual effects in headers.
|
|
qmail-inject doesn't waste the time to enforce this absurd restriction.
|
|
If you will suffer from someone sending you ``flash mail,'' go find a
|
|
better mail reader.
|
|
|
|
qmail-inject's ``Cc: recipient list not shown: ;'' successfully stops
|
|
sendmail from adding Apparently-To. Unfortunately, old versions of
|
|
sendmail will append a host name. This wasn't fixed until sendmail 8.7.
|
|
How many years has it been since RFC 822 came out?
|
|
|
|
sendmail discards duplicate addresses. This has probably resulted in
|
|
more lost and stolen mail over the years than the entire Chicago branch
|
|
of the United States Postal Service. The qmail system delivers messages
|
|
exactly as it's told to do. Along the same lines: qmail-inject is both
|
|
unable and unwilling to support anything like sendmail's (default)
|
|
nometoo option. Of course, a list manager could support nometoo.
|
|
|
|
There should be a mechanism in qmail-inject that does for envelope
|
|
recipients what Return-Path does for the envelope sender. Then
|
|
qmail-inject -n could print the recipients.
|
|
|
|
Should qmail-inject bounce messages with no recipients? Should there be
|
|
an option for this? If it stays as is (accept the message), qmail-inject
|
|
could at least avoid invoking qmail-queue.
|
|
|
|
It is possible to extract non-unique Message-IDs out of qmail-inject.
|
|
Here's how: stop qmail-inject before it gets to the third line of
|
|
main(), then wait until the pids wrap around, then restart qmail-inject
|
|
and blast the message through, then start another qmail-inject with the
|
|
same pid in the same second. I'm not sure how to fix this. (Of course,
|
|
the user could just type in his own non-unique Message-IDs.)
|
|
|
|
The bat book says: ``Rules that hide hosts in a domain should be applied
|
|
only to sender addresses.'' Recipient masquerading works fine with
|
|
qmail. None of sendmail's pitfalls apply, basically because qmail has a
|
|
straight paper path.
|
|
|
|
I expect to receive some pressure to make up for the failings of MUA
|
|
writers who don't understand the concept of reliability. (``Like, duh,
|
|
you mean I was supposed to check the sendmail exit code?'')
|
|
|
|
|
|
3. Receiving mail from the network (tcp-env, qmail-smtpd)
|
|
|
|
RFC 1123 requires VRFY support, but says that it's okay if an
|
|
implementation can be configured to not allow VRFY. qmail-smtpd doesn't
|
|
allow VRFY. If you desperately want your SMTP server (i.e., inetd) to
|
|
provide useful information for VRFY, just compile and install sendmail.
|
|
Were the RFC 1123 writers aware of the as-if principle of interface
|
|
specification? ... They say that VRFY and EXPN are important for
|
|
tracking down cross-host mailing list loops. Catch up to the 1990s,
|
|
guys: with Delivered-To, mailing list loops do absolutely no damage,
|
|
_and_ one of the list administrators gets a bounce that shows exactly
|
|
how the loop occurred. Solve the problem, not the symptom. ... There's a
|
|
vastly superior alternative to EXPN. Hint: finger postmaster@ai.mit.edu.
|
|
|
|
Should dns.c make special allowances for 127.0.0.1/localhost?
|
|
|
|
badmailfrom (like 8BITMIME) is a waste of code space.
|
|
|
|
|
|
4. Adding messages to the queue (qmail-queue)
|
|
|
|
Should qmail-queue try to make sure enough disk space is free in
|
|
advance? When qmail-queue is invoked by qmail-local or (with ESMTP)
|
|
qmail-smtpd or qmail-qmtpd, it could be told a size in advance. I wish
|
|
UNIX had an atomic allocate-disk-space routine...
|
|
|
|
The qmail.h interface (reflecting the qmail-queue interface, which in
|
|
turn reflects the current queue file structure) is constitutionally
|
|
incapable of handling an address that contains a 0 byte. I can't imagine
|
|
that this will be a problem.
|
|
|
|
Should qmail-queue not bother queueing a message with no recipients?
|
|
|
|
|
|
5. Handling queued mail (qmail-send, qmail-clean)
|
|
|
|
The queue directory must be local. Mounting it over NFS is extremely
|
|
dangerous---not that this stops people from running sendmail that way!
|
|
Perhaps it is worth putting together a diskless-host qmail package with
|
|
just qmail-inject and an SMTP client in place of qmail-queue. Sending
|
|
mail to the server via SMTP is of course vastly better than trying to do
|
|
anything over NFS. If the NFS server is up but the mail server is down,
|
|
users will just have to wait.
|
|
|
|
Queue reliability demands that single-byte writes be atomic. This is
|
|
true for a fixed-block filesystem such as UFS, and for a logging
|
|
filesystem such as LFS.
|
|
|
|
qmail-send uses 8 bytes of memory per queued message. Double that for
|
|
reallocation. (Fix: use a small forest of heaps; i.e., keep several
|
|
prioqs.) Double again for buddy malloc()s. (Fix: be clever about the
|
|
heap sizes.) 32 bytes is worrisome, but not devastating. Even on my
|
|
disk-heavy memory-light machine, I'd run out of inodes long before
|
|
running out of memory.
|
|
|
|
Some mail systems organize the queue by host. This is pointless as a
|
|
means of splitting up the queue directory. The real issue is what to do
|
|
when you suddenly find out that a host is up. For local SLIP/PPP links
|
|
you know in advance which hosts need this treatment, so you can handle
|
|
them with virtualdomains and serialmail.
|
|
|
|
For the old queue structure I implemented recipient list compression:
|
|
if mail goes out to a giant mailing list, and most of the recipients are
|
|
delivered, make a new, compressed, todo list. But this really isn't
|
|
worth the effort: it saves only a tiny bit of CPU time.
|
|
|
|
qmail-send doesn't have any notions of precedence, priority, fairness,
|
|
importance, etc. It handles the queue in first-seen-first-served order.
|
|
One could put a lot of work into doing something different, but that
|
|
work would be a waste: given the triggering mechanism and qmail's
|
|
deferral strategy, it is exceedingly rare for the queue to contain more
|
|
than one deliverable message at any given moment.
|
|
|
|
Exception: Even with all the concurrency tricks, qmail-send can end up
|
|
spending a few minutes on a mailing list with thousands of remote
|
|
entries. A user might send a new message to a remote address in the
|
|
meantime. Perhaps qmail-send should limit its time per message to,
|
|
say, thirty recipients. This will require some way to mark recipients
|
|
who were already done on this pass. Possible approach: Maintain two todo
|
|
lists (for both L and R). Always work on the earlier todo list. Move
|
|
deferrals to the other todo list.
|
|
|
|
qmail-send will never start a pass for a job that it already has. This
|
|
means that, if one delivery takes longer than the retry interval, the
|
|
next pass will be delayed. I implemented the opposite strategy for the
|
|
old queue structure. Some hassles: mark() had to understand how job
|
|
input was buffered; every new delivery had to check whether the same
|
|
mpos in the same message was already being done.
|
|
|
|
Some things that qmail-send does synchronously: queueing a bounce
|
|
message; doing a cleanup via qmail-clean; classifying and rewriting all
|
|
the addresses in a new message. As usual, making these asynchronous
|
|
would require some housekeeping, but could speed things up a bit.
|
|
(Making bounces asynchronous, without POSIX waitpid(), means that
|
|
wait_pid() has to keep a buffer of previous wait()s. Ugh.)
|
|
|
|
fsync() is a bottleneck. To make this asynchronous would require gobs of
|
|
dedicated output processes whose only purpose in life is to watch data
|
|
get written to the disk. Inconceivable! (``You keep using that word. I
|
|
do not think that word means what you think it means.'')
|
|
|
|
On the other hand, I could survive without fsync()ing the local and
|
|
remote and info files as long as I don't unlink todo. This would require
|
|
redefining the queue states. I need to see how much speed can be gained.
|
|
|
|
Currently qmail-send sends at most one bounce message for each incoming
|
|
message. This means that the sender doesn't get flooded with copies of
|
|
his own message. On the other hand, a single slow address can hold up
|
|
bounces for a bunch of fast addresses. It would be easy to call
|
|
injectbounce() more often. What is the best strategy? This feels like
|
|
the TCP-buffering issue... don't want to pepper the other guy with
|
|
little packets, but do want to get the data across.
|
|
|
|
qmail-stop implementation: setuid to UID_SEND; kill -TERM -1. Given how
|
|
simple this is, I'm not inclined to set up some tricky locking solution
|
|
where qmail-send records its pid etc. But I just know that, if I provide
|
|
this qmail-stop program, someone will screw himself by making another
|
|
uid the same as UID_SEND, or making UID_SEND be root, or whatever.
|
|
Aargh. Maybe use another named pipe... New solution: Run qmail-start
|
|
under an external service controller---it runs in the foreground now.
|
|
|
|
Bounce messages could include more statistical information in the first
|
|
paragraph: when I received the message, how many recipients I was
|
|
supposed to handle, how many I successfully dealt with, how many I
|
|
already told you about, how many are still in the queue. Have to
|
|
emphasize that the number of recipients _here_ is perhaps less than the
|
|
number of recipients on the original message.
|
|
|
|
The readdir() interface hides I/O errors. Lower-level interfaces would
|
|
lead me into a thicket of portability problems. I'm really not sure what
|
|
to do about this. Of course, a hard I/O error means that mail is toast,
|
|
but a soft I/O error shouldn't cause any trouble.
|
|
|
|
job_open() or pass_dochan() could be paranoid about the same id,channel
|
|
already being open; but, since messdone() is so paranoid, the worst
|
|
possible effect of a bug along these lines would be double delivery.
|
|
|
|
Mathematical amusement: The optimal retry schedule is essentially,
|
|
though not exactly, independent of the actual distribution of message
|
|
delay times. What really matters is how much cost you assign to retries
|
|
and to particular increases in latency. qmail's current quadratic retry
|
|
schedule says that an hour-long delay in a day-old message is worth the
|
|
same as a ten-minute delay in an hour-old message; this doesn't seem so
|
|
unreasonable.
|
|
|
|
Insider information: AOL retries their messages every five minutes for
|
|
three days straight. Hmmm.
|
|
|
|
|
|
6. Sending mail through the network (qmail-rspawn, qmail-remote)
|
|
|
|
Are there any hosts, anywhere, whose mailers are bogged down by huge
|
|
messages to multiple recipients at a single host? For typical hosts,
|
|
multiple RCPTs per SMTP aren't an ``efficiency feature''; they're a
|
|
_slowness_ feature. Separate SMTP transactions have much lower latency.
|
|
|
|
The multiple-RCPT bandwidth gain _might_ be noticeable for a machine
|
|
that sends most messages to a smarthost. It would be easy to have
|
|
qmail-rspawn supply qmail-remote with all the addresses at once, as long
|
|
as qmail-send says when it's about to block... Putting recipients into
|
|
the right order is clearly the UA's job. One multiple-RCPT pitfall is
|
|
that a remote host might not be able to deal with (say) 10 recipients,
|
|
even though RFC 821 says everyone has to be able to handle 100;
|
|
qmail-rspawn would have to notice this and back off. (Not that other
|
|
mailers do. Sometimes I'm amazed Internet mail works at all.)
|
|
|
|
In the opposite direction: It's tempting to remove the @host part of the
|
|
qmail-remote recip argument. Or at least avoid double-dns_cname.
|
|
|
|
There are lots of reasons that qmail-rspawn should take a more active
|
|
role in qmail-remote's activities. It should call separate programs to
|
|
do (1) MX lookups, (2) SMTP connections, (3) QMTP connections.
|
|
|
|
I bounce ambiguous MXs. (An ``ambiguous MX'' is a best-preference MX
|
|
record sending me mail for a host that I don't recognize as local.)
|
|
Automatically treating ambiguous MXs as local is incompatible with my
|
|
design decision to keep local delivery working when the network goes
|
|
down. It puts more faith in DNS than DNS deserves. Much better: Have
|
|
your MX records generated automatically from control/locals.
|
|
|
|
If I successfully connect to an MX host but it temporarily refuses to
|
|
accept the message, I give up and put the message back into the queue.
|
|
But several documents seem to suggest that I should try further MX
|
|
records. What are they thinking? My approach deals properly with downed
|
|
hosts, hosts that are unreachable through a firewall, and load
|
|
balancing; what else do people use multiple MX records for?
|
|
|
|
Currently qmail-remote sends data in 1024-byte buffers. Perhaps it
|
|
should try to take account of the MTU.
|
|
|
|
Perhaps qmail-remote should allocate a fixed amount of DNS/connect()
|
|
time across any number of MXs; this idea is due to Mark Delany.
|
|
|
|
RFC 821 doesn't say what it means by ``text.'' qmail-remote assumes that
|
|
the server's reply text doesn't contain bare LFs.
|
|
|
|
|
|
7. Delivering mail locally (qmail-lspawn, qmail-local)
|
|
|
|
qmail-local doesn't support comsat. comsat is a pointless abomination.
|
|
Use qbiff if you want that kind of notification.
|
|
|
|
The getpwnam() interface hides I/O errors. Solution: qmail-pw2u.
|
|
|
|
|
|
8. sendmail V8's new features
|
|
|
|
sendmail-8.8.0/doc/op/op.me includes a list of big improvements of
|
|
sendmail 8.8.0 over sendmail 5.67. Here's how qmail stacks up against
|
|
each of those improvements. (Of course, qmail has its own improvements,
|
|
but that's not the point of this list.)
|
|
|
|
Connection caching, MX piggybacking: Nope. (Profile. Don't speculate.)
|
|
|
|
Response to RCPT command is fast: Yup.
|
|
|
|
IP addresses show up in Received lines: Yup.
|
|
|
|
Self domain literal is properly handled: Yup.
|
|
|
|
Different timeouts for QUIT, RCPT, etc.: No, just a single timeout.
|
|
|
|
Proper <> handling, route-address pruning: Yes, but not configurable.
|
|
|
|
ESMTP support: Yup. (Server-side, including PIPELINING.)
|
|
|
|
8-bit clean: Yup. (Including server-side 8BITMIME support; same as
|
|
sendmail with the 8 option.)
|
|
|
|
Configurable user database: Yup.
|
|
|
|
BIND support: Yup.
|
|
|
|
Keyed files: Yes, in qmsmac.
|
|
|
|
931/1413/Ident/TAP: Yup.
|
|
|
|
Correct 822 address list parsing: Yup. (Note that sendmail still has
|
|
some major problems with quoting.)
|
|
|
|
List-owner handling: Yup.
|
|
|
|
Dynamic header allocation: Yup.
|
|
|
|
Minimum number of disk blocks: Yes, via tunefs -m.
|
|
|
|
Checkpointing: Yes, but not configurable---qmail always checkpoints.
|
|
|
|
Error message configuration: Nope.
|
|
|
|
GECOS matching: Not directly, but easy to hook in.
|
|
|
|
Hop limit configuration: No. (qmail's limit is 100 hops. qmail offers
|
|
automatic loop protection much more advanced than hop counting.)
|
|
|
|
MIME error messages: No. (qmail uses QSBMF error messages, which are
|
|
much easier to parse.)
|
|
|
|
Forward file path: Yes, via /etc/passwd.
|
|
|
|
Incoming SMTP configuration: Yes, via inetd or tcpserver.
|
|
|
|
Privacy options: Yes, but they're not options.
|
|
|
|
Best-MX mangling: Nope. See section 6 for further discussion.
|
|
|
|
7-bit mangling: Nope. qmail always uses 8 bits.
|
|
|
|
Support for up to 20 MX records: Yes, and more. qmail has no limits
|
|
other than memory.
|
|
|
|
Correct quoting of name-and-address headers: Yup.
|
|
|
|
VRFY and EXPN now different: Nope. qmail always hides this information.
|
|
|
|
Multi-word classes, deferred macro expansion, separate envelope/header
|
|
$g processing, separate per-mailer envelope and header processing, new
|
|
command line flags, new configuration lines, new mailer flags, new
|
|
macros: These are sendmail-specific; they wouldn't even make sense for
|
|
qmail. For example, _of course_ qmail handles envelopes and headers
|
|
separately; they're almost entirely different objects!
|
|
|
|
|
|
9. Miscellany
|
|
|
|
sendmail-clone and qsmhook are too bletcherous to be documented. (The
|
|
official replacement for qsmhook is preline, together with the
|
|
qmail-command environment variables.)
|
|
|
|
I've considered making install atomic, but this is very difficult to do
|
|
right, and pointless if it isn't done right.
|
|
|
|
RN suggests automatically putting together a reasonable set of lines for
|
|
/etc/passwd. I perceive this as getting into the adduser business, which
|
|
is worrisome: I'll be lynched the first time I screw up somebody's
|
|
passwd file. This should be left to OS-specific installation scripts.
|
|
|
|
The BSD 4.2 inetd didn't allow a username. I think I can safely forget
|
|
about this. (DS notes that the username works under Ultrix even though
|
|
it's undocumented.)
|
|
|
|
I should clean up the bput/put choices.
|
|
|
|
Some of the stralloc_0()s indicate that certain lower-level routines
|
|
should grok stralloc.
|
|
|
|
RN suggests having qlist smash the case of the incoming host name.
|
|
|
|
K1J suggests that mailing list subscription managers should have
|
|
a three-way handshake, to prevent person A from subscribing person B to
|
|
a mailing list. qlist doesn't do this, but ezmlm does.
|
|
|
|
qmail assumes that all times are positive; that pid_t, time_t and ino_t
|
|
fit into unsigned long; that gid_t fits into int; that the character set
|
|
is ASCII; and that all pointers are interchangeable. Do I care?
|
|
|
|
The bat book justifies sendmail's insane line-splitting mechanism by
|
|
pointing out that it might be useful for ``a 40-character braille
|
|
print-driving program.'' C'mon, guys, is that your best excuse?
|
|
|
|
qmail's mascot is a dolphin.
|