I just discovered this page of Elizabeth Stinson
explaining the the differences between UID, EUID, SUId and FSUID and how they take effect for Linux, BSD, Solaris, and other Unices. Great document!
As backup her page is cited here:
SETUID syscall: modifies userIDs Access control in Unix systems: based on a process's userIDs Each process has a set of userIDs and groupIDs -- these userIDs and groupIDs determine which resources [files, network ports, ...] that process can access -- the privileged userIDs and groupIDs allow a process to access restricted system resources userID == 0 --> ROOT; process can access all system resources In some apps, a user process needs extra privileges; e.g. permission to read or change the password file "principle of least privilege" == process should DROP those extra privs, ASAP UID-setting syscalls: offered by UNIX systems; used by a process to raise & drop its privilege level; poorly designed, not well documented USER ID MODEL: - each user has a unique UID - UID determines which resources a user can access Each process has 3 UIDs: (1) real UID: identifies process OWNER (2) effective UID: used in access control decisions (3) saved user ID: stores a previous UID so that it can be restored later Each process has 3 groupIDs: (1) real GID (2) effective GID (3) saved GID In Linux, each process *also* has: (1) FSUID (2) FSGID --used for access control to the file system --FSUID usually follows effective UID unless it's explicitly set by the setfsuid syscall --FSGID usually follows effective GID unless explicitly set by setfsgid() When a process is created by FORK, the created process inherits its parent's UIDs When a process executes a NEW FILE via EXEC, that executing process keeps ITS OWN UIDs unless the SETUID in the new file is set. -- if the SETUID bit is set in the file we're EXECing, then --> process's EUID == file owner's UID --> process's SUID == file owner's UID To drop privilege temporarily, a process will remove the privileged UID from its EUID but keep it saved as its saved UID (SUID); later the process can restore the privileges by setting its EUID to its SUID. To drop privileges permanently a process removes the privileged UID from all three of its UIDs; thereafter the process cannot restore that privileged access Bell Labs patented Dennis Ritchie's idea to have a bit to indicate whether when a file is executed that file should be executed with the privileges of its owner (normal) or THE INVOKER (setuid bit == 1 for latter case). Early UNIX: a process had 2 UIDs, RUID & EUID --Only one syscall, SETUID: if (EUID == 0) RUID = ; EUID = ; else EUID = ; Problem: no way to temporarily drop root privileges only to restore them later UNIX / \ / \ Sys V BSD System V: added SUID and SETUEID syscall SETEUID: if (EUID == 0) EUID can be set to any value; else EUID can be set to RUID or SUID ONLY SETUID was modified: if (EUID != 0) EUID can be set; else EUID = ; RUID = ; SUID = ; Sys V: so if you're not root, you can use SETUID to set EUID and you can use SETEUID to set EUID = RUID or EUID = SUID; if you ARE root, you can use SETUID to set EUID, SUID, RUID and you can use SETEUID to set EUID to anything; BSD: dropped SETUID, created SETREUID SETREUID: if (EUID == 0) RUID = anyUID; EUID = anyUID; else RUID = EUID; OR EUID = RUID; POSIX: SETUID can set all three UIDs whether you're root or not SETRESUID(newRUID,newEUID,newSUID) -- to call this function, the EUID of the calling process must be ROOT OR -- each of the 3 params must equal one of the process's 3 UIDs -- all or nothing effect; -- FreeBSD & Linux offer SETRESUID; Solaris does not; SOLARIS: through /proc FS any process can examine its 3 UIDs and a superuser process can set any of those UIDs; SETEUID(newEUID): --sets EUID; doesn't touch RUID or SUID --Among UNIX systems, if the current EUID != ROOT (a) Solaris, Linux newEUID can equal EUID, RUID, or SUID (b) FreeBSD newEUID can equal RUID or SUID SETREUID(newRUID,newEUID) --modifies RUID and EUID and in some cases SUID (a) Solaris & Linux: a process can swap RUID & EUID (b) FreeBSD: a process can't switch RUID & EUID SETUID(newUID): POSIX (a) Linux & Solaris: newUID must equal RUID or SUID (if EUID != 0) (b) FreeBSD: newUID may equal EUID, too The action of SETUID depends on whether th process is privileged or not (a) Linux & Solaris: if (EUID == 0) setuid(newID) sets RUID = newUID; EUID = newUID; SUID = newUID; else setuid(newUID) sets EUID = newUID; (b) FreeBSD: setuid(newID) sets RUID = EUID = SUID = newID regardless of whether current EUID is 0 or not SETFSUID(newFSUID) --FSUID used for access control to the FS --FSUID == EUID unless FSUID explicitly set --tries to maintian invariant: FSUID = 0 only if RUID, SUID, OR EUID == 0 --if change EUID, that changed val will be propagated to FSUID Buggy tho; while every setuid & setreuid sets FSUID to EUID, if call setresuid and *don't change* EUID, then setresuid will NOT set FSUID = EUID E.g. RUID = EUID = SUID = 0; FSUID = 0; setresuid(x,x,-1) RUID = EUID = FSUID = x; SUID = 0; setfsuid(0) RUID = EUID = x; SUID = FSUID = 0; setresuid(-1,-1,x) RUID = EUID = SUID = x; FSUID = 0; --> INVARIANT violated (passing -1 as value means don't change this UID) SETGID & relatives: permissions check for setregid != permissions check for setreuid (in Solaris) Privileges carried by EUID... so having EGID == 0 doesn't buy you anything