Real UIDs vs Effective UIDs and Setuid Programs

4 minute read

Intro

In all *nix systems, a UID or a “User ID Number” is an integer which maps your particular username (whoami) to this unique integer. These mappings between the actual username and the UIDs is maintained in the /etc/passwd file. Every running process has at least two UIDs present in the process control block (task_struct in linux source code) which can be seen by this command

cat /proc/${PID}/status | grep 'Uid'

On my system, this results into 1000 1000 1000 1000 which are respectively the uid, euid, suid and fsuid respectively for this process.

What Real and Effective UIDs are?

Simply stated, real UIDs identify the user who launched the process, while the effective UIDs determine what resources the process is eligible to access or has access to.

A simple analogy can be drawn - Within a company, the ID card of every person tells us about who the person is whereas the Secret Keys that they have authorizes them to various resouces in company. It’s the ID cards vs secret keys thing.

Doesn’t clarifies…hang on a sec!

To make things more clear, we’ll have to dive deeper into what set-uid programs are.

Set-UID programs

Whenever you change your password using the passwd command, the changes are made to /etc/passwd and /etc/shadow files. The passwords aren’t stored directly in /etc/passwd; instead they are stored in /etc/shadow in hashed form. Furthermore, any user can read the /etc/passwd file whereas only the root user is allowed to read the /etc/shadow file.

The passwd command when run as a non-root user offers us to change the password of the current user only, but changing the password requires changes to be made to /etc/shadow and /etc/passwd files. How come the passwd command edit these files even when it doesn’t have sufficient privileges?

The answer lies in the fact that passwd is a Set-UID program meaning that it changes to some other user (here root) for a moment to make changes to these files.

Set-UID bit in Inodes

Consider a little program in C:

#include<stdio.h>
#include<unistd.h>
void printIDs(){
	printf("ruid = %d, euid = %d\n", getuid(), geteuid());
}

int main(){
	printIDs();
	int ret_val = setuid(0);
	if (ret_val == -1){
		perror("Errored!");
		_exit(-1);
	}
	printIDs();
	return 0;
}

Output (a.out):
UID GID
Real 1000 Real 1001
Effective 1000 Effective 1001
Errored!: Operation not permitted

Why doesn’t setuid work? And how was it working in the case of passwd? The answer is the “Set-UID” bit present in Inode of the executable of this program.

Set-UID bit runs the program with the effective UID set as the owner of this file instead of the one who is running this file, so that if root owns this executable and msharma runs this executable, then reuid will be that of msharma (1000) while euid will be that of root (0).

Suppose a.out was the object file for this program. stat a.out command tells us that msharma is the owner of this file since msharma has compiled this file. If we change the ownership of this file to root, and set the setuid bit and run our executable

sudo chown root:root ./a.out
sudo chmod +s ./a.out

Output (a.out) :
ruid = 1000, euid = 0
ruid = 0, euid = 0

Here, the program started off with root privileges since euid was 0 initially, and when we did setuid(0), we changed the ruid too. Since, here we were root this changes doesn’t makes much sense but if setuid bit was set by some other non-root user, then the advantages would be apparent. Check this SO Question which explains temporarily or permanently dropping root privileges (consider the case when you wish to fork a new process, and then exec in child with lowered privileges).

Note: Make sure your partition is not mounted with `nosuid` flag. This disables the whole set-uid bit business  at the file-system level.

Extra Readings

There’s yet another kind of UIDs namely set-saved UIDs which can help to regain access once we had dropped privileges. You can check them out below.

Tips for Writing Secure Setuid Programs
The Excellent - GNU libc manual
SO Question - Using the setuid bit properly

I shall create another post explaining in what ways we can potentially break set-uid programs and how much of a security risk they possess if not coded properly.

Updated: