Here is the re-spin of the 64-bit capabilities patch I was supposed to
send last week.  This version is backward-compatible with old libcap.
A new libcap will need to make sure to set the cap_t's version to
CAPABILITY_VERSION_V2.  The current libcap leaves it unset, so if the
kernel gets an unset version it has to old libcap and use V1.

This passes my file capabilities tests and ltp.  (That's a half-lie, as
I got slab corruption during one flock ltp test - second ltp run didn't
get corruption.  Whether it's my code, random other code, or a VM error,
I haven't yet figured out)

Of course the capget01 and capget02 ltp tests fail with -EINVAL when
run as root, since all 64 bits are set for root.  For the moment
that could be 'fixed' by only setting valid bits (0-31), but that would
stop working as soon as we introduce a new bit.  As non-root, the
tests pass.

thanks,
-serge

PS - both these patches make checkpatch complain about new typedefs.
I don't mind ripping them out, but am sticking with existing style
in that code.  Changing that probably takes a separate patch.

>From 5689df8467768f6d1d19ba5daf80a435f4dcdd85 Mon Sep 17 00:00:00 2001
From: Serge E. Hallyn <[EMAIL PROTECTED]>
Date: Mon, 22 Oct 2007 13:18:11 -0400
Subject: [PATCH 4/4] capabilities: implement 64-bit capabilities (v2)

We are completely out of capability bits, and at least
SMACK and user namespaces could use new bits.

So convert capabilities to 64 bits.

Unfortunately libcap apparently does not set the
capability_version on the cap_t into a capget().  So
to support old libcap, if the user calls capget without
asking for 64bit caps, we assume 32-bit caps.  Otherwise
we get segfault from writing past the cap_t bounds.

Signed-off-by: Serge E. Hallyn <[EMAIL PROTECTED]>
---
 fs/proc/array.c            |    6 +-
 include/linux/capability.h |   54 ++++++++++++++++++++-------
 kernel/capability.c        |   88 ++++++++++++++++++++++++++++++++-----------
 security/commoncap.c       |   36 ++++++++++++++----
 4 files changed, 136 insertions(+), 48 deletions(-)

diff --git a/fs/proc/array.c b/fs/proc/array.c
index 3f4d824..c8ea46d 100644
--- a/fs/proc/array.c
+++ b/fs/proc/array.c
@@ -288,9 +288,9 @@ static inline char *task_sig(struct task_struct *p, char 
*buffer)
 
 static inline char *task_cap(struct task_struct *p, char *buffer)
 {
-    return buffer + sprintf(buffer, "CapInh:\t%016x\n"
-                           "CapPrm:\t%016x\n"
-                           "CapEff:\t%016x\n",
+    return buffer + sprintf(buffer, "CapInh:\t%016lx\n"
+                           "CapPrm:\t%016lx\n"
+                           "CapEff:\t%016lx\n",
                            cap_t(p->cap_inheritable),
                            cap_t(p->cap_permitted),
                            cap_t(p->cap_effective));
diff --git a/include/linux/capability.h b/include/linux/capability.h
index c45917f..b3722d1 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -29,37 +29,61 @@ struct task_struct;
    library since the draft standard requires the use of malloc/free
    etc.. */
 
-#define _LINUX_CAPABILITY_VERSION  0x19980330
+#define CAPABILITY_VERSION_V1  0x19980330
+#define CAPABILITY_VERSION_V2  0x20071015
+#define _LINUX_CAPABILITY_VERSION CAPABILITY_VERSION_V2
 
 typedef struct __user_cap_header_struct {
        __u32 version;
        int pid;
 } __user *cap_user_header_t;
 
-typedef struct __user_cap_data_struct {
-        __u32 effective;
-        __u32 permitted;
-        __u32 inheritable;
+typedef struct __user_cap_data_v1_struct {
+       __u32 effective;
+       __u32 permitted;
+       __u32 inheritable;
+} cap_user_data_v1_t;
+
+typedef struct __user_cap_data_v2_struct {
+       __u64 effective;
+       __u64 permitted;
+       __u64 inheritable;
+} cap_user_data_v2_t;
+
+typedef union cap_user_data {
+       cap_user_data_v1_t v1;
+       cap_user_data_v2_t v2;
 } __user *cap_user_data_t;
 
 #define XATTR_CAPS_SUFFIX "capability"
 #define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX
 
-#define XATTR_CAPS_SZ (3*sizeof(__le32))
+#define XATTR_CAPS_SZ_1 (3*sizeof(__le32))
+#define XATTR_CAPS_SZ_2 (2*sizeof(__le64) + sizeof(__le32))
 #define VFS_CAP_REVISION_MASK  0xFF000000
 #define VFS_CAP_REVISION_1     0x01000000
+#define VFS_CAP_REVISION_2     0x02000000
 
-#define VFS_CAP_REVISION       VFS_CAP_REVISION_1
+#define VFS_CAP_REVISION       VFS_CAP_REVISION_2
+#define XATTR_CAPS_SZ          XATTR_CAPS_SZ_2
 
 #define VFS_CAP_FLAGS_MASK     ~VFS_CAP_REVISION_MASK
 #define VFS_CAP_FLAGS_EFFECTIVE        0x000001
 
-struct vfs_cap_data {
-       __u32 magic_etc;  /* Little endian */
-       __u32 permitted;    /* Little endian */
-       __u32 inheritable;  /* Little endian */
+struct vfs_cap_data_v1 {
+       __le32 magic_etc;  /* Little endian */
+       __le32 permitted;    /* Little endian */
+       __le32 inheritable;  /* Little endian */
 };
 
+struct vfs_cap_data_v2 {
+       __le32 magic_etc;  /* Little endian */
+       __le64 permitted;    /* Little endian */
+       __le64 inheritable;  /* Little endian */
+};
+
+typedef struct vfs_cap_data_v2 vfs_cap_data;
+
 /**
  ** POSIX-draft defined capabilities.
  **/
@@ -118,6 +142,8 @@ struct vfs_cap_data {
 
 #define CAP_SETUID           7
 
+#define CAP_V2_MASK ~0xFFUL
+
 
 /**
  ** Linux-specific capabilities
@@ -293,12 +319,12 @@ struct vfs_cap_data {
 
 #define _USER_CAP_HEADER_SIZE  (2*sizeof(__u32))
 #define _KERNEL_CAP_T_SIZE     (sizeof(kernel_cap_t))
-#define CAP_TO_MASK(x) (1 << (x))
+#define CAP_TO_MASK(x) (1ULL << (x))
 
 #ifdef CONFIG_CAP_STRICT_TYPECHECKS
 
 typedef struct kernel_cap_struct {
-       __u32 cap;
+       __u64 cap;
 } kernel_cap_t;
 
 #define to_cap_t(x) { .cap = x, }
@@ -307,7 +333,7 @@ typedef struct kernel_cap_struct {
 
 #else
 
-typedef __u32 kernel_cap_t;
+typedef __u64 kernel_cap_t;
 
 #define to_cap_t(x) (x)
 #define cap_t(x) (x)
diff --git a/kernel/capability.c b/kernel/capability.c
index 7094ad9..1aa8226 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -27,6 +27,12 @@ kernel_cap_t cap_fs_mask_set = to_cap_t(CAP_FS_MASK);
  */
 static DEFINE_SPINLOCK(task_capability_lock);
 
+static inline int cap_hi_bits(kernel_cap_t p)
+{
+       return (cap_t(p) >> 32);
+
+}
+
 /*
  * For sys_getproccap() and sys_setproccap(), any of the three
  * capability set pointers may be NULL -- indicating that that set is
@@ -48,14 +54,16 @@ asmlinkage long sys_capget(cap_user_header_t header, 
cap_user_data_t dataptr)
        pid_t pid;
        __u32 version;
        struct task_struct *target;
-       struct __user_cap_data_struct data;
+       union cap_user_data data;
        kernel_cap_t pE, pP, pI;
 
        if (get_user(version, &header->version))
                return -EFAULT;
 
-       if (version != _LINUX_CAPABILITY_VERSION) {
-               if (put_user(_LINUX_CAPABILITY_VERSION, &header->version))
+       if (version != CAPABILITY_VERSION_V1 &&
+                               version != CAPABILITY_VERSION_V2) {
+               version = CAPABILITY_VERSION_V1;
+               if (put_user(version, &header->version))
                        return -EFAULT;
                return -EINVAL;
        }
@@ -79,18 +87,34 @@ asmlinkage long sys_capget(cap_user_header_t header, 
cap_user_data_t dataptr)
                target = current;
 
        ret = security_capget(target, &pE, &pI, &pP);
-       data.effective = cap_t(pE);
-       data.inheritable = cap_t(pI);
-       data.permitted = cap_t(pP);
 
 out:
        read_unlock(&tasklist_lock);
        spin_unlock(&task_capability_lock);
 
-       if (!ret && copy_to_user(dataptr, &data, sizeof data))
-               return -EFAULT;
-
-       return ret;
+       if (ret)
+               return ret;
+
+       switch (version) {
+       case CAPABILITY_VERSION_V1:
+               if (cap_hi_bits(pE) || cap_hi_bits(pP) || cap_hi_bits(pI))
+                       return -EINVAL;
+               data.v1.effective = (__u32)cap_t(pE);
+               data.v1.inheritable = (__u32)cap_t(pI);
+               data.v1.permitted = (__u32)cap_t(pP);
+               if (copy_to_user(dataptr, &data.v1, sizeof(data.v1)))
+                       return -EFAULT;
+               return 0;
+       case CAPABILITY_VERSION_V2:
+               data.v2.effective = cap_t(pE);
+               data.v2.inheritable = cap_t(pI);
+               data.v2.permitted = cap_t(pP);
+               if (copy_to_user(dataptr, &data.v2, sizeof(data.v2)))
+                       return -EFAULT;
+               return 0;
+       default:
+               return -EINVAL;
+       }
 }
 
 /*
@@ -177,7 +201,8 @@ static inline int cap_set_all(kernel_cap_t *effective,
  */
 asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t 
data)
 {
-       kernel_cap_t inheritable, permitted, effective;
+       kernel_cap_t pI2, pP2, pE2;
+       __u32 pI1, pP1, pE1;
        __u32 version;
        struct task_struct *target;
        int ret;
@@ -186,7 +211,9 @@ asmlinkage long sys_capset(cap_user_header_t header, const 
cap_user_data_t data)
        if (get_user(version, &header->version))
                return -EFAULT;
 
-       if (version != _LINUX_CAPABILITY_VERSION) {
+       if (version != CAPABILITY_VERSION_V1 &&
+                               version != CAPABILITY_VERSION_V2) {
+               version = _LINUX_CAPABILITY_VERSION;
                if (put_user(_LINUX_CAPABILITY_VERSION, &header->version))
                        return -EFAULT;
                return -EINVAL;
@@ -198,10 +225,25 @@ asmlinkage long sys_capset(cap_user_header_t header, 
const cap_user_data_t data)
        if (pid && pid != task_pid_vnr(current) && !capable(CAP_SETPCAP))
                return -EPERM;
 
-       if (copy_from_user(&effective, &data->effective, sizeof(effective)) ||
-           copy_from_user(&inheritable, &data->inheritable, 
sizeof(inheritable)) ||
-           copy_from_user(&permitted, &data->permitted, sizeof(permitted)))
-               return -EFAULT;
+       switch (version) {
+       case CAPABILITY_VERSION_V1:
+               if (copy_from_user(&pE1, &data->v1.effective, sizeof(pE1)) ||
+                   copy_from_user(&pI1, &data->v1.inheritable, sizeof(pI1)) ||
+                   copy_from_user(&pP1, &data->v1.permitted, sizeof(pP1)))
+                       return -EFAULT;
+               cap_assign(pE2, (__u64)pE1);
+               cap_assign(pP2, (__u64)pP1);
+               cap_assign(pI2, (__u64)pI1);
+               break;
+       case CAPABILITY_VERSION_V2:
+               if (copy_from_user(&pE2, &data->v2.effective, sizeof(pE2)) ||
+                   copy_from_user(&pI2, &data->v2.inheritable, sizeof(pI2)) ||
+                   copy_from_user(&pP2, &data->v2.permitted, sizeof(pP2)))
+                       return -EFAULT;
+               break;
+       default:
+               return -EINVAL;
+       }
 
        spin_lock(&task_capability_lock);
        read_lock(&tasklist_lock);
@@ -221,17 +263,17 @@ asmlinkage long sys_capset(cap_user_header_t header, 
const cap_user_data_t data)
           we now put them into effect. */
        if (pid < 0) {
                if (pid == -1)  /* all procs other than current and init */
-                       ret = cap_set_all(&effective, &inheritable, &permitted);
+                       ret = cap_set_all(&pE2, &pI2, &pP2);
 
                else            /* all procs in process group */
-                       ret = cap_set_pg(-pid, &effective, &inheritable,
-                                        &permitted);
+                       ret = cap_set_pg(-pid, &pE2, &pI2,
+                                        &pP2);
        } else {
-               ret = security_capset_check(target, &effective, &inheritable,
-                                           &permitted);
+               ret = security_capset_check(target, &pE2, &pI2,
+                                           &pP2);
                if (!ret)
-                       security_capset_set(target, &effective, &inheritable,
-                                           &permitted);
+                       security_capset_set(target, &pE2, &pI2,
+                                           &pP2);
        }
 
 out:
diff --git a/security/commoncap.c b/security/commoncap.c
index 05f5d5c..30ef5b7 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -190,26 +190,46 @@ int cap_inode_killpriv(struct dentry *dentry)
        return inode->i_op->removexattr(dentry, XATTR_NAME_CAPS);
 }
 
-static inline int cap_from_disk(struct vfs_cap_data *caps,
+union vfs_cap_union {
+       struct vfs_cap_data_v1 v1;
+       struct vfs_cap_data_v2 v2;
+};
+
+static inline int cap_from_disk(union vfs_cap_union *caps,
                                struct linux_binprm *bprm,
                                int size)
 {
        __u32 magic_etc;
 
-       if (size != XATTR_CAPS_SZ)
+       if (size != XATTR_CAPS_SZ_1 && size != XATTR_CAPS_SZ_2)
                return -EINVAL;
 
-       magic_etc = le32_to_cpu(caps->magic_etc);
+       magic_etc = le32_to_cpu(caps->v1.magic_etc);
 
        switch ((magic_etc & VFS_CAP_REVISION_MASK)) {
-       case VFS_CAP_REVISION:
+       case VFS_CAP_REVISION_1:
+               if (size != XATTR_CAPS_SZ_1)
+                       return -EINVAL;
+               if (magic_etc & VFS_CAP_FLAGS_EFFECTIVE)
+                       bprm->cap_effective = true;
+               else
+                       bprm->cap_effective = false;
+               cap_assign(bprm->cap_permitted,
+                               (__u64)le32_to_cpu(caps->v1.permitted));
+               cap_assign(bprm->cap_inheritable,
+                               (__u64)le32_to_cpu(caps->v1.inheritable));
+               return 0;
+       case VFS_CAP_REVISION_2:
+               if (size != XATTR_CAPS_SZ_2)
+                       return -EINVAL;
                if (magic_etc & VFS_CAP_FLAGS_EFFECTIVE)
                        bprm->cap_effective = true;
                else
                        bprm->cap_effective = false;
-               cap_assign(bprm->cap_permitted, le32_to_cpu(caps->permitted));
+               cap_assign(bprm->cap_permitted,
+                                       le64_to_cpu(caps->v2.permitted));
                cap_assign(bprm->cap_inheritable,
-                                       le32_to_cpu(caps->inheritable));
+                                       le64_to_cpu(caps->v2.inheritable));
                return 0;
        default:
                return -EINVAL;
@@ -221,7 +241,7 @@ static int get_file_caps(struct linux_binprm *bprm)
 {
        struct dentry *dentry;
        int rc = 0;
-       struct vfs_cap_data incaps;
+       union vfs_cap_union incaps;
        struct inode *inode;
 
        if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID) {
@@ -236,7 +256,7 @@ static int get_file_caps(struct linux_binprm *bprm)
 
        rc = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS, NULL, 0);
        if (rc > 0) {
-               if (rc == XATTR_CAPS_SZ)
+               if (rc == XATTR_CAPS_SZ_2 || rc == XATTR_CAPS_SZ_1)
                        rc = inode->i_op->getxattr(dentry, XATTR_NAME_CAPS,
                                                &incaps, XATTR_CAPS_SZ);
                else
-- 
1.5.1.1.GIT

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

Reply via email to