Setuid Demystified: Hao Chen David Wagner Drew Dean
Setuid Demystified: Hao Chen David Wagner Drew Dean
Setuid Demystified: Hao Chen David Wagner Drew Dean
group IDs, which are not closely related to the topic of this paper and Formal methods have gained a reputation as being im-
which will not be discussed.
practical to apply to large software systems, so it may access. In particular, user ID zero is reserved for the su-
be surprising that we found formal methods so useful in peruser root who can access all resources.
our effort. We will show how our formal model enables
many tasks that would otherwise be too error-prone or Each process has three user IDs: the real user ID (real
laborious to undertake. Our success comes from using uid, or ruid), the effective user ID (effective uid, or euid),
lightweight techniques to answer a well-defined question and the saved user ID (saved uid, or suid). The real uid
about the system; we are not attempting to prove that a identifies the owner of the process, the effective uid is
kernel is correct! Abstraction plays a major role in sim- used in most access control decisions, and the saved uid
plifying the system so that simple analysis techniques are stores a previous user ID so that it can be restored later.
sufficient. Similarly, a process has three group IDs: the real group
ID, the effective group ID, and the saved group ID. In
This paper is organized as the follows. Section 2 dis- most cases, the properties of the group IDs parallel the
cusses related work. Section 3 provides background on properties of their user ID counterparts. For simplicity,
the user ID model. Section 4 reviews the evolution of we will focus on the user IDs and will mention the group
the uid-setting system calls. Section 5 compares and IDs only when there is the potential for confusion or pit-
contrasts the semantics of the uid-setting system calls in falls. In Linux, each process has also an fsuid and an
three major Unix systems. Section 6 describes the formal fsgid which are used for access control to the filesystem.
user ID model and its applications. Section 7 analyzes The fsuid usually follows the value in the effective uid
two security vulnerabilities caused by misuse of the uid- unless explicitly set by the setfsuid system call. Simi-
setting system calls. Section 8 provides guidelines on the larly, the fsgid usually follows the value in the effective
proper usage of the uid-setting system calls and proposes gid unless explicitly set by the setfsgid system call. Since
a high-level API to the user ID model. the fsuid and fsgid are Linux specific, we will not discuss
them except when we point out an inconsistency in the
handling of them in the Linux kernel.
3 User ID Model
This section provides background on the user ID model. • To drop privilege permanently, a process removes
Each user in a Unix system has a unique user ID. The the privileged user ID from all three user IDs.
user ID determines which system resources the user can Thereafter, the process can never restore privilege.
4 History • If the effective uid was zero, then the real uid and
effective uid could be set to any user ID.
Bell Laboratories filed a patent application on Den- • Otherwise, either the real uid or the effective uid
nis Ritchie’s invention of a bit to specify that a pro- could be set to value of the other one.
gram should execute with the permissions of its owner,
rather than invoker, in 1973. The patent was granted in Therefore, the setreuid system call enabled a process to
1979 [4]. Thus, setuid programs and related system calls swap the real uid and effective uid.
have existed through most of Unix history.
The POSIX standard [5] codified a new specification for
the setuid call. In an attempt to be POSIX compliant,
4.1 Early Unix 4.4 BSD replaced 4.2 BSD’s old setreuid model with
the POSIX/System V style saved uid model. It modified
setuid so that setuid set all three user IDs regardless of
In early Unix systems, a process had two user IDs: the whether the effective uid of a process was zero, therefore
real uid and the effective uid. Only one system call, se- allowing any process to permanently drop privileges.
tuid, modified them according to the following rule: if
the effective uid was zero, setuid set both the real uid
and effective uid; otherwise, setuid could only set the 4.4 Modern Unix
effective uid to the real uid [1]. This model had the prob-
lem that a process could not temporarily drop the root
privilege in its effective uid and restore it later. As Unix As System V and BSD influenced each other, both sys-
diverged into System V and BSD, each system solved the tems implemented setuid, seteuid, and setreuid, although
problem in a different way. with different semantics. None of these system calls,
however, allowed the direct manipulation of the saved
uid (although it could be modified indirectly through se-
4.2 System V tuid and setreuid). Therefore, some modern Unix sys-
tems introduced a new call, setresuid, to allow the modi-
fication of each of the three user IDs directly.
System V added a new user ID called the saved uid to
each process. Also added was a new system call, seteuid,
whose rules were:
5 Complexity of Uid-setting System Calls
• If the effective uid was zero, seteuid could set the
effective uid to any user ID. A process modifies its user IDs by the uid-setting sys-
tem calls: setuid, seteuid, setreuid, and in some systems,
• Otherwise, seteuid could set the effective uid to only
setresuid. Each of the system calls involves two steps.
the real uid or saved uid.
First, it checks if the process has permission to invoke
the system call. If so, it then modifies the user IDs of the
seteuid did not change the real uid or saved uid. Further- process according to certain rules.
more, System V modified setuid so that if the effective
uid was not zero, setuid functioned as seteuid (changing In this section, we compare and contrast the semantics
only the effective uid); otherwise, setuid set all three user of the uid-setting system calls among Linux 2.4.18 [8],
IDs. Solaris 8 [6], and FreeBSD 4.4 [7]. The behavior of the
uid-setting system calls was discovered by a combina-
tion of manual inspection of kernel source code and for-
4.3 BSD mal methods. We will defer discussion of the latter until
Section 6.
4.2 BSD kept the real uid and effective uid but changed
the system call from setuid to setreuid. Processes could The POSIX Specification To understand the seman-
then directly control both their user IDs, under the fol- tics of the uid-setting system calls, we begin with the
lowing rules: POSIX standard, which has influenced the design of the
system calls in many systems. In particular, the behavior If { POSIX SAVED IDS} is defined:
of setuid(newuid) is defined by the POSIX specification.
See Figure 1 for the relevant text. 1. If the process has appropriate privileges, the se-
tuid() function sets the real user ID, effective user
The POSIX standard refers repeatedly to the term ap- ID, and the [saved user ID] to newuid.
propriate privileges, which is defined in Section 2.3 of 2. If the process does not have appropriate privi-
POSIX 1003.1-1988 as: leges, but newuid is equal to the real user ID or
the [saved user ID], the setuid() function sets the
An implementation-defined means of associ- effective user ID to newuid; the real user ID and
ating privileges with a process with regard to [saved user ID] remain unchanged by this func-
the function calls and function call options de- tion call.
fined in this standard that need special privi-
leges. There may be zero or more such means. Otherwise:
1. If the process has appropriate privileges, the se-
Essentially, the term appropriate privilege serves as a tuid() function sets the real user ID and effective
wildcard that allows compliant operating systems to user ID to newuid.
use any policy whatsoever for deeming when a call
to setuid should be allowed. The conditional flag 2. If the process does not have appropriate privi-
{ POSIX SAVED IDS} parametrizes the specification, leges, but newuid is equal to the real user ID,
allowing POSIX-compatible operating systems to use ei- the setuid() function sets the effective user ID to
ther of two schemes (as described in Figure 1). We will newuid; the real user ID remains unchanged by
see how different interpretations of the term appropriate this function call.
privilege have led to considerable differences in the be- (POSIX 1003.1-1988, Section 4.2.2.2)
havior of the uid-setting system calls between operating
systems. Figure 1: An excerpt from the POSIX specification [5]
covering the behavior of the setuid system call.
5.1 Operating System-Specific Differences
newuid=geteuid(), in addition to when its effective uid is
zero. Also in contrast to Solaris, FreeBSD does not de-
Much of the confusion is caused by different interpreta- fine { POSIX SAVED IDS}, although every FreeBSD
tions of appropriate privileges among Unix systems. process does have a saved uid. Therefore, by calling se-
tuid(newuid), a process sets both its real uid and effective
uid to newuid if the system call is permitted, in agree-
Solaris In Solaris 8, a System V based system, a ment with POSIX. FreeBSD also sets the saved uid in all
process is considered to have appropriate privileges permitted setuid calls.
if its effective uid is zero (root). Also, Solaris de-
fines { POSIX SAVED IDS}. Consequently, calling se-
tuid(newuid) sets all three user IDs to newuid if the ef-
fective uid is zero, but otherwise sets only the effective Linux Linux introduces a capability2 model for finer-
uid to newuid (if the setuid call is permitted). grained control of privileges. Instead of a single level
of privilege determined by the effective uid (i.e., root or
non-root), there are a number of capability bits each of
FreeBSD FreeBSD 4.4 interprets appropriate privi- which is used to determine access control to certain re-
leges differently, as noted in Appendix B4.2.2 of POSIX: sources3 . One of them, the SETUID capability, carries
the POSIX appropriate privileges. To make the new ca-
The behavior of 4.2BSD and 4.3BSD that al- 2 Beware: the word “capability” is a bit of a misnomer. In this con-
lows setting the real ID to the effective ID is text, it refers to special privileges that a process can possess, and not
to the usual meaning in the security literature of an unforgeable refer-
viewed as a value-dependent special case of ence. Regrettably, the former usage comes from the POSIX standard
appropriate privilege. and seems to be in common use, and so we follow their convention in
this paper.
3 More accurately, a Linux process has three sets of capabilities, but
This means that a process is deemed to have ap- only the set of effective capabilities determine access control. All ref-
propriate privileges when it calls setuid(newuid) with erences to capabilities in this paper refer to the effective capabilities.
pability model compatible with the traditional user ID uid unchanged. However, when the current effective uid
model where appropriate privileges are carried by a zero is not zero, there is a slight difference in the permis-
effective uid, the Linux SETUID capability tracks the ef- sion required by seteuid among Unix systems. While
fective uid during all uid-setting system calls: Whenever Solaris and Linux allow the parameter neweuid to be
the effective uid becomes zero, the SETUID capability equal to any of the three user IDs, FreeBSD only allows
is set; whenever the effective uid becomes non-zero, the neweuid to be equal to either the real uid or saved uid;
SETUID capability is cleared. in FreeBSD, the effective uid is not used in the decision.
As a surprising result, seteuid(geteuid()), which a pro-
However, the SETUID capability can be modified out- grammer might intuitively expect to be always permitted,
side the uid-setting system calls. A process can clear can fail in FreeBSD, e.g., when ruid=100, euid=200, and
its SETUID capability, and a process with the SETP- suid=100.
CAP capability can remove the SETUID capability of
other processes (but note that in Linux 2.4.18, no process
has or can acquire the SETPCAP capability, a change
setreuid() The semantics of setreuid is confusing. It
that was made to close a security hole; see Section 7.1
modifies the real uid and effective uid, and in some
for details). Therefore, explicitly setting or clearing the
cases, the saved uid. The rule by which the saved uid
SETUID capability changes the properties of uid-setting
is modified is complicated. Furthermore, the permis-
systems calls.
sion required for setreuid differs among the three op-
erating systems. In Solaris and Linux, a process can
always swap the real uid and effective uid by calling
5.2 Comparison among Uid-setting System
setreuid(geteuid(), getuid()). In FreeBSD, however, se-
Calls
treuid(geteuid(), getuid()) sometimes fails, e.g., when
ruid=100, euid=200, and suid=100.
Next we compare and contrast the uid-setting system
calls and point out several unexpected properties and an
inconsistency in the handling of fsuid in the Linux ker- setuid() Although setuid is the only uid-setting sys-
nel. tem call standardized in POSIX 1003.1-1988, it is also
the most confusing one. First, the required permission
differs among Unix systems. Both Linux and Solaris
setresuid() setresuid has the clearest semantics among require the parameter newuid to be equal to either the
the four uid-setting system calls. The permission check real uid or saved uid if the effective uid is not zero. As
for setresuid() is intuitive and common to all OSs: for the a surprising result, setuid(geteuid()), which a program-
setresuid() system call to be allowed, either the euid of mer might reasonably expect to be always permitted, can
the process must be root, or each of the three parameters fail in some cases, e.g., when ruid=100, euid=200, and
must be equal to one of the three user IDs of the process. suid=100. On the other hand, setuid(geteuid()) always
As each of the real uid, effective uid, and saved uid is succeeds in FreeBSD. Second, the action of setuid dif-
set directly by setresuid, the programmer knows clearly fers not only among different operating systems but also
what to expect after the call. Moreover, the setresuid between privileged and unprivileged processes. In So-
call is guaranteed to have an all-or-nothing effect: if it laris and Linux, if the effective uid is zero, a successful
succeeds, all user IDs are changed, and if it fails, none setuid(newuid) call sets all three user IDs to newuid; oth-
are; it will not fail after having changed some but not all erwise, it sets only the effective user ID to newuid. On
of the user IDs. the other hand, in FreeBSD a successful setuid(newuid)
call sets all three user IDs to newuid regardless of the
Note that while FreeBSD and Linux offer setresuid, So- effective uid.
laris does not. However, Solaris does offer equivalent
functionality via the /proc filesystem. Any process can
examine its three user IDs, and a superuser process can setfsuid() In Linux, each process has also an fsuid in
set any of them, in line with the traditional System V addition to its real uid, effective uid, and saved uid. The
notion of appropriate privilege. fsuid is used for access control to the filesystem. It nor-
mally follows the effective uid unless when explicitly set
by the setfsuid system call. The Linux kernel tries to
seteuid() seteuid has also a clear semantics. It sets maintain the invariant that the fsuid is zero only if at least
the effective uid while leaving the real uid and saved one of the real uid, effective uid, or saved uid is zero, as
ruid=euid=suid=0 by the egid in the setgid-like calls, but this is not how
fsuid=0 it actually works. This misconception caused a mistake
setresuid(x,x,-1) in the manual page of setgid in Redhat Linux 7.2 (Sec-
ruid=euid=fsuid=x tion 6.4.1).
suid=0
setfsuid(0) In many Unix systems, a process has also a set of supple-
ruid=euid=x
mentary group IDs which are modified by the setgroups
suid=fsuid=0 and initgroups calls. They are not closely related to the
setresuid(-1,-1,x)
topic of this paper and will not be discussed.
ruid=euid=suid=x
fsuid=0
On all operating systems, we extend our model further to We can easily extend the simple models to include more
deal with system calls that fail (i.e., when invoking call user ID values, which are appropriate for applications
c in line 6 of B UILD M ODEL ()). It is sometimes useful that use more than two user ID values. Figure 7 shows a
to be able to reason about whether a system call has suc- model where the set of user ID values is {0, x, y} where
ceeded or failed, and one way is to add a bit to the state x and y are distinct non-root user ID values. This is the
denoting whether the previous system call returned suc- fully general model of Unix user IDs.
cessfully or not.
setuid(0) setuid(0)
setuid(1)
setuid(0) setuid(0)
setuid(1)
setuid(1) setuid(1)
Figure 4: Three finite state automata describing the setuid system call in Linux, Solaris, and FreeBSD, respectively.
Ellipses represent states of the FSA, where a notation like “R=1,E=0,S=1” indicates that euid = 0 and ruid = suid 6= 0.
Each transition is labelled with the system call it corresponds to. To avoid cluttering the diagram, we omit the error
states and (in Linux) the capability bits that otherwise would appear in our deduced model.
R=1,E=0,S=1 seteuid(0) R=1,E=1,S=0 seteuid(1) R=0,E=1,S=1 seteuid(1) R=0,E=1,S=0 seteuid(1)
setreuid(1, 1)
R=1,E=1,S=0 setresuid(1, 1, 0)
setresuid(1, 1, 0) setresuid(0, 0, 0)
setresuid(1, 1, 1)
Figure 5: Three finite state automata describing the seteuid, setreuid, setresuid system calls in Linux respectively.
Ellipses represent states of the FSA, where a notation like “R=1,E=0,S=1” indicates that euid = 0 and ruid = suid 6= 0.
Each transition is labelled with the system call it corresponds to.
R=y,E=x,S=y setuid(x) R=y,E=y,S=x setuid(y) R=x,E=y,S=x setuid(y) R=x,E=y,S=y setuid(y)
R=y,E=y,S=y setuid(x) setuid(y) R=y,E=x,S=x setuid(x) R=x,E=x,S=x setuid(x) setuid(y) R=x,E=x,S=y setuid(x)
Figure 6: A finite state automaton describing the setuid system call in Linux. This FSA considers only two distinct
non-root user ID values x and y. Ellipses represent states of the FSA, where a notation like “R=x,E=y,S=x” indicates
that euid = y and ruid = suid = x. Each transition is labelled with the system call it corresponds to.
R=0,E=y,S=x setuid(y) R=y,E=x,S=0 setuid(x) R=x,E=y,S=0 setuid(y) R=0,E=x,S=y setuid(x) R=y,E=y,S=x setuid(0) setuid(y) R=x,E=y,S=y setuid(0) setuid(y)
setuid(0) R=0,E=x,S=x setuid(x) setuid(y) R=y,E=y,S=0 setuid(x) setuid(y) setuid(0) setuid(0) R=x,E=x,S=0 setuid(x) setuid(y) R=0,E=y,S=y setuid(x) setuid(y) setuid(0) R=y,E=x,S=x setuid(0) setuid(x) R=x,E=x,S=y setuid(0) setuid(x)
R=y,E=0,S=y R=0,E=0,S=x R=y,E=0,S=0 R=0,E=y,S=0 setuid(x) setuid(y) R=0,E=x,S=0 setuid(x) setuid(y) R=x,E=0,S=y R=x,E=0,S=x R=y,E=0,S=x R=x,E=0,S=0 R=0,E=0,S=y
setuid(0) setuid(0) setuid(0) setuid(0) setuid(0) setuid(0) setuid(0) setuid(0) setuid(0) setuid(0)
setuid(y) setuid(y) R=y,E=x,S=y setuid(0) setuid(x) setuid(y) setuid(x) setuid(x) setuid(x) R=0,E=0,S=0 setuid(0) setuid(y) setuid(x) setuid(y) setuid(x) setuid(y) setuid(y) setuid(x) setuid(y) R=x,E=y,S=x setuid(0) setuid(y) setuid(x) setuid(x)
Figure 7: A finite state automaton describing the setuid system call in Linux. This FSA considers three user ID values:
the root user ID and two distinct non-root user ID values x and y. Ellipses represent states of the FSA, where a notation
like “R=0,E=x,S=y” indicates that ruid = 0, euid = x and suid = y. Each transition is labelled with the system call it
corresponds to.
A critical requirement is that the operating system must s (the implementation may freely choose one). Finally,
behave deterministically given the equivalence class of the G ETA LL S TATES() function must return a pair (S, C)
c c0 so that S contains at least one representative from each
the current state. More precisely, if s t and s0 u
0 0
where (s, c) ≡OS (s , c ), then we require t ≡S u. The equivalence class of ≡S and so that every equivalence
intuition is that the behavior of the operating system will class of ≡OS contains some element (s, c) with c ∈ C.
depend only on which equivalence class we are in, and
not on any other information about the state. For in- When these general requirements are satisfied, the
stance, the behavior of the operating system cannot de- B UILD M ODEL() algorithm from Figure 3 will correctly
pend on any global variables that don’t appear in the state infer a valid finite-state model for the underlying oper-
s; if it does, these global variables must be included into ating system. The proof is easy. We will write [x] for
the statespace S. As another example, a system call im- the equivalence class containing x, e.g., [s] = {t ∈
c
plementation that attempts to allocate memory and re- S : s ≡S t}. If s → t appears in the final FSA out-
turned an error code if this allocation fails will violate put by B UILD M ODEL(), then there must have been a
our requirement, because the success or failure of the step at which, for some s0 ∈ [s], t0 ∈ [t], and c0 with
memory allocation introduces non-determinism, which (s, c) ≡OS (s0 , c0 ), we executed c0 in state s0 at line 6
is prohibited. We can see that this requirement is non- and transitioned to state t0 . (This follows from the cor-
trivial, and it must be verified by manual inspection of rectness of S ET S TATE() and G ET S TATE().) The latter
c0 c
the source code before our algorithm in Figure 3 can be means that s0 t0 , from which it follows that s t00 for
safely applied; we will return to this issue later. 00
some t ∈ [t], since the OS respects ≡OS . Conversely,
c0
if s0 t0 for some s0 , c0 , t0 , then by the correctness of
Next, there are three requirements on the instantiation of
G ETA LL S TATES(), there will be some s and c satisfy-
the G ET S TATE(), S ET S TATE(), and G ETA LL S TATES()
ing (s, c) ≡OS (s0 , c0 ) so that we enter line 6 with s, c,
subroutines. First, the G ET S TATE() routine must return
and thanks to the deterministic nature of the operating
(a representative for) the equivalence class of the current c
system we will discover the transition s → t for some
state of the operating system. Note that it is natural to 0
t ≡S t . Thus, the FSA output by B UILD M ODEL() is
represent equivalence classes internally by singling out
exactly what it should be. Consequently, all that remains
a unique representative for each equivalence class and
is to check that these requirements are satisfied by our
using this value. Second, the S ET S TATE() procedure
instantiation of the schema.
with parameter s must somehow cause the operating sys-
tem to enter a state s0 in the same equivalence class as
We argue this next for the implementation shown in This completes our justification for the correctness of our
Figure 3. Let U denote the set of concrete uids (e.g., method for extracting a formal model to capture the be-
all 32-bit values), so that S = U × U × U. Say havior of the operating system.
that a map σ : U → U is a valid substitution if it
is bijective and fixes 0, i.e., σ(0) = 0. Each such
substitution can be extended to one on S by working 6.4 Applications
component-wise, i.e., σ(r, e, s) = (σ(r), σ(e), σ(s)),
and we can extend it to work on system calls by apply-
ing the substitution to the arguments of the system call, The resulting formal model has many applications. We
e.g., σ(setreuid(r, e)) = setreuid(σ(r), σ(e)). have already discussed in Section 5 the semantics of the
We define our equivalence relation ≡S on S as fol- setuid system calls and pointed out pitfalls; this relied
lows: two states s, s0 ∈ S are equivalent if there is heavily on the FSA formal model. Next, we will dis-
a valid substitution σ such that σ(s) = s0 . Similarly, cuss several additional applications: verifying documen-
(s, c) ≡OS (s0 , c0 ) holds if there is some valid substitu- tation and checking conformance with informal specifi-
tion σ so that σ(s) = s0 and σ(c) = c0 . cations; identifying cross-platform semantic differences
that might indicate potential portability issues; detecting
The correctness of G ET S TATE() and S ET S TATE() is im- inconsistency in the handling of user IDs within an OS
mediate. Also, so long as n ≥ 6, G ETA LL S TATES() kernel; and checking the proper usage of the uid-setting
is correct since the choice of uids u1 , . . . , un is imma- system calls in programs automatically.
terial: every pair (s, c) ∈ S × C is equivalent to some
pair (s0 , c0 ) ∈ S × C, since we can simply map the first
six non-zero uids in (s, c) to u1 , . . . , u6 respectively, and
6.4.1 Verifying Accuracy of Manual Pages
there can be at most six non-zero uids in (s, c). Actu-
ally, we can see that the algorithm in Figure 3 comes
from a finer partition than that given by ≡OS : for exam- Manual pages are the primary source of information for
ple, (u1 , u1 , u1 ) and (u2 , u2 , u2 ) are unnecessarily dis- Unix programmers, but unfortunately they are often in-
tinguished. This causes no harm to the correctness of the complete or wrong. FSAs are useful in verifying the ac-
result, and only unnecessarily increases the size of the curacy of manual pages of uid-setting system calls. For
resulting FSA. We gave the variant shown in Figure 3 each call, if its FSA is small and its description in man-
because it is simpler to present, but in practice our im- ual pages is simple, we check if each transition in the
plementation does use the coarser relation ≡S . FSA agrees with the description by hand. Otherwise, we
build another FSA based on the description and compare
All that remains to check is that the operating system re- this FSA to the original FSA built by simulation. Differ-
spects and behaves deterministically with respect to this ences between the two FSAs indicate discrepancies be-
equivalence class. We verify this by manual inspection of tween the behavior of the system call and its description
the kernel sources, which shows that in Linux, FreeBSD, in manual pages.
and Solaris the only operations that the uid-setting sys-
tem calls perform on user IDs are equality testing of two The following are a few examples of problematic docu-
user IDs, comparison to zero, copying one user ID to an- mentation that we have found using our formal model:
other, and setting a user ID to zero. Moreover, the oper-
ating system behavior does not depend on anything else,
with one exception: Linux depends on whether the SE- • The man page of setuid in Redhat Linux 7.2 fails to
TUID capability is enabled for the process, so on Linux mention the SETUID capability, which affects the
we add an extra bit to each state indicating whether this behavior of setuid.
capability is enabled. Thus, our verification task amounts
to checking that user IDs are treated as an abstract data • The man page of setreuid in FreeBSD 4.4 says:
type with only four operations (equality testing, compar-
Unprivileged users may change the real
ison to zero, and so on) and that the side effects and re-
user ID to the effective user ID and vice-
sults of the system call do not depend on anything outside
versa; only the super-user may make
the state S. In our experience, verifying that the operat-
other changes.
ing system satisfies these conditions is much easier than
fully understanding its behavior, as the former is an al- However, this is incorrect. Swapping the real uid
most purely mechanical process. and effective uid does not always succeed, such as
when ruid=100, euid=200, suid=100, contrary to
what the man page suggests. The correct descrip- uid, or saved uid is zero. To verify this invariant, we ex-
tion is “Unprivileged users may change the real user tend the formal model of user IDs with the fsuid and au-
ID to the real uid or saved uid, and change the effec- tomatically create an FSA of the model on Linux. From
tive uid to the real uid, effective uid, or saved uid.” the FSA, we discovered that the invariant does not always
hold, because the state where fsuid = 0 and ruid 6= 0,
• The man page of setgid in Redhat Linux 7.2 says euid 6= 0, suid 6= 0 is reachable. For example, the call
sequence in Figure 2 will violate the invariant. The prob-
The setgid function checks the effective lem results from an inconsistency in the handling of the
gid of the caller and if it is the superuser, fsuid in the uid-setting system calls. While every suc-
all process related group ID’s are set to cessful setuid and setreuid call sets the fsuid to the ef-
gid. fective uid, a successful setresuid call will fail to do the
same if the effective uid does not change during the call.
In reality, the effective uid is checked instead of the
The problem has been confirmed by the Linux commu-
effective gid.
nity.
printf() setuid(1)
Line 1 Line 2 Line 3 Misuses of uid-setting system calls have caused many se-
curity vulnerabilities, which are good lessons in learning
(b) Program FSA of the program in Figure 8(a) the proper usage of the system calls. We will analyze two
such incidents in older versions of sendmail.
The vulnerability was caused by the overloaded seman- We provide guidelines on the proper usage of the uid-
tics of setuid. Depending on whether a process has the setting system calls. First, we discuss general guidelines
SETUID capability, setuid sets one user ID or all three that apply to all setuid programs. Then, we focus on ap-
user IDs, but it returns a success code in both cases. The plications that use the uid-setting system calls in a spe-
vulnerability can be avoided by replacing setuid(newuid) cific way. We propose a high-level API for these appli-
with setresuid(newuid, newuid, newuid) if available, or cations to manage their privileges. The API is easier to
with setreuid(newuid, newuid) otherwise. understand and to use than the Unix API.
to newuid regardless of the effective user ID. We envision
A user A user the following scenarios where setuid may be misused:
executes sendmail executes sendmail
ruid=euid=suid!=0 ruid=euid=suid!=0
rgid!=smmsp rgid!=smmsp • If a setuid-root program temporarily drops root
egid=sgid=smmsp egid=sgid=smmsp privilege with seteuid(getuid()) and later calls se-
sendmail calls sendmail calls
tuid(getuid()) with the intention of permanently
setgid(getgid()) setgid(getgid()) dropping all root privileges, the program does not
get the intended behavior on Linux or Solaris, be-
ruid=euid=suid!=0 ruid=euid=suid!=0
rgid=egid=sgid!=smmsp
cause the saved user ID remains root. (However,
rgid=egid!=smmsp
(wrong assumption) sgid=smmsp the program does receive the intended behavior on
FreeBSD.)
sendmail executes An attacker
the rest of code takes over sendmail
• Also on Linux or Solaris, in a setuid-root pro-
and executes
setregid(-1, smmsp) gram, calling setuid(getuid()) permanently drops
root privileges; however, in a setuid-non-root pro-
ruid=euid=suid!=0
rgid!=smmsp
gram (e.g., a program that is setuid-Alice where Al-
egid=sgid=smmsp ice is a non-root user), calling setuid(getuid()) will
not permanently drop Alice’s privileges, because
The attacker the saved user ID remains Alice. This is particu-
executes code with
smmsp group privilege
larly confusing, because the way setuid-root pro-
grams permanently drop privileges does not work
in setuid-non-root programs on Linux or Solaris.
(a) The programmer’s (b) Real execution of send-
mental model of an mail by a malicious user
expected execution trace
8.1.2 Obeying the Proper Order of System Calls
setuid(getuid()) setgid(getgid())
Figure 12: An example of a program that verifies that
ruid=euid=suid=100 ruid=euid=suid=100 it has properly dropped root privileges. The verification
rgid=egid=sgid=200 rgid=egid=200, sgid=0
is achieved by checking that unpermitted uid-setting sys-
tem calls will fail. Note that a full implementation should
(a) A program correctly (b) A program fails to also check the return code from setuid and verify that all
drops both user and group drop group privileges per- three user IDs are as expected after the call to setuid.
privileges permanently by manently because it calls
calling setgid(getgid()) be- setuid(getuid()) before set-
fore setuid(getuid) gid(getgid()) examine its fsuid via the /proc filesystem since Linux
does not offer a getfsuid call.
Figure 11: Proper order of dropping user and group priv-
ileges. Figure (a), on the left, shows proper usage; figure
(b) shows what can go wrong if one gets the order back- Verifying Failures Once an attacker takes control of a
wards. process, the attacker may insert arbitrary code into the
process. Therefore, for further assurance on security,
the process should ensure that all unpermitted uid-setting
a process permanently drops privilege, since such an ac- system calls will fail. For example, after dropping privi-
tion usually precedes operations that, if executed with lege permanently, the process should verify that attempts
privilege, may compromise the system. to restore privilege will fail. This is shown in Figure 12.
Be aware that the Linux-specific setfsuid system call re-
turns the previous fsuid from before the call and does not 8.2 An Improved API for Privilege Manage-
return any error message to the caller on failure. This is ment
one motivation for our next guideline.
int restore_priv()
• Restore privilege. {
int ruid, euid, suid;
if (getresuid(&ruid, &euid, &suid) < 0)
We propose a new API that offers the ability to perform return ERROR_SYSCALL;
each of these tasks directly and easily. The API contains if (setresuid(-1, suid, -1) < 0)
return ERROR_SYSCALL;
three functions: if (geteuid() != suid)
return ERROR_SYSCALL;
return 0;
• drop priv temp(new uid): Drop privilege temporar- }
ily. Move the privileged user ID from the effective
uid to the saved uid. Assign new uid to the effective
uid. Figure 14: A possible implementation of the high-level
API for systems with setresuid.
• drop priv perm(new uid): Drop privilege perma-
nently. Assign new uid to all the real uid, effective To use this implementation, an application must meet the
uid, and saved uid. following requirements:
• restore priv: Restore privilege. Copy the privileged
user ID from the saved uid to the effective uid. • When the process starts, its effective uid contains
the privileged user ID. This is true in most circum-
stances. When a process is run by a privileged user,
By raising the level of abstraction, we free programmers all three user IDs contain the privileged user ID. If
to think more about their desired security policy and less the process is run as a privileged user, i.e., its exe-
about the mechanism of implementing this policy. Fig- cutable is setuid’ed to the privileged user and is run
ure 13 illustrates the action of these functions pictorially by an unprivileged user, both the effective uid and
with a simple state diagram. saved uid of the process contain the privilege user
ID.
8.2.2 Implementation • If the privileged user ID is not zero, then the unpriv-
ileged user ID must be stored in the real uid when
the process starts. This requirement enables the pro-
We implement the new API as wrapper functions to the cess to replace the privileged user ID in the effective
uid-setting system calls. The implementation uses setre- uid with the unprivileged user ID in drop priv temp
suid if available since it has the clearest semantics and and drop priv perm. This is the case when a non-
it is able to set each of the user IDs independently, as root user executes an executable that is setuid’ed
shown in Figure 14. If setresuid or its equivalent is not to another non-root user. On the other hand, if the
available, the implementation uses seteuid and setreuid, privileged user ID is zero, then there is no such re-
as shown in Figure 15. quirement, since the process can set its user IDs to
uid_t priv_uid;
• It does the right thing even in cases where root is not
int drop_priv_temp(uid_t new_uid) involved, i.e., where the privileged user ID is not the
{ superuser.
int old_euid = geteuid();
9 Future Work
Figure 15: A possible implementation of the high-level
API for systems without setresuid.
We plan to study how the uid-setting system calls affect
other properties of a process, such as the ability to receive
arbitrary values. signals and to dump cores. We may also study how to
• The process does not make any uid-setting system extend the formal models for multi-threaded programs.
calls that change any of the three user IDs. Such a Topics to investigate include in-kernel races and how the
call may cause the process to enter a state not cov- user IDs are inherited during the creation of new threads
ered by the FSA in Figure 13, on which the high- in different Unix systems.
level API and the implementation are based.
• It does not affect the real uid. We have studied the proper usage of the uid-setting sys-
tem calls by two approaches. First, we documented the
• It guarantees that all transitions in Figure 13 suc-
semantics of the uid-setting system calls in three major
ceed.
Unix systems (Linux, Solaris, and FreeBSD) and identi-
• It verifies that the user IDs are as expected after each fied their differences. We then showed how to formalize
uid-setting system call. this problem using formal methods, and we proposed a
new algorithm for constructing a formal model of the se-
mantics of the uid-setting system calls. Using the result-
ing formal model, we identified semantic differences of
the uid-setting system calls among Unix systems and dis-
covered inconsistency within an OS kernel. Finally, we
provided guidelines for proper usage of the uid-setting
system calls and proposed a high-level API for manag-
ing user IDs that is more comprehensible, usable, and
portable than the usual Unix API.
Acknowledgment
References
[1] Chris Torek and Casper H.S. Dik. Setuid mess. http:
//yarchive.net/comp/setuid_mess.html.
[2] Richard Stevens. Advanced Programming in the UNIX
Environment. Addison-Wesley Publishing Company,
1992.
[3] Matt Bishop. How to write a setuid program. ;login:,
12(1):5–11, 1987.
[4] Dennis M. Ritchie. Protection of data file contents.
United States Patent #4,135,240. Available from http:
//www.uspto.gov.
[5] IEEE Standard 1003.1-1998: IEEE standard portable op-
erating system interface for computer environments. In-
stitute of Electrical and Electronics Engineers, 1988.
[6] http://www.sun.com/software/solaris/.
[7] http://www.freebsd.org.
[8] http://www.kernel.org.
[9] dm(8). 4.4 BSD System Manager’s Manual.
[10] Simon N. Foley. Implementing chinese walls in unix.
Computers and Security Journal, 16(6):551–563, Decem-
ber 1997.
[11] http://www.sendmail.org/.
[12] Sendmail Inc. Sendmail workaround for linux capabilities
bug. http://www.sendmail.org/sendmail.
8.10.1.LINUX-SECURITY.txt.
[13] Michal Zalewski. Multiple local sendmail vulnerabili-
ties. http://razor.bindview.com/publish/
advisories/adv_sm812.html.
[14] Hao Chen, David Wagner, and Drew Dean. An infras-
tructure for examining security properties of software.
manuscript in preparation.