summaryrefslogtreecommitdiff
path: root/include/linux/capability.h
diff options
context:
space:
mode:
authorAndrew Morgan <morgan@kernel.org>2008-02-04 22:29:42 -0800
committerLinus Torvalds <torvalds@woody.linux-foundation.org>2008-02-05 09:44:20 -0800
commite338d263a76af78fe8f38a72131188b58fceb591 (patch)
treef3f046fc6fd66de43de7191830f0daf3bc4ec8eb /include/linux/capability.h
parent8f6936f4d29aa14e54a2470b954a2e1f96322988 (diff)
downloadlwn-e338d263a76af78fe8f38a72131188b58fceb591.tar.gz
lwn-e338d263a76af78fe8f38a72131188b58fceb591.zip
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>
Diffstat (limited to 'include/linux/capability.h')
-rw-r--r--include/linux/capability.h221
1 files changed, 155 insertions, 66 deletions
diff --git a/include/linux/capability.h b/include/linux/capability.h
index 7a8d7ade28a0..a934dac672dd 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -23,13 +23,20 @@ struct task_struct;
kernel might be somewhat backwards compatible, but don't bet on
it. */
-/* XXX - Note, cap_t, is defined by POSIX to be an "opaque" pointer to
+/* Note, cap_t, is defined by POSIX (draft) to be an "opaque" pointer to
a set of three capability sets. The transposition of 3*the
following structure to such a composite is better handled in a user
library since the draft standard requires the use of malloc/free
etc.. */
-#define _LINUX_CAPABILITY_VERSION 0x19980330
+#define _LINUX_CAPABILITY_VERSION_1 0x19980330
+#define _LINUX_CAPABILITY_U32S_1 1
+
+#define _LINUX_CAPABILITY_VERSION_2 0x20071026
+#define _LINUX_CAPABILITY_U32S_2 2
+
+#define _LINUX_CAPABILITY_VERSION _LINUX_CAPABILITY_VERSION_2
+#define _LINUX_CAPABILITY_U32S _LINUX_CAPABILITY_U32S_2
typedef struct __user_cap_header_struct {
__u32 version;
@@ -42,43 +49,42 @@ typedef struct __user_cap_data_struct {
__u32 inheritable;
} __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 VFS_CAP_REVISION_MASK 0xFF000000
+#define VFS_CAP_FLAGS_MASK ~VFS_CAP_REVISION_MASK
+#define VFS_CAP_FLAGS_EFFECTIVE 0x000001
+
#define VFS_CAP_REVISION_1 0x01000000
+#define VFS_CAP_U32_1 1
+#define XATTR_CAPS_SZ_1 (sizeof(__le32)*(1 + 2*VFS_CAP_U32_1))
-#define VFS_CAP_REVISION VFS_CAP_REVISION_1
+#define VFS_CAP_REVISION_2 0x02000000
+#define VFS_CAP_U32_2 2
+#define XATTR_CAPS_SZ_2 (sizeof(__le32)*(1 + 2*VFS_CAP_U32_2))
+
+#define XATTR_CAPS_SZ XATTR_CAPS_SZ_2
+#define VFS_CAP_U32 VFS_CAP_U32_2
+#define VFS_CAP_REVISION VFS_CAP_REVISION_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 */
+ __le32 magic_etc; /* Little endian */
struct {
- __u32 permitted; /* Little endian */
- __u32 inheritable; /* Little endian */
- } data[1];
+ __le32 permitted; /* Little endian */
+ __le32 inheritable; /* Little endian */
+ } data[VFS_CAP_U32];
};
#ifdef __KERNEL__
-/* #define STRICT_CAP_T_TYPECHECKS */
-
-#ifdef STRICT_CAP_T_TYPECHECKS
-
typedef struct kernel_cap_struct {
- __u32 cap;
+ __u32 cap[_LINUX_CAPABILITY_U32S];
} kernel_cap_t;
-#else
-
-typedef __u32 kernel_cap_t;
-
-#endif
-
-#define _USER_CAP_HEADER_SIZE (2*sizeof(__u32))
+#define _USER_CAP_HEADER_SIZE (sizeof(struct __user_cap_header_struct))
#define _KERNEL_CAP_T_SIZE (sizeof(kernel_cap_t))
#endif
@@ -121,10 +127,6 @@ typedef __u32 kernel_cap_t;
#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. */
@@ -147,8 +149,12 @@ typedef __u32 kernel_cap_t;
** Linux-specific capabilities
**/
-/* Transfer any capability in your permitted set to any pid,
- remove any capability in your permitted set from any pid */
+/* Without VFS support for capabilities:
+ * Transfer any capability in your permitted set to any pid,
+ * remove any capability in your permitted set from any pid
+ * With VFS support for capabilities (neither of above, but)
+ * Add any capability to the current process' inheritable set
+ */
#define CAP_SETPCAP 8
@@ -309,70 +315,153 @@ typedef __u32 kernel_cap_t;
#define CAP_SETFCAP 31
+/*
+ * Bit location of each capability (used by user-space library and kernel)
+ */
+
+#define CAP_TO_INDEX(x) ((x) >> 5) /* 1 << 5 == bits in __u32 */
+#define CAP_TO_MASK(x) (1 << ((x) & 31)) /* mask for indexed __u32 */
+
#ifdef __KERNEL__
/*
* Internal kernel functions only
*/
-#ifdef STRICT_CAP_T_TYPECHECKS
+#define CAP_FOR_EACH_U32(__capi) \
+ for (__capi = 0; __capi < _LINUX_CAPABILITY_U32S; ++__capi)
+
+# define CAP_FS_MASK_B0 (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))
+
+#if _LINUX_CAPABILITY_U32S != 2
+# error Fix up hand-coded capability macro initializers
+#else /* HAND-CODED capability initializers */
+
+# define CAP_EMPTY_SET {{ 0, 0 }}
+# define CAP_FULL_SET {{ ~0, ~0 }}
+# define CAP_INIT_EFF_SET {{ ~CAP_TO_MASK(CAP_SETPCAP), ~0 }}
+# define CAP_FS_SET {{ CAP_FS_MASK_B0, 0 }}
+# define CAP_NFSD_SET {{ CAP_FS_MASK_B0|CAP_TO_MASK(CAP_SYS_RESOURCE), 0 }}
+
+#endif /* _LINUX_CAPABILITY_U32S != 2 */
+
+#define CAP_INIT_INH_SET CAP_EMPTY_SET
+
+# define cap_clear(c) do { (c) = __cap_empty_set; } while (0)
+# define cap_set_full(c) do { (c) = __cap_full_set; } while (0)
+# define cap_set_init_eff(c) do { (c) = __cap_init_eff_set; } while (0)
+
+#define cap_raise(c, flag) ((c).cap[CAP_TO_INDEX(flag)] |= CAP_TO_MASK(flag))
+#define cap_lower(c, flag) ((c).cap[CAP_TO_INDEX(flag)] &= ~CAP_TO_MASK(flag))
+#define cap_raised(c, flag) ((c).cap[CAP_TO_INDEX(flag)] & CAP_TO_MASK(flag))
+
+#define CAP_BOP_ALL(c, a, b, OP) \
+do { \
+ unsigned __capi; \
+ CAP_FOR_EACH_U32(__capi) { \
+ c.cap[__capi] = a.cap[__capi] OP b.cap[__capi]; \
+ } \
+} while (0)
+
+#define CAP_UOP_ALL(c, a, OP) \
+do { \
+ unsigned __capi; \
+ CAP_FOR_EACH_U32(__capi) { \
+ c.cap[__capi] = OP a.cap[__capi]; \
+ } \
+} while (0)
+
+static inline kernel_cap_t cap_combine(const kernel_cap_t a,
+ const kernel_cap_t b)
+{
+ kernel_cap_t dest;
+ CAP_BOP_ALL(dest, a, b, |);
+ return dest;
+}
-#define to_cap_t(x) { x }
-#define cap_t(x) (x).cap
+static inline kernel_cap_t cap_intersect(const kernel_cap_t a,
+ const kernel_cap_t b)
+{
+ kernel_cap_t dest;
+ CAP_BOP_ALL(dest, a, b, &);
+ return dest;
+}
-#else
+static inline kernel_cap_t cap_drop(const kernel_cap_t a,
+ const kernel_cap_t drop)
+{
+ kernel_cap_t dest;
+ CAP_BOP_ALL(dest, a, drop, &~);
+ return dest;
+}
-#define to_cap_t(x) (x)
-#define cap_t(x) (x)
+static inline kernel_cap_t cap_invert(const kernel_cap_t c)
+{
+ kernel_cap_t dest;
+ CAP_UOP_ALL(dest, c, ~);
+ return dest;
+}
-#endif
+static inline int cap_isclear(const kernel_cap_t a)
+{
+ unsigned __capi;
+ CAP_FOR_EACH_U32(__capi) {
+ if (a.cap[__capi] != 0)
+ return 0;
+ }
+ return 1;
+}
-#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)
+static inline int cap_issubset(const kernel_cap_t a, const kernel_cap_t set)
+{
+ kernel_cap_t dest;
+ dest = cap_drop(a, set);
+ return cap_isclear(dest);
+}
-#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))
+/* Used to decide between falling back on the old suser() or fsuser(). */
-static inline kernel_cap_t cap_combine(kernel_cap_t a, kernel_cap_t b)
+static inline int cap_is_fs_cap(int cap)
{
- kernel_cap_t dest;
- cap_t(dest) = cap_t(a) | cap_t(b);
- return dest;
+ const kernel_cap_t __cap_fs_set = CAP_FS_SET;
+ return !!(CAP_TO_MASK(cap) & __cap_fs_set.cap[CAP_TO_INDEX(cap)]);
}
-static inline kernel_cap_t cap_intersect(kernel_cap_t a, kernel_cap_t b)
+static inline kernel_cap_t cap_drop_fs_set(const kernel_cap_t a)
{
- kernel_cap_t dest;
- cap_t(dest) = cap_t(a) & cap_t(b);
- return dest;
+ const kernel_cap_t __cap_fs_set = CAP_FS_SET;
+ return cap_drop(a, __cap_fs_set);
}
-static inline kernel_cap_t cap_drop(kernel_cap_t a, kernel_cap_t drop)
+static inline kernel_cap_t cap_raise_fs_set(const kernel_cap_t a,
+ const kernel_cap_t permitted)
{
- kernel_cap_t dest;
- cap_t(dest) = cap_t(a) & ~cap_t(drop);
- return dest;
+ const kernel_cap_t __cap_fs_set = CAP_FS_SET;
+ return cap_combine(a,
+ cap_intersect(permitted, __cap_fs_set));
}
-static inline kernel_cap_t cap_invert(kernel_cap_t c)
+static inline kernel_cap_t cap_drop_nfsd_set(const kernel_cap_t a)
{
- kernel_cap_t dest;
- cap_t(dest) = ~cap_t(c);
- return dest;
+ const kernel_cap_t __cap_fs_set = CAP_NFSD_SET;
+ return cap_drop(a, __cap_fs_set);
}
-#define cap_isclear(c) (!cap_t(c))
-#define cap_issubset(a,set) (!(cap_t(a) & ~cap_t(set)))
-
-#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)
+static inline kernel_cap_t cap_raise_nfsd_set(const kernel_cap_t a,
+ const kernel_cap_t permitted)
+{
+ const kernel_cap_t __cap_nfsd_set = CAP_NFSD_SET;
+ return cap_combine(a,
+ cap_intersect(permitted, __cap_nfsd_set));
+}
-#define cap_is_fs_cap(c) (CAP_TO_MASK(c) & CAP_FS_MASK)
+extern const kernel_cap_t __cap_empty_set;
+extern const kernel_cap_t __cap_full_set;
+extern const kernel_cap_t __cap_init_eff_set;
int capable(int cap);
int __capable(struct task_struct *t, int cap);