su for dropping user privileges.00 4 * * 1-6 /bin/su adm -c "/usr/lib/acct/runacct 2> /usr/adm/acct/nite/fd2log"— UNIX System Administration by David Fielder and Bruce H. Hunter, published in 1986
Like M. Fielder's and M. Hunter's 1986 book, one can find many instances in books, on the World Wide Web, in tutorials, and even on manual pages, of abusing su for dropping superuser privileges and running programs with ordinary user privileges — in cron jobs, /etc/rc scripts, init.d scripts, and even from /etc/inittab.
They are all wrong.
Don't abuse su for this purpose.
It has never in fact been the function of su, and for the past two decades people have been triggering errors with this abusage.
Over the past decade or so, as of 2014, this error has gradually become more and more blatant, going from a few ignorable warning messages in obscure log files to systems that fail to function, but it has in fact been there all of this time.
su adds privileges; it does not drop them.The
sucommand makes it easy to log in as yourself, and then elevate your privilege temporarily to root, […].— The UNIX Operating System by Kaare Christian, published in 1988
The su command authenticates an additional user (by default the superuser, but it can be anyone) in the same login session, and runs a command (by default a shell program) under the aegis of that user.
Thought of in this way, it should be fairly obvious that it is a mechanism for adding privileges, namely adding the privileges of the newly authenticated user to the current login session.
If, for example, one is user A at an interactive shell and one runs su B then one has two shells available, one running under the aegis of user A and one running under the aegis of user B, and one has the privileges of both users at one's fingertips.
(With job control, switching between the two is a matter of the suspend and fg commands.)
You may stop the shell and place it in the background with the
suspendcommand; you can return to it later usingfg.— Essential System Administration by Æleen Frisch, first published in 1991
The problem is that, thanks to hugely outdated documentation and outright computer folklore that still circulates, people think of su in terms of raw internal mechanics, rather than in terms of its functional behaviour.
su is thought of as looking up accounts in the system password database, calling the setgid() and setuid() system calls to switch its process GIDs and UIDs, and then execvp() to overlay itself with the target program.
M. Fielder and M. Hunter were not wrong in 1986.
Although even back then the user guides and manuals only ever said that this was a "switch user" command that spawned a "second shell", with no guarantees as to how many processes were involved, and most were clear that one could get back to the still present privileges that one started out with; clearly not a desirable thing when what one wants to do is drop privileges so that they cannot be reobtained.
They were also abundantly clear that this was a tool used in login sessions, which the aforementioned cron jobs, /etc/rc scripts, init.d scripts, and suchlike are not.
[…]sustarts a new shell with the user ID you specified. To exit this shell and return to your original UID, type an EOF to the new shell.— UNIX and XENIX Reference Guide by Greg Dykema, published in 1988
But, aside from a few hold-out BSDs and BSD-derived systems (such as Android), su hasn't worked this way for roughly two decades, as of 2014.
On the Unices such as Solaris and AIX, on Linux, and on the other BSDs such as FreeBSD and PC-BSD, su has a completely different mechanism, and what used to hold true of it as a byproduct of its original mechanism no longer holds true now, and hasn't done so for a long while.
The 1980s truisms are long gone.
The reason for this is PAM (Pluggable Authentication Modules).
The su command is a PAM-enabled application with a service name of su. […] The authentication mechanisms used when PAM is enabled depend on the configuration for the
suservice in/etc/pam.conf. Thesucommand requires/etc/pam.confentries for theauth,account,password, andsessionmodule types. In order for thesucommand to exhibit a similar behavior through PAM authentication as seen in standard AIX®authentication, thepam_allowrootmodule must be used assufficientand called beforepam_aixin both theauthandaccountsuservice stacks.—
sucommand, IBM AIX Manuals since at least the turn of the 21st century
In the middle 1990s, with the advent of PAM, commands such as su and login changed drastically, starting with the proprietary Unices and gradually filtering down to Linux and the BSDs.
In particular, PAM now controls the actual function of su.
All of the authentication behaviour, including the superuser being able to bypass the authentication step, is actually encoded in the series of PAM modules that is defined for the "su" application in PAM.
(Witness the /etc/pam.d/su.conf file on a Debian system or the sample /etc/pam.conf content given in the IBM AIX manual for su, for examples.)
PAM itself operates in terms of what it calls "user sessions".
Programs such as login and su call the PAM library function pam_open_session() to "open" a login session and something must call the PAM library function pam_close_session() to "close" it.
Furthermore, that something must call pam_close_session() with the same security context — the same effective UID and effective GIDs — in which pam_open_session() was originally called.
Session close needs the privileges to be able to reverse whatever was done by session open (writing accounting database records, dealing with per-user per-login session directories and services, and so forth).
The authenticated user xyrself won't necessarily have those privileges.
(It is basic UNIX security that unprivileged users don't have the rights to overwrite their own entries in the accounting database.)
In 2000 and 2001, the GNU world was catching up with the commercial Unices.
The GNU su and login programs underwent a series of tweaks as a result of PAM being used on Linux, because PAM broke the old implementation.
See Ben Gertzfield's 2000 patches for Debian su.
Around the same time, the same changes were made to the FreeBSD su.
Witness David J. MacKenzie's 2001 patches to FreeBSD su and login.
The new implementation called fork() and only dropped privileges in the child, retaining a privileged account parent process that could call the PAM "user session" cleanup function once the child exited.
(See contemporary accounts such as this one from Ben Collins.)
An initial implementation where the PAM "user session" was opened in the parent process and closed in the child was found to be faulty, with bugs such as Debian Bug #195048, Debian Bug #580434, and Debian Bug #599731 a.k.a. Gentoo Bug #246813 because the unprivileged child did not have the access rights to undo all of the session setup that opening the session did in the privileged parent.
For the next decade, bits and pieces kept popping up related to PAM and su.
David Z Maze reported GDM not using PAM properly in 2001.
In 2004 there was a problem with the SELinux pluggable authentication module.
The Freedesktop.org people came late to the party in 2013, ten years after su had been switched to PAM, when pkexec had to be fixed to make the right PAM library calls for closing "user sessions" within the privileged parent process.
(pkexec is much the same tool as su, in that it authenticates an additional user into the current login session, except that it uses PolicyKit for authorization and PolicyKit style authentication agents instead of the system account database and a plain password prompt on the terminal.)
So too did sudo, with a bug caused by sudo opening the "user session" in the child process and vainly attempting to close it in the parent reared its ugly head as Debian Bug #660739 in 2014.
PAM thus, years ago, made su unsuitable for use as a dæmon helper tool:
su now fork()s a subprocess rather than overlaying itself with the target program that it is to run.
Dæmon privilege-dropping helpers need to have the dæmon running in the same process, so that a dæmon supervisor can send control signals to it.
su now sets up and tears down user sessions, and all sorts of things associated with them by whatever pluggable authentication modules the system administrator has enabled, from kerberos keys and SELinux security contexts through per-user "runtime" directories to temporary network mounts.
Dæmons aren't associated with controlling TTYs or login sessions, and the mechanisms of user login sessions should not be affixed to them.
These are in addition to the non-PAM things that make it unsuitable, such as su being sensitive to the target account's choice of interactive login shell, making it impossible to straightforwardly drop privileges with su to a "nologin" unprivileged account — a commonly employed paradigm for dæmons that run under the aegises of dedicated user accounts that shouldn't ever be used for real user login.
Fortunately, the right way to drop privileges in a dæmon had already been around for some several years before the GNU tools changed, being only a couple of years younger than PAM itself, in fact.
Create a small wrapper binary with C (e.g.
/usr/bin/setidor/bin/setid) to perform basically the following (about 10-20 lines):
takes arguments and one option
first argument is always the userid to change the identity to
the rest of the arguments would be stored as a command.
the option, if present, could toggle whether the command is run through
execorsystem(default toexec?).
setuid,setgidandinitgroupsto the specified user
execorsystemthe command— Pekka Savola, Debian Bug #55219, 2001-10-27
Writing in 2001 in a Debian Bug discussing a problem where an abuse of su to drop privileges had stopped working, M. Savola was not in fact describing a hypothetical future.
He was in fact, possibly unknowingly, describing a tool that had existed for several years at that point.
Indeed, by 2001 people were already busy cloning it into their own toolsets.
That tool was setuidgid from Daniel J. Bernstein's daemontools, first released in 1997 (originally under the name setuser in daemontools 0.51).
By the start of the 21st century it was already being duplicated into daemontools clones, and today in 2014 we now have a range of such tools:
setuidgid, to which Uwe Ohse added setuidgidfromenvsetuidgid from Adam Sampson's freedt
s6-setuidgid from Laurent Bercot's s6, accompanied by s6-applyuidgid
chpst from Gerrit Pape's runit
runuid from Wayne Marshall's perp, with a more chpst-like alternative in runtool
setuidgid from nosh, which also has a setuidgid-fromenv
All of these tools were specifically designed for dropping privileges from a privileged process, in a simple, self-contained, easily security auditable, and one-way manner.
A couple of them explicitly say this on their manual pages.
All of these tools do the very thing that su used to do, as an accident of implementation, long ago: take an account name and a command as program arguments; look up the account in the system account database; change the process' UID, GID, and supplementary groups; and chain load the command in the same process.
| The wrong way | The right way | |
|---|---|---|
| Origin | Command | |
| Fielder and Hunter's book | /bin/su adm -c "/usr/lib/acct/runacct 2> /usr/adm/acct/nite/fd2log" |
(shell) setuidgid adm sh -c "exec /usr/lib/acct/runacct 2> /usr/adm/acct/nite/fd2log" |
(execline) setuidgid adm redirfd -w 2 /usr/adm/acct/nite/fd2log /usr/lib/acct/runacct |
||
(nosh) setuidgid adm fdredir -w 2 /usr/adm/acct/nite/fd2log /usr/lib/acct/runacct |
||
| Daniel Reed's wrapper for FreeCiv | su -s /bin/bash nobody -c "XAUTHORITY=$TMP /usr/local/freeciv/bin/civclient $@" & |
XAUTHORITY=$TMP setuidgid nobody /usr/local/bin/civclient $@ &(This is, of course, an abuse of nobody for running a dæmon, which is also wrong.)
|