-/* Common capabilities, needed by capability.o and root_plug.o
+/* Common capabilities, needed by capability.o and root_plug.o
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
#include <linux/xattr.h>
#include <linux/hugetlb.h>
#include <linux/mount.h>
+#include <linux/sched.h>
+
+/* Global security state */
+
+unsigned securebits = SECUREBITS_DEFAULT; /* systemwide security settings */
+EXPORT_SYMBOL(securebits);
int cap_netlink_send(struct sock *sk, struct sk_buff *skb)
{
EXPORT_SYMBOL(cap_netlink_recv);
+/*
+ * NOTE WELL: cap_capable() cannot be used like the kernel's capable()
+ * function. That is, it has the reverse semantics: cap_capable()
+ * returns 0 when a task has a capability, but the kernel's capable()
+ * returns 1 for this case.
+ */
int cap_capable (struct task_struct *tsk, int cap)
{
/* Derived from include/linux/sched.h:capable. */
kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
/* Derived from kernel/capability.c:sys_capget. */
- *effective = cap_t (target->cap_effective);
- *inheritable = cap_t (target->cap_inheritable);
- *permitted = cap_t (target->cap_permitted);
+ *effective = target->cap_effective;
+ *inheritable = target->cap_inheritable;
+ *permitted = target->cap_permitted;
return 0;
}
+#ifdef CONFIG_SECURITY_FILE_CAPABILITIES
+
+static inline int cap_block_setpcap(struct task_struct *target)
+{
+ /*
+ * No support for remote process capability manipulation with
+ * filesystem capability support.
+ */
+ return (target != current);
+}
+
+static inline int cap_inh_is_capped(void)
+{
+ /*
+ * Return 1 if changes to the inheritable set are limited
+ * to the old permitted set. That is, if the current task
+ * does *not* possess the CAP_SETPCAP capability.
+ */
+ return (cap_capable(current, CAP_SETPCAP) != 0);
+}
+
+#else /* ie., ndef CONFIG_SECURITY_FILE_CAPABILITIES */
+
+static inline int cap_block_setpcap(struct task_struct *t) { return 0; }
+static inline int cap_inh_is_capped(void) { return 1; }
+
+#endif /* def CONFIG_SECURITY_FILE_CAPABILITIES */
+
int cap_capset_check (struct task_struct *target, kernel_cap_t *effective,
kernel_cap_t *inheritable, kernel_cap_t *permitted)
{
- /* Derived from kernel/capability.c:sys_capset. */
- /* verify restrictions on target's new Inheritable set */
- if (!cap_issubset (*inheritable,
- cap_combine (target->cap_inheritable,
- current->cap_permitted))) {
+ if (cap_block_setpcap(target)) {
+ return -EPERM;
+ }
+ if (cap_inh_is_capped()
+ && !cap_issubset(*inheritable,
+ cap_combine(target->cap_inheritable,
+ current->cap_permitted))) {
+ /* incapable of using this inheritable set */
+ return -EPERM;
+ }
+ if (!cap_issubset(*inheritable,
+ cap_combine(target->cap_inheritable,
+ current->cap_bset))) {
+ /* no new pI capabilities outside bounding set */
return -EPERM;
}
return inode->i_op->removexattr(dentry, XATTR_NAME_CAPS);
}
-static inline int cap_from_disk(__le32 *caps, struct linux_binprm *bprm,
- int size)
+static inline int cap_from_disk(struct vfs_cap_data *caps,
+ struct linux_binprm *bprm, unsigned size)
{
__u32 magic_etc;
+ unsigned tocopy, i;
- if (size != XATTR_CAPS_SZ)
+ if (size < sizeof(magic_etc))
return -EINVAL;
- magic_etc = le32_to_cpu(caps[0]);
+ magic_etc = le32_to_cpu(caps->magic_etc);
switch ((magic_etc & VFS_CAP_REVISION_MASK)) {
- case VFS_CAP_REVISION:
- if (magic_etc & VFS_CAP_FLAGS_EFFECTIVE)
- bprm->cap_effective = true;
- else
- bprm->cap_effective = false;
- bprm->cap_permitted = to_cap_t( le32_to_cpu(caps[1]) );
- bprm->cap_inheritable = to_cap_t( le32_to_cpu(caps[2]) );
- return 0;
+ case VFS_CAP_REVISION_1:
+ if (size != XATTR_CAPS_SZ_1)
+ return -EINVAL;
+ tocopy = VFS_CAP_U32_1;
+ break;
+ case VFS_CAP_REVISION_2:
+ if (size != XATTR_CAPS_SZ_2)
+ return -EINVAL;
+ tocopy = VFS_CAP_U32_2;
+ break;
default:
return -EINVAL;
}
+
+ if (magic_etc & VFS_CAP_FLAGS_EFFECTIVE) {
+ bprm->cap_effective = true;
+ } else {
+ bprm->cap_effective = false;
+ }
+
+ for (i = 0; i < tocopy; ++i) {
+ bprm->cap_permitted.cap[i] =
+ le32_to_cpu(caps->data[i].permitted);
+ bprm->cap_inheritable.cap[i] =
+ le32_to_cpu(caps->data[i].inheritable);
+ }
+ while (i < VFS_CAP_U32) {
+ bprm->cap_permitted.cap[i] = 0;
+ bprm->cap_inheritable.cap[i] = 0;
+ i++;
+ }
+
+ return 0;
}
/* Locate any VFS capabilities: */
{
struct dentry *dentry;
int rc = 0;
- __le32 v1caps[XATTR_CAPS_SZ];
+ struct vfs_cap_data vcaps;
struct inode *inode;
if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID) {
if (!inode->i_op || !inode->i_op->getxattr)
goto out;
- rc = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, &v1caps,
- XATTR_CAPS_SZ);
+ rc = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, &vcaps,
+ XATTR_CAPS_SZ);
if (rc == -ENODATA || rc == -EOPNOTSUPP) {
/* no data, that's ok */
rc = 0;
if (rc < 0)
goto out;
- rc = cap_from_disk(v1caps, bprm, rc);
+ rc = cap_from_disk(&vcaps, bprm, rc);
if (rc)
printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n",
__FUNCTION__, rc, bprm->filename);
/* Derived from fs/exec.c:compute_creds. */
kernel_cap_t new_permitted, working;
- new_permitted = cap_intersect (bprm->cap_permitted, cap_bset);
- working = cap_intersect (bprm->cap_inheritable,
+ new_permitted = cap_intersect(bprm->cap_permitted,
+ current->cap_bset);
+ working = cap_intersect(bprm->cap_inheritable,
current->cap_inheritable);
- new_permitted = cap_combine (new_permitted, working);
+ new_permitted = cap_combine(new_permitted, working);
if (bprm->e_uid != current->uid || bprm->e_gid != current->gid ||
!cap_issubset (new_permitted, current->cap_permitted)) {
/* For init, we want to retain the capabilities set
* in the init_task struct. Thus we skip the usual
* capability rules */
- if (!is_init(current)) {
+ if (!is_global_init(current)) {
current->cap_permitted = new_permitted;
- current->cap_effective = bprm->cap_effective ?
- new_permitted : 0;
+ if (bprm->cap_effective)
+ current->cap_effective = new_permitted;
+ else
+ cap_clear(current->cap_effective);
}
/* AUD: Audit candidate if current->cap_effective is set */
if (!issecure (SECURE_NO_SETUID_FIXUP)) {
if (old_fsuid == 0 && current->fsuid != 0) {
- cap_t (current->cap_effective) &=
- ~CAP_FS_MASK;
+ current->cap_effective =
+ cap_drop_fs_set(
+ current->cap_effective);
}
if (old_fsuid != 0 && current->fsuid == 0) {
- cap_t (current->cap_effective) |=
- (cap_t (current->cap_permitted) &
- CAP_FS_MASK);
+ current->cap_effective =
+ cap_raise_fs_set(
+ current->cap_effective,
+ current->cap_permitted);
}
}
break;
return cap_safe_nice(p);
}
-int cap_task_kill(struct task_struct *p, struct siginfo *info,
- int sig, u32 secid)
+/*
+ * called from kernel/sys.c for prctl(PR_CABSET_DROP)
+ * done without task_capability_lock() because it introduces
+ * no new races - i.e. only another task doing capget() on
+ * this task could get inconsistent info. There can be no
+ * racing writer bc a task can only change its own caps.
+ */
+long cap_prctl_drop(unsigned long cap)
{
- if (info != SEND_SIG_NOINFO && (is_si_special(info) || SI_FROMKERNEL(info)))
- return 0;
-
- if (secid)
- /*
- * Signal sent as a particular user.
- * Capabilities are ignored. May be wrong, but it's the
- * only thing we can do at the moment.
- * Used only by usb drivers?
- */
- return 0;
- if (cap_issubset(p->cap_permitted, current->cap_permitted))
- return 0;
- if (capable(CAP_KILL))
- return 0;
-
- return -EPERM;
+ if (!capable(CAP_SETPCAP))
+ return -EPERM;
+ if (!cap_valid(cap))
+ return -EINVAL;
+ cap_lower(current->cap_bset, cap);
+ return 0;
}
#else
int cap_task_setscheduler (struct task_struct *p, int policy,
{
return 0;
}
-int cap_task_kill(struct task_struct *p, struct siginfo *info,
- int sig, u32 secid)
-{
- return 0;
-}
#endif
void cap_task_reparent_to_init (struct task_struct *p)
{
- p->cap_effective = CAP_INIT_EFF_SET;
- p->cap_inheritable = CAP_INIT_INH_SET;
- p->cap_permitted = CAP_FULL_SET;
+ cap_set_init_eff(p->cap_effective);
+ cap_clear(p->cap_inheritable);
+ cap_set_full(p->cap_permitted);
p->keep_capabilities = 0;
return;
}