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] Example program 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 will succeed.
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 setreuid()/setresuid()
> 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.