2008-10-20 22:28:26

by Eric Paris

[permalink] [raw]
Subject: [PATCH 0/4] Audit support for file capabilities

The following series implements audit support for file capabilities. Audit
emits relevant fcaps infor for all path records, any time fcaps actually
escalate permissions, and we now print the arguments to sys_capset for when a
process tries to modify cap info.

---

Eric Paris (4):
AUDIT: emit new record type showing all capset information
AUDIT: audit when fcaps increase the permitted or inheritable capabilities
AUDIT: output permitted and inheritable fcaps in PATH records
CAPABILITIES: add cpu endian vfs caps structure


include/linux/audit.h | 22 +++++
include/linux/capability.h | 12 +++
kernel/auditsc.c | 201 +++++++++++++++++++++++++++++++++++++++++++-
kernel/capability.c | 3 +
security/commoncap.c | 131 ++++++++++++++++-------------
5 files changed, 308 insertions(+), 61 deletions(-)


2008-10-20 22:26:22

by Eric Paris

[permalink] [raw]
Subject: [PATCH 1/4] CAPABILITIES: add cpu endian vfs caps structure

This patch add a generic cpu endian caps structure and enternally available
functions which retrieve fcaps information from disk. This information is
necessary so fcaps information can be collected and recorded by the audit
system.

Signed-off-by: Eric Paris <[email protected]>
---

include/linux/capability.h | 7 ++
security/commoncap.c | 128 +++++++++++++++++++++++++-------------------
2 files changed, 79 insertions(+), 56 deletions(-)

diff --git a/include/linux/capability.h b/include/linux/capability.h
index 9d1fe30..9d64a9c 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))

diff --git a/security/commoncap.c b/security/commoncap.c
index 399bfdb..888b292 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 -ENODATA;
+
+ 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 -ENODATA;
+ }
+ 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,8 +299,7 @@ static int get_file_caps(struct linux_binprm *bprm)
{
struct dentry *dentry;
int rc = 0;
- struct vfs_cap_data vcaps;
- struct inode *inode;
+ struct cpu_vfs_cap_data vcaps;

if (bprm->file->f_vfsmnt->mnt_flags & MNT_NOSUID) {
bprm_clear_caps(bprm);
@@ -285,24 +307,18 @@ static int get_file_caps(struct linux_binprm *bprm)
}

dentry = dget(bprm->file->f_dentry);
- inode = dentry->d_inode;
- 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;
+ rc = get_vfs_caps_from_disk(dentry, &vcaps);
+ if (rc < 0) {
+ if (rc == -EINVAL)
+ printk(KERN_NOTICE "%s: get_vfs_caps_from_disk returned %d for %s\n",
+ __func__, rc, bprm->filename);
+ else if (rc == -ENODATA)
+ rc = 0;
goto out;
}
- if (rc < 0)
- goto out;

- rc = cap_from_disk(&vcaps, bprm, rc);
- if (rc == -EINVAL)
- printk(KERN_NOTICE "%s: cap_from_disk returned %d for %s\n",
- __func__, rc, bprm->filename);
+ rc = bprm_caps_from_vfs_caps(&vcaps, bprm);

out:
dput(dentry);

2008-10-20 22:26:45

by Eric Paris

[permalink] [raw]
Subject: [PATCH 2/4] AUDIT: output permitted and inheritable fcaps in PATH records

This patch will print cap_permitted and cap_inheritable data in the PATH
records of any file that has file capabilities set. Files which do not
have fcaps set will not have different PATH records.

An example audit record if you run:
setcap "cap_net_admin+pie" /bin/bash
/bin/bash

type=SYSCALL msg=audit(1224539233.882:54): arch=c000003e syscall=59 success=yes exit=0 a0=207fc80 a1=2083cb0 a2=2083c70 a3=7fffb32dff60 items=2 ppid=2326 pid=2328 auid=0 uid=500 gid=500 euid=500 suid=500 fsuid=500 egid=500 sgid=500 fsgid=500 tty=pts0 ses=1 comm="bash" exe="/bin/bash" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)
type=EXECVE msg=audit(1224539233.882:54): argc=1 a0="-bash"
type=CWD msg=audit(1224539233.882:54): cwd="/home/test"
type=PATH msg=audit(1224539233.882:54): item=0 name="/bin/bash" inode=49161 dev=fd:00 mode=0100755 ouid=0 ogid=0 rdev=00:00 obj=system_u:object_r:shell_exec_t:s0 cap_fP=0000000000001000 cap_fI=0000000000001000 cap_fE=1 cap_fVer=2
type=PATH msg=audit(1224539233.882:54): 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

Signed-off-by: Eric Paris <[email protected]>
---

