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.