This page briefly describes how to setup sendmail to run without ever having root security. This means that no matter how badly sendmail gets hacked you can't lose any more than mail stored or transferred through the host in question. As opposed to a traditional sendmail setup where a the discovery of the next sendmail bug will usually leave your system wide open to attack.

At the same time as doing this I've setup sendmail to run with TCPD, this means that it's not possible for a remote host to setup bogus reverse DNS records and send spam-mail to the world through my system. Of course it's still possible for someone to forward spam-mail through my system, but at least I'll know the domain they come from (and I can easily ban that domain from connecting to me in future).

This will noticably reduce performance, but if you're running sendmail then you're either running a small amount of mail traffic (in which case it won't matter much) or you're running a dedicated mail hub (in which case don't bother reading any further - you may as well run sendmail as root). If you really need performance then you probably want to run Qmail (as I am used to run), Postfix (which I now run) or Zmailer which I have not personally evaluated - but a lot of people I respect like it. I have switched almost all machines I'm involved with to Qmail, and then to Postfix, but it was a bit of work and I expect that many people won't want to go to the effort.

There is one problem with this that I have not yet resolved. If sendmail isn't running as root then a user with a pipe in their .forward file will have the program run as mail instead of as the user.


I've setup these changes on systems that are live at the time. I expect that you'll want to do the same so I've made a list of things to do in order which should hopefully cause minimal disruption to service.

1A) Check permissions on all relevant files.

1B) Make sendmail use smrsh.

2A) Setup sendmail to run from inetd and stop it running as a daemon.

2B) Make "sendmail -q" run from cron or run it as a non-TCP daemon from su.

3) Make sendmail SUID/SGID mail.


1A) Before you do anything serious you have to ensure that the right permissions are setup. The first thing to do is to create a "mail" user-id in /etc/passwd and a "mail" group in /etc/group. The mail user should have /bin/false (or something similar) as it's shell, and "*" in the password field. I have considered installing a shell-script to run "sendmail -q" as the shell for the mail user and allowing anyone who wants to flush the queue to login as mail (I'm still considering the potential risks of this).

Now you must have /var/spool/mail owned by root:mail with mode 1775 (NB I've never tried setting up sendmail with mail anywhere else - if you get this working with mail in $HOME/.mail then please write a section for this file and I'll include it). This allows sendmail to create lock files when run as group mail, and it can also allow other programs to access mail when they don't have root privs. Mode 1770 should do if you don't have local user-owned programs such as "elm" accessing mail.

I recommend the use of the tag bit as suggested by a gentleman named Dovid (who's second name I unfortunately lost by deleting his messages before writing this page) to prevent elm from changing the permissions on files. Otherwise when you delete items from your mail spool file via elm it'll stuff up the permissions.

Under the /var/spool/mail directory all files must have "mail" as the group and have mode 660 so that sendmail can write to them. If we want to get really paranoid then mode 620 would probably work, but I haven't tested this...

The directory /var/spool/mqueue should be owned by mail and have mode 0700. I would like to give it a group of mail and mode 0770 to allow sendmail to be only SGID mail and not SUID mail, but my attempts to do so failed (see the description in section 3).


1B) Unfortunately I can't remember the details of how to set this up (I omitted to mention it on my original page). The idea is to limit the number of programs that sendmail can run to things that are safe to be run as "I can read anyone's mail". If someone could provide a submission for this section I'd appreciate it.
2A) To allow sendmail to accept mail on port 25 while running as non-root you need to run it from inetd (or some similar Internet super-server daemon). This involves adding a line such as the following to /etc/inetd.conf to run sendmail (NB the parameter "-bs" is to tell sendmail to talk SMTP on stdin/stdout):
smtp	stream	tcp	nowait	mail	/usr/sbin/tcpd	/usr/sbin/sendmail -bs
If you are using xinetd (which I recommend) then you need the following config:
service smtp
{
	socket_type	= stream
	protocol	= tcp
	wait		= no
	user		= mail
	group		= mail
	server		= /usr/local/sbin/tcpd/sendmail
	server_args	= -bs
	nice		= 5
	instances	= 20
}
Xinetd allows me to specify the nice value for sendmail and the maximum number of instances of it which may run at once. If you run sendmail as a stand-alone daemon (with -bd which seems to be the default setup for all distributions) then it will limit the number of instances it'll run. Without that we need xinetd to enforce the limits. The nice value will allow me to login and fix things is sendmail runs wild and uses excessive CPU time. The file /usr/local/sbin/tcpd/sendmail is a symbolic link to /usr/sbin/tcpd on my system, I put this in place because xinetd doesn't support using a different value for argv[0] the way inetd does.

