Hal Pomeranz, Deer Run Associates
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 farther, 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 configuration. 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 which 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 at this point.
To understand where it is appropriate to run Sendmail as an unprivileged user, consider the reasons why Sendmail runs as root:
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 which then 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 work-around 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, 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 which 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.
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 will often 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 you do that, however, it's a good idea to shut down the running Sendmail daemon so that it doesn't get confused while you're in the middle of making your 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 system 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. So what we're going to do is 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 since we are assuming that the mail relay we're configuring never actually looks at its alias database then this shouldn't really be an issue.
Now we're ready to tweak our Sendmail configuration.
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:
While not recommended practice, the alternative would be to edit your sendmail.cf file directly and make sure the RunAsUser option is set as follows:
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.
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 also 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, since 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 an 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 have also set up similar configurations for my consulting clients.
Hal Pomeranz (email@example.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.