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
su
command 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
suspend
command; 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.
[…]su
starts 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
su
service in/etc/pam.conf
. Thesu
command requires/etc/pam.conf
entries for theauth
,account
,password
, andsession
module types. In order for thesu
command to exhibit a similar behavior through PAM authentication as seen in standard AIX®authentication, thepam_allowroot
module must be used assufficient
and called beforepam_aix
in both theauth
andaccount
su
service stacks.—
su
command, 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/setid
or/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
exec
orsystem
(default toexec
?).
setuid
,setgid
andinitgroups
to the specified user
exec
orsystem
the 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 setuidgidfromenv
setuidgid
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.)
|