Once you've put those config-file changes in place you need to stop sendmail running as a daemon. It's typically started as "sendmail -bd -qXXm" at boot time to run as a daemon and regularly process the queue every XX minutes. I suggest that you just put an "exit 0" at the start of /etc/init.d/sendmail or if you're not running SysV init then just comment out the appropriate line in /etc/rc.d/rc.inet1. Then you can just kill the sendmail daemon.

Now sendmail will still be running as root (because it's SUID), but that's OK for the moment. Also it won't be scanning the queue, but things will work for a couple of hours without a queue scan with no problems.


2B) The next thing to do is setup some method of scanning the sendmail queue regularly. I've done it by editing the cron entry for the mail user with "crontab -e -u mail" and inserting the following line:
*/10 * * * *	/usr/sbin/sendmail -q
I'm scanning the queue every 10 minutes, most people will want to scan it less frequently (so replace 10 with a larger number). One problem with this method is that if the queue takes more than 10 minutes to scan then we'll have multiple processes scanning it at once, so my setup is not ideal in this regard.

I think that the thing to do would be to get a program which runs a program if it's not already running and use it from cron to run "sendmail -q", or have some type of cron daemon which supports this level of functionality. If you know of any such software then please tell me where I can find the source.

Another method which has been suggested is to have the following run from system startup scripts. I have not tested it, but the idea looks good.

su mail -c 'sendmail -q10m
Thanks to Marcel Waldvogel mwa@tik.ee.ethz.ch for the suggestion.
3) The final step is to make sendmail SUID mail and SGID mail. It should be possible to avoid having sendmail SUID mail by putting the following line in your sendmail.mc file and then re-building your sendmail.cf:
define(`confTEMP_FILE_MODE', `0660')dnl
I believe that is supposed to give the temporary files in /var/spool/mqueue permissions mode 0660. However it seems to only give them mode 0640. This means that if a user on the system sends mail locally by using the command "mail user@domain" then the files in /var/spool/mqueue will be owned by the user in question (so if the first attempt to send the message fails it won't be sent on the next attempt due to lack of write permission to the control file). To solve this I made sendmail SUID mail (for queuing permissions) and SGID mail (for delivery permissions). One problem with this is that in a typical setup sendmail needs to have write access to /etc/aliases.db when run as "newaliases" to rebuild the aliases database. To solve this I have added the following line to my sendmail.mc file:
define(`ALIAS_FILE', `/etc/mail/aliases')
I also made the directory /etc/mail writable by group mail. Then I realised that sendmail doesn't do the locking on the db file that I thought it did. So I just made the /etc/mail/aliases.db file owned by "root:mail" and gave it permissions 664. Thanks to "Kari E. Hurtta" Kari.Hurtta@ozone.FMI.FI for giving me the tip about ALIAS_FILE which also led to this discovery.

Here are the commands I typed to change the permissions on sendmail, if you are running a Linux system following the FSSTND then you should be able to do cut/paste from here (no guarantees):

chown mail:mail /usr/sbin/sendmail
chmod 6555 /usr/sbin/sendmail
chown mail /var/spool/mqueue/*
chgrp mail /etc/aliases.db   (or wherever it is)
chmod 664 /etc/aliases.db
Now you should have sendmail running happily without any special privs.

Thanks to Bennett Todd bet@newritz.mordor.net for suggesting some improvements.


Copyright © 1997, 2000, 2006 Russell Coker and Faye Coker, may be distributed freely.