diff -urN linux/Documentation/capabilities.txt linux-agm/Documentation/capabilities.txt --- linux/Documentation/capabilities.txt Wed Dec 31 16:00:00 1969 +++ linux-agm/Documentation/capabilities.txt Sat Apr 18 17:57:24 1998 @@ -0,0 +1,140 @@ +capabilities.txt Andrew G. Morgan + <mor...@transmeta.com> + 1998/4/18 + + 1 Kernel capabilities + + The basic idea behind capabilities is that the superuser concept on + normal UNIX systems is too coarsely grained, i.e., you either have all + access to the system or no privileged access as an ordinary user. + + With capabilities, the privilege normally given to the user with UID=0 + is split into a number of independent capabilities. Each capability + is designed to give limited access to kernel-level privilege, eg., to + reconfigure a TTY or a network device, or to override read access on + a file that the reading user wouldn't normally be permitted to access. + + 1.1 Definitions + + Capabilities are stored as bit-sets. Currently these are 32 bits + each, but the kernel code has [WILL HAVE] the ability to truncate and + zero-fill as required to talk to other code that wants a different + size. [WARNING, THIS IS NOT YET TESTED.] + + 1.1.1 Process capabilities + + Each process has three sets of capabilities: Effective; Inheritable; + and Permitted. + + 1.1.1.1 Inheritable + + These capabilities may be inherited through exec(). In other words, + if a running process has capabilities "raised" in this set and then + exec()s another file, these capabilities are directly inherited by the + new executable. [THIS IS A COMMENT FOR WHEN FILE CAPS ARE + IMPLEMTNED :- However, this alone is not enough to actually grant + capabilities to the new executable: the executed file must have the + Inheritable capabilities set too.] + + 1.1.1.2 Permitted + + The capabilities that the process may raise in the Effective and + Inheritable sets. + + 1.1.1.3 Effective + + Subset of Permitted; used to determine what the process can actually + do. + + 1.1.2 File capabilities + + WARNING: there is currently no support for file capabilities + + Each file has three sets of capabilities, when the file is exec()'d + these three capabilities combine with those of the exec()'ing process + to establish the newly exec()'d process' capabilities. + + 1.1.2.1 Inheritable + + This is a mask against which the Inheritable capabilities of the + exec()'ing process are filtered. Only capabilities in this set may be + inherited by the exec()'d process. + + Note, on filesystems that do not support capabilities, all executables + are assumed to have { ~0 } Inheritable capabilities. + + 1.1.2.2 Effective + + This set must have either all capabilities raised or all capabilities + lowered. It is used to indicate whether the program knows about + capabilities. If this set has capabilities raised, the program will + start with all of its Permitted capabilities in its Effective set. If + this set is empty, the program will start with an empty Effective set, + and will have to explicitly raise the capabilities it needs. + + 1.1.2.3 Permitted + + This set is the set of capabilities required by the executable in + order to do its job. These will appear in the Permitted set of the + process after exec()ing this executable. + + 1.2 Capability inheritance + + Following a sys_exec() call, the process' capability sets are modified + in the following way: + + pI' = pI + pP' = fP | (fI & pI) + pE' = pP' & fE [NB. fE is 0 or ~0] + + Key: I=Inheritable, P=Permitted, E=Effective // p=process, f=file + ' indicates post-exec(). + + Note, the Inheritable set is fully preserved across a sys_exec() + call. + + Process capabilities are _not_ modified on fork (clone). + + 1.3 Compatibility mode + + The historical situation, of the superuser being omnipotent, is + partially preserved following a system call to sys_exec(). This + backward compatibility is preserved only in the case that + (issecure(SECURE_NOROOT) == 0). + + 1.3.1 Details + + Backward compatibility takes the following form: + + 1 setuid-root binaries, automatically have their Effective + and Permitted capabilities raised to equal the Inheritable + capabilities of the process that calls sys_exec(). + + 2 setuid-NON-root binaries, automatically have their Effective + capabilities cleared. If the UID of the process calling + sys_exec() is 0, the Permitted capabilities are raised to + equal the Inheritable capabilities of the process that called + sys_exec(). Such processes become "capability smart". + + 3 non-setuid binaries directly inherit the capabilities of the + process that sys_exec()s them. + + 1.3.2 Discussion + + Firstly, we note that this backward compatibility has only one effect + on systems that are not capability aware: + + Point 2, of section 1.3.1, implies that such binaries + inherit the EUID of root, but none of root's kernel level + privileges. + + Secondly, on a system that runs binaries which are capability aware, + without setting SECURE_NOROOT mode, it is possible to further restrict + the root account: + + Point 1, of section 1.3.1, implies that a parent process + can reduce its Inheritable capabilities and thus prevent any + of its children from _ever_ acquiring all/any of root's kernel + level privileges. An immediate consequence of this is that it + becomes possible to implement escape proof sys_chroot() cells. + diff -urN linux/fs/exec.c linux-agm/fs/exec.c --- linux/fs/exec.c Thu Apr 2 09:04:00 1998 +++ linux-agm/fs/exec.c Sun Apr 19 02:21:51 1998 @@ -622,7 +622,6 @@ * allowed set to maximum and the application to "capability * smart"). */ - if (!issecure(SECURE_NOROOT)) { if (bprm->e_uid == 0 || current->uid == 0) cap_set_full(bprm->cap_inheritable); @@ -630,24 +629,27 @@ cap_set_full(bprm->cap_effective); } - /* We use a conservative definition of suid for capabilities. - * The process is suid if the permitted set is not a subset of - * the current permitted set after the exec call. - * new permitted set = forced | (allowed & inherited) - * pP' = fP | (fI & pI) - */ - - if ((bprm->cap_permitted.cap | - (current->cap_inheritable.cap & - bprm->cap_inheritable.cap)) & - ~current->cap_permitted.cap) { - id_change = 1; + if (!id_change) { + /* Only if pP' is _not_ a subset of pP, do we consider there + * has been a capability related "change of id". In such + * cases, we need to check that the elevation of privilege + * does not go against other system constraints. The new + * Permitted set is defined below -- see (***). */ + + kernel_cap_t working = + cap_combine(bprm->cap_permitted, + cap_intersect(bprm->cap_inheritable, + current->cap_inheritable)); + if (!cap_issubset(working, current->cap_permitted)) { + id_change = 1; + } } if (id_change) { - /* We can't suid-execute if we're sharing parts of the executable */ - /* or if we're being traced (or if suid execs are not allowed) */ - /* (current->mm->count > 1 is ok, as we'll get a new mm anyway) */ + /* We can't suid-execute if we're sharing parts of the executable + * or if we're being traced (or if suid execs are not allowed) + * (current->mm->count > 1 is ok, as we'll get a new mm anyway) + */ if (IS_NOSUID(inode) || (current->flags & PF_PTRACED) || (current->fs->count > 1) @@ -669,7 +671,7 @@ * The formula used for evolving capabilities is: * * pI' = pI - * pP' = fP | (fI & pI) + * (***) pP' = fP | (fI & pI) * pE' = pP' & fE [NB. fE is 0 or ~0] * * I=Inheritable, P=Permitted, E=Effective // p=process, f=file diff -urN linux/fs/proc/array.c linux-agm/fs/proc/array.c --- linux/fs/proc/array.c Wed Mar 11 15:53:18 1998 +++ linux-agm/fs/proc/array.c Sat Apr 18 15:55:02 1998 @@ -656,11 +656,17 @@ "Pid:\t%d\n" "PPid:\t%d\n" "Uid:\t%d\t%d\t%d\t%d\n" - "Gid:\t%d\t%d\t%d\t%d\n", + "Gid:\t%d\t%d\t%d\t%d\n" + "CapEffective:\t%08x\n" + "CapInheritable:\t%08x\n" + "CapPermitted:\t%08x\n", get_task_state(p), p->pid, p->p_pptr->pid, p->uid, p->euid, p->suid, p->fsuid, - p->gid, p->egid, p->sgid, p->fsgid); + p->gid, p->egid, p->sgid, p->fsgid, + p->cap_effective.cap, + p->cap_inheritable.cap, + p->cap_permitted.cap); return buffer; } diff -urN linux/include/asm-i386/unistd.h linux-agm/include/asm-i386/unistd.h --- linux/include/asm-i386/unistd.h Tue Apr 7 08:05:05 1998 +++ linux-agm/include/asm-i386/unistd.h Sun Apr 19 00:40:26 1998 @@ -189,6 +189,8 @@ #define __NR_pwrite 181 #define __NR_chown 182 #define __NR_getcwd 183 +#define __NR_getproccap 184 +#define __NR_setproccap 185 /* user-visible error numbers are in the range -1 - -122: see <asm-i386/errno.h> */ diff -urN linux/include/linux/capability.h linux-agm/include/linux/capability.h --- linux/include/linux/capability.h Tue Apr 14 13:00:11 1998 +++ linux-agm/include/linux/capability.h Sun Apr 19 02:06:18 1998 @@ -1,7 +1,7 @@ /* * This is <linux/capability.h> * - * Andrew G. Morgan <mor...@parc.power.net> + * Andrew G. Morgan <mor...@transmeta.com> * Alexander Kjeldaas <as...@guardian.no> * with help from Aleph1, Roland Buresund and Andrew Main. */ @@ -17,20 +17,29 @@ kernel might be somewhat backwards compatible, but don't bet on it. */ -#define _LINUX_CAPABILITY_VERSION 0x19980330 +#define _LINUX_CAPABILITY_VERSION 0x19980418 + +/* XXX - Note, cap_t, is defined by POSIX to be an "opaque" pointer to + a set of three capability sets. The transposition of 3*the + following structure to such a composite is better handled in a user + library since the draft standard requires the use of malloc/free + etc.. */ typedef struct _user_cap_struct { __u32 version; - __u32 size; + __u32 size; /* number of bytes of capability bits */ __u8 cap[1]; -} *cap_t; +} *cap_user_t; #ifdef __KERNEL__ typedef struct kernel_cap_struct { - int cap; + __u32 cap; } kernel_cap_t; +#define _USER_CAP_HEADER_SIZE (2*sizeof(__u32)) +#define _KERNEL_CAP_T_SIZE (sizeof(kernel_cap_t)) + #endif @@ -189,15 +198,43 @@ #define CAP_FULL_SET { ~0 } #define CAP_TO_MASK(x) (1 << (x)) -#define cap_raise(c, flag) (c.cap |= CAP_TO_MASK(flag)) -#define cap_lower(c, flag) (c.cap &= ~CAP_TO_MASK(flag)) -#define cap_raised(c, flag) (c.cap & CAP_TO_MASK(flag)) - -#define cap_isclear(c) (!c.cap) - -#define cap_copy(dest,src) do { (dest).cap = (src).cap; } while(0) -#define cap_clear(c) do { c.cap = 0; } while(0) -#define cap_set_full(c) do { c.cap = ~0; } while(0) +#define cap_raise(c, flag) ((c).cap |= CAP_TO_MASK(flag)) +#define cap_lower(c, flag) ((c).cap &= ~CAP_TO_MASK(flag)) +#define cap_raised(c, flag) ((c).cap & CAP_TO_MASK(flag)) + +static inline kernel_cap_t cap_combine(kernel_cap_t a, kernel_cap_t b) +{ + kernel_cap_t dest; + dest.cap = a.cap | b.cap; + return dest; +} + +static inline kernel_cap_t cap_intersect(kernel_cap_t a, kernel_cap_t b) +{ + kernel_cap_t dest; + dest.cap = a.cap & b.cap; + return dest; +} + +static inline kernel_cap_t cap_drop(kernel_cap_t a, kernel_cap_t drop) +{ + kernel_cap_t dest; + dest.cap = a.cap & ~drop.cap; + return dest; +} + +static inline kernel_cap_t cap_invert(kernel_cap_t c) +{ + kernel_cap_t dest; + dest.cap = ~c.cap; + return dest; +} + +#define cap_isclear(c) (!(c).cap) +#define cap_issubset(a,set) (!((a).cap & ~(set).cap)) + +#define cap_clear(c) do { (c).cap = 0; } while(0) +#define cap_set_full(c) do { (c).cap = ~0; } while(0) #define cap_is_fs_cap(c) ((c) & CAP_FS_MASK) diff -urN linux/kernel/capability.c linux-agm/kernel/capability.c --- linux/kernel/capability.c Wed Dec 31 16:00:00 1969 +++ linux-agm/kernel/capability.c Sun Apr 19 02:09:38 1998 @@ -0,0 +1,165 @@ +/* + * linux/kernel/capability.c + * + * Copyright (C) 1997 Andrew Main <zef...@fysh.org> + * Integrated into 2.1.97+ Andrew G. Morgan <mor...@transmeta.com> + */ + +#include <linux/config.h> +#include <linux/errno.h> +#include <linux/kernel.h> +#include <linux/capability.h> +#include <linux/mm.h> +#include <linux/sched.h> +#include <linux/string.h> + +/* + * User programs might have been compiled with a different idea of the number + * of capabilities. These functions provide a standard mechanism for + * translating between kernelspace and userspace capability structures. + * Bitsets are truncated if too big, and zero-filled if too small. + */ + +static int cap_fromuser(kernel_cap_t *k, const cap_user_t u) +{ + size_t usize=0, rsize; + + if (!access_ok(VERIFY_READ, u, _USER_CAP_HEADER_SIZE)) + return -EINVAL; + + /* XXX - we do not pay attention to the u->version */ + + copy_from_user(&usize, &(u->size), sizeof(u->size)); + if (usize > _KERNEL_CAP_T_SIZE) + rsize = _KERNEL_CAP_T_SIZE; + else + rsize = usize; + + if (!access_ok(VERIFY_READ, u->cap, rsize)) + return -EINVAL; + + copy_from_user(k, u->cap, rsize); + if (rsize < _KERNEL_CAP_T_SIZE) + memset(rsize + (__u8 *) k, 0, _KERNEL_CAP_T_SIZE - rsize); + + return 0; +} + + +static int cap_touser(cap_user_t u, const kernel_cap_t *k, size_t usize) +{ + struct _user_cap_struct uheader; + + if (!access_ok(VERIFY_WRITE, u, usize)) + return -EINVAL; + + if (usize < _USER_CAP_HEADER_SIZE) + return -EINVAL; + + if (usize > (_USER_CAP_HEADER_SIZE+_KERNEL_CAP_T_SIZE)) + usize = _KERNEL_CAP_T_SIZE; + else + usize -= _USER_CAP_HEADER_SIZE; + + uheader.version = _LINUX_CAPABILITY_VERSION; + uheader.size = usize; + copy_to_user(u, &uheader, _USER_CAP_HEADER_SIZE); + copy_to_user(u->cap, k, usize); + + return 0; +} + +/* + * For sys_getproccap() and sys_setproccap(), any of the three + * capability set pointers may be NULL -- indicating that that set is + * uninteresting and/or not to be changed. + */ + +asmlinkage int sys_getproccap(size_t usize, cap_user_t eset, cap_user_t iset, + cap_user_t pset) +{ + int error; + + if (pset) { + if ((error = cap_touser(pset,¤t->cap_permitted,usize))) + return error; + } + + if (iset) { + if ((error = cap_touser(iset,¤t->cap_inheritable,usize))) + return error; + } + + if (eset) { + if ((error = cap_touser(eset,¤t->cap_effective,usize))) + return error; + } + + return 0; +} + +/* + * The restrictions on setting capabilities are specified by POSIX: + * + * I: any raised capabilities must be a subset of the (old) Permitted + * P: permitted capabilities can only be removed and never added. + * E: must be set to a subset of (new) Permitted + */ + +asmlinkage int sys_setproccap(size_t usize, const cap_user_t eset, + const cap_user_t iset, const cap_user_t pset) +{ + kernel_cap_t inheritable, permitted, effective; + int error; + + /* copy from userspace */ + if (eset) { + if ((error = cap_fromuser(&effective, eset, usize))) + return error; + } + if (iset) { + if ((error = cap_fromuser(&inheritable, iset, usize))) + return error; + } + if (pset) { + if ((error = cap_fromuser(&permitted, pset, usize))) + return error; + } + + /* If we are changing the Inheritable set, the _newly-raised_ + capabilities must be a subset of the _old_Permitted_ set. */ + + if (!iset) { + inheritable = current->cap_inheritable; + } else if (!cap_isclear(cap_drop(inheritable, cap_combine + (current->inheritable, + current->permitted) + ))) { + return -EPERM; + } + + /* verify _new_Permitted_ is a subset of the _old_Permitted_ set */ + + if (!pset) { + permitted = current->cap_permitted; + } else if (!cap_issubset(permitted, current->cap_permitted)) { + return -EPERM; + } + + /* verify the _new_Effective_ is a subset of the _new_Permitted_ */ + + if (!eset) { + effective = cap_intersect(current->cap_effective, permitted); + } else if (!cap_issubset(effective, permitted)) { + return -EPERM; + } + + /* having verified that the proposed changes are legal, + we realize the new capabilities. */ + + current->cap_inheritable = inheritable; + current->cap_permitted = permitted; + current->cap_effective = effective; + + return 0; +}