Running
Sendmail as an Unprivileged User
Hal Pomeranz
One of the knocks on Sendmail from a security perspective is that
the MTA daemon that's listening on port 25 is running with root
privileges. The problem there is that if there's a remotely exploitable
vulnerability in Sendmail, then the attacker instantly gets root
privileges, allowing them to do anything at all on the system. It
turns out, however, that when you have a mail server that is operating
purely as a mail relay -- a machine that is always just forwarding
email and never actually doing local delivery or alias expansion
-- it is possible to run Sendmail as an unprivileged user.
Before we go any further, let me attempt to avoid a large number
of letters to the editor by acknowledging that other MTA daemons
like Postfix and QMail already run as non-privileged users in their
default configurations. This is one of the architectural advantages
these mail systems have over Sendmail from a security perspective.
I refuse to open the religious debate about the superiority of various
MTAs; use whichever one is simplest for you to get running and that
you have the staff to manage and maintain. I prefer Sendmail, but
that's probably because I've been working with Sendmail for almost
20 years.
To understand where it is appropriate to run Sendmail as an unprivileged
user, consider the reasons why Sendmail runs as root:
- Only processes running as root are allowed to bind to
network ports below 1024, at least on Unix machines. So the MTA
daemon needs to be root in order to bind to port 25/tcp,
the default SMTP port.
- When Sendmail is performing local delivery, it needs to be
root so that it has appropriate privileges when appending
email to different user's mailboxes, reading and executing the
instructions in user .forward files, and handling aliases
that execute external programs.
- Various Sendmail configuration files and directories, like
the aliases database and the mail queue directory, are only accessible
to the root user. Sendmail must run as root to access
this information.
The first point doesn't really seem to be much of an obstacle.
There are many daemons (Apache, BIND, etc.) that are initially started
with root privileges so that they can bind to a low port
number, but they subsequently give up root privileges when
handling incoming service requests. Sendmail actually includes similar
functionality. The third point doesn't seem that difficult to handle
either. The workaround here is to simply identify the directories
Sendmail needs to operate in, and then make sure to properly set
the permissions and ownerships on those directories so that they're
accessible only by the special unprivileged user that the Sendmail
daemon will be running as.
The second point is the show-stopper in many cases. If the machine
is performing local delivery (even if the machine is just doing
alias expansion and does not have local user mailboxes), then it
can be difficult to get around the need to have the Sendmail MTA
running as root. However, these days it's becoming increasingly
rare for organizations to use their Unix Sendmail servers for local
delivery. In most cases, Sendmail is used as a pure relay.
For example, consider the machines that move incoming Internet
email into an organization's internal (typically Windows-based)
mail infrastructure and vice versa. Since these machines just flip
email from one machine to another and never do any part of the normal
local delivery process, we can simply ignore any privilege requirements
in this case. Happily, these mail relay systems are also typically
the machines where we are most concerned about security, since they
are the machines that speak directly to other Internet-connected
hosts and are in the most vulnerable positions in our network architecture.
It's actually pretty straightforward to get Sendmail running as
an unprivileged user on your mail relay servers. There's just a
little bit of system reconfiguration and then a new configuration
directive in your Sendmail configuration file. The rest of this
article will give you a simple recipe for performing this reconfiguration.
System Configuration
The first step is to create a new user ID and group ID that your
Sendmail MTA daemon will run as. Do not use the special smmsp
user and group that the MSP uses; create a new user and group ID.
I often will create a user and group called sendmail with
UID and GID 24 (smmsp is usually UID and GID 25). Nobody
will ever actually log in as this user, so you can lock the password
entry and use an invalid shell and home directory. Here are some
sample commands you can use for setting up this user and group,
assuming your system supports the traditional useradd and
groupadd commands:
groupadd -g 24 sendmail
useradd -u 24 -g 24 -M -d /var/spool/mqueue \
-s /dev/null sendmail
Note that you need to create the new group first because we're going
to use that group as one of the arguments to the useradd command.
As you can probably guess, the -u and -g options are
used to specify the user ID and group ID for the new user and group.
The -d option specifies the home directory for the new user,
and the -M option tells the useradd command not to create
this directory (since it already exists). The -s option specifies
the default command shell for the user; here /dev/null is an
invalid shell to help prevent user logins via this account. Note that
some useradd commands refuse to allow you to specify a shell
that isn't listed in /etc/shells.
Now we're going to start messing around with ownerships and permissions
of various files and directories. Before that, however, it's a good
idea to shut down the running Sendmail daemon so that it doesn't
get confused while we're in the middle of making changes. On most
systems, the commands /etc/init.d/sendmail stop or pkill
sendmail will work.
There are really two critical directories for the MTA process:
the queue directory (/var/spool/mqueue) and the /etc/mail
configuration area. The following commands should set appropriate
permissions on these directories:
chown -R sendmail:sendmail /var/spool/mqueue
chmod 700 /var/spool/mqueue
chgrp -R sendmail /etc/mail
chmod -R g+r /etc/mail
chmod g+s /etc/mail
The queue directory should be owned by whatever user and group you
created to run the MTA daemon as (sendmail and sendmail
in our example). The permissions on the /var/spool/mqueue directory
should already be mode 700, but a little redundancy never hurt anybody.
You still want the /etc/mail directory and the files in
it to be owned by root, because you only want legitimate
systems administrators to be messing around with these files. But
we need to make sure that the files in this directory are at least
readable by the MTA daemon when it's running unprivileged. Thus,
we're going to change the group ownership on the directory and its
contents to our sendmail group, and then use chmod -R
g+r /etc/mail to give group read permissions to everything in
the directory. Actually, the files in there are probably already
group-readable, but it never hurts to be sure.
The last chmod g+s /etc/mail command adds the set-GID
bit onto the /etc/mail directory. On Unix systems, if set-GID
is set on a directory, then any new file created in that directory
will automatically inherit the group ownership of the directory
(as opposed to the group membership of the user that creates the
file, which would be the default). In this way, we can make sure
that any newly created files end up with the proper ownerships --
like when you're rebuilding your access DB or mailertable
and virtusertable databases with the makemap program.
Note that if you're on a platform that keeps the aliases database
in the /etc directory instead of /etc/mail, then you'll
want to run the command chgrp sendmail /etc/aliases* in addition
to the commands you see on this slide. You would need to issue the
same command every time you rebuilt the aliases database, but this
shouldn't really be an issue since we are assuming that the mail
relay we're configuring never actually looks at its alias database.
Now we're ready to tweak our Sendmail configuration.
The RUN_AS_USER Option
Sendmail allows you to specify an alternate user and group to
run as with the RUN_AS_USER configuration option. If you're
adding this directive to an m4-style macro configuration
file, then the correct syntax is:
define(`confRUN_AS_USER', `sendmail:sendmail')
While it is not recommended practice, the alternative would be to
edit your sendmail.cf file directly and make sure the RunAsUser
option is set as follows:
O RunAsUser=sendmail:sendmail
Creating your configuration files from m4 macros is much more
maintainable in the long run, however, so try to avoid directly hacking
on your sendmail.cf files.
Use m4 to generate your new sendmail.cf and overwrite
the existing configuration file on your mail server. Now start the
Sendmail process (/etc/init.d/sendmail start in most cases).
If you look at the running MTA process after your reconfiguration,
you may be surprised to see that it's still running as root.
This is expected behavior. The master MTA process always runs as
root, but remember that when a new SMTP connection comes
in the master MTA process must fork a copy of itself to handle the
incoming connection. The "child" process will run as the unprivileged
user and group you specify with the RUN_AS_USER option. So
while the master MTA process itself runs as root, outsiders
will only ever be able to communicate with unprivileged child processes.
You can test the configuration by using telnet to connect
to port 25 (telnet localhost 25). If you get a process listing
in another window, you should see a sendmail process running
as the sendmail user:
# ps -ef | grep send
root 1628 1 0 11:43 ? 00:47:22 sendmail:
accepting connections
smmsp 1634 1 0 11:43 ? 00:12:11 sendmail:
Queue runner@01:00:00 for /var/spool/clientmqueue
sendmail 31878 1628 0 14:23 ? 00:00:01 sendmail:
server localhost.localdomain [127.0.0.1] cmd read
The first process is the normal MTA daemon listening on 25/tcp for
incoming SMTP connections. The second process is the standard queue
runner process that watches the MSP queue. The third process is
the MTA child process that's handling your telnet connection.
Close the telnet session to make this process go away.
Other Considerations
There is no difficulty using this unprivileged Sendmail configuration
with common Milters, like MIMEDefang and milter-greylist
(my personal favorite greylisting implementation). You just need
to make sure that the socket created by these Milters is readable
by your unprivileged sendmail user. Don't forget to pay attention
to the permissions on the socket itself and to the permissions on
the directory where the socket is created.
With Sendmail running as an unprivileged user, I believe it would
also be reasonable to run Sendmail chroot()ed. There's not
much point in running a root-owned process under chroot()
restriction, because an attacker who takes over the process can
use root privilege to escape the chroot() restriction
in most cases. But with our unprivileged relay configuration, chroot()
might actually be useful and not that difficult to configure since
Sendmail doesn't need to interact with much of the OS when operating
purely as a relay. Unfortunately, I haven't sat down and attempted
to create this configuration, so chroot()ing Sendmail is
left as an exercise to the reader. However, I did find an older
document (circa 2003) by Brian Clapper on chroot()ing Sendmail
under FreeBSD, which might be useful as a starting point: http://www.clapper.org/bmc/docs/sendmail-chroot.html.
I hope you find this configuration useful for improving the security
of your mail relay servers. I've been using it on my personal mail
servers for some time now with great success, and I have also set
up similar configurations for my consulting clients.
Hal Pomeranz (hal@deer-run.com) has enjoyed nearly 20
years of professional Sendmail hacking experience. He also enjoys
wearing hair shirts, sleeping on a bed of nails, and shoving bamboo
spikes under his fingernails and setting them on fire. |