include/linux/capability.h | 5 +++
kernel/auditsc.c | 82 +++++++++++++++++++++++++++++++++++++++++---
2 files changed, 82 insertions(+), 5 deletions(-)

diff --git a/include/linux/capability.h b/include/linux/capability.h
index 9d64a9c..3f7920b 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -53,6 +53,7 @@ typedef struct __user_cap_data_struct {
#define XATTR_NAME_CAPS XATTR_SECURITY_PREFIX XATTR_CAPS_SUFFIX

#define VFS_CAP_REVISION_MASK 0xFF000000
+#define VFS_CAP_REVISION_SHIFT 24
#define VFS_CAP_FLAGS_MASK ~VFS_CAP_REVISION_MASK
#define VFS_CAP_FLAGS_EFFECTIVE 0x000001

@@ -524,6 +525,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..bf1f11a 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"

@@ -84,6 +85,15 @@ int audit_n_rules;
/* determines whether we collect data for signals sent */
int audit_signals;

+struct audit_cap_data {
+ kernel_cap_t permitted;
+ kernel_cap_t inheritable;
+ union {
+ unsigned int fE;
+ kernel_cap_t effective;
+ };
+};
+
/* When fs/namei.c:getname() is called, we store the pointer in name and
* we don't let putname() free it (instead we free all of the saved
* pointers at syscall exit time).
@@ -100,6 +110,8 @@ struct audit_names {
gid_t gid;
dev_t rdev;
u32 osid;
+ struct audit_cap_data fcap;
+ unsigned int fcap_ver;
};

struct audit_aux_data {
@@ -1171,6 +1183,35 @@ static void audit_log_execve_info(struct audit_context *context,
kfree(buf);
}

+static void audit_log_cap(struct audit_buffer *ab, char *prefix, kernel_cap_t *cap)
+{
+ int i;
+
+ audit_log_format(ab, " %s=", prefix);
+ CAP_FOR_EACH_U32(i) {
+ audit_log_format(ab, "%08x", cap->cap[(_KERNEL_CAPABILITY_U32S-1) - i]);
+ }
+}
+
+static void audit_log_fcaps(struct audit_buffer *ab, struct audit_names *name)
+{
+ kernel_cap_t *perm = &name->fcap.permitted;
+ kernel_cap_t *inh = &name->fcap.inheritable;
+ int log = 0;
+
+ if (!cap_isclear(*perm)) {
+ audit_log_cap(ab, "cap_fP", perm);
+ log = 1;
+ }
+ if (!cap_isclear(*inh)) {
+ audit_log_cap(ab, "cap_fI", inh);
+ log = 1;
+ }
+
+ if (log)
+ audit_log_format(ab, " cap_fE=%d cap_fVer=%x", name->fcap.fE, name->fcap_ver);
+}
+
static void audit_log_exit(struct audit_context *context, struct task_struct *tsk)
{
int i, call_panic = 0;
@@ -1421,6 +1462,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 +1830,36 @@ 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;
+
+ memset(&name->fcap.permitted, 0, sizeof(kernel_cap_t));
+ memset(&name->fcap.inheritable, 0, sizeof(kernel_cap_t));
+ name->fcap.fE = 0;
+ name->fcap_ver = 0;
+
+ if (!dentry)
+ return 0;
+
+ rc = get_vfs_caps_from_disk(dentry, &caps);
+ if (rc)
+ return rc;
+
+ name->fcap.permitted = caps.permitted;
+ name->fcap.inheritable = caps.inheritable;
+ name->fcap.fE = !!(caps.magic_etc & VFS_CAP_FLAGS_EFFECTIVE);
+ name->fcap_ver = (caps.magic_etc & VFS_CAP_REVISION_MASK) >> VFS_CAP_REVISION_SHIFT;
+
+ 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 +1868,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 +1903,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 +1964,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 +1978,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 +1999,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;
}

2008-10-20 22:27:38

by Eric Paris

[permalink] [raw]
Subject: [PATCH 3/4] AUDIT: audit when fcaps increase the permitted or inheritable capabilities

Any time fcaps are used to increase a processes pP or pE we will crate a new
audit record which contains the entire set of known information about the
executable in question, fP, fI, fE, version and includes the parent processes
pE, pI, pP. This record type will only be emitted from execve syscalls.

an example of making ping use fcaps instead of setuid:

setcap "cat_net_raw+pe" /bin/ping

type=SYSCALL msg=audit(1224539237.785:55): arch=c000003e syscall=59 success=yes exit=0 a0=1b511c0 a1=1b50aa0 a2=1b5f140 a3=3445170a70 items=2 ppid=2328 pid=2358 auid=0 uid=500 gid=500 euid=500 suid=500 fsuid=500 egid=500 sgid=500 fsgid=500 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(1224539237.785:55): argc=2 a0="ping" a1="127.0.0.1"
type=UNKNOWN[1321] msg=audit(1224539237.785:55): cap_fVer=2 cap_fP=0000000000002000 cap_fI=0000000000000000 cap_fE=1 cap_pP=0000000000001000 cap_pI=0000000000000000 cap_pE=0000000000001000 cap_bprmEff=0000000000002000
type=CWD msg=audit(1224539237.785:55): cwd="/home/test"
type=PATH msg=audit(1224539237.785:55): 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_fP=0000000000002000 cap_fE=1 cap_fVer=2
type=PATH msg=audit(1224539237.785:55): 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

Signed-off-by: Eric Paris <[email protected]>
---

include/linux/audit.h | 12 ++++++++
kernel/auditsc.c | 71 +++++++++++++++++++++++++++++++++++++++++++++++++
security/commoncap.c | 3 ++
3 files changed, 86 insertions(+), 0 deletions(-)

diff --git a/include/linux/audit.h b/include/linux/audit.h
index 6272a39..88a0f04 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -99,6 +99,7 @@
#define AUDIT_OBJ_PID 1318 /* ptrace target */
#define AUDIT_TTY 1319 /* Input on an administrative TTY */
#define AUDIT_EOE 1320 /* End of multi-record event */
+#define AUDIT_BPRM_FCAPS 1321 /* Information about fcaps increasing perms */

#define AUDIT_AVC 1400 /* SE Linux avc denial or grant */
#define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */
@@ -452,6 +453,8 @@ extern int __audit_mq_timedsend(mqd_t mqdes, size_t msg_len, unsigned int msg_pr
extern int __audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, unsigned int __user *u_msg_prio, const struct timespec __user *u_abs_timeout);
extern int __audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification);
extern int __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat);
+extern int __audit_log_bprm_fcaps(struct linux_binprm *bprm, struct cpu_vfs_cap_data *caps);
+

