✱✱✱ Quick download for the impatient ✱✱✱
prctl()
)?This is a patch for the Linux kernel that provides support for “cuppabilities”, or underprivileged processes, through a Linux Security Module. This grew as an alternative, and more consensual approach, after an earlier patch, with a similar goal, had been heavily criticized.
Short answer: not at all.
Longer answer: I had an earlier patch that made various changes to Linux capabilities, such as making them inheritable by default (with non-POSIX semantics) and adding support for underprivileged processes through capabilities. Given the citicism that this patch has met, I have abandoned it. Rather, I am providing its functionality in a different way:
inhcaps
mount option to
make capabilities inheritable by default on that filesystem (while
otherwise retaining the POSIX.1e semantics),
andThe two are entirely independent and compatible with one another; neither is compatible with the earlier (abandoned) patch, which they supersede.
Whereas POSIX.1e “capabilities” are a framework to handle processes having extra privileges, “cuppabilities” are something of the opposite: a mechanism to create underprivileged processes (such as: processes which cannot fork, which cannot execute suid/sgid binaries, or which cannot modify the filesystem). While this is not (nearly) as flexible as SElinux, it is also much easier to handle and understand, and enables any user (not only root) to launch underprivileged processes.
The name “cuppabilities” is merely meant to be a joke
on “capabilities” since they are something like
capabilities in reverse (cf. \cap
↔
\cup
, in TeX for example). It's also a pun on
“culpability”.
See this FTP directory.
The present version (0.5.2.1) is to be applied against Linux version 2.6.18-rc6, although it should not be too picky about that. (The possibility of serving a git tree is being considered.)
It has received very little testing as of yet, so it should be considered alpha quality. However, given that it is conceptually very simple, it is believed (by its author, viz., me) to be stable, at least insofar as it shouldn't make your kernel panic or anything of the sort: the worst sort of bug which is expected is if there is an unthought-of way to evade cuppabilities.
Still, I won't promise anything: if this patch turns your computer into a newt, makes your lizards crabby or changes the meaning of life, don't blame me for it.
The cuppabilities module is called cuppability.ko
; it
can also be compiled as part of the kernel proper. To enable
cuppabilities, just load the module:
# modprobe cuppability
The cuppabilities module is incompatible with the
capability
module as such: however, it provides common
capability support through the commoncap
module. It is
believed to be compatible to some extent with SElinux,
provided SElinux is loaded first (as primary
security module); however, some cuppabilities might not function
correctly under SElinux (this is work in progress), and
the usefulness of cuppabilities under SElinux is, in any
case, of dubious value.
To any task is associated a set of cuppabilities: cuppabilities are negative, i.e., they restrict the operations which the task can perform. Normal processes have no cuppabilities: i.e., they are not restricted by the cuppabilities module.
Cuppabilities are preserved across fork()
and
exec()
(except, in the latter, for
suid/sgid binaries, see below). The only way to strengthen
cuppabilities is for the process itself to use the
prctl()
interface documented below; however, this can be done prior to
exec()
, making it easy to create a launch wrapper that
starts a program with strengthened cuppabilities.
Each cuppability bit restricts a certain kind of operation. See below for a list of bits currently defined.
Whenever any suid/sgid program is
executed, all cuppabilities are cleared, so as to provide the callee
with an environment that is not unexpected. This is true even if the
suid/sgid bit is for the same user/group as
the caller program: this makes it trivial to evade cuppabilities if
precautions are not taken such as raisong CUP_EXEC_SXID
,
see below.
Not at all. It is expected that, usually, processes having certain capabilities will have no cuppabilities, but this is not enforced by the system. Since the filesystem currently offers no support for capabilities (for capability inheritance, that is), there is no reason to clear cuppabilities in any circumstance other than executing suid/sgid binaries (see above).
Whereas capabilities are not inherited by default (barring a special patch and mount option), cuppabilities always are.
Note that the cuppability
module provides support for
capabilities through the commoncap
module (don't try to
load capability.ko
, just load commoncap.ko
and cuppability.ko
).
prctl()
)?Two prctl()
calls are provided to manipulate
cuppabilities:
prctl(PR_GET_CUPS,0,0,0,0)
returns the cuppabilities
of the current task; this is also accessible through
/proc/self/status
as the Cupline,
prctl(PR_ADD_CUPS,cups,0,0,0)
adds some cuppabilities
to the current task: this always succeeds and performs a bitwise OR
of the current task's cuppabilities with the given parameter.There is no way to remove cuppabilities from a process (apart from suid/sgid exec, see above).
Cuppabilities are stored as a simple bit field, each bit indicating
whether a cuppability is unset (0 = unrestricted) or set (1 =
restricted); each CUP_
* constant is the order of the
corresponding bit in the field. See below
for a list of currently defined cuppabilities.
The following cuppabilities are currently defined:
CUP_FORK
(0)CUP_EXEC
(1)execve()
. Please note that this is
useless for security purposes (see the security notes below).CUP_EXEC_ONCE
(2)execve()
except just once: in other
words, this sets CUP_EXEC
on the next exec. The point is
that you can write a wrapper which sets this cuppability and then
executes another program.CUP_EXEC_SXID
(3)CUP_PTRACE
(4)ptrace()
. This should nearly always be
set if you want anything remotely secure.CUP_WRITE
(5)/dev/null
and /dev/zero
(up to version 0.5.1 of the patch, it even
forbade opening FIFOs, Unix-domain sockets and any
kind of device: so this is now relaxed slightly).CUP_SET_SXID
(6) [version 0.5.2 onward]CUP_EXEC_SXID
when the latter
cannot be used for some reason.CUP_OPEN_FILE
(7) [version 0.5.2 onward]CUP_SET_TIMES
(8) [version 0.5.2.1 onward]utime()
/utimes()
function to set the time to anything other than the current time.CUP_NET_ANY
(16) [version 0.5.2.1 onward]You should know at least the following:
CUP_EXEC_SXID
and
CUP_PTRACE
when adding cuppabilities: this is to avoid
the trivial way to evade cuppabilities, either by executing a
suid/sgid self binary or by
ptrace()
ing a non-restricted process of the same uid.
Future versions will try to make this a little less obvious to
evade, but it is probably impossible to make it foolproof.CUP_EXEC
does not give you
anything in the matter of security (it might be useful for testing
purposes, or to provide protection against accidental, rather than
malicious, damage). Indeed, execve()
can be (to a
large extent) emulated with mmap()
or perhaps even
simply read()
: so if you're relying on
CUP_EXEC
for security purposes, you are almost
certainly down the wrong track.$HOME
, it can do a lot of damage, such as
modifying your .profile
or similar things. So for
security purposes, at least CUP_WRITE
should be raised,
and even that will not offer a good protection against, e.g.,
malicious network use (adding a cuppability for that is being
considered).The following simple program provides a wrapper to launch a program with no write access to the filesystem:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/prctl.h> #ifndef PR_GET_CUPS #define PR_GET_CUPS 21 #define PR_ADD_CUPS 22 #endif #ifndef CUP_TO_MASK #define CUP_TO_MASK(x) (1UL << (x)) #endif #ifndef CUP_FORK #define CUP_FORK 0 /* Forbid fork() */ #define CUP_EXEC 1 /* Forbid exec() */ #define CUP_EXEC_ONCE 2 /* Forbid exec() except just once */ #define CUP_EXEC_SXID 3 /* Forbid s[ug]id exec() */ #define CUP_PTRACE 4 /* Forbid ptrace() */ #define CUP_WRITE 5 /* Forbid writing to filesystems */ #define CUP_SET_SXID 6 /* Forbid making inodes suid/sgid */ #define CUP_OPEN_FILE 7 /* Forbid opening regular files */ #define CUP_SET_TIMES 8 /* Forbid setting file times */ #define CUP_NET_ANY 16 /* Forbid any use of network */ #endif int main (int argc, char *argv[]) { const char *shell; prctl (PR_ADD_CUPS, CUP_TO_MASK(CUP_EXEC_SXID) | CUP_TO_MASK(CUP_PTRACE) | CUP_TO_MASK(CUP_WRITE), 0, 0, 0); shell = getenv ("SHELL"); if ( ! shell ) shell = "/bin/sh"; if ( argc > 1 ) return execvp (argv[1], argv+1); else return execl (shell, shell, NULL); return 0; }
CUP_SET_TIMES
and
CUPS_NET_ANY
.CUP_SET_SXID
and
CUPS_OPEN_FILE
. It also makes CUP_WRITE
a
little more lenient (so it will accept to open a
FIFO, for example).CUP_WRITE
, display of cuppabilities through
/proc/$PID/status
, and a few trivial bugfixes.