3

I am completely new to *NIX based OSes. One of the things that baffles me is that a process or program may execute setuid(0) and then perform privileged operations and revert back to its normal uid.

My question is what is the mechanism in *NIX to prevent any arbitrary process from possessing root ?

If I write a simple C program that calls setuid(0) under what conditions will that call succeed and under what conditions will it fail ?

5
  • Yes so which user can? Can only a root user invoke this method?
    – ng.newbie
    Oct 22, 2017 at 16:36
  • If we accept your unproven claim that any process can run as root, that would be a major security hole that has for some reason gone unpatched in the decades unix has been available. A simpler answer is instead that your claim is false; why then would you make such a claim?
    – thrig
    Oct 22, 2017 at 16:59
  • @thrig What confuses me is that looking at the source for the su binary - you can see that there is a call to setuid(0), so was just wondering how it is possible there. And if it possible there why isn't it possible in other cases ? What prerequisites are needed for the method to successfully execute ? That was my question. I know my claim is false.
    – ng.newbie
    Oct 22, 2017 at 17:03
  • su has the setuid permission bit set
    – thrig
    Oct 22, 2017 at 18:10
  • @thrig Would you please tell me which permission but needs to be set in order for a successful call to setuid(0) ?
    – ng.newbie
    Oct 22, 2017 at 18:29

3 Answers 3

12

The basic idea is that a process may only reduce its privileges. A process may not gain any privileges. There is one exception: a process that executes a program from a file that has a setuid or setgid flag set gains the privileges expressed by this flag.

Note how this mechanism does not allow a program to run arbitrary code with elevated privileges. The only code that can be run with elevated privileges is setuid/setgid executables.

The root user, i.e. the user with id 0, is more privileged than anything else. A process with user 0 is allowed to do anything. (Group 0 is not special.)