static inline int audit_ipc_obj(struct kern_ipc_perm *ipcp)
{
@@ -501,6 +504,14 @@ static inline int audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat)
return __audit_mq_getsetattr(mqdes, mqstat);
return 0;
}
+
+static inline int audit_log_bprm_fcaps(struct linux_binprm *bprm, struct cpu_vfs_cap_data *caps)
+{
+ if (unlikely(!audit_dummy_context()))
+ return __audit_log_bprm_fcaps(bprm, caps);
+ return 0;
+}
+
extern int audit_n_rules;
extern int audit_signals;
#else
@@ -532,6 +543,7 @@ extern int audit_signals;
#define audit_mq_timedreceive(d,l,p,t) ({ 0; })
#define audit_mq_notify(d,n) ({ 0; })
#define audit_mq_getsetattr(d,s) ({ 0; })
+#define audit_log_bprm_fcaps(b, c) 0
#define audit_ptrace(t) ((void)0)
#define audit_n_rules 0
#define audit_signals 0
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index bf1f11a..6d2487c 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -196,6 +196,14 @@ struct audit_aux_data_pids {
int pid_count;
};

+struct audit_aux_data_bprm_fcaps {
+ struct audit_aux_data d;
+ struct audit_cap_data fcap;
+ unsigned int fcap_ver;
+ struct audit_cap_data pcap;
+ kernel_cap_t bprm_effective;
+};
+
struct audit_tree_refs {
struct audit_tree_refs *next;
struct audit_chunk *c[31];
@@ -1375,6 +1383,18 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
audit_log_format(ab, "fd0=%d fd1=%d", axs->fd[0], axs->fd[1]);
break; }

+ case AUDIT_BPRM_FCAPS: {
+ struct audit_aux_data_bprm_fcaps *axs = (void *)aux;
+ audit_log_format(ab, "cap_fVer=%x", axs->fcap_ver);
+ audit_log_cap(ab, "cap_fP", &axs->fcap.permitted);
+ audit_log_cap(ab, "cap_fI", &axs->fcap.inheritable);
+ audit_log_format(ab, " cap_fE=%d", axs->fcap.fE);
+ audit_log_cap(ab, "cap_pP", &axs->pcap.permitted);
+ audit_log_cap(ab, "cap_pI", &axs->pcap.inheritable);
+ audit_log_cap(ab, "cap_pE", &axs->pcap.effective);
+ audit_log_cap(ab, "cap_bprmEff", &axs->bprm_effective);
+ break; }
+
}
audit_log_end(ab);
}
@@ -2502,6 +2522,57 @@ int __audit_signal_info(int sig, struct task_struct *t)
}

