Related
I wonder where Linux kernel keeps 'ruid' and 'euid'.
Below is what I know about them.
When an user runs a file and the file turns to a process, the process gets to have ruid and euid.
If the file had been set to use setuid, euid of the process would change to user id of the owner of that file, and if not, euid would not change and be the same as ruid.
Then, Linux kernel allows the process to run another process or use other resources in the system according to ruid and euid.
So, I think that means kernel has to keep ruid and euid of each process somewhere in RAM.
I thought the 'somewhere' is in PCB, but PCB block does not have fields for ruid and euid.
I tried to find them in the process file of '/proc' directory, but failed.
Where does Linux keep ruid and euid of running processes?
Here is an explanation of how it works in new kernels:
From user-space point of view, real and effective user ID can be changed using setreuid() syscall. See man 2 setreuid for usage details
Kernel is using struct cred for storing UID and EUID
Each process has its own struct cred; take a look at .cred field in struct task_struct
RUID is stored in .uid field of struct cred; see setreuid() syscall code:
struct cred *new;
kuid_t kruid, keuid;
...
kruid = make_kuid(ns, ruid);
keuid = make_kuid(ns, euid);
...
new->uid = kruid;
new->euid = keuid;
...
return commit_creds(new);
commit_creds() function is actually sets RUID and EUID to current process
See also this answer to get a clue about older kernels: How to get current process's UID and EUID in Linux Kernel 4.2?
I am already aware of the real user id. It is the unique number for a user in the system.
On my system, my uid is
$ echo $UID
1014
$
What do the other two IDs stands for?
And what is the use of effective user id and saved user id and where do we use them in the system?
The distinction between a real and an effective user id is made because you may have the need to temporarily take another user's identity (most of the time, that would be root, but it could be any user). If you only had one user id, then there would be no way of changing back to your original user id afterwards (other than taking your word for granted, and in case you are root, using root's privileges to change to any user).
So, the real user id is who you really are (the one who owns the process), and the effective user id is what the operating system looks at to make a decision whether or not you are allowed to do something (most of the time, there are some exceptions).
When you log in, the login shell sets both the real and effective user id to the same value (your real user id) as supplied by the password file.
Now, it also happens that you execute a setuid program, and besides running as another user (e.g. root) the setuid program is also supposed to do something on your behalf. How does this work?
After executing the setuid program, it will have your real id (since you're the process owner) and the effective user id of the file owner (for example root) since it is setuid.
The program does whatever magic it needs to do with superuser privileges and then wants to do something on your behalf. That means, attempting to do something that you shouldn't be able to do should fail. How does it do that? Well, obviously by changing its effective user id to the real user id!
Now that setuid program has no way of switching back since all the kernel knows is your id and... your id. Bang, you're dead.
This is what the saved set-user id is for.
I'll try to explain step by step with some examples.
Short background
Each process has its own 'Process credentials' which include attributes like PID, the PPID, PGID, session ID and also the real and effective user and group IDs:
RUID, EUID, RGID, EGID.
We'll focus on those.
Part 1: Understand UID and GID
Now I'll log into a shell with my credentials and run:
$ grep $LOGNAME /etc/passwd
rotem:x:1000:1000:rotem,,,:/home/rotem:/bin/bash
You can see my logname (rotem), the UID and GID which are both 1000, and other details like the shell I'm logged into.
Part 2: Understand RUID and RGID
Every process has an owner and belongs to a group.
In our shell, every process that we'll now run will inherit the privileges of my user account and will run with the same UID and GID.
Let's run a simple command to check it:
$ sleep 10 & ps aux | grep 'sleep'
And check for the process UID and GID:
$ stat -c "%u %g" /proc/$pid/
1000 1000
Those are the real user ID (RUID) and real group ID (RGID) of the process.
(*) Check other options to view the UID and GID and ways to get this in a single line.
For now, accept the fact that the EUID and EGID attributes are 'redundant' and just equals to RUID and RGID behind the scenes.
Part 3: Understand EUID and EGID
Let's take the ping command as an example.
Search for the binary location with the which command then run ls -la:
-rwsr-xr-x 1 root root 64424 Mar 10 2017 ping
You can see that the owner and the group of the file are root. This is because the ping command needs to open up a special socket and the Linux kernel demands root privilege for that.
But how can I use ping if I don't have root privilege?
Notice the 's' letter instead of 'x' in the owner part of the file permission.
This is a special permission bit for specific binary executable files (like ping and sudo) which is known as setuid.
This is where EUID and EGID come into play.
What will happen is when a setuid binary like ping executes, the process changes its Effective User ID (EUID) from the default RUID to the owner of this special binary executable file which in this case is - root.
This is all done by the simple fact that this file has the setuid bit.
The kernel makes the decision whether this process has the privilege by looking on the EUID of the process. Because now the EUID points to root, the operation won't be rejected by the kernel.
Notice: On latest Linux releases the output of the ping command will look different because of the fact that they adopted the Linux Capabilities approach instead of this setuid approach - for those who are not familiar - read here.
Part 4: What about SUID and SGID?
The Saved user ID (SUID) is being used when a privileged process is running (as root for example) and it needs to do some unprivileged tasks.
In that case, the effective UID (EUID) from before will be saved inside SUID and then changed to an unprivileged task. When the unprivileged task is completed, the EUID will be taken from the value of SUID and switch back to privileged account.
Real user id is the user that spawned the process.
Effective user id is the user determined by the setuid bit on the binary being executed.
Here are some truths about uids and euids, with the manual sources for each:
You can use euid when you're spawning as root and you need to temporarily drop privileges and still be able to regain root privileges after, as in man setuid(2):
Thus, a set-user-ID-root program wishing to temporarily drop root privileges, as‐
sume the identity of an unprivileged user, and then regain root privileges after‐
ward cannot use setuid(). You can accomplish this with seteuid(2).
You can also use it to raise your privileges from a setuid program. If your effective user id is root, everything will react as if you are root, except I think the only exception is file access checks will check your real user id rather than effective user id, which is a source of confusion, as in man access(2):
The check is done using the calling process's real UID and GID, rather
than the effective IDs as is done when actually attempting an operation
(e.g., open(2)) on the file. Similarly, for the root user, the check
uses the set of permitted capabilities rather than the set of effective
capabilities; and for non-root users, the check uses an empty set of
capabilities.
When calling bash, it doesn't propagate euid unless you pass -p as in man bash(1):
If the shell is started with the effective user (group) id not equal to the real
user (group) id, and the -p option is not supplied, no startup files are read,
shell functions are not inherited from the environment, the SHELLOPTS, BASHOPTS,
CDPATH, and GLOBIGNORE variables, if they appear in the environment, are ignored,
and the effective user id is set to the real user id. If the -p option is sup‐
plied at invocation, the startup behavior is the same, but the effective user id
is not reset.
When using sudo, both effective and real user id's are set as in man sudo(8):
When sudo executes a command, the security policy specifies the execution environ‐
ment for the command. Typically, the real and effective user and group and IDs are
set to match those of the target user, as specified in the password database, and
the group vector is initialized based on the group database (unless the -P option
was specified).
This is how I understand it. The file an user executes(equivalent to starting a process) will have a RUID equal to that user's id. Important thing to note here is that the uid which created a file is not the same as the uid that executes the file. They can be the same or different. So, RUID may vary depending on the UID that executes the file. When a file has the setuid bit on it, whenever an uid executes that file, that uid will temporary be replaced with the file owner's uid. So, if we have a file owned by uid 456 and has the setuid bit on it, whenever uid 123 executes that file, that file will be executed with the uid 456. In this scenario, uid 123 is the RUID and uid 456 is the EUID.
I want to kill all programs running in the same directory as I do.
I need to find which programs are running right now and kill them (and to be careful to not kill myself).
I am running my program in Ubuntu(Linux).
I need to use this command:
int kill(pid_t pid, int sig);
How I can do it?
*The programs live in the same directory .
Stricto sensu, your question does not make sense. By the time you are getting the directory of a process, it could have called chdir(2) before you kill it (and then you should not have killed it).
On Linux, to get information about processes, use proc(5). So use readdir(3) after opendir(3) on /proc/ (filter only the numerical directories, like /proc/1234/ which corresponds to process of pid 1234). For each process there, use readlink(2) on /proc/1234/cwd to get its directory (and on /proc/1234/exe to get its executable, if it matters). Use getcwd(2) and getpid(2) to get current directory and current process.
BTW, your kill(2) is a syscall (listed in syscalls(2)), not a command. The command is kill(1) to be usually run from a shell.
You should read Advanced Linux Programming.
At last, your desired behavior to kill every process running in your directory is extremely user unfriendly. So at least document it, and perhaps give some way to disable that behavior. A more gentle way would be to make some temporary directory (using mkdtemp(3)) then chdir(2) into it (then perhaps unlink(2) or rmdir(2) it).
See also pkill(1) and pgrep
According to the man pages for issetugid, the call is supposed to either (1) alert to uid/gid changes; or (2) alert to a possible tainted environment. The function name suggests a third purpose.
First question: what is it purpose?
When I look at the implementations available (for example, on Linux system as a library since Linux kernel does not provide the API), I find the following:
if (getuid() != geteuid()) return 1;
if (getgid() != getegid()) return 1;
return 0;
On Solaris, it looks as follows:
return ((curproc->p_flag & SUGID) != 0);
I'm a bit suspicious, but that's partially because its difficult understand what functions like geteuid and getegid return across all platforms - for example, BSD, Linux, Unix and Solaris.
Second question: is the Linux code semantically equivalent to Solaris code?
Third question: are geteuid and getegid implemented the same across platforms? How about for systems that have I three id's play - real, effective, and saved?
Fourth question: is the effective id the only id's that matter here?
If a process starts as UID = 0 and temporarily drops privileges, then the saved id's come into play. A process that temporarily drops root does not need to exec and should not be tainted.
Fifth question: is a process that temporarily drops root tainted?
Sixth question: should a process whose effective id is the saved id be considered tainted?
Six questions is a bit much to answer in a system designed for one question to answer, especially if no one person knows the answers to all six, but I'll try...
1) The purpose of issetugid() is to let libraries know if they're being used in a program that was run with raised privileges so they can avoid risky behavior such as trusting LD_LIBRARY_PATH, NLSPATH, etc. environment variables that would let the caller load modules that can abuse the raised privileges. You can see some historical discussions on it like this ncurses 4.1 security bug thread.
2) That code appears to be less secure than the BSD & Solaris versions, since it doesn't take into account the saved setid bits.
3) They probably have different implementations on different kernels - look at the platform source code to find out.
4, 5 & 6) No, yes, yes - a process that can change its euid or egid back to higher levels should still not trust environment variables that cause it to load user-provided code to exploit them.
I don't know issetugid(), but I can learn by reading BSD or Solaris manual pages. The function comes from OpenBSD.
1) OpenBSD's manual for issetugid(2) says, "The issetugid() function returns 1 if the process was made setuid or setgid as the result of the last or other previous execve() system calls. Otherwise it returns 0." It then suggests using issetugid() to check whether files named in environment variables are safe to open.
2) No, your Linux and Solaris code are not equivalent. A process running setuid might set its real uid to its effective uid without cleaning its environment variables. For example, uid_t uid = geteuid(); setresuid(uid, uid, uid); would set both real uid and saved uid to effective uid. Then your Linux issetugid() would return 0, but Solaris issetugid() would return 1.
Solaris checks the SUGID process flag at exec time. Illumos, the free fork of Solaris, sets SUGID in src/uts/common/os/exec.c when executing a file. OpenBSD has similar logic. OpenBSD's manual says,
If a child process executes a new executable file, a new issetugid status will be determined. This status is based on the existing process's uid, euid, gid, and egid permissions and on the modes of the executable file. If the new executable file modes are setuid or setgid, or if the existing process is executing the new image with uid != euid or gid != egid, the new process will be considered issetugid.
Solaris and OpenBSD compare the ids at exec time. Your Linux code delays the comparison until the call to issetugid(), so it is not equivalent.
3) The geteuid() and getegid() functions seem to do the same thing everywhere; they simply return the effective user id and the effective group id.
4) The saved ids don't matter. The process might have changed those ids without cleaning its environment variables. None of the real, effective, or saved ids tell us who set the environment variables for the current process.
5) At least on OpenBSD and Solaris, a process that temporarily drops root does not become tainted. OpenBSD's manual page says,
The issetugid() system call's result is unaffected by calls to setuid(), setgid(), or other such calls. In case of a fork(), the child process inherits the same status.
The status of issetugid() is only affected by execve().
When a process temporarily drops root with setuid() or seteuid(), it does not execute a file, so its issetugid() value does not change.
But FreeBSD, DragonFly BSD, and NetBSD define issetugid() more strictly. FreeBSD's manual for issetugid(2) says,
A process is tainted if it was created as a result of an execve(2) system call which had either of the setuid or setgid bits set (and extra privileges were given as a result) or if it has changed any of its real, effective or saved user or group ID's since it began execution.
With these systems, a process dropping root does force its issetugid() value to 1.
6) No, an effective id equal to a saved id does not taint a process. If it did, then every process would be tainted, because every process has its saved id set to its effective id at exec time.
In Linux system, an unprivileged user launches a program. The process created has the capabilities CAP_NET_RAW,CAP_NET_ADMIN with mode as effective,permitted,inheritable.
This process then creates a child process by calling fork and execv to invoke another program udhcpc, but the child process does not inherit the capabilities CAP_NET_RAW,CAP_NET_ADMIN as expected. Even though before setting the capabilities I have called prctl(PR_SET_KEEPCAPS, 1).
Any suggestion on what to do to inherit unprivileged parent process' capabilities upon fork followed by execve?
On execve(), the file capability sets of the file being executed (in this case, udhcpc) are inspected and combined with the thread's capability sets. In particular, the file's Inheritable set is AND-ed with the thread's Inheritable set to determine the new Permitted set, and the file's Effective bit must be set in order for the new Effective set to be copied from the Permitted set.
This implies that in your case you must use setcap cap_net_raw,cap_net_admin=ei /path/to/udhcpc to obtain the effect you want (in addition to setting the capabilities in the parent process - the prctl() is not necessary).
According to "The Linux Programming Interface" by Michael Kerrisk (No Starch Press, 2010):
Since kernel 2.6.24, it is possible to attach capabilities to a file.
Various other features were added in kernels 2.6.25 and 2.6.26 in
order to complete the capabilities implementation.
The tools sucap and execcap are what you should look up. However they are, if I recall limited to restricting, not granting capabilities. Look at :
http://www.linuxjournal.com/article/5737
and
http://lkml.indiana.edu/hypermail/linux/kernel/0503.1/2540.html
Extracted from the manual, There have been some changes. According to it fork does not change capabilities. And now there is an ambient set, it seems that this is for what you are trying to do.
Ambient (since Linux 4.3):
This is a set of capabilities that are preserved across an execve(2) of a program that is not privileged. The ambient capability set obeys the invariant that no capability can ever
be ambient if it is not both permitted and inheritable.
The ambient capability set can be directly modified using
prctl(2). Ambient capabilities are automatically lowered if
either of the corresponding permitted or inheritable
capabilities is lowered.
Executing a program that changes UID or GID due to the set-
user-ID or set-group-ID bits or executing a program that has
any file capabilities set will clear the ambient set. Ambient
capabilities are added to the permitted set and assigned to
the effective set when execve(2) is called.
A child created via fork(2) inherits copies of its parent's
capability sets. See below for a discussion of the treatment of
capabilities during execve(2).
…
P'(ambient) = (file is privileged) ? 0 : P(ambient)
P'(permitted) = (P(inheritable) & F(inheritable)) |
(F(permitted) & cap_bset) | P'(ambient)
P'(effective) = F(effective) ? P'(permitted) : P'(ambient)
P'(inheritable) = P(inheritable) [i.e., unchanged]
where:
P denotes the value of a thread capability set before the
execve(2)
P' denotes the value of a thread capability set after the
execve(2)
F denotes a file capability set
cap_bset is the value of the capability bounding set (described
below).
It is useful to have a wrapper program that can execute any program with specific capabilities, without having to set capabilities on target programs. Such a wrapper is particularly useful to run software from a build directory (where setcap would be cumbersome) or to run interpreters like Python (where it would be inappropriate).
As explained in other answers, ambient capabilities solve this, but they are only available since kernel 4.3. It is possible to work around this problem by having the wrapper load the target program directly instead of using exec. By that, I mean open the executable, map relevant sections, set up the stack, etc., and jump to its code. This is a pretty complicated task, but luckily the wine-preloader program from the Wine project does exactly that (and some other things that are irrelevant for this purpose).
Run something like this as root to set up the wrapper:
cp /usr/bin/wine-preloader /path/to/wrapper
setcap cap_net_raw+ep /path/to/wrapper # set whatever capabilities you need
Now we have a copy of wine-preloader that is able to run any program with those capabilities:
/path/to/wrapper /path/to/executable arguments...
This works but there are some pitfalls:
The target program must be a path to an executable, it cannot find programs in PATH.
It does not work if the target program is a script with an interpreter (#!).
The wine-preloader prints a message about not being able to find something (but it still runs the program fine).