Most processes keep running with the same privileges. Programs that log a user in or start a daemon start as root, then drop all privileges and execute the desired program as the user (e.g. the user's login shell or session manager, or the daemon). Setuid (or setgid) programs can operate as the target user and group, but many switch between the caller's privileges and their own additional privileges depending on what they're doing, using the mechanisms I am going to describe now.

Every process has three user IDs: the real user ID (RUID), the effective user ID (EUID), and the saved user ID (SUID). The idea is that a process can temporarily gain privileges, then abandon them when it doesn't need them anymore, and gain them back when it needs them again. There's a similar mechanism for groups, with a real group ID (RGID), an effective group ID (EGID), a saved group ID (SGID) and supplementary groups. The way they work is:

  • Most programs keep the same real UID and GID throughout. The main exception is login programs (and daemon launchers), which switch their RUID and RGID from root to the target user and group.
  • File access, and operations that require root privileges, look at the effective UID and GID. Privileged programs often switch their effective IDs depending on whether they're executing a privileged operation.
  • The saved IDs allow switching the effective IDs back and forth. A program may switch its effective ID between the saved ID and the real ID.

A program that needs to perform certain actions with root privileges normally runs with its EUID set to the RUID, but calls seteuid to set its EUID to 0 before running the action that requires privileges and calls seteuid again to the EUID change back to the RUID afterwards. In order to perform the call to seteuid(0) even though the EUID at the time is not 0, the SUID must be 0.

The same mechanism can be used to gain group privileges. A typical example is a game that saves high scores of local users. The game executable is setgid games. When the game starts, its EGID is set to games, but it changes back to the RGID so as not to risk performing any action that the user isn't normally allowed to do. When the game is about to save a high score, it changes its EGID temporarily to games. This way:

  • Because the high score file requires privileges that ordinary users don't have, the only way to add an entry to the high score file is to play the game.
  • If there's a security vulnerability in the game, the worst that it can do is grant a user permission to the games group, allowing them to cheat on high scores.
  • If there's a bug in the game that doesn't result in the program calling the setegid function, e.g. a bug that only causes the game to write to an unintended file, then that bug doesn't allow cheating on high scores, because the game doesn't have the permission to write to the high score file without calling setegid.

What I wrote above is describes a basic traditional Unix system. Some modern systems have other features that complement the traditional Unix privilege model. These features come in addition to the basic user/group effective/real system and sometimes interact with it. I won't go into any detail about these additional features, but I'll just mention three features of the Linux security model.

  • The permission to perform many actions is granted via a capability rather than to user ID 0. For example, changing user IDs requires the capability CAP_SETUID, rather than having user ID 0. Programs running as user ID 0 receive all capabilities unless they go out of their way, and programs running with CAP_SETUID can acquire root privileges, so in practice running as root and having CAP_SETUID are equivalent.
  • Linux has several security frameworks that can restrict what a process can do, even if that process is running as user ID 0. With some security frameworks, unlike with the traditional Unix model and capabilities, a process may gain privileges upon execve due to the security framework's configuration rather than due to flags in the executable file's metadata.
  • Linux has user namespaces. A process running as root in a namespace only has privileges inside that namespace.
1
  • Thanks for the explanation. I had no idea about the saved ID stuff!
    – jjohn
    Jan 15, 2018 at 17:22
2

In addition to the user id, a process is also associated with an effective user id. When a setuid root program like su is executed, the effective uid is set to 0, but the real uid stays the same. The effective uid is, as the name says, the uid that is used for permission checks, the real uid just tells "who we really are". A process with an effective uid equal to zero can successfully call setuid(0) to change the real uid to zero.

6
  • So all processes that need to execute setuid(0) the effective uid needs to be 0 in advance, correct ? Then why isn't the effective uid just used for root operations ? Why call setuid(0) again ? I mean any process with effective uid set to 0 will obviously be able to execute setuid(0), so why have a call again the code ?
    – ng.newbie
    Oct 22, 2017 at 18:26
  • Also is there any way a normal process can set it's eid to 0 so it can successfully call setuid(0) ?
    – ng.newbie
    Oct 22, 2017 at 18:28
  • Quoting the setuid(2) man page: "setuid() sets the effective user ID of the calling process. If the effective UID of the caller is root, the real UID and saved set-user-ID are also set." So calling setuid(0) as (effective) root just sets the real uid to 0, too. Not doing this affects for example the access(2) system call, which uses the real uid to do the check. ps also shows the su process as running by root because is shows the real uid. (Btw. the access(2) system call is essentially useless because of race conditions.) Oct 22, 2017 at 18:51
  • No, there is no way a normal (unprivileged) process can successfully call setuid(0). An unprivileged program can use setuid() to change its effective uid, though. If the program is started as suid non-root, the effective uid and the real uid are different. The program can call setuid() to set the effective uid to the real uid. A privileged program can use setuid() to drop privileges by setting both uids to a value of its own choice. Oct 22, 2017 at 19:30
  • Why is there an effective uid and a real uid ? What does that accomplish? What would be the harm if there was only one uid?
    – ng.newbie
    Oct 22, 2017 at 19:57
0

In order for setuid(some_uid) to work, the following conditions need to be met:

  1. The executable's file needs to be owned by some_uid1.
  2. The executable's file needs to have the setuid permission.

The setuid permission can be granted or removed with chmod:

chmod u+s execuables_file_name
chmod u-s execuables_file_name

The setuid permission can be seen when displaying the file permissions as the execute permission will be replaced by an s if the setuid permission is granted.

> ll execuables_file_name
-rwsrwxr-x 1 root root 0 Sep 30 17:23 execuables_file_name* 

If for some reason the owner user doesn't have execute permissions, then it will be displayed as a capital S instead:

> ll execuables_file_name
-rwSrwxr-x 1 root root 0 Sep 30 17:23 execuables_file_name* 

Note that scripts are an entirely different beast.

A thorough guide to permissions.

1. setuid(some_uid) will also succeed if transitioning to the user id that started the process

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .