The “Cuppabilities” Linux Security Module

by David A. Madore

✱✱✱ Quick download for the impatient ✱✱✱

Table of contents

What's this?

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.

How is this related to your earlier capabilities patch?

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:

The two are entirely independent and compatible with one another; neither is compatible with the earlier (abandoned) patch, which they supersede.

What are “cuppabilities”?

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.

Why the name, “cuppabilities”?

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

Where can I get it?

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

How stable is it?

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.

How do I load 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.

What are the design principles?

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.

How do cuppabilities interact with suid/sgid executables?

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.

How do cuppabilities interact with capabilities?

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

How do I manipulate cuppabilities (using prctl())?

Two prctl() calls are provided to manipulate cuppabilities:

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.

What cuppabilities exist that I could play with?

The following cuppabilities are currently defined:

CUP_FORK (0)
This forbids process creation.
CUP_EXEC (1)
This forbids execve(). Please note that this is useless for security purposes (see the security notes below).
CUP_EXEC_ONCE (2)
This forbids 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)
This forbids suid/sgid exec. This should nearly always be set if you want anything remotely secure.
CUP_PTRACE (4)
This forbids ptrace(). This should nearly always be set if you want anything remotely secure.
CUP_WRITE (5)
This forbids any kind of write access to any filesystem. It also forbids opening any device for writing except /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]
This forbids adding a suid or sgid bit. This is meant as a poor kind of substitute for CUP_EXEC_SXID when the latter cannot be used for some reason.
CUP_OPEN_FILE (7) [version 0.5.2 onward]
This forbids opening a regular file (for reading or writing). It does not prevent opening a directory, fifo, socket or device.
CUP_SET_TIMES (8) [version 0.5.2.1 onward]
This forbid using the utime()/utimes() function to set the time to anything other than the current time.
CUP_NET_ANY (16) [version 0.5.2.1 onward]
This disables any kind of network socket creation.

What are the security considerations when using cuppabilities?

You should know at least the following:

How can one make something useful of this patch?

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;
}

What are the differences between the various versions of the patch?