Capability update

Alexander Kjeldaas (astor@guardian.no)
Thu, 19 Nov 1998 22:38:50 +0100


--WIyZ46R2i8wDzkSu
Content-Type: text/plain; charset=us-ascii

Currently capabilities can't be inherited during exec since we don't
have filesystem support for capabilities. The execed file needs to
have capabilities set in its 'allowed' set to receive capabilities
from the execing process (this mechanism is bypassed for root
processes in emulation mode).

Inheriting capabilities is necessary in order to use capabilities on a
system without filesystem support. The following patch introduces a
securebit controlling whether to emulate/fake filesystem capabilities.
It also includes some cleanups:
- cap_raised macro renamed to CAP_RAISED (cap_raised was used in
fs/exec.c:prepare_binfmt)
- suser/fsuser functions removed
- easier to compile kernel for users that want to use SETPCAP

astor

-- 
 Alexander Kjeldaas, Guardian Networks AS, Trondheim, Norway
 http://www.guardian.no/

--WIyZ46R2i8wDzkSu Content-Type: text/plain; charset=us-ascii Content-Disposition: attachment; filename="linus.cap2"

diff -urN linux/fs/exec.c linux.cap/fs/exec.c --- linux/fs/exec.c Thu Nov 19 20:53:03 1998 +++ linux.cap/fs/exec.c Thu Nov 19 21:12:12 1998 @@ -553,7 +553,7 @@ */ static inline int must_not_trace_exec(struct task_struct * p) { - return (p->flags & PF_PTRACED) && !cap_raised(p->p_pptr->cap_effective, CAP_SYS_PTRACE); + return (p->flags & PF_PTRACED) && !CAP_RAISED(p->p_pptr->cap_effective, CAP_SYS_PTRACE); } /* @@ -604,10 +604,16 @@ id_change = 1; } - /* We don't have VFS support for capabilities yet */ - cap_clear(bprm->cap_inheritable); - cap_clear(bprm->cap_permitted); - cap_clear(bprm->cap_effective); + if (!issecure(SECURE_CAPFS)) { + bprm->cap_inheritable = current->cap_permitted; + cap_clear(bprm->cap_permitted); + cap_set_full(bprm->cap_effective); + } else { + /* We don't have VFS support for capabilities yet */ + cap_clear(bprm->cap_inheritable); + cap_clear(bprm->cap_permitted); + cap_clear(bprm->cap_effective); + } /* To support inheritance of root-permissions and suid-root * executables under compatibility mode, we raise the diff -urN linux/include/linux/capability.h linux.cap/include/linux/capability.h --- linux/include/linux/capability.h Wed Nov 18 14:18:02 1998 +++ linux.cap/include/linux/capability.h Thu Nov 19 21:40:48 1998 @@ -95,10 +95,6 @@ #define CAP_FSETID 4 -/* Used to decide between falling back on the old suser() or fsuser(). */ - -#define CAP_FS_MASK 0x1f - /* Overrides the restriction that the real or effective user ID of a process sending a signal must match the real or effective user ID of the process receiving the signal. */ @@ -267,6 +263,13 @@ #ifdef __KERNEL__ /* + * Start init with the SETPCAP capability + */ + +/* #define INIT_WITH_SETPCAP 1 */ + + +/* * Internal kernel functions only */ @@ -282,15 +285,27 @@ #endif +#define CAP_ROOT_SET to_cap_t(~0) +#define CAP_FS_SET to_cap_t(CAP_TO_MASK(CAP_CHOWN) |\ + CAP_TO_MASK(CAP_DAC_OVERRIDE) |\ + CAP_TO_MASK(CAP_DAC_READ_SEARCH) |\ + CAP_TO_MASK(CAP_FOWNER) |\ + CAP_TO_MASK(CAP_FSETID)) #define CAP_EMPTY_SET to_cap_t(0) #define CAP_FULL_SET to_cap_t(~0) -#define CAP_INIT_EFF_SET to_cap_t(~0 & ~CAP_TO_MASK(CAP_SETPCAP)) -#define CAP_INIT_INH_SET to_cap_t(~0 & ~CAP_TO_MASK(CAP_SETPCAP)) + +#ifdef INIT_WITH_SETPCAP +# define CAP_INIT_EFF_SET to_cap_t(~0) +# define CAP_INIT_INH_SET to_cap_t(~0) +#else +# define CAP_INIT_EFF_SET to_cap_t(~0 & ~CAP_TO_MASK(CAP_SETPCAP)) +# define CAP_INIT_INH_SET to_cap_t(~0 & ~CAP_TO_MASK(CAP_SETPCAP)) +#endif #define CAP_TO_MASK(x) (1 << (x)) #define cap_raise(c, flag) (cap_t(c) |= CAP_TO_MASK(flag)) #define cap_lower(c, flag) (cap_t(c) &= ~CAP_TO_MASK(flag)) -#define cap_raised(c, flag) (cap_t(c) & CAP_TO_MASK(flag)) +#define CAP_RAISED(c, flag) (cap_t(c) & CAP_TO_MASK(flag)) static inline kernel_cap_t cap_combine(kernel_cap_t a, kernel_cap_t b) { @@ -326,8 +341,6 @@ #define cap_clear(c) do { cap_t(c) = 0; } while(0) #define cap_set_full(c) do { cap_t(c) = ~0; } while(0) #define cap_mask(c,mask) do { cap_t(c) &= cap_t(mask); } while(0) - -#define cap_is_fs_cap(c) (CAP_TO_MASK(c) & CAP_FS_MASK) #endif /* __KERNEL__ */ diff -urN linux/include/linux/sched.h linux.cap/include/linux/sched.h --- linux/include/linux/sched.h Wed Nov 18 14:18:02 1998 +++ linux.cap/include/linux/sched.h Thu Nov 19 22:09:39 1998 @@ -541,6 +541,8 @@ void *dev_id); extern void free_irq(unsigned int irq, void *dev_id); +#define suser() capable(CAP_SYS_ADMIN) + /* * This has now become a routine instead of a macro, it sets a flag if * it returns true (to do BSD-style accounting where the process is flagged @@ -552,28 +554,8 @@ * fsuser(). This is done, along with moving fsuser() checks to be * last. * - * These will be removed, but in the mean time, when the SECURE_NOROOT - * flag is set, uids don't grant privilege. - */ -extern inline int suser(void) -{ - if (!issecure(SECURE_NOROOT) && current->euid == 0) { - current->flags |= PF_SUPERPRIV; - return 1; - } - return 0; -} - -extern inline int fsuser(void) -{ - if (!issecure(SECURE_NOROOT) && current->fsuid == 0) { - current->flags |= PF_SUPERPRIV; - return 1; - } - return 0; -} - -/* + * [...] + * * capable() checks for a particular capability. * New privilege checks should use this interface, rather than suser() or * fsuser(). See include/linux/capability.h for defined capabilities. @@ -582,7 +564,7 @@ extern inline int capable(int cap) { #if 1 /* ok now */ - if (cap_raised(current->cap_effective, cap)) + if (CAP_RAISED(current->cap_effective, cap)) #else if (cap_is_fs_cap(cap) ? current->fsuid == 0 : current->euid == 0) #endif diff -urN linux/include/linux/securebits.h linux.cap/include/linux/securebits.h --- linux/include/linux/securebits.h Thu Apr 2 02:26:34 1998 +++ linux.cap/include/linux/securebits.h Thu Nov 19 21:53:09 1998 @@ -18,6 +18,15 @@ privileges. When unset, setuid doesn't change privileges. */ #define SECURE_NO_SETUID_FIXUP 2 +/* When set, we read capability bits from the filesystem. If the + filesystem doesn't support capabilities, capabilities are + impossible to inherit during exec on that filesystem. If unset, + the allowed set from the filesystem is emulated using the + premitted set of the executing process. Thus when unset it is + possible to inherit capabilities even if there isn't any filesystem + support for capabilities. */ +#define SECURE_CAPFS 4 + /* Each securesetting is implemented using two bits. One bit specify whether the setting is on or off. The other bit specify whether the setting is fixed or not. A setting which is fixed cannot be changed diff -urN linux/kernel/sys.c linux.cap/kernel/sys.c --- linux/kernel/sys.c Wed Nov 18 14:14:41 1998 +++ linux.cap/kernel/sys.c Thu Nov 19 21:29:22 1998 @@ -317,10 +317,10 @@ * * 1) When set*uiding _from_ one of {r,e,s}uid == 0 _to_ all of * {r,e,s}uid != 0, the permitted and effective capabilities are - * cleared. + * modified. * * 2) When set*uiding _from_ euid == 0 _to_ euid != 0, the effective - * capabilities of the process are cleared. + * capabilities of the process are modified. * * 3) When set*uiding _from_ euid != 0 _to_ euid == 0, the effective * capabilities are set to the permitted capabilities. @@ -333,13 +333,14 @@ extern inline void cap_emulate_setxuid(int old_ruid, int old_euid, int old_suid) { + kernel_cap_t cap_root_set = CAP_ROOT_SET; if ((old_ruid == 0 || old_euid == 0 || old_suid == 0) && (current->uid != 0 && current->euid != 0 && current->suid != 0)) { - cap_clear(current->cap_permitted); - cap_clear(current->cap_effective); + cap_t(current->cap_permitted) &= ~cap_t(cap_root_set); + cap_t(current->cap_effective) &= ~cap_t(cap_root_set); } if (old_euid == 0 && current->euid != 0) { - cap_clear(current->cap_effective); + cap_t(current->cap_effective) &= ~cap_t(cap_root_set); } if (old_euid != 0 && current->euid == 0) { current->cap_effective = current->cap_permitted; @@ -573,17 +574,18 @@ * operate on the fs-specific bits of the process' effective * capabilities * - * FIXME - is fsuser used for all CAP_FS_MASK capabilities? + * FIXME - is fsuser used for all CAP_FS_SET capabilities? * if not, we might be a bit too harsh here. */ if (!issecure(SECURE_NO_SETUID_FIXUP)) { + kernel_cap_t cap_fs_set = CAP_FS_SET; if (old_fsuid == 0 && current->fsuid != 0) { - cap_t(current->cap_effective) &= ~CAP_FS_MASK; + cap_t(current->cap_effective) &= ~cap_t(cap_fs_set); } if (old_fsuid != 0 && current->fsuid == 0) { cap_t(current->cap_effective) |= - (cap_t(current->cap_permitted) & CAP_FS_MASK); + cap_t(current->cap_permitted) & cap_t(cap_fs_set); } } diff -urN linux/net/core/rtnetlink.c linux.cap/net/core/rtnetlink.c --- linux/net/core/rtnetlink.c Wed Nov 18 14:13:08 1998 +++ linux.cap/net/core/rtnetlink.c Thu Nov 19 21:04:35 1998 @@ -245,7 +245,7 @@ static int rtnetlink_done(struct netlink_callback *cb) { - if (cap_raised(NETLINK_CB(cb->skb).eff_cap, CAP_NET_ADMIN) && cb->nlh->nlmsg_flags&NLM_F_ATOMIC) + if (CAP_RAISED(NETLINK_CB(cb->skb).eff_cap, CAP_NET_ADMIN) && cb->nlh->nlmsg_flags&NLM_F_ATOMIC) rtnl_shunlock(); return 0; } @@ -300,7 +300,7 @@ sz_idx = type>>2; kind = type&3; - if (kind != 2 && !cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) { + if (kind != 2 && !CAP_RAISED(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN)) { *errp = -EPERM; return -1; } @@ -315,13 +315,13 @@ goto err_inval; /* Super-user locks all the tables to get atomic snapshot */ - if (cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN) + if (CAP_RAISED(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN) && nlh->nlmsg_flags&NLM_F_ATOMIC) atomic_inc(&rtnl_rlockct); if ((*errp = netlink_dump_start(rtnl, skb, nlh, link->dumpit, rtnetlink_done)) != 0) { - if (cap_raised(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN) && nlh->nlmsg_flags&NLM_F_ATOMIC) + if (CAP_RAISED(NETLINK_CB(skb).eff_cap, CAP_NET_ADMIN) && nlh->nlmsg_flags&NLM_F_ATOMIC) atomic_dec(&rtnl_rlockct); return -1; }

--WIyZ46R2i8wDzkSu--

- To unsubscribe from this list: send the line "unsubscribe linux-kernel" in the body of a message to majordomo@vger.rutgers.edu Please read the FAQ at http://www.tux.org/lkml/