
 The design of popa3d
======================

This file describes the design goals and the reasoning behind some of the
design decisions of my tiny POP3 daemon, popa3d.

 Why popa3d?
-------------

There're lots of different POP3 servers -- with different feature sets,
performance, and reliability. However, as far as I know, before I started
the work on popa3d, there had been only one with security as one of its
primary design goals: qmail-pop3d. Unfortunately, it would only work with
qmail, and only with its new maildir format. While both qmail and maildirs
do indeed have some advantages, a lot of people continue running other
MTAs, and/or use the older mailbox format, for various reasons. Many of
them need a POP3 server.

 For whom?
-----------

Currently, popa3d isn't intended for use at large sites. It doesn't have
some of the features that many large sites need, such as built-in virtual
domain support.

However, due to the ease of installation, it should be ideal for smaller
sites that only need the basic POP3 functionality.

 The design goals
==================

Well, the goals themselves are obvious; they're probably the same for most
other POP3 servers as well. It's their priority that differs. For popa3d,
the goals are:

1. Security (to the extent that is possible with POP3 at all, of course).
2. Reliability (again, as limited by the mailbox format and the protocol).
3. RFC compliance (slightly relaxed to work with real-world POP3 clients).
4. Performance (limited by the more important goals, above).

Obviously, just like the comments indicate, none of the goals can be met
completely, and balanced decisions need to be made.

 Security
==========

First, it is important that none of the popa3d users get a false sense of
security just because it was the primary design goal. The POP3 protocol
accepts passwords in plaintext, and thus, if you care about the security
of your individual user accounts, should only be used either in trusted
networks, or tunneled over encrypted channels. There exist extensions to
the protocol (such as the APOP and AUTH commands) that are supposed to
fix this problem. I am not supporting them yet. I might change this in
the future, but this isn't going to really fix the problem anyway (for
various reasons that I might explain here at a later time as well).

It is also important to understand that nothing can be perfectly secure.
I can make mistakes. While the design of popa3d makes it harder for those
to turn into security holes, this is nevertheless still possible.

Having that said, let's get to the security-critical design decisions.

 Privilege management
----------------------

Initially, popa3d is started as root to handle a connection. However, it
does very little work as root: switching to other UIDs, communication
with child processes, and authentication information checks (which often
involve accessing /etc/shadow). The following privilege switches happen
during a successful POP3 session, with /etc/shadow authentication:

			 startup as root
				|
			-----------------
			|child		|parent
			v		v
	drop to user popa3d,		still as root,
	handle the AUTHORIZATION	wait for and
	state, write the results, - - >	read the authentication
	and exit			information
					|
			-----------------
			|child		|parent
			v		v
	getspnam(3), crypt(3),		wait for and
	check, write the result,  - - >	read the authentication
	and exit (to clean up)		result
					|
					v
					drop to the authenticated user,
					handle the TRANSACTION state,
					possibly UPDATE the mailbox,
					and exit

 Trust
-------

No part of popa3d trusts any information obtained from external sources
(that is, the data is never assumed to be of the expected format, and is
treated as subject to authorization checks). This includes POP3 commands,
mailbox contents, and even popa3d's own less-privileged child process for
the AUTHORIZATION state handling.

 DoS attacks
-------------

Just like with most other software, there exist ways to cause a Denial of
Service, by supplying popa3d with an enormous amount of otherwise valid
input. I am aware of the following attacks:

1. Connection flood. When running in the standalone mode, popa3d does
quite a few checks to significantly reduce the impact of such attacks
by limiting resource consumption (child processes and logging rate),
while still providing full service for other source IP addresses and
logging everything that might be important. However, when running from
an inetd clone, the handling of these attacks is left up to your inetd
and the kernel.

2. Huge mailbox sizes, either in message count or bytes. There're limits
in popa3d (see params.h) that are intended to prevent this attack from
stopping the entire service. Depending on your disk and other quotas, it
may still be possible to stop individual users from getting their mail.

 Reliability
=============

Quoting D. J. Bernstein, "the mbox format ... is inherently unreliable".
This is indeed the case, because of the lack of atomical updates. (There
is, however, a way to detect and fix corrupted mailboxes at boot-up, but
that requires a lot of fsync(2) activity (to ensure that writes are done
in order), and thus doesn't seem practical.)

While popa3d, just like other mail software that deals with mailboxes,
doesn't guarantee reliability over system crashes, it still makes sense
to talk about its operation on an otherwise stable system.

 Interaction with other MUAs
-----------------------------

Similarly to cucipop (but unlike qpopper), popa3d works on the original
mailbox file, without copying it first. However, unlike cucipop, it is
able to ensure that the mailbox doesn't get corrupted if another MUA
modifies it during the POP session. Before each mailbox access, popa3d
checks its timestamp and, if that has changed, determines if that is due
to new mail that has just been delivered, or other changes made to the
mailbox. In the latter case, the POP session is silently aborted (which
doesn't violate the RFC).

 Mailbox access
----------------

Except for the total size and message count limits mentioned above (and
you can disable even those), there're no other artificial limits on the
mailbox contents. In particular, there're no line length limits; unlike
with qmail-pop3d, lines don't even need to fit in the available memory.
NUL bytes are allowed in messages as well.

 Locking
---------

Because of dropping to the user "completely" (that is, not even keeping
a GID of mail like some other POP3 servers do), popa3d only uses flock(2)
for locking. As a result, it isn't safe over NFS. This is a place where
I choose security over either functionality or reliability.

 RFC compliance
================

I tried to make popa3d as strictly RFC 1939 compliant as possible. Most
other POP3 servers have extra "features" that violate the RFC. Examples
include: wrapping long commands (no matter if they're valid or not) and
thus generating multiple -ERR responses (if not even worse: processing
something from the middle of the line as a command) to a single command,
processing "LIST 4294967297" as "LIST 1" instead of reporting the error,
ignoring past a NUL byte till end of line and thus misinterpreting the
command. While these are mostly harmless, they can theoretically cause
a POP3 client not to detect the unavailability of a protocol extension.

There's however one place where popa3d's RFC compliance is deliberately
relaxed: popa3d accepts commands terminated by single LF's, even though
the RFC says the commands are terminated by a CRLF pair.

 Performance
=============

Despite the two extra "security" fork(2) calls, popa3d seems to behave
fairly efficiently: the efficient mailbox parsing code and the lack of
mailbox copying compensate for the extra fork's.

Here's some real performance data that I've collected (popa3d running
via inetd; larger sites would use the standalone mode instead):

   24864     295.50re      16.92cp   popa3d*
   12749    4578.88re      15.50cp   popa3d

That is, 12749 POP3 sessions took 32.42 minutes of CPU time; of those,
more than a half was spent in the temporary child processes. It's not
that bad though, as this system is running an expensive crypt(3) that
gets accounted to the child /etc/shadow authentication processes.

Before upgrading to popa3d, the same machine was running qpopper (out
of inetd, too):

   12025    3169.38re      35.56cp   popper

It used to take even a bit more CPU for slightly less POP3 sessions. It
is likely that cucipop would be faster though.

--
Solar Designer <solar@false.com>
