ruid: This is the real user ID of the user that started the process.
euid: This is the effective user ID, is what the system looks to when deciding what privileges the process should have. In most cases, the
euidwill be the same as the
ruid, but a SetUID binary is an example of a case where they differ. When a SetUID binary starts, the
euidis set to the owner of the file, which allows these binaries to function.
suid: This is the saved user ID, it's used when a privileged process (most cases running as root) needs to drop privileges to do some behavior, but needs to then come back to the privileged state.
setuidwould set the
ruid. In fact, when for a privileged process, it does. But in the general case, it actually sets the
euid. From the man page:
setuid() sets the effective user ID of the calling process. If the calling process is privileged (more precisely: if the process has the CAP_SETUID capability in its user namespace), the real UID and saved set-user-ID are also set.
setuid(0)as root, this is sets all the ids to root, and basically locks them in (because
suidis 0, it loses the knowledge or any previous user - of course, root processes can change to any user they want).
An unprivileged process may change its real UID, effective UID, and saved set-user-ID, each to one of: the current real UID, the current effective UID, or the current saved set-user-ID.A privileged process (on Linux, one having the CAP_SETUID capability) may set its real UID, effective UID, and saved set-user-ID to arbitrary values.
setuidbecause the most common case is to go to root, and in that case,
setuidis effectively the same as
execvesystem call executes a program specified in the first argument. The second and third arguments are arrays, the arguments (
argv) and the environment (
envp). There are several other system calls that are based on
execve, referred to as
exec(man page). They are each just wrappers on top of
execveto provide different shorthands for calling
execvestarts a program, it uses the same memory space as the calling program, replacing that program, and newly initiating the stack, heap, and data segments. It wipes out the code for the program and writes the new program into that space.
suidon a call to
execve? It does not change the metadata associated with the process. The man page explicitly states:
The process’s real UID and real GID, as well as its supplementary group IDs, are unchanged by a call to execve().
euid, with a longer paragraph describing what happens. Still, it’s focused on if the new program has the SetUID bit set. Assuming that isn’t the case, then the
euidis also unchanged by
suidis copied from the
The effective user ID of the process is copied to the saved set-user-ID; similarly, the effective group ID is copied to the saved set-group-ID. This copying takes place after any effective ID changes that occur because of the set-user-ID and set-group-ID mode bits.
systemis a completely different approach to starting a new process. Where
execveoperates at the process level within the same process,
forkto create a child process and then executes in that child process using
execl("/bin/sh", "sh", "-c", command, (char *) NULL);
execlis just a wrapper around
execvewhich converts string arguments into the
argvarray and calls
execve. It’s important to note that
shto call the command.
Turn on privileged mode. In this mode, the $ENV and $BASH_ENV files are not processed, shell functions are not inherited from the environment, and the SHELLOPTS, BASHOPTS, CDPATH, and GLOBIGNORE variables, if they appear in the environment, are ignored. If the shell is started with the effective user (group) id not equal to the real user (group) id, and the -p option is not supplied, these actions are taken and the effective user id is set to the real user id. If the -p option is supplied at startup, the effective user id is not reset. Turning this option off causes the effective user and group ids to be set to the real user and group ids.
euidis set to
ruidwhen Bash is run.
-i Specify that the shell is interactive; see below. An implementation may treat specifying the -i option as an error if the real user ID of the calling process does not equal the effective user ID or if the real group ID does not equal the effective group ID.
idruns as nobody:
ruidof 99 (nobody) and an
euidof 1000 (frank). When it reaches the
setuidcall, those same values are set.
systemis called, and I would expect to see
uidof 99, but also an
euidof 1000. Why isn’t there one? The issue is that
shis symlinked to
bashin this distribution:
/bin/sh sh -c id, which is effectively
/bin/bash bash -c id. When
bashis called, with no
-p, then it sees
ruidof 99 and
euidof 1000, and sets
idreturns uid of 1000:
setreuidcall set both
euidto 1000, so when
bash, they matched, and things continued as frank.
execveIf my understanding above is correct, I could also not worry about messing with the uids, and instead call
execve, as that will carry though the existing IDs. That will work, but there are traps. For example, common code might look like this:
id. This works, returning what I expect:
[r]uidis 99, but the
setuid(0), then it would work fine (assuming the process had permission to do that), as then it changes all three ids to 0. But as a non-root user, this just sets the
euidto 1000 (which is already was), and then calls
bashon Jail. And when
ruidof 99 and
euidof 1000, it will drop the
euidback to 99.