User and group IDs

Updated: April 19, 2023

A process has different kinds of user and group IDs that can be modified to affect its privileges.

The user and group IDs of a process are important attributes of the security of a system. When opening files and resource manager paths, whether the process's user or group IDs match the file's owner or group determines if its access is controlled by owner, group, or world permissions. When the process creates a file, its user and group ID are used to set the ownership of the file.

User IDs and group IDs are integers that may have any value other than -1. While negative IDs are technically valid, using negative IDs is strongly discouraged and will likely cause problems. A user ID of 0 is special. A process with a user ID of 0, usually referred to as root, typically has greater privileges and is considered a “superuser.” User and group IDs can be associated with names in /etc/passwd and /etc/group but they can also just be numeric IDs with no name associated with them. You don't have to create a user or group to make use of a new user or group ID.

Types of user and group IDs

A process actually has three user IDs, three main group IDs, and zero or more supplementary group IDs. For the user and main group IDs, there is a real, effective and saved-set ID:

real
This ID can be considered the user or group ID that owns the process. If a user was logged in running commands from a shell, all processes resulting from that session would most likely have a real user ID matching the user ID of that user. It can be obtained by calling getuid()/getgid() as appropriate.
effective
This is the ID that is used for security checks and for determining ownership of newly created files. While normally the same as the real ID, when a setuid or setgid binary is executed this ID is set equal to the owning user ID or group ID of the file (depending on whether the binary was setuid, setgid, or setuid and setgid). It can be obtained by calling geteuid()/getegid() as appropriate.
saved-set
Normally equal to the real user or group ID. Like the effective ID, this ID is set equal to the owning user ID or group ID of the file when a setuid or setgid binary is executed. This allows a process resulting from, for example, executing a setuid binary to call seteuid() to temporarily have privileges and file ownership determined by the original user ID.

In almost all cases, the three IDs are the same and you can ignore the distinction. However, when dealing with setuid and setgid binaries, the three IDs are important. When a setuid or setgid binary is executed, the process runs as the user ID, group ID, or user and group ID associated with the binary. This usually means it can perform some actions it would otherwise not be able to do. Most commonly, setuid programs are owned by root, allowing full root privileges. For example, the passwd utility makes use of setuid to allow updates to the normally heavily protected shadow file.

The supplementary group IDs are zero or more group IDs that may be used to grant additional privileges to a process. You can get and set supplementary group IDs using getgroups() and setgroups(). For file or resource manager access tests, a process is considered to be part of the file's group if its effective group ID or any of its supplementary group IDs match the file's. Resource managers might also perform other security checks based on a process's supplementary groups.

Managing user and group IDs

All credentials may be obtained by calling ConnectClientInfo(), ConnectClientInfoExt(), or ConnectClientInfoAble().

Other than by executing a setuid or setgid binary, for a process to change its user or group ID or to create a process with a particular user or group ID, that ID must match one of its real, effective or saved set IDs or the process must have the appropriate ability.

For a supplementary group to be added or removed from a process, the process performing the operation must have the SETGID ability for the group ID being added or removed. Unlike the user and main group ID, supplementary group IDs may be set on other processes but only in one particular situation: if a process spawns a child in the held state using the POSIX_SPAWN_HOLD flag with posix_spawnattr_setxflags(), then that process can change its child's supplementary groups by calling setgroupspid() until the point where it has permitted the child to start executing.

To view the user and group IDs for a process, run pidin user.

The system assumes that unrelated processes run with different user IDs (and usually group IDs). It expects, for example, that all resource managers are started or configured to switch to unique IDs. This maintains the maximum isolation of one process from the next. In certain circumstances, a process might be permitted to perform some action or to perform it without the need for an ability based on it sharing a common user ID with another process (e.g., kill()).