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); -- Linux-audit mailing list Linux-audit@redhat.com https://www.redhat.com/mailman/listinfo/linux-audit