linux-security-module November 2007 archive
Main Archive Page > Month Archives  > linux-security-module archives
linux-security-module: Re: [PATCH] 64 bit capabilities

Re: [PATCH] 64 bit capabilities

From: Serge E. Hallyn <serue_at_nospam>
Date: Sat Nov 10 2007 - 00:01:55 GMT
To: Andrew Morgan <morgan@kernel.org>


Quoting Andrew Morgan (morgan@kernel.org):
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Andrew, Serge
>
> The attached patch (e3d27bcb07485a6c8927c8e4f5483d35a99680c3) adds
> 64-bit capability support to the kernel. This version of the patch is
> designed to apply against the 2.6.23-mm1 tree.
>
> FWIW libcap-2.00 supports this change (and earlier capability formats)
>
> http://www.kernel.org/pub/linux/libs/security/linux-privs/kernel-2.6/
>
> Cheers
>
> Andrew
>
> Note: to apply this patch against Linus' upstream kernel, you will first
> have to undo this other patch from Serge:
>
> From b68680e4731abbd78863063aaa0dca2a6d8cc723 Mon Sep 17 00:00:00 2001
> From: Serge E. Hallyn <serue@us.ibm.com>
> Date: Sun, 21 Oct 2007 16:41:38 -0700
> Subject: [PATCH] capabilities: clean up file capability reading
>
> It seems that this patch has made it into 2.6.24-rc1, but it is not
> present in 2.6.23-mm1.
>
> -----BEGIN PGP SIGNATURE-----
> Version: GnuPG v1.2.6 (GNU/Linux)
>
> iD8DBQFHMr5rQheEq9QabfIRAkWuAJ9vQBefhA31KWobFGkIugMnPiS7TwCgkeNg
> DXC3U5OPNO/w9ERJBltxMKo=
> =SjLL
> -----END PGP SIGNATURE-----

> >From e3d27bcb07485a6c8927c8e4f5483d35a99680c3 Mon Sep 17 00:00:00 2001
> From: Andrew G. Morgan <morgan@kernel.org>
> Date: Wed, 7 Nov 2007 23:17:06 -0800
> Subject: [PATCH] Add 64-bit capability support to the kernel.
>
> The patch has 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/
>
> Signed-off-by: Andrew G. Morgan <morgan@kernel.org>

Other than the one comment below,

Acked-by: Serge Hallyn <serue@us.ibm.com>

> ---
> fs/nfsd/auth.c | 10 +-
> fs/proc/array.c | 21 +++-
> include/linux/capability.h | 222 +++++++++++++++++++++++++++++++-------------
> kernel/capability.c | 89 ++++++++++++++++--
> mm/oom_kill.c | 5 +-
> security/commoncap.c | 93 +++++++++++++------
> security/dummy.c | 17 ++-
> 7 files changed, 332 insertions(+), 125 deletions(-)
>

[...]
> diff --git a/security/commoncap.c b/security/commoncap.c
> index 43f9027..dd63129 100644
> --- a/security/commoncap.c
> +++ b/security/commoncap.c
> @@ -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
> @@ -87,9 +87,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;
> }
>
> @@ -190,28 +190,54 @@ 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)

Note that you switched this to unsigned, but the caller is still sending in an int (rc).

> {
> __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: */
> @@ -219,7 +245,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) {
> @@ -232,8 +258,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;
> @@ -242,7 +268,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);
> @@ -337,8 +363,11 @@ 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 */
> @@ -460,13 +489,17 @@ 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;
> @@ -557,9 +590,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;
> }
> diff --git a/security/dummy.c b/security/dummy.c
> index 6d895ad..bd7e5b3 100644
> --- a/security/dummy.c
> +++ b/security/dummy.c
> @@ -36,14 +36,19 @@ static int dummy_ptrace (struct task_struct *parent, struct task_struct *child)
> static int dummy_capget (struct task_struct *target, kernel_cap_t * effective,
> kernel_cap_t * inheritable, kernel_cap_t * permitted)
> {
> - *effective = *inheritable = *permitted = 0;
> if (target->euid == 0) {
> - *permitted |= (~0 & ~CAP_FS_MASK);
> - *effective |= (~0 & ~CAP_TO_MASK(CAP_SETPCAP) & ~CAP_FS_MASK);
> + cap_set_full(*permitted);
> + cap_set_init_eff(*effective);
> + } else {
> + cap_clear(*permitted);
> + cap_clear(*effective);
> }
> - if (target->fsuid == 0) {
> - *permitted |= CAP_FS_MASK;
> - *effective |= CAP_FS_MASK;
> +
> + cap_clear(*inheritable);
> +
> + if (target->fsuid != 0) {
> + *permitted = cap_drop_fs_set(*permitted);
> + *effective = cap_drop_fs_set(*effective);
> }
> return 0;
> }
> --
> 1.5.1.3
>

thanks, Andrew.

-serge
-
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html