Postfix
Overview - Security
Up one level | Introduction | Goals and features | Global architecture | Queue Management | Security
Introduction
By definition, mail software processes information from
potentially untrusted sources. Therefore, mail software must be written with
great care, even when it runs with user privileges, and even when it does not
talk directly to a network.
Postfix is a complex system. The initial release has about 30,000 lines of
code (after deleting the comments). With a system that complex, the security of
the system should not depend on a single mechanism. If it did, one single error
would be sufficient to compromise the entire mail system. Therefore, Postfix
uses multiple layers of defense to control the damage from software and other
errors.
Least privilege
Most Postfix daemon programs can be run at fixed low
privilege in a chrooted environment. This is especially true for the programs
that are exposed to the network: the SMTP server and SMTP client. Although
chroot(2), even when combined with low privilege, is no guarantee against system
compromise it does add a considerable hurdle. And we all know that every little
bit helps.
Insulation
Postfix uses separate processes to insulate activities from
each other. In particular, there is no direct path from the network to the
security-sensitive local delivery programs. An intruder first has to break
through multiple programs. Some parts of the Postfix system are multi-threaded.
However, all programs that interact with the outside world are single-threaded.
Separate processes give better insulation than multiple threads within a shared
address space.
Controlled environment
No Postfix mail delivery program runs under
control of a user process. Instead, most Postfix programs run under control of a
resident master daemon that runs in a controlled environment, without any
parent-child relationship to user processes. This approach eliminates exploits
that involve signals, open files, environment variables, and other process
attributes that the UNIX system passes on from a possibly-malicious parent to a
child.
Set-uid
No Postfix program is set-uid. Introducing the concept
was the biggest mistake made in UNIX history. Set-uid (and its weaker
cousin, set-gid) causes more trouble than it is worth. Each time a new
feature is added to the UNIX system, set-uid causes a security problem:
shared libraries, the /proc file system, multi-language support, to
mention just a few examples. Set-uid makes it impossible to introduce
some of the features that make UNIX successors such as plan9 so attractive, for
example, per-process file system name spaces.
Initially, the maildrop queue directory was world-writable, so that
local processes could submit mail without assistance from a set-uid or set-gid
command or from a mail daemon process. The maildrop directory was not used for
mail coming in via the network, and its queue files were not readable for
unprivileged users.
A writable directory opens up opportunities for annoyance: a local user can
make hard links to someone else's maildrop files so they don't go away and/or
are delivered multiple times; a local user can fill the maildrop directory with
garbage and try to make the mail system crash; and a local user can hard link
someone else's files into the maildrop directory and try to have them delivered
as mail. However, Postfix queue files have a specific format; less than one in
10^12 non-Postfix files would be recognized as a valid Postfix queue file.
Because of the potential for misbehavior, Postfix has abandoned the
world-writable maildrop directory, and uses a small set-gid
postdrop helper program for mail submission.
Trust
As mentioned elsewhere in the overview, Postfix programs do not
trust the contents of queue files or of the Postfix internal IPC messages. Queue
files have no on-disk record for deliveries to sensitive destinations such as
files or commands. Instead, programs such as the local delivery agent attempt to
make security-sensitive decisions on the basis of first-hand information.
Of course, Postfix programs do not trust data received from the network,
either. In particular, Postfix filters sender-provided data before exporting it
via environment variables. If there is one lesson that people have learned from
Web site security disasters it is this one: don't let any data from the
network near a shell. Filtering is the best we can do.
Large inputs
- Memory for strings and buffers is allocated dynamically, in order to
prevent buffer overrun problems.
- Long lines in message input are broken up into sequences of
reasonably-sized chunks, and are reconstructed upon delivery.
- Diagnostics are truncated (in one single place!) before they are passed to
the syslog(3) interface, in order to prevent buffer overruns on older
platforms. However, no general attempt is made to truncate data before it is
passed to system calls or to library routines. On some platforms, the software
may still exhibit buffer overrun problems, due to vulnerabilities in the
underlying software.
- No specific attempt is made to defend against unreasonably-long
command-line arguments. UNIX kernels impose their own limits, which should be
sufficient to deal with runaway programs or with malicious users.
Other defenses
- The number of in-memory instances of any object type is limited, to
prevent the mail system from becoming wedged under heavy load.
- In case of problems, the software pauses before sending an error response
to a client, before terminating with a fatal error, or before attempting to
restart a failed program. The purpose is to prevent runaway conditions that
only make problems worse.
Up one level | Introduction | Goals and features | Global architecture | Queue Management | Security