/**
+ * __audit_log_bprm_fcaps - store information about a loading bprm and relevant fcaps
+ * @bprm pointer to the bprm being processed
+ * @caps the caps read from the disk
+ *
+ * Simply check if the proc already has the caps given by the file and if not
+ * store the priv escalation info for later auditing at the end of the syscall
+ */
+int __audit_log_bprm_fcaps(struct linux_binprm *bprm, struct cpu_vfs_cap_data *caps)
+{
+ struct audit_aux_data_bprm_fcaps *ax;
+ struct audit_context *context = current->audit_context;
+ struct task_struct *tsk = current;
+
+ if (likely(!audit_enabled || !context || context->dummy))
+ return 0;
+
+ /*
+ * bprm is in pP interset pI &&
+ * we will change pE &&
+ * bprm is in pE intersect pI
+ *
+ * fcaps make no different so don't record this
+ */
+ if (cap_issubset(bprm->cap_post_exec_permitted, cap_intersect(tsk->cap_permitted, tsk->cap_inheritable)) &&
+ bprm->cap_effective &&
+ cap_issubset(bprm->cap_post_exec_permitted, cap_intersect(tsk->cap_effective, tsk->cap_inheritable)))
+ return 0;
+
+ ax = kmalloc(sizeof(*ax), GFP_KERNEL);
+ if (!ax)
+ return -ENOMEM;
+
+ ax->d.type = AUDIT_BPRM_FCAPS;
+ ax->d.next = context->aux;
+ context->aux = (void *)ax;
+
+ ax->fcap.permitted = caps->permitted;
+ ax->fcap.inheritable = caps->inheritable;
+ ax->fcap.fE = !!(caps->magic_etc & VFS_CAP_FLAGS_EFFECTIVE);
+ ax->fcap_ver = (caps->magic_etc & VFS_CAP_REVISION_MASK) >> VFS_CAP_REVISION_SHIFT;
+
+ ax->pcap.permitted = tsk->cap_permitted;
+ ax->pcap.inheritable = tsk->cap_inheritable;
+ ax->pcap.effective = tsk->cap_effective;
+
+ ax->bprm_effective = bprm->cap_post_exec_permitted;
+
+ return 0;
+}
+
+/**
* audit_core_dumps - record information about processes that end abnormally
* @signr: signal value
*
diff --git a/security/commoncap.c b/security/commoncap.c
index 888b292..9bb285d 100644
--- a/security/commoncap.c
+++ b/security/commoncap.c
@@ -8,6 +8,7 @@
*/

#include <linux/capability.h>
+#include <linux/audit.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
@@ -320,6 +321,8 @@ static int get_file_caps(struct linux_binprm *bprm)

rc = bprm_caps_from_vfs_caps(&vcaps, bprm);

+ audit_log_bprm_fcaps(bprm, &vcaps);
+
out:
dput(dentry);
if (rc)

2008-10-20 22:28:01

by Eric Paris

[permalink] [raw]
Subject: [PATCH 4/4] AUDIT: emit new record type showing all capset information

When the capset syscall is used it is not possible for audit to record the
actual capbilities being added/removed. This patch adds a new record type
which emits the target pid and the eff, inh, and perm cap sets.

example output if you audit capset syscalls would be:

type=SYSCALL msg=audit(1224539227.691:49): arch=c000003e syscall=126 success=yes exit=0 a0=11e8014 a1=11e801c a2=80000000 a3=7fffe3dc90d0 items=0 ppid=2262 pid=2323 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=1 comm="setcap" exe="/usr/sbin/setcap" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)
type=UNKNOWN[1322] msg=audit(1224539227.691:49): pid=0 CapEff=ffffffffffffffff CapInh=ffffffffffffffff CapPrm=ffffffffffffffff

Signed-off-by: Eric Paris <[email protected]>
---

include/linux/audit.h | 10 ++++++++++
kernel/auditsc.c | 48 ++++++++++++++++++++++++++++++++++++++++++++++++
kernel/capability.c | 3 +++
3 files changed, 61 insertions(+), 0 deletions(-)

diff --git a/include/linux/audit.h b/include/linux/audit.h
index 88a0f04..b5c7c3f 100644
--- a/include/linux/audit.h
+++ b/include/linux/audit.h
@@ -100,6 +100,7 @@
#define AUDIT_TTY 1319 /* Input on an administrative TTY */
#define AUDIT_EOE 1320 /* End of multi-record event */
#define AUDIT_BPRM_FCAPS 1321 /* Information about fcaps increasing perms */
+#define AUDIT_CAPSET 1322 /* Record showing argument to sys_capset */

#define AUDIT_AVC 1400 /* SE Linux avc denial or grant */
#define AUDIT_SELINUX_ERR 1401 /* Internal SE Linux Errors */
@@ -454,6 +455,7 @@ extern int __audit_mq_timedreceive(mqd_t mqdes, size_t msg_len, unsigned int __u
extern int __audit_mq_notify(mqd_t mqdes, const struct sigevent __user *u_notification);
extern int __audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat);
extern int __audit_log_bprm_fcaps(struct linux_binprm *bprm, struct cpu_vfs_cap_data *caps);
+extern int __audit_log_capset(pid_t pid, kernel_cap_t *eff, kernel_cap_t *inh, kernel_cap_t *perm);


static inline int audit_ipc_obj(struct kern_ipc_perm *ipcp)
@@ -512,6 +514,13 @@ static inline int audit_log_bprm_fcaps(struct linux_binprm *bprm, struct cpu_vfs
return 0;
}

+static inline int audit_log_capset(pid_t pid, kernel_cap_t *eff, kernel_cap_t *inh, kernel_cap_t *perm)
+{
+ if (unlikely(!audit_dummy_context()))
+ return __audit_log_capset(pid, eff, inh, perm);
+ return 0;
+}
+
extern int audit_n_rules;
extern int audit_signals;
#else
@@ -544,6 +553,7 @@ extern int audit_signals;
#define audit_mq_notify(d,n) ({ 0; })
#define audit_mq_getsetattr(d,s) ({ 0; })
#define audit_log_bprm_fcaps(b, c) 0
+#define audit_log_capset(pid, e, i, p) do { ; } while (0)
#define audit_ptrace(t) ((void)0)
#define audit_n_rules 0
#define audit_signals 0
diff --git a/kernel/auditsc.c b/kernel/auditsc.c
index 6d2487c..0db824f 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -204,6 +204,12 @@ struct audit_aux_data_bprm_fcaps {
kernel_cap_t bprm_effective;
};

+struct audit_aux_data_capset {
+ struct audit_aux_data d;
+ pid_t pid;
+ struct audit_cap_data cap;
+};
+
struct audit_tree_refs {
struct audit_tree_refs *next;
struct audit_chunk *c[31];
@@ -1395,6 +1401,14 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
audit_log_cap(ab, "cap_bprmEff", &axs->bprm_effective);
break; }

+ case AUDIT_CAPSET: {
+ struct audit_aux_data_capset *axs = (void *)aux;
+ audit_log_format(ab, "pid=%d", axs->pid);
+ audit_log_cap(ab, "CapEff", &axs->cap.effective);
+ audit_log_cap(ab, "CapInh", &axs->cap.inheritable);
+ audit_log_cap(ab, "CapPrm", &axs->cap.permitted);
+ break; }
+
}
audit_log_end(ab);
}
@@ -2573,6 +2587,40 @@ int __audit_log_bprm_fcaps(struct linux_binprm *bprm, struct cpu_vfs_cap_data *c
}

/**
+ * __audit_log_capset - store information about the arguments to the capset syscall
+ * @pid target pid of the capset call
+ * @eff effective cap set
+ * @inh inheritible cap set
+ * @perm permited cap set
+ *
+ * Record the aguments userspace sent to sys_capset for later printing by the
+ * audit system if applicable
+ */
+int __audit_log_capset(pid_t pid, kernel_cap_t *eff, kernel_cap_t *inh, kernel_cap_t *perm)
+{
+ struct audit_aux_data_capset *ax;
+ struct audit_context *context = current->audit_context;
+
+ if (likely(!audit_enabled || !context || context->dummy))
+ return 0;
+
+ ax = kmalloc(sizeof(*ax), GFP_KERNEL);
+ if (!ax)
+ return -ENOMEM;
+
+ ax->d.type = AUDIT_CAPSET;
+ ax->d.next = context->aux;
+ context->aux = (void *)ax;
+
+ ax->pid = pid;
+ ax->cap.effective = *eff;
+ ax->cap.inheritable = *eff;
+ ax->cap.permitted = *perm;
+
+ return 0;
+}
+
+/**
* audit_core_dumps - record information about processes that end abnormally
* @signr: signal value
*
diff --git a/kernel/capability.c b/kernel/capability.c
index 33e51e7..3d7fc70 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -7,6 +7,7 @@
* 30 May 2002: Cleanup, Robert M. Love <[email protected]>
*/

+#include <linux/audit.h>
#include <linux/capability.h>
#include <linux/mm.h>
#include <linux/module.h>
@@ -457,6 +458,8 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
i++;
}

+ audit_log_capset(pid, &effective, &inheritable, &permitted);
+
if (pid && (pid != task_pid_vnr(current)))
ret = do_sys_capset_other_tasks(pid, &effective, &inheritable,
&permitted);

2008-10-21 05:51:17

by Andrew G. Morgan

[permalink] [raw]
Subject: Re: [PATCH 1/4] CAPABILITIES: add cpu endian vfs caps structure

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Eric Paris wrote:
> This patch add a generic cpu endian caps structure and enternally available
> functions which retrieve fcaps information from disk. This information is
> necessary so fcaps information can be collected and recorded by the audit
> system.
>
[...]
> 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) {

Shouldn't the above still be (i >= tocopy) ?

Cheers

Andrew
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (Darwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFI/W2e+bHCR3gb8jsRAtDBAJ94XoCVXRDDycns4KEmLLBlYODiqACfUD7W
wx996Hso7mGH9lcc8Eiq89k=
=gx3P
-----END PGP SIGNATURE-----

2008-10-21 05:54:01

by Andrew G. Morgan

[permalink] [raw]
Subject: Re: [PATCH 3/4] AUDIT: audit when fcaps increase the permitted or inheritable capabilities

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Eric Paris wrote:
> Any time fcaps are used to increase a processes pP or pE we will crate a new
> audit record which contains the entire set of known information about the
> executable in question, fP, fI, fE, version and includes the parent processes
> pE, pI, pP. This record type will only be emitted from execve syscalls.

I'm confused by the choice of when to log this event.

File capabilities are required to give a process 'any' active
capabilities. That is they don't affect pI -> pI', but without fI or fP,
the post-execve() process is guaranteed to have no pP or pE capabilities.

Logging execve()s where there is only an increase in capabilities seems
wrong to me. To me it seems equally important to log any event where an
execve() yields pP != 0.

> diff --git a/security/commoncap.c b/security/commoncap.c
> index 888b292..9bb285d 100644
> --- a/security/commoncap.c
> +++ b/security/commoncap.c
> @@ -8,6 +8,7 @@
> */
>
> #include <linux/capability.h>
> +#include <linux/audit.h>
> #include <linux/module.h>
> #include <linux/init.h>
> #include <linux/kernel.h>
> @@ -320,6 +321,8 @@ static int get_file_caps(struct linux_binprm *bprm)
>
> rc = bprm_caps_from_vfs_caps(&vcaps, bprm);
>
> + audit_log_bprm_fcaps(bprm, &vcaps);
> +

When rc != 0, the execve() will fail. Is it appropriate to log in this case?

Cheers

Andrew
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (Darwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFI/W5F+bHCR3gb8jsRAhM9AJ9oJL4PmdtMwHEkN0Xh0ZTHBlJPzgCfVT/8
1Rq4wgGWftqpaVXBmeAsEi8=
=W8R9
-----END PGP SIGNATURE-----

2008-10-21 13:22:38

by Eric Paris

[permalink] [raw]
Subject: Re: [PATCH 1/4] CAPABILITIES: add cpu endian vfs caps structure

On Mon, 2008-10-20 at 22:50 -0700, Andrew G. Morgan wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Eric Paris wrote:
> > This patch add a generic cpu endian caps structure and enternally available
> > functions which retrieve fcaps information from disk. This information is
> > necessary so fcaps information can be collected and recorded by the audit
> > system.
> >
> [...]
> > 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) {
>
> Shouldn't the above still be (i >= tocopy) ?

just replaced this with

if (i >= tocopy)
break;

I'm already zero'ing out the cap data at the beginning so there isn't
really a need for me to zero the upper bits if they aren't set.

-Eric

2008-10-21 19:18:00

by Serge E. Hallyn

[permalink] [raw]
Subject: Re: [PATCH 3/4] AUDIT: audit when fcaps increase the permitted or inheritable capabilities

Quoting Andrew G. Morgan ([email protected]):
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Eric Paris wrote:
> > Any time fcaps are used to increase a processes pP or pE we will crate a new
> > audit record which contains the entire set of known information about the
> > executable in question, fP, fI, fE, version and includes the parent processes
> > pE, pI, pP. This record type will only be emitted from execve syscalls.
>
> I'm confused by the choice of when to log this event.
>
> File capabilities are required to give a process 'any' active
> capabilities. That is they don't affect pI -> pI', but without fI or fP,
> the post-execve() process is guaranteed to have no pP or pE capabilities.
>
> Logging execve()s where there is only an increase in capabilities seems
> wrong to me. To me it seems equally important to log any event where an
> execve() yields pP != 0.

True.

... except if (!issecure(SECURE_NOROOT) && uid==0) I guess?

And then it also might be interesting in the case where
(!issecure(SECURE_NOROOT) && uid==0) and pP is not full.

> > diff --git a/security/commoncap.c b/security/commoncap.c
> > index 888b292..9bb285d 100644
> > --- a/security/commoncap.c
> > +++ b/security/commoncap.c
> > @@ -8,6 +8,7 @@
> > */
> >
> > #include <linux/capability.h>
> > +#include <linux/audit.h>
> > #include <linux/module.h>
> > #include <linux/init.h>
> > #include <linux/kernel.h>
> > @@ -320,6 +321,8 @@ static int get_file_caps(struct linux_binprm *bprm)
> >
> > rc = bprm_caps_from_vfs_caps(&vcaps, bprm);
> >
> > + audit_log_bprm_fcaps(bprm, &vcaps);
> > +
>
> When rc != 0, the execve() will fail. Is it appropriate to log in this case?

It might fail because fP contains bits not in pP', right? That's
probably interesting to auditors.

-serge

2008-10-22 12:52:04

by Andrew G. Morgan

[permalink] [raw]
Subject: Re: [PATCH 3/4] AUDIT: audit when fcaps increase the permitted or inheritable capabilities

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

[s/[email protected]/[email protected]/]

Serge E. Hallyn wrote:
>> Logging execve()s where there is only an increase in capabilities seems
>> wrong to me. To me it seems equally important to log any event where an
>> execve() yields pP != 0.
>
> True.
>
> ... except if (!issecure(SECURE_NOROOT) && uid==0) I guess?
>
> And then it also might be interesting in the case where
> (!issecure(SECURE_NOROOT) && uid==0) and pP is not full.

I guess so, although this seems like a case of being interested in a
(unusual) non-privileged execve().

>>> rc = bprm_caps_from_vfs_caps(&vcaps, bprm);
>>>
>>> + audit_log_bprm_fcaps(bprm, &vcaps);
>>> +
>> When rc != 0, the execve() will fail. Is it appropriate to log in this case?
>
> It might fail because fP contains bits not in pP', right? That's
> probably interesting to auditors.

In which case, how is the fact it didn't execute captured in the audit log?

Cheers

Andrew
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (Darwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFI/yG9+bHCR3gb8jsRAii1AKCDluqUSVyAKP67/9bhEgqdlx3xdACg0dn4
81bi/3eMaP1FqfdVK2u/BpM=
=QBli
-----END PGP SIGNATURE-----

2008-10-22 14:27:23

by Serge E. Hallyn

[permalink] [raw]
Subject: Re: [PATCH 3/4] AUDIT: audit when fcaps increase the permitted or inheritable capabilities

Quoting Andrew G. Morgan ([email protected]):
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> [s/[email protected]/[email protected]/]
>
> Serge E. Hallyn wrote:
> >> Logging execve()s where there is only an increase in capabilities seems
> >> wrong to me. To me it seems equally important to log any event where an
> >> execve() yields pP != 0.
> >
> > True.
> >
> > ... except if (!issecure(SECURE_NOROOT) && uid==0) I guess?
> >
> > And then it also might be interesting in the case where
> > (!issecure(SECURE_NOROOT) && uid==0) and pP is not full.
>
> I guess so, although this seems like a case of being interested in a
> (unusual) non-privileged execve().

I'm not sure what you mean - but this can only happen if bits are taken
out of the capability bounding set, right?

> >>> rc = bprm_caps_from_vfs_caps(&vcaps, bprm);
> >>>
> >>> + audit_log_bprm_fcaps(bprm, &vcaps);
> >>> +
> >> When rc != 0, the execve() will fail. Is it appropriate to log in this case?
> >
> > It might fail because fP contains bits not in pP', right? That's
> > probably interesting to auditors.
>
> In which case, how is the fact it didn't execute captured in the audit log?

I assume as a FAIL? (Not sure of the exact wording in the logs)

-serge

2008-10-23 04:15:18

by Andrew G. Morgan

[permalink] [raw]
Subject: Re: [PATCH 3/4] AUDIT: audit when fcaps increase the permitted or inheritable capabilities

-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA1

Serge E. Hallyn wrote:
>>> ... except if (!issecure(SECURE_NOROOT) && uid==0) I guess?
>>>
>>> And then it also might be interesting in the case where
>>> (!issecure(SECURE_NOROOT) && uid==0) and pP is not full.
>> I guess so, although this seems like a case of being interested in a
>> (unusual) non-privileged execve().
>
> I'm not sure what you mean - but this can only happen if bits are taken
> out of the capability bounding set, right?

Yes, it can happen as you say.

This is a case of an unprivileged uid==0 execution. Since we don't
appear to want to audit other non-privileged execve()s, its not clear to
me that this one deserves attention.

>>>>> rc = bprm_caps_from_vfs_caps(&vcaps, bprm);
>>>>>
>>>>> + audit_log_bprm_fcaps(bprm, &vcaps);
>>>>> +
>>>> When rc != 0, the execve() will fail. Is it appropriate to log in this case?
>>> It might fail because fP contains bits not in pP', right? That's
>>> probably interesting to auditors.
>> In which case, how is the fact it didn't execute captured in the audit log?
>
> I assume as a FAIL? (Not sure of the exact wording in the logs)

OK. As long as its clearly identified as a failure and the logs are not
misleading - making it look like the execve() succeeded with privilege -
then I'm not as concerned.

Cheers

Andrew
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.4.7 (Darwin)
Comment: Using GnuPG with Mozilla - http://enigmail.mozdev.org

iD8DBQFI//oF+bHCR3gb8jsRAjZxAKCoSXL7CwTfQJt7Wn55nT8MwHbiEgCcD+Qm
VVHHZ9QiInaVb2faUt9Q77E=
=gJU0
-----END PGP SIGNATURE-----

2008-10-29 21:58:58

by Eric Paris

[permalink] [raw]
Subject: Re: [PATCH 3/4] AUDIT: audit when fcaps increase the permitted or inheritable capabilities

On Wed, 2008-10-22 at 21:13 -0700, Andrew G. Morgan wrote:
> -----BEGIN PGP SIGNED MESSAGE-----
> Hash: SHA1
>
> Serge E. Hallyn wrote:
> >>> ... except if (!issecure(SECURE_NOROOT) && uid==0) I guess?
> >>>
> >>> And then it also might be interesting in the case where
> >>> (!issecure(SECURE_NOROOT) && uid==0) and pP is not full.
> >> I guess so, although this seems like a case of being interested in a
> >> (unusual) non-privileged execve().
> >
> > I'm not sure what you mean - but this can only happen if bits are taken
> > out of the capability bounding set, right?
>
> Yes, it can happen as you say.
>
> This is a case of an unprivileged uid==0 execution. Since we don't
> appear to want to audit other non-privileged execve()s, its not clear to
> me that this one deserves attention.

So what did you two agree on for when to collect fcaps type information?
Any time bprm->cap_post_exec_permitted is non-zero?

> >>>>> rc = bprm_caps_from_vfs_caps(&vcaps, bprm);
> >>>>>
> >>>>> + audit_log_bprm_fcaps(bprm, &vcaps);
> >>>>> +
> >>>> When rc != 0, the execve() will fail. Is it appropriate to log in this case?
> >>> It might fail because fP contains bits not in pP', right? That's
> >>> probably interesting to auditors.
> >> In which case, how is the fact it didn't execute captured in the audit log?
> >
> > I assume as a FAIL? (Not sure of the exact wording in the logs)
>
> OK. As long as its clearly identified as a failure and the logs are not
> misleading - making it look like the execve() succeeded with privilege -
> then I'm not as concerned.

The syscall record (rather than this auxilary fcaps record) will
indicate that the syscall failed. it says something like success=no.

2008-10-30 13:42:18

by Serge E. Hallyn

[permalink] [raw]
Subject: Re: [PATCH 3/4] AUDIT: audit when fcaps increase the permitted or inheritable capabilities

Quoting Eric Paris ([email protected]):
> On Wed, 2008-10-22 at 21:13 -0700, Andrew G. Morgan wrote:
> > -----BEGIN PGP SIGNED MESSAGE-----
> > Hash: SHA1
> >
> > Serge E. Hallyn wrote:
> > >>> ... except if (!issecure(SECURE_NOROOT) && uid==0) I guess?
> > >>>
> > >>> And then it also might be interesting in the case where
> > >>> (!issecure(SECURE_NOROOT) && uid==0) and pP is not full.
> > >> I guess so, although this seems like a case of being interested in a
> > >> (unusual) non-privileged execve().
> > >
> > > I'm not sure what you mean - but this can only happen if bits are taken
> > > out of the capability bounding set, right?
> >
> > Yes, it can happen as you say.
> >
> > This is a case of an unprivileged uid==0 execution. Since we don't
> > appear to want to audit other non-privileged execve()s, its not clear to
> > me that this one deserves attention.
>
> So what did you two agree on for when to collect fcaps type information?
> Any time bprm->cap_post_exec_permitted is non-zero?

And (bprm->e_uid!=0 && current->uid!=0 && !issecure(SECURE_NOROOT)) -
otherwise you'll get a hit for every file executed by root. If you're
ok with that noise, then it's fine with me. (I assume it can be
filtered out using an audit rule by userspace anyway?)

The inverse case that I suggested is interesting because root was
prevented from running with full capabilities - which could be
indicative of a sendmail-capabilities-bug style of attack.

-serge