Purpose of issetugid? - linux

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.

Related

Why many Linux distros use setuid instead of capabilities?

capabilities(7) are a great way for not giving all root privileges to a process and AFAIK can be used instead of setuid(2). According to this and many others,
"Unfortunately, still many binaries have the setuid bit set, while they should be replaced with capabilities instead."
As a simple example, on Ubuntu,
$ ls -l `which ping`
-rwsr-xr-x 1 root root 44168 May 8 2014 /bin/ping
As you know, setting suid/guid on a file, changes the effective user ID to root. So if there the suid-enabled program contains a flaw, the non-privileged user can break-out and become the equivalent of the root user.
My question is why many Linux distributions still use setuid method while setting capabilities can be used instead with less security concerns?
This may not give the reason why some dudes somewhere decided one way or another, but some auditing tools and interfaces may not yet know about capabilities.
An example is the proc_connector netlink interface and the programs based on it (like forkstat): there are events for a process changing its credentials, but not for it changing its capabilities.
FWIW, the cause why you may not get eg a net_raw+ep ping(8) instead of a setuid one on a Debian-like distro is because that depends on the setcap(8) utility from the libcap2-bin package already existing before you install ping. From iputils-ping.postinst:
if command -v setcap > /dev/null; then
if setcap cap_net_raw+ep /bin/ping; then
chmod u-s /bin/ping
else
echo "Setcap failed on /bin/ping, falling back to setuid" >&2
chmod u+s /bin/ping
fi
else
echo "Setcap is not installed, falling back to setuid" >&2
chmod u+s /bin/ping
fi
Also notice that ping itself will drop any setuid privileges and switch to use capabilities on Linux upon starting, so your concerns about it may be a bit exagerated. From ping.c:
int
main(int argc, char **argv)
{
struct addrinfo hints = { .ai_family = AF_UNSPEC, .ai_protocol = IPPROTO
_UDP, .ai_socktype = SOCK_DGRAM, .ai_flags = getaddrinfo_flags };
struct addrinfo *result, *ai;
int status;
int ch;
socket_st sock4 = { .fd = -1 };
socket_st sock6 = { .fd = -1 };
char *target;
limit_capabilities();
From ping_common.c
void limit_capabilities(void)
{
...
if (setuid(getuid()) < 0) {
perror("setuid");
exit(-1);
}
Consider a different planet were capabilites patches were rejected, the submitter had to try harder to make a neighborly improvement, and came up with this:
filesystems can be mounted suid, nosuid, or capsuid.
if mounted capsuid, setuid bits on ${file} don't count unless .${file}.suid_capability also exists, is setuid, and is either 0-length or parseable in some standardized format:
-r-sr-xr-x. 1 carton carton 3812 Dec 27 15:39 t1
-r-Sr--r--. 1 carton carton 0 Dec 27 15:39 .t1.suid_capability
if the file is empty, setuid bit works as normal. If it has parseable contents, it works as partial-root capabilities.
if desired the .t1.suid_capability files can be annotated with fields for:
binding to a hash of their matching 't1' file
binding to an absolute path, so for example a setuid file within a chroot does not become "hot" until you chroot into it.
binding to a public key or hash nonce loaded at boot identifying the installed system like a uuid. If the private key or nonce is hidden from a backup server, traditional password resets would still work but some forms of rootkit persistence would become more difficult. It also lets you work with images of other systems in subdirectories, automatically making them effectively "nosuid"-mounted even if you are undisciplined about mount options and elect not to use the absolute path feature.
Now answer some questions about this planet relative to our own:
any missing functionality compared to our native planet?
which system works better with 'ls', 'tar', 'rsync', 'cpio', 'find', 'pax', 'mtree'? with gentoo's ebuild sandbox? with LXC?
which system works better with diskless or guest systems running on NFS or 9p roots?
which system works better with tripwire?
Now repeat those questions considering three systems instead of two:
our planet: mysterious capabilities metadata in filesystems
alternate planet using civilized Unix approach
mosvy's 'ping' example, where the binary drops privileges right after it starts
With the third system are finally starting to lose features relative to status quo: Someone could write a hypothetical capabilities-aware tripwire that works less well in the 'ping' example. Has anyone done that, though? . . . anyone outside NSA---have the people forcing this complexity on us done that work to deliver (relatively small) value from the complexity? And if that's the feature-hill we want to die on (as opposed to reducing attack surface), is there maybe a better way to get it, like dm-verity, that once again moots the complexity?
Now let's make a slight refinement to mosvy's 'ping':
crt.o or some early runtime startup code reads the sidecar file and /etc/suid_capability_hash_nonce. It drops suid if /etc/suid_capability_hash_nonce exists but the sidecar file does not (in "alternate planet" terms, the existence of /etc/suid_capability_hash_nonce makes the whole system '-o capsuid'-mounted). The early runtime startup code handles parsing the sidecars and dropping to the enumerated capabilities so that logic doesn't have to be coded into main.c one-by-one.
Honestly, I think the 'ping' example is superior because programs know what capabilities they need, and the need changes only when the program's source code changes. By setting them in a drop_privileges()-style function the need will never get out of sync. Fanned-out distributions won't have to scramble to update capability sets.
If you disagree and want the sidecar/capabilities-style system, something equivalent could have been implemented without tampering with kernel, mount options, bootup, any of it: old dracut-nfsroot initrds would keep working. It is just a matter of style.
This is all a Socratic way of saying capabilities offers nothing beyond dm-verity + 'ping'-style privileges-dropping. There is nothing "unfortunate" about eschewing complexity that's not pulling its weight.
Every few years CADT developers invent some new form of metadata to cram into filesystem directories: Finder metadata, POSIX ACLs, SELinux contexts, NFS ACLs. What will they think of next? How long will it take to update all the filesystems, all the network protocols, all the fileutils tools, all the non-Linux storage operating systems, all the fancy licensed "enterprise" tape backup suites? Will anyone ever get around to updating all that stuff, or will we just limp along with substandard tools? Will even the core feature be documented usefully, or will it be the undocumented plaything of a few annoying "distributions"? Will the feature work most of the time, but stop systems from booting unexpectedly and create chicken-and-egg recovery problems? Will the feature actually stop any attacks?
Is it fair to all the people who have to clean up after them and tolerate complexity in their own systems that made different, simpler choices, just to unbreak interop that their new feature broke?
The value delivered is particularly low in this case, but we have enough experience to set the value bar high on this type of proposal. I think it was a mistake to accept the feature into Linux and applaud any distribution that avoids it.
Capability support was really enabled in 2008 when file capabilities were added to the kernel. So, that's 13 years and counting to replace setuid. Your question is very valid.
If capability support wasn't implemented in a backwardly compatible way in Linux, it would either have been rejected at birth, or things would have surely changed by now! I suspect it all comes down to the fact that there is no economic incentive to adopt them when their benefit is only evident temporarily when a bug in some code is found to be exploitable.
I think that people are resigned to all the ways that setuid binaries seem to be exploitable and the point fixes that people quickly roll out when another vulnerability surfaces. Buffer overflow -> launch shell -> root exploit -> code fix -> start over. They are comfortable with the idea that user identity = privilege (ie., root). This spills into how people want to describe the futility of breaking down all powerful root into independent capabilities, when a chain of exploits can yield all privilege. Clearly, the pervasive idea that privilege and identity should be equivalent is why Ambient capabilities even exist.
Capabilities, however, when used as they were intended to be used are not identity = privilege. They are capable binaries = privilege - where the privilege is reduced relative to a user identity executing arbitrary code, by the combination of those capability bits and the code in the actual program that has them.
If you write code that edits files, it is clear it shouldn't need to worry about being directly abused for raw ethernet packet formation, or loading kernel modules. Exploiting a bug in that code may well allow a malicious edit of a file but, unlike setuid, it won't allow strange packets to be sent on the network. At least not without tricking the code of some independently capable executable into doing it by proxy.
However, no one intentionally writes buggy code, so I think the answer to your question comes down to another question: "where exactly is the benefit of limiting how exploitable an exploit is?".
I suspect that if some distribution were to figure out how to restructure their code base to eliminate setuid-root binaries, in favor of file capabilities, it would fare better (certainly no worse) over time as code exploits are found vs. other distributions clinging to setuid-root. But, until such a distribution comes into being, I can't fault the idea that that is just an opinion.

What do getresuid() and setresuid() do?

What do the functions getresuid(&arg1,&arg2,&arg3) and setresuid(arg1,arg2,arg3) do?
It would be great if a really basic explanation of these functions were given.
From the credentials(7) man page (abridged):
On Linux, each process has the following user and group identifiers:
Real user ID and real group ID. These IDs determine who owns the process.
Effective user ID and effective group ID. These IDs are used by the kernel to determine the permissions that the process will have when accessing shared resources such as message queues, shared memory, and semaphores. On most UNIX systems, these IDs also determine the permissions when accessing files. However, Linux uses the file system IDs for this task.
Saved set-user-ID and saved set-group-ID. These IDs are used in set-user-ID and set-group-ID programs to save a copy of the corresponding effective IDs that were set when the program was executed. A set-user-ID program can assume and drop privileges by switching its effective user ID back and forth between the values in its real user ID and saved set-user-ID.
Those functions get and set all three of those UIDs in one call. And as always, see the full man page for complete details.

Change or hide process name in htop

It seems that htop shows all running processes to every user, and process names in htop contain all the file names that I include in the command line. Since I usually use very long file names that actually contains a lot of detailed information about my project, I do not want such information to be visible to every one (but I am OK that other users see what software that I am running).
How can I hide the details in the process name?
How can I hide the details in the process name?
Since kernel 3.3, you can mount procfs with the hidepid option set to 1 or 2.
The kernel documentation file proc.txt describe this option:
The following mount options are supported:
hidepid= Set proc access mode.
hidepid=0 means classic mode - everybody may access all /proc directories
(default).
hidepid=1 means users may not access any /proc directories but their own. Sensitive files like cmdline, sched*, status are now protected against other users. This makes it impossible to learn whether any user runs specific program (given the program doesn't reveal itself by its behaviour). As an additional bonus, as /proc//cmdline is unaccessible for other users, poorly written programs passing sensitive information via program arguments are now protected against local eavesdroppers.
hidepid=2 means hidepid=1 plus all /proc will be fully invisible to other users. It doesn't mean that it hides a fact whether a process with a specific pid value exists (it can be learned by other means, e.g. by "kill -0 $PID"), but it hides process' uid and gid, which may be learned by stat()'ing /proc// otherwise. It greatly complicates an intruder's task of gathering information about running processes, whether some daemon runs with elevated privileges, whether other user runs some sensitive program, whether other users run any program at all, etc.

How to execve a process, retaining capabilities in spite of missing filesystem-based capabilities?

I want to make system usable without setuid, file "+p" capabilities, and in general without things which are disabled when I set PR_SET_NO_NEW_PRIVS.
With this approach (init sets PR_SET_NO_NEW_PRIVS and filesystem-based capability elevation no longer possible) you cannot "refill" your capabilities and only need to be careful not to "splatter" them.
How to execve some other process without "splattering" any granted capabilities (such as if the new program's file is setcap =ei)? Just "I trust this new process as I trust myself". For example, a capability is given to a user (and the user wants to exercise it in any program he starts)...
Can I make the entire filesystem permanently =ei? I want to keep the filesystem just not interfering with the scheme, not capable of granting or revoking capabilities; controlling everything through parent->child things.
I am not saying that I recommend this for what you are doing, but here it is.
Extracted from the manual, There have been some changes. According to it: fork does not change capabilities. And now there is an ambient set added in Linux kernel 4.3, 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).
Transformation of capabilities during execve()
During an execve(2), the kernel calculates the new capabilities of
the process using the following algorithm:
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).
A privileged file is one that has capabilities or has the set-user-ID
or set-group-ID bit set.
There is currently no simple way to do that, if you refer to the capabilities' man page:
During an execve(2), the kernel calculates the new capabilities of the process
using the following algorithm:
P'(permitted) = (P(inheritable) & F(inheritable)) | (F(permitted) & cap_bset)
P'(effective) = F(effective) ? P'(permitted) : 0
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 capability set after the execve(2)
F denotes a file capability set
cap_bset is the value of the capability bounding set
If the file you want to execute doesn't have its fP bit set, or if its fI bits aren't set, your process will have no permitted and therefore no effective capabilities.
Setting the whole file system permitted and inheritance bits would be technically possible but that would not make much sense since it would strongly reduce the security on the system, (edit: and as you mentioned that won't work for new executables).
You can indeed give some capabilities to a user with pam_cap, but you can't let them execute any file they just compiled using that. Capabilities are by design made to give power to programs and not to users, you can read in Hallyn's paper:
A key insight is the observation that programs, not people, exercise
privilege. That is, everything done in a computer is via
agents—programs—and only if these programs know what to do with
privilege can they be trusted to wield it.
See also the POSIX draft 1003.1e, which defines POSIX capabilities, page 310:
It is also not appropriate to establish for a process chain (a
sequence of programs within a single process) a set of capabilities
that remains fixed and active throughout the life of that chain. [...]
This is an application of the principle of least privilege, and it
applies equally to users and to processes.
Someone asked to introduce what you want to do as a feature in this Linux kernel mailing list recently (dec. 2012), and there are some very interesting answers given. Some people argue that dropping file capabilities in inheritance rules across exec would introduce some security problems and that capabilities are not designed for such a feature, even though no explanation is given wrt which security issue it would introduce:/
The only way to do that currently is to modify the way capabilities are inherited in the Linux kernel (2 files to modify, I tested it successfully on a 3.7 kernel), but it's not clear whether that is secured or not as mentioned above.
On old kernels (before 2.6.33) there was an option to compile without file's capabilities (CONFIG_SECURITY_FILE_CAPABILITIES), but I doubt working with such an old kernel is an option for you.
I think (my understanding), that the best way to use capabilities is:
For programs that need capabilities and are trusted including trusted not to leak capabilities: e.g. the packet sniffing part of wire-shark, a web server that needs to listen on port 80.
new programs, capabilities aware: set permitted.
legacy programs, not capabilities aware: set permitted and effective
For programs that will leak capabilities, and have code that could (sometimes) use a capability: set inherited
e.g. for chmod set inherit CAP_FOWNER, if user needs super powers (those normally held by root), then they need to use setpriv (or equivalent, this could be rolled into sudo), else it works in unprivileged mode.
When a process needs to fork and share some capabilities, then and only then use ambient. Probably same executable; if it was a different one, then this new one would have permitted or inherited set on the file. [Edit: I have just realised that you do not need ambient if you do not exec. If I think of a use-case for ambient, in a well set up system, then I will add it here. Ambient can be used as a transitional mechanism, when inherited is not set on files that could use it.]
Uses of ambient:
On a system where files do not have the correct capabilities. ( a transitional technique).
For shell scripts, that can not have capabilities (as they can not have setuid), except on systems that have fixed and then allow setuid on scripts.
Add more here.

fork and execve to inherit unprivileged parent process' capabilities

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).

Resources