Set-User-ID and Set-Group-ID Programs

A set-user-ID program allows a process to gain privileges it would not normally have, by setting the process’s effective user ID to the same value as the user ID (owner) of the executable file. A set-group-ID program performs the analogous task for the process’s effective group ID. (The terms set-user-ID program and set-group-ID program are sometimes abbreviated as set-UID program and set-GID program.)

Like any other file, an executable program file has an associated user ID and group ID that define the ownership of the file. In addition, an executable file has two special permission bits: the set-user-ID and set-group-ID bits. (In fact, every file has these two permission bits, but it is their use with executable files that interests us here.) These permission bits are set using the chmod command. An unprivileged user can set these bits for files that they own. A privileged user (CAP_FOWNER) can set these bits for any file. Here’s an example:

$ su

Password:
# ls -l prog

-rwxr-xr-x    1 root     root       302585 Jun 26 15:05 prog
# chmod u+s prog                        Turn on set-user-ID permission bit

# chmod g+s prog
                        Turn on set-group-ID permission bit

As shown in this example, it is possible for a program to have both of these bits set, although this is uncommon. When ls -l is used to list the permissions for a program that has the set-user-ID or set-group-ID permission bit set, then the x that is normally used to indicate that execute permission is set is replaced by an s:

# ls -l prog

-rwsr-sr-x    1 root     root       302585 Jun 26 15:05 prog

When a set-user-ID program is run (i.e., loaded into a process’s memory by an exec()), the kernel sets the effective user ID of the process to be the same as the user ID of the executable file. Running a set-group-ID program has an analogous effect for the effective group ID of the process. Changing the effective user or group ID in this way gives a process (in other words, the user executing the program) privileges it would not normally have. For example, if an executable file is owned by root (superuser) and has the set-user-ID permission bit enabled, then the process gains superuser privileges when that program is run.

Set-user-ID and set-group-ID programs can also be designed to change the effective IDs of a process to something other than root. For example, to provide access to a protected file (or other system resource), it may suffice to create a special-purpose user (group) ID that has the privileges required to access the file, and create a set-user-ID (set-group-ID) program that changes the effective user (group) ID of a process to that ID. This permits the program to access the file without allowing it all of the privileges of the superuser.

Sometimes, we’ll use the term set-user-ID-root to distinguish a set-user-ID program that is owned by root from one owned by another user, which merely gives a process the privileges accorded to that user.

Note

We have now started using the term privileged in two different senses. One is the sense defined earlier: a process with an effective user ID of 0, which has all of the privileges accorded to root. However, when we are talking about a set-user-ID program owned by a user other than root, we’ll sometimes refer to a process as gaining the privileges accorded to the user ID of the set-user-ID program. Which sense of the term privileged we mean in each case should be clear from the context.

For reasons that we explain in Be Careful When Executing a Program, the set-user-ID and set-group-ID permission bits don’t have any effect for shell scripts on Linux.

Examples of commonly used set-user-ID programs on Linux include: passwd(1), which changes a user’s password; mount(8) and umount(8), which mount and unmount file systems; and su(1), which allows a user to run a shell under a different user ID. An example of a set-group-ID program is wall(1), which writes a message to all terminals owned by the tty group (normally, every terminal is owned by this group).

In Password Encryption and User Authentication, we noted that the program in Example 8-2 needed to be run from a root login so that it could access the /etc/shadow file. We could make this program runnable by any user by making it a set-user-ID-root program, as follows:

$ su

Password:
# chown root check_password             Make this program owned by root

# chmod u+s check_password              With the set-user-ID bit enabled
# ls -l check_password

-rwsr-xr-x    1 root   users    18150 Oct 28 10:49 check_password
# exit

$ whoami                                This is an unprivileged login

mtk
$ ./check_password                      But we can now access the shadow

Username: avr                           password file using this program

Password:
Successfully authenticated: UID=1001

The set-user-ID/set-group-ID technique is a useful and powerful tool, but one that can result in security breaches in applications that are poorly designed. In Chapter 38, we list a set of good practices that should be observed when writing set-user-ID and set-group-ID programs.