In a UNIX system, one can easily become confused by the concepts of RUID and EUID. This note aims to clarify and differentiate between them to help users identify these identifiers more easily.

Environment Setup

To explore these concepts further, we will need to create an executable file and a plain-text file to observe how they behave in various scenarios.

We can utilize the functions getpid, getuid, geteuid, getgid, and getegid to obtain information about the running process, including its PID, RUID, RGID, EUID, and EGID. Below is the code snippet to achieve this:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>

int main()
{
    int pid = getpid();
    int uid = getuid();
    int gid = getgid();
    int euid = geteuid();
    int egid = getegid();
    printf("pid=%d uid=%d gid=%d euid=%d egid=%d\n", pid, uid, gid, euid, egid);

    int fp = open("a_file.txt", O_RDWR); // reads the plain-text file
    if (fp == -1) {
        char *msg;
        perror(msg);
    }

}

Let’s name the file pinfo.c and compile it to pinfo, and then create a plain-text file a_file.txt under the same directory.

Then, let’s assign the permission for a_file.txt to 600, and pinfo to 750.

$ ls -l pinfo a_file.txt
-rw------- 1 bar bar     0 Jul  4 16:33 a_file.txt
-rwxr-x--- 1 bar bar 17008 Jul  4 16:30 pinfo

You’ll also need to create an user and add that to the same group with the owner (bar) of the plain-text and the executable file, in our example, the group would be bar.

useradd foo
passwd foo
usermod -a -G bar foo

Now, let’s move on to our experiment.

Run the program

Output of the process when the owner runs it

./pinfo
pid=10911 uid=1000 gid=1000 euid=1000 egid=1000

Output of the process when foo runs it

./pinfo
pid=10945 uid=1001 gid=1001 euid=1001 egid=1001
Permission denied

Explanation

The permission for a_file.txt is 700, which only allows the owner bar to read and write. foo is not the file owner, so he cannot access a_file.txt even when it has the execution permission for pinfo.

User execution permission as s

Now let’s change the permission bit of user to s by executing chmod u+s ./pinfo (with the owner, bar).

Output of the process when the owner runs it is the same as previous experiment (except the PID)

Output of the process when foo runs it

./pinfo
pid=11062 uid=1001 gid=1001 euid=1000 egid=1001

Notice that the EUID has change to bar’s id (1000).

Explanation

executing chmod u+s ./pinfo updates the execution bit to s. It means that whoever runs the program, will have the same privileges as the executable file owner, so foo executes ./pinfo with bar’s privileges, hence has access to a_file.txt. As you can see from the result, the Permission denied message is gone, meaning the file was read successfully.

Group execution permission as s

Now, let’s reset the file permission of pinfo to 750, and then add a s permission to group by chmod g+s pinfo-R

You should see

ls -l pinfo a_file.txt
-rw------- 1 bar bar     0 Jul  4 16:33 a_file.txt
-rwxr-s--- 1 bar bar 17008 Jul  4 16:30 pinfo

By executing the program, the output remains the same (except the pid) when the owner runs it

Output of the process when foo runs it

./pinfo
pid=11379 uid=1001 gid=1001 euid=1001 egid=1000
Permission denied

Explanation

notice the EGID is 1000, which is bar, however the permission for a_file.txt allows only the owner to access it. Update a_file.txt with chmod g+rw a_file.txt will allow foo accessing the file.

Conclusion

To Simply put: The real user (with RUID) runs the executable file as the effective user (with EUID). The same applies to the group. With this feature, we can create application that allow other users to run application with certain privileges. If you ever wonder how does the command sudo work. This is exactly the trick by setting the EUID of the executable sudo to the root. Historically, some of the OS implementation sets the EUID for ping so that it has the right privilege for network connection. I hope you find this note helpful.