Add 64-bit capability support to the kernel

The patch supports legacy (32-bit) capability userspace, and where possible
translates 32-bit capabilities to/from userspace and the VFS to 64-bit
kernel space capabilities.  If a capability set cannot be compressed into
32-bits for consumption by user space, the system call fails, with -ERANGE.

FWIW libcap-2.00 supports this change (and earlier capability formats)

 http://www.kernel.org/pub/linux/libs/security/linux-privs/kernel-2.6/

[akpm@linux-foundation.org: coding-syle fixes]
[akpm@linux-foundation.org: use get_task_comm()]
[ezk@cs.sunysb.edu: build fix]
[akpm@linux-foundation.org: do not initialise statics to 0 or NULL]
[akpm@linux-foundation.org: unused var]
[serue@us.ibm.com: export __cap_ symbols]
Signed-off-by: Andrew G. Morgan <morgan@kernel.org>
Cc: Stephen Smalley <sds@tycho.nsa.gov>
Acked-by: Serge Hallyn <serue@us.ibm.com>
Cc: Chris Wright <chrisw@sous-sol.org>
Cc: James Morris <jmorris@namei.org>
Cc: Casey Schaufler <casey@schaufler-ca.com>
Signed-off-by: Erez Zadok <ezk@cs.sunysb.edu>
Signed-off-by: Andrew Morton <akpm@linux-foundation.org>
Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
This commit is contained in:
Andrew Morgan
2008-02-04 22:29:42 -08:00
committed by Linus Torvalds
parent 8f6936f4d2
commit e338d263a7
7 changed files with 349 additions and 125 deletions

View File

@@ -1,4 +1,4 @@
/* 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
@@ -93,9 +93,9 @@ int cap_capget (struct task_struct *target, kernel_cap_t *effective,
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;
}
@@ -197,28 +197,51 @@ int cap_inode_killpriv(struct dentry *dentry)
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: */
@@ -226,7 +249,7 @@ static int get_file_caps(struct linux_binprm *bprm)
{
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) {
@@ -239,8 +262,8 @@ static int get_file_caps(struct linux_binprm *bprm)
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;
@@ -249,7 +272,7 @@ static int get_file_caps(struct linux_binprm *bprm)
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);
@@ -344,8 +367,10 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
* capability rules */
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 */
@@ -467,13 +492,15 @@ int cap_task_post_setuid (uid_t old_ruid, uid_t old_euid, uid_t old_suid,
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;
@@ -577,9 +604,9 @@ int cap_task_kill(struct task_struct *p, struct siginfo *info,
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;
}