A common idiom to drop all user privileges is to do a seteuid(new_uid) followed
by a setuid(new_uid). The two steps are done to ensure that the saved uid is
also set to new_uid on some (stupid) platforms.
Unfortunately this doesn't work on Linux. A setuid(geteuid()) call will fail
when the euid != cuid.
I am not sure what the motivation for this check is, as it doesn't seems to add
any security (one already has the euid privileges, or lack thereof). To make
matters doubly confusing, the same restriction is *not* applied to setgid, where
setgid(getegid()) works fine.
Created attachment 92099 [details]
Here is a simple example illustrating the issue. Run this with (uid, euid) as
root, the setuid call will fail.
If one replaces the setuid and seteuid with setgid and setegid, the second call
Your test program is buggy. When you seteuid() you are no longer effectively
root so you lose the capability to setuid() to someone else.
setuid(), then seteuid()
Alternatively for more flexibility and BSD like behaviour you can use
> Your test program is buggy. When you seteuid() you
> are no longer effectively root so you lose the
> capability to setuid() to someone else.
Well, you are not setuid()ing to someone else. You are setuid()ing
to your current euid. Seeing as how you already have these privileges,
I can't see how such a restriction could possibly be useful.
I am aware of setresuid(), it is nice but non-standard and not widely
supported. setreuid() is ambiguous as to the behaviour wrt saved UIDs
so IMO can't be trusted for portable software.
BSD allows the order shown in the test program and many BSD programs
use that order to drop privs. This seems like a break in portability
for zero gain.
Linux implements POSIX/SYS5 semantics for setuid/setuid and BSD semantics for
the BSD extensions. It has always taken this approach. For portable code you
should only rely on POSIX and SUS defined behaviour I agree.