type=SYSCALL msg=audit(1224342849.465:43): arch=c000003e syscall=59 success=yes exit=0
a0=25b6a00 a1=2580410 a2=2580140 a3=8 items=2 ppid=2219 pid=2266 auid=0 uid=0 gid=0 euid=0
suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=1 comm="ping"
exe="/bin/ping" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
key=(null)
type=EXECVE msg=audit(1224342849.465:43): argc=2 a0="ping"
a1="127.0.0.1"
type=CWD msg=audit(1224342849.465:43): cwd="/root"
type=PATH msg=audit(1224342849.465:43): item=0 name="/bin/ping" inode=49227
dev=fd:00 mode=0100755 ouid=0 ogid=0 rdev=00:00 obj=system_u:object_r:ping_exec_t:s0
cap_permitted=0000000000002000 cap_inheritable=0000000000000000
type=PATH msg=audit(1224342849.465:43): item=1 name=(null) inode=507963 dev=fd:00
mode=0100755 ouid=0 ogid=0 rdev=00:00 obj=system_u:object_r:ld_so_t:s0
This good? If either cap_permitted or cap_inheritable have anything set
I show them both. In the above example would you rather I only showed
cap_permitted and dropped cap_inheritable? Did I see correctly that
it's possible to set a cap_effective on a file? Does it do anything? I
didn't see that getting used or read in the kernel, so I didn't put any
way to display it in kernel....
-Eric
---
include/linux/capability.h | 11 ++++
kernel/auditsc.c | 68 ++++++++++++++++++++++++--
security/commoncap.c | 115 +++++++++++++++++++++++++-------------------
3 files changed, 140 insertions(+), 54 deletions(-)
diff --git a/include/linux/capability.h b/include/linux/capability.h
index 9d1fe30..a3785e7 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -96,6 +96,13 @@ typedef struct kernel_cap_struct {
__u32 cap[_KERNEL_CAPABILITY_U32S];
} kernel_cap_t;
+/* exact same as vfs_cap_data but in cpu endian and always filled completely */
+struct cpu_vfs_cap_data {
+ __u32 magic_etc;
+ kernel_cap_t permitted;
+ kernel_cap_t inheritable;
+};
+
#define _USER_CAP_HEADER_SIZE (sizeof(struct __user_cap_header_struct))
#define _KERNEL_CAP_T_SIZE (sizeof(kernel_cap_t))
@@ -517,6 +524,10 @@ kernel_cap_t cap_set_effective(const kernel_cap_t pE_new);
extern int capable(int cap);
+/* audit system wants to get cap info from files as well */
+struct dentry;
+extern int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data
*cpu_caps);
+
#endif /* __KERNEL__ */
#endif /* !_LINUX_CAPABILITY_H */
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index cf5bc2f..800fc55 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -65,6 +65,7 @@
#include <linux/highmem.h>
#include <linux/syscalls.h>
#include <linux/inotify.h>
+#include <linux/capability.h>
#include "audit.h"
@@ -100,6 +101,8 @@ struct audit_names {
gid_t gid;
dev_t rdev;
u32 osid;
+ kernel_cap_t cap_permitted;
+ kernel_cap_t cap_inheritable;
};
struct audit_aux_data {
@@ -1171,6 +1174,33 @@ static void audit_log_execve_info(struct audit_context *context,
kfree(buf);
}
+static void audit_log_fcaps(struct audit_buffer *ab, struct audit_names *name)
+{
+ int i;
+ int print = 0;
+ kernel_cap_t *perm = &name->cap_permitted;
+ kernel_cap_t *inh = &name->cap_inheritable;
+
+ CAP_FOR_EACH_U32(i) {
+ if (perm->cap[i] || inh->cap[i]) {
+ print = 1;
+ break;
+ }
+ }
+
+ if (likely(!print))
+ return;
+
+ audit_log_format(ab, " %s", "cap_permitted=");
+ CAP_FOR_EACH_U32(i) {
+ audit_log_format(ab, "%08x",
perm->cap[(_KERNEL_CAPABILITY_U32S-1) - i]);
+ }
+ audit_log_format(ab, " %s", "cap_inheritable=");
+ CAP_FOR_EACH_U32(i) {
+ audit_log_format(ab, "%08x",
inh->cap[(_KERNEL_CAPABILITY_U32S-1) - i]);
+ }
+}
+
static void audit_log_exit(struct audit_context *context, struct task_struct *tsk)
{
int i, call_panic = 0;
@@ -1421,6 +1451,8 @@ static void audit_log_exit(struct audit_context *context, struct
task_struct *ts
}
}
+ audit_log_fcaps(ab, n);
+
audit_log_end(ab);
}
@@ -1787,8 +1819,33 @@ static int audit_inc_name_count(struct audit_context *context,
return 0;
}
+
+static inline int audit_copy_fcaps(struct audit_names *name, const struct dentry
*dentry)
+{
+ struct cpu_vfs_cap_data caps;
+ int rc, i;
+
+ memset(&name->cap_permitted, 0, sizeof(kernel_cap_t));
+ memset(&name->cap_inheritable, 0, sizeof(kernel_cap_t));
+
+ if (!dentry)
+ return 0;
+
+ rc = get_vfs_caps_from_disk(dentry, &caps);
+ if (rc)
+ return rc;
+
+ CAP_FOR_EACH_U32(i) {
+ name->cap_permitted.cap[i] = caps.permitted.cap[i];
+ name->cap_inheritable.cap[i] = caps.inheritable.cap[i];
+ }
+ return 0;
+}
+
+
/* Copy inode data into an audit_names. */
-static void audit_copy_inode(struct audit_names *name, const struct inode *inode)
+static void audit_copy_inode(struct audit_names *name, const struct dentry *dentry,
+ const struct inode *inode)
{
name->ino = inode->i_ino;
name->dev = inode->i_sb->s_dev;
@@ -1797,6 +1854,7 @@ static void audit_copy_inode(struct audit_names *name, const struct
inode *inode
name->gid = inode->i_gid;
name->rdev = inode->i_rdev;
security_inode_getsecid(inode, &name->osid);
+ audit_copy_fcaps(name, dentry);
}
/**
@@ -1831,7 +1889,7 @@ void __audit_inode(const char *name, const struct dentry *dentry)
context->names[idx].name = NULL;
}
handle_path(dentry);
- audit_copy_inode(&context->names[idx], inode);
+ audit_copy_inode(&context->names[idx], dentry, inode);
}
/**
@@ -1892,7 +1950,7 @@ void __audit_inode_child(const char *dname, const struct dentry
*dentry,
if (!strcmp(dname, n->name) ||
!audit_compare_dname_path(dname, n->name, &dirlen)) {
if (inode)
- audit_copy_inode(n, inode);
+ audit_copy_inode(n, NULL, inode);
else
n->ino = (unsigned long)-1;
found_child = n->name;
@@ -1906,7 +1964,7 @@ add_names:
return;
idx = context->name_count - 1;
context->names[idx].name = NULL;
- audit_copy_inode(&context->names[idx], parent);
+ audit_copy_inode(&context->names[idx], NULL, parent);
}
if (!found_child) {
@@ -1927,7 +1985,7 @@ add_names:
}
if (inode)
- audit_copy_inode(&context->names[idx], inode);
+ audit_copy_inode(&context->names[idx], NULL, inode);
else
context->names[idx].ino = (unsigned long)-1;
}
diff --git a/security/commoncap.c b/security/commoncap.c
index 399bfdb..9f109b9 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -202,17 +202,70 @@ 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,
- struct linux_binprm *bprm, unsigned size)
+static inline int bprm_caps_from_vfs_caps(struct cpu_vfs_cap_data *caps,
+ struct linux_binprm *bprm)
{
+ unsigned i;
+ int ret = 0;
+
+ if (caps->magic_etc & VFS_CAP_FLAGS_EFFECTIVE)
+ bprm->cap_effective = true;
+ else
+ bprm->cap_effective = false;
+
+ CAP_FOR_EACH_U32(i) {
+ __u32 permitted = caps->permitted.cap[i];
+ __u32 inheritable = caps->inheritable.cap[i];
+
+ /*
+ * pP' = (X & fP) | (pI & fI)
+ */
+ bprm->cap_post_exec_permitted.cap[i] =
+ (current->cap_bset.cap[i] & permitted) |
+ (current->cap_inheritable.cap[i] & inheritable);
+
+ if (permitted & ~bprm->cap_post_exec_permitted.cap[i]) {
+ /*
+ * insufficient to execute correctly
+ */
+ ret = -EPERM;
+ }
+ }
+
+ /*
+ * For legacy apps, with no internal support for recognizing they
+ * do not have enough capabilities, we return an error if they are
+ * missing some "forced" (aka file-permitted) capabilities.
+ */
+ return bprm->cap_effective ? ret : 0;
+}
+
+int get_vfs_caps_from_disk(const struct dentry *dentry, struct cpu_vfs_cap_data
*cpu_caps)
+{
+ struct inode *inode = dentry->d_inode;
__u32 magic_etc;
unsigned tocopy, i;
- int ret;
+ int size;
+ struct vfs_cap_data caps;
+
+ memset(cpu_caps, 0, sizeof(struct cpu_vfs_cap_data));
+
+ if (!inode || !inode->i_op || !inode->i_op->getxattr)
+ return 0;
+
+ size = inode->i_op->getxattr((struct dentry *)dentry, XATTR_NAME_CAPS, &caps,
+ XATTR_CAPS_SZ);
+ if (size == -ENODATA || size == -EOPNOTSUPP) {
+ /* no data, that's ok */
+ return 0;
+ }
+ if (size < 0)
+ return size;
if (size < sizeof(magic_etc))
return -EINVAL;
- magic_etc = le32_to_cpu(caps->magic_etc);
+ cpu_caps->magic_etc = magic_etc = le32_to_cpu(caps.magic_etc);
switch ((magic_etc & VFS_CAP_REVISION_MASK)) {
case VFS_CAP_REVISION_1:
@@ -229,46 +282,16 @@ static inline int cap_from_disk(struct vfs_cap_data *caps,
return -EINVAL;
}
- if (magic_etc & VFS_CAP_FLAGS_EFFECTIVE) {
- bprm->cap_effective = true;
- } else {
- bprm->cap_effective = false;
- }
-
- ret = 0;
-
CAP_FOR_EACH_U32(i) {
- __u32 value_cpu;
-
- if (i >= tocopy) {
- /*
- * Legacy capability sets have no upper bits
- */
- bprm->cap_post_exec_permitted.cap[i] = 0;
+ if (i > tocopy) {
+ cpu_caps->permitted.cap[i] = 0;
+ cpu_caps->inheritable.cap[i] = 0;
continue;
}
- /*
- * pP' = (X & fP) | (pI & fI)
- */
- value_cpu = le32_to_cpu(caps->data[i].permitted);
- bprm->cap_post_exec_permitted.cap[i] =
- (current->cap_bset.cap[i] & value_cpu) |
- (current->cap_inheritable.cap[i] &
- le32_to_cpu(caps->data[i].inheritable));
- if (value_cpu & ~bprm->cap_post_exec_permitted.cap[i]) {
- /*
- * insufficient to execute correctly
- */
- ret = -EPERM;
- }
+ cpu_caps->permitted.cap[i] = le32_to_cpu(caps.data[i].permitted);
+ cpu_caps->inheritable.cap[i] = le32_to_cpu(caps.data[i].inheritable);
}
-
- /*
- * For legacy apps, with no internal support for recognizing they
- * do not have enough capabilities, we return an error if they are
- * missing some "forced" (aka file-permitted) capabilities.
- */
- return bprm->cap_effective ? ret : 0;
+ return 0;
}
/* Locate any VFS capabilities: */
@@ -276,7 +299,7 @@ static int get_file_caps(struct linux_binprm *bprm)
{
struct dentry *dentry;
int rc = 0;
- struct vfs_cap_data vcaps;
+ struct cpu_vfs_cap_data vcaps;
struct inode *inode;
if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID) {
@@ -289,17 +312,11 @@ 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, &vcaps,
- XATTR_CAPS_SZ);
- if (rc == -ENODATA || rc == -EOPNOTSUPP) {
- /* no data, that's ok */
- rc = 0;
- goto out;
- }
+ rc = get_vfs_caps_from_disk(dentry, &vcaps);
if (rc < 0)
goto out;
- rc = cap_from_disk(&vcaps, bprm, rc);
+ rc = bprm_caps_from_vfs_caps(&vcaps, bprm);
if (rc == -EINVAL)
printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n",
__func__, rc, bprm->filename);