LKML Archive on lore.kernel.org
help / color / mirror / Atom feed
* [PATCH -v3 1/5] Capabilities: document the order of arguments to cap_issubset
@ 2008-11-07 15:13 Eric Paris
  2008-11-07 15:14 ` [PATCH -v3 2/5] CAPABILITIES: add cpu endian vfs caps structure Eric Paris
                   ` (4 more replies)
  0 siblings, 5 replies; 9+ messages in thread
From: Eric Paris @ 2008-11-07 15:13 UTC (permalink / raw)
  To: linux-kernel, linux-audit; +Cc: sgrubb, morgan, serue, viro

Document the order of arguments for cap_issubset.  It's not instantly clear
which order the argument should be in.  So give an example.

Signed-off-by: Eric Paris <eparis@redhat.com>
---

 include/linux/capability.h |    7 +++++++
 1 files changed, 7 insertions(+), 0 deletions(-)

diff --git a/include/linux/capability.h b/include/linux/capability.h
index 9d1fe30..9f44150 100644
--- a/include/linux/capability.h
+++ b/include/linux/capability.h
@@ -454,6 +454,13 @@ static inline int cap_isclear(const kernel_cap_t a)
 	return 1;
 }
 
+/*
+ * Check if "a" is a subset of "set".
+ * return 1 if ALL of the capabilities in "a" are also in "set"
+ *	cap_issubset(0101, 1111) will return 1
+ * return 0 if ANY of the capabilities in "a" are not in "set"
+ *	cap_issubset(1111, 0101) will return 0
+ */
 static inline int cap_issubset(const kernel_cap_t a, const kernel_cap_t set)
 {
 	kernel_cap_t dest;


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH -v3 2/5] CAPABILITIES: add cpu endian vfs caps structure
  2008-11-07 15:13 [PATCH -v3 1/5] Capabilities: document the order of arguments to cap_issubset Eric Paris
@ 2008-11-07 15:14 ` Eric Paris
  2008-11-07 15:14 ` [PATCH -v3 3/5] AUDIT: output permitted and inheritable fcaps in PATH records Eric Paris
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 9+ messages in thread
From: Eric Paris @ 2008-11-07 15:14 UTC (permalink / raw)
  To: linux-kernel, linux-audit; +Cc: sgrubb, morgan, serue, viro

This patch add a generic cpu endian caps structure and externally 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 <eparis@redhat.com>
Acked-by: Serge Hallyn <serue@us.ibm.com>
---

 include/linux/capability.h |    7 ++
 security/commoncap.c       |  129 ++++++++++++++++++++++++--------------------
 2 files changed, 78 insertions(+), 58 deletions(-)

diff --git a/include/linux/capability.h b/include/linux/capability.h
index 9f44150..83e4f33 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 3976613..8bb95ed 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,13 @@ 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;
-			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;
-		}
+		if (i >= tocopy)
+			break;
+		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 +296,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;
 
 	bprm_clear_caps(bprm);
 
@@ -285,24 +304,18 @@ static int get_file_caps(struct linux_binprm *bprm)
 		return 0;
 
 	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);


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH -v3 3/5] AUDIT: output permitted and inheritable fcaps in PATH records
  2008-11-07 15:13 [PATCH -v3 1/5] Capabilities: document the order of arguments to cap_issubset Eric Paris
  2008-11-07 15:14 ` [PATCH -v3 2/5] CAPABILITIES: add cpu endian vfs caps structure Eric Paris
@ 2008-11-07 15:14 ` Eric Paris
  2008-11-10 14:46   ` Serge E. Hallyn
  2008-11-07 15:14 ` [PATCH -v3 4/5] AUDIT: collect info when execve results in caps in pE Eric Paris
                   ` (2 subsequent siblings)
  4 siblings, 1 reply; 9+ messages in thread
From: Eric Paris @ 2008-11-07 15:14 UTC (permalink / raw)
  To: linux-kernel, linux-audit; +Cc: sgrubb, morgan, serue, viro

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(1225741937.363:230): arch=c000003e syscall=59 success=yes exit=0 a0=2119230 a1=210da30 a2=20ee290 a3=8 items=2 ppid=2149 pid=2923 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=3 comm="ping" exe="/bin/ping" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)
type=EXECVE msg=audit(1225741937.363:230): argc=2 a0="ping" a1="www.google.com"
type=CWD msg=audit(1225741937.363:230):  cwd="/root"
type=PATH msg=audit(1225741937.363:230): item=0 name="/bin/ping" inode=49256 dev=fd:00 mode=0104755 ouid=0 ogid=0 rdev=00:00 obj=system_u:object_r:ping_exec_t:s0 cap_fp=0000000000002000 cap_fi=0000000000002000 cap_fe=1 cap_fver=2
type=PATH msg=audit(1225741937.363:230): item=1 name=(null) inode=507915 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 <eparis@redhat.com>
---

 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 83e4f33..a2fd8fd 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
 
@@ -531,6 +532,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..f87f31a 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;
 	}


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH -v3 4/5] AUDIT: collect info when execve results in caps in pE
  2008-11-07 15:13 [PATCH -v3 1/5] Capabilities: document the order of arguments to cap_issubset Eric Paris
  2008-11-07 15:14 ` [PATCH -v3 2/5] CAPABILITIES: add cpu endian vfs caps structure Eric Paris
  2008-11-07 15:14 ` [PATCH -v3 3/5] AUDIT: output permitted and inheritable fcaps in PATH records Eric Paris
@ 2008-11-07 15:14 ` Eric Paris
  2008-11-10 14:53   ` Serge E. Hallyn
  2008-11-07 15:14 ` [PATCH -v3 5/5] AUDIT: emit new record type showing all capset information Eric Paris
  2008-11-10 14:28 ` [PATCH -v3 1/5] Capabilities: document the order of arguments to cap_issubset Serge E. Hallyn
  4 siblings, 1 reply; 9+ messages in thread
From: Eric Paris @ 2008-11-07 15:14 UTC (permalink / raw)
  To: linux-kernel, linux-audit; +Cc: sgrubb, morgan, serue, viro

Any time fcaps or a setuid app under SECURE_NOROOT is used to result in a
non-zero pE we will crate a new audit record which contains the entire set
of known information about the executable in question, fP, fI, fE, fversion
and includes the process's pE, pI, pP.  Before and after the bprm capability
are applied.  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(1225742021.015:236): arch=c000003e syscall=59 success=yes exit=0 a0=1457f30 a1=14606b0 a2=1463940 a3=321b770a70 items=2 ppid=2929 pid=2963 auid=0 uid=500 gid=500 euid=500 suid=500 fsuid=500 egid=500 sgid=500 fsgid=500 tty=pts0 ses=3 comm="ping" exe="/bin/ping" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)
type=UNKNOWN[1321] msg=audit(1225742021.015:236): fver=2 fp=0000000000002000 fi=0000000000000000 fe=1 old_pp=0000000000000000 old_pi=0000000000000000 old_pe=0000000000000000 new_pp=0000000000002000 new_pi=0000000000000000 new_pe=0000000000002000
type=EXECVE msg=audit(1225742021.015:236): argc=2 a0="ping" a1="127.0.0.1"
type=CWD msg=audit(1225742021.015:236):  cwd="/home/test"
type=PATH msg=audit(1225742021.015:236): item=0 name="/bin/ping" inode=49256 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(1225742021.015:236): item=1 name=(null) inode=507915 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 <eparis@redhat.com>
---

 include/linux/audit.h |   26 +++++++++++++++++++
 kernel/auditsc.c      |   68 +++++++++++++++++++++++++++++++++++++++++++++++++
 security/commoncap.c  |   23 ++++++++++++++++-
 3 files changed, 116 insertions(+), 1 deletions(-)

diff --git a/include/linux/audit.h b/include/linux/audit.h
index 6272a39..8cfb9fe 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,7 @@ 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 void __audit_log_bprm_fcaps(struct linux_binprm *bprm, kernel_cap_t *pP, kernel_cap_t *pE);
 
 static inline int audit_ipc_obj(struct kern_ipc_perm *ipcp)
 {
@@ -501,6 +503,29 @@ static inline int audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat)
 		return __audit_mq_getsetattr(mqdes, mqstat);
 	return 0;
 }
+
+/*
+ * ieieeeeee, an audit function without a return code!
+ *
+ * This function might fail!  I decided that it didn't matter.  We are too late
+ * to fail the syscall and the information isn't REQUIRED for any purpose.  It's
+ * just nice to have.  We should be able to look at past audit logs to figure
+ * out this process's current cap set along with the fcaps from the PATH record
+ * and use that to come up with the final set.  Yeah, its ugly, but all the info
+ * is still in the audit log.  So I'm not going to bother mentioning we failed
+ * if we couldn't allocate memory.
+ *
+ * If someone changes their mind they could create the aux record earlier and
+ * then search here and use that earlier allocation.  But I don't wanna.
+ *
+ * -Eric
+ */
+static inline void audit_log_bprm_fcaps(struct linux_binprm *bprm, kernel_cap_t *pP, kernel_cap_t *pE)
+{
+	if (unlikely(!audit_dummy_context()))
+		__audit_log_bprm_fcaps(bprm, pP, pE);
+}
+
 extern int audit_n_rules;
 extern int audit_signals;
 #else
@@ -532,6 +557,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, p, e) 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 f87f31a..b8b5a2a 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	old_pcap;
+	struct audit_cap_data	new_pcap;
+};
+
 struct audit_tree_refs {
 	struct audit_tree_refs *next;
 	struct audit_chunk *c[31];
@@ -1375,6 +1383,20 @@ 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, "fver=%x", axs->fcap_ver);
+			audit_log_cap(ab, "fp", &axs->fcap.permitted);
+			audit_log_cap(ab, "fi", &axs->fcap.inheritable);
+			audit_log_format(ab, " fe=%d", axs->fcap.fE);
+			audit_log_cap(ab, "old_pp", &axs->old_pcap.permitted);
+			audit_log_cap(ab, "old_pi", &axs->old_pcap.inheritable);
+			audit_log_cap(ab, "old_pe", &axs->old_pcap.effective);
+			audit_log_cap(ab, "new_pp", &axs->new_pcap.permitted);
+			audit_log_cap(ab, "new_pi", &axs->new_pcap.inheritable);
+			audit_log_cap(ab, "new_pe", &axs->new_pcap.effective);
+			break; }
+
 		}
 		audit_log_end(ab);
 	}
@@ -2502,6 +2524,52 @@ 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
+ *
+ * this can fail and we don't care.  See the note in audit.h for
+ * audit_log_bprm_fcaps() for my explaination....
+ *
+ * -Eric
+ */
+void __audit_log_bprm_fcaps(struct linux_binprm *bprm, kernel_cap_t *pP, kernel_cap_t *pE)
+{
+	struct audit_aux_data_bprm_fcaps *ax;
+	struct audit_context *context = current->audit_context;
+	struct cpu_vfs_cap_data vcaps;
+	struct dentry *dentry;
+
+	ax = kmalloc(sizeof(*ax), GFP_KERNEL);
+	if (!ax)
+		return;
+
+	ax->d.type = AUDIT_BPRM_FCAPS;
+	ax->d.next = context->aux;
+	context->aux = (void *)ax;
+
+	dentry = dget(bprm->file->f_dentry);
+	get_vfs_caps_from_disk(dentry, &vcaps);
+	dput(dentry);
+
+	ax->fcap.permitted = vcaps.permitted;
+	ax->fcap.inheritable = vcaps.inheritable;
+	ax->fcap.fE = !!(vcaps.magic_etc & VFS_CAP_FLAGS_EFFECTIVE);
+	ax->fcap_ver = (vcaps.magic_etc & VFS_CAP_REVISION_MASK) >> VFS_CAP_REVISION_SHIFT;
+
+	ax->old_pcap.permitted = *pP;
+	ax->old_pcap.inheritable = current->cap_inheritable;
+	ax->old_pcap.effective = *pE;
+
+	ax->new_pcap.permitted = current->cap_permitted;
+	ax->new_pcap.inheritable = current->cap_inheritable;
+	ax->new_pcap.effective = current->cap_effective;
+}
+
+/**
  * audit_core_dumps - record information about processes that end abnormally
  * @signr: signal value
  *
diff --git a/security/commoncap.c b/security/commoncap.c
index 8bb95ed..0b88160 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>
@@ -373,6 +374,9 @@ int cap_bprm_set_security (struct linux_binprm *bprm)
 
 void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
 {
+	kernel_cap_t pP = current->cap_permitted;
+	kernel_cap_t pE = current->cap_effective;
+
 	if (bprm->e_uid != current->uid || bprm->e_gid != current->gid ||
 	    !cap_issubset(bprm->cap_post_exec_permitted,
 			  current->cap_permitted)) {
@@ -406,7 +410,24 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
 			cap_clear(current->cap_effective);
 	}
 
-	/* AUD: Audit candidate if current->cap_effective is set */
+	/*
+	 * Audit candidate if current->cap_effective is set
+	 *
+	 * We do not bother to audit if 3 things are true:
+	 *   1) cap_effective has all caps
+	 *   2) we are root
+	 *   3) root is supposed to have all caps (SECURE_NOROOT)
+	 * Since this is just a normal root execing a process.
+	 *
+	 * Number 1 above might fail if you don't have a full bset, but I think
+	 * that is interesting information to audit.
+	 */
+	if (!cap_isclear(current->cap_effective)) {
+		if (!cap_issubset(CAP_FULL_SET, current->cap_effective) ||
+		    (bprm->e_uid != 0) || (current->uid != 0) ||
+		    issecure(SECURE_NOROOT))
+			audit_log_bprm_fcaps(bprm, &pP, &pE);
+	}
 
 	current->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
 }


^ permalink raw reply	[flat|nested] 9+ messages in thread

* [PATCH -v3 5/5] AUDIT: emit new record type showing all capset information
  2008-11-07 15:13 [PATCH -v3 1/5] Capabilities: document the order of arguments to cap_issubset Eric Paris
                   ` (2 preceding siblings ...)
  2008-11-07 15:14 ` [PATCH -v3 4/5] AUDIT: collect info when execve results in caps in pE Eric Paris
@ 2008-11-07 15:14 ` Eric Paris
  2008-11-10 14:55   ` Serge E. Hallyn
  2008-11-10 14:28 ` [PATCH -v3 1/5] Capabilities: document the order of arguments to cap_issubset Serge E. Hallyn
  4 siblings, 1 reply; 9+ messages in thread
From: Eric Paris @ 2008-11-07 15:14 UTC (permalink / raw)
  To: linux-kernel, linux-audit; +Cc: sgrubb, morgan, serue, viro

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(1225743140.465:76): arch=c000003e syscall=126 success=yes exit=0 a0=17f2014 a1=17f201c a2=80000000 a3=7fff2ab7f060 items=0 ppid=2160 pid=2223 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(1225743140.465:76): pid=0 cap_pi=ffffffffffffffff cap_pp=ffffffffffffffff cap_pe=ffffffffffffffff

Signed-off-by: Eric Paris <eparis@redhat.com>
---

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

diff --git a/include/linux/audit.h b/include/linux/audit.h
index 8cfb9fe..6fbebac 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 void __audit_log_bprm_fcaps(struct linux_binprm *bprm, kernel_cap_t *pP, kernel_cap_t *pE);
+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)
 {
@@ -526,6 +528,13 @@ static inline void audit_log_bprm_fcaps(struct linux_binprm *bprm, kernel_cap_t
 		__audit_log_bprm_fcaps(bprm, pP, pE);
 }
 
+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
@@ -558,6 +567,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, p, e) do { ; } while (0)
+#define audit_log_capset(pid, e, i, p) ({ 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 b8b5a2a..d1d45b4 100644
--- a/kernel/auditsc.c
+++ b/kernel/auditsc.c
@@ -204,6 +204,12 @@ struct audit_aux_data_bprm_fcaps {
 	struct audit_cap_data	new_pcap;
 };
 
+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];
@@ -1397,6 +1403,14 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
 			audit_log_cap(ab, "new_pe", &axs->new_pcap.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, "cap_pi", &axs->cap.inheritable);
+			audit_log_cap(ab, "cap_pp", &axs->cap.permitted);
+			audit_log_cap(ab, "cap_pe", &axs->cap.effective);
+			break; }
+
 		}
 		audit_log_end(ab);
 	}
@@ -2570,6 +2584,40 @@ void __audit_log_bprm_fcaps(struct linux_binprm *bprm, kernel_cap_t *pP, kernel_
 }
 
 /**
+ * __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..aaa2262 100644
--- a/kernel/capability.c
+++ b/kernel/capability.c
@@ -7,6 +7,7 @@
  * 30 May 2002:	Cleanup, Robert M. Love <rml@tech9.net>
  */
 
+#include <linux/audit.h>
 #include <linux/capability.h>
 #include <linux/mm.h>
 #include <linux/module.h>
@@ -457,6 +458,10 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
 		i++;
 	}
 
+	ret = audit_log_capset(pid, &effective, &inheritable, &permitted);
+	if (ret)
+		return ret;
+
 	if (pid && (pid != task_pid_vnr(current)))
 		ret = do_sys_capset_other_tasks(pid, &effective, &inheritable,
 						&permitted);


^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH -v3 1/5] Capabilities: document the order of arguments to cap_issubset
  2008-11-07 15:13 [PATCH -v3 1/5] Capabilities: document the order of arguments to cap_issubset Eric Paris
                   ` (3 preceding siblings ...)
  2008-11-07 15:14 ` [PATCH -v3 5/5] AUDIT: emit new record type showing all capset information Eric Paris
@ 2008-11-10 14:28 ` Serge E. Hallyn
  4 siblings, 0 replies; 9+ messages in thread
From: Serge E. Hallyn @ 2008-11-10 14:28 UTC (permalink / raw)
  To: Eric Paris; +Cc: linux-kernel, linux-audit, sgrubb, morgan, viro

Quoting Eric Paris (eparis@redhat.com):
> Document the order of arguments for cap_issubset.  It's not instantly clear
> which order the argument should be in.  So give an example.
> 
> Signed-off-by: Eric Paris <eparis@redhat.com>

Acked-by: Serge Hallyn <serue@us.ibm.com>

Thanks, Eric.

-serge

> ---
> 
>  include/linux/capability.h |    7 +++++++
>  1 files changed, 7 insertions(+), 0 deletions(-)
> 
> diff --git a/include/linux/capability.h b/include/linux/capability.h
> index 9d1fe30..9f44150 100644
> --- a/include/linux/capability.h
> +++ b/include/linux/capability.h
> @@ -454,6 +454,13 @@ static inline int cap_isclear(const kernel_cap_t a)
>  	return 1;
>  }
> 
> +/*
> + * Check if "a" is a subset of "set".
> + * return 1 if ALL of the capabilities in "a" are also in "set"
> + *	cap_issubset(0101, 1111) will return 1
> + * return 0 if ANY of the capabilities in "a" are not in "set"
> + *	cap_issubset(1111, 0101) will return 0
> + */
>  static inline int cap_issubset(const kernel_cap_t a, const kernel_cap_t set)
>  {
>  	kernel_cap_t dest;

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH -v3 3/5] AUDIT: output permitted and inheritable fcaps in PATH records
  2008-11-07 15:14 ` [PATCH -v3 3/5] AUDIT: output permitted and inheritable fcaps in PATH records Eric Paris
@ 2008-11-10 14:46   ` Serge E. Hallyn
  0 siblings, 0 replies; 9+ messages in thread
From: Serge E. Hallyn @ 2008-11-10 14:46 UTC (permalink / raw)
  To: Eric Paris; +Cc: linux-kernel, linux-audit, sgrubb, morgan, viro

Quoting Eric Paris (eparis@redhat.com):
> 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(1225741937.363:230): arch=c000003e syscall=59 success=yes exit=0 a0=2119230 a1=210da30 a2=20ee290 a3=8 items=2 ppid=2149 pid=2923 auid=0 uid=0 gid=0 euid=0 suid=0 fsuid=0 egid=0 sgid=0 fsgid=0 tty=pts0 ses=3 comm="ping" exe="/bin/ping" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)
> type=EXECVE msg=audit(1225741937.363:230): argc=2 a0="ping" a1="www.google.com"
> type=CWD msg=audit(1225741937.363:230):  cwd="/root"
> type=PATH msg=audit(1225741937.363:230): item=0 name="/bin/ping" inode=49256 dev=fd:00 mode=0104755 ouid=0 ogid=0 rdev=00:00 obj=system_u:object_r:ping_exec_t:s0 cap_fp=0000000000002000 cap_fi=0000000000002000 cap_fe=1 cap_fver=2
> type=PATH msg=audit(1225741937.363:230): item=1 name=(null) inode=507915 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 <eparis@redhat.com>

Acked-by: Serge Hallyn <serue@us.ibm.com>

...
> +struct audit_cap_data {
> +	kernel_cap_t		permitted;
> +	kernel_cap_t		inheritable;
> +	union {
> +		unsigned int	fE;
> +		kernel_cap_t	effective;
> +	};
> +};

To help future readers, it might be helpful to have a comment here to
explain that fE is used when it describes a file cap, and effective when
it describes a process cap.  Maybe that's obvious enough, I'm not sure.

thanks,
-serge

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH -v3 4/5] AUDIT: collect info when execve results in caps in pE
  2008-11-07 15:14 ` [PATCH -v3 4/5] AUDIT: collect info when execve results in caps in pE Eric Paris
@ 2008-11-10 14:53   ` Serge E. Hallyn
  0 siblings, 0 replies; 9+ messages in thread
From: Serge E. Hallyn @ 2008-11-10 14:53 UTC (permalink / raw)
  To: Eric Paris; +Cc: linux-kernel, linux-audit, sgrubb, morgan, viro

Quoting Eric Paris (eparis@redhat.com):
> Any time fcaps or a setuid app under SECURE_NOROOT is used to result in a
> non-zero pE we will crate a new audit record which contains the entire set
> of known information about the executable in question, fP, fI, fE, fversion
> and includes the process's pE, pI, pP.  Before and after the bprm capability
> are applied.  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(1225742021.015:236): arch=c000003e syscall=59 success=yes exit=0 a0=1457f30 a1=14606b0 a2=1463940 a3=321b770a70 items=2 ppid=2929 pid=2963 auid=0 uid=500 gid=500 euid=500 suid=500 fsuid=500 egid=500 sgid=500 fsgid=500 tty=pts0 ses=3 comm="ping" exe="/bin/ping" subj=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023 key=(null)
> type=UNKNOWN[1321] msg=audit(1225742021.015:236): fver=2 fp=0000000000002000 fi=0000000000000000 fe=1 old_pp=0000000000000000 old_pi=0000000000000000 old_pe=0000000000000000 new_pp=0000000000002000 new_pi=0000000000000000 new_pe=0000000000002000
> type=EXECVE msg=audit(1225742021.015:236): argc=2 a0="ping" a1="127.0.0.1"
> type=CWD msg=audit(1225742021.015:236):  cwd="/home/test"
> type=PATH msg=audit(1225742021.015:236): item=0 name="/bin/ping" inode=49256 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(1225742021.015:236): item=1 name=(null) inode=507915 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 <eparis@redhat.com>

Acked-by: Serge Hallyn <serue@us.ibm.com>

thanks

-serge

> ---
> 
>  include/linux/audit.h |   26 +++++++++++++++++++
>  kernel/auditsc.c      |   68 +++++++++++++++++++++++++++++++++++++++++++++++++
>  security/commoncap.c  |   23 ++++++++++++++++-
>  3 files changed, 116 insertions(+), 1 deletions(-)
> 
> diff --git a/include/linux/audit.h b/include/linux/audit.h
> index 6272a39..8cfb9fe 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,7 @@ 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 void __audit_log_bprm_fcaps(struct linux_binprm *bprm, kernel_cap_t *pP, kernel_cap_t *pE);
> 
>  static inline int audit_ipc_obj(struct kern_ipc_perm *ipcp)
>  {
> @@ -501,6 +503,29 @@ static inline int audit_mq_getsetattr(mqd_t mqdes, struct mq_attr *mqstat)
>  		return __audit_mq_getsetattr(mqdes, mqstat);
>  	return 0;
>  }
> +
> +/*
> + * ieieeeeee, an audit function without a return code!
> + *
> + * This function might fail!  I decided that it didn't matter.  We are too late
> + * to fail the syscall and the information isn't REQUIRED for any purpose.  It's
> + * just nice to have.  We should be able to look at past audit logs to figure
> + * out this process's current cap set along with the fcaps from the PATH record
> + * and use that to come up with the final set.  Yeah, its ugly, but all the info
> + * is still in the audit log.  So I'm not going to bother mentioning we failed
> + * if we couldn't allocate memory.
> + *
> + * If someone changes their mind they could create the aux record earlier and
> + * then search here and use that earlier allocation.  But I don't wanna.
> + *
> + * -Eric
> + */
> +static inline void audit_log_bprm_fcaps(struct linux_binprm *bprm, kernel_cap_t *pP, kernel_cap_t *pE)
> +{
> +	if (unlikely(!audit_dummy_context()))
> +		__audit_log_bprm_fcaps(bprm, pP, pE);
> +}
> +
>  extern int audit_n_rules;
>  extern int audit_signals;
>  #else
> @@ -532,6 +557,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, p, e) 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 f87f31a..b8b5a2a 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	old_pcap;
> +	struct audit_cap_data	new_pcap;
> +};
> +
>  struct audit_tree_refs {
>  	struct audit_tree_refs *next;
>  	struct audit_chunk *c[31];
> @@ -1375,6 +1383,20 @@ 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, "fver=%x", axs->fcap_ver);
> +			audit_log_cap(ab, "fp", &axs->fcap.permitted);
> +			audit_log_cap(ab, "fi", &axs->fcap.inheritable);
> +			audit_log_format(ab, " fe=%d", axs->fcap.fE);
> +			audit_log_cap(ab, "old_pp", &axs->old_pcap.permitted);
> +			audit_log_cap(ab, "old_pi", &axs->old_pcap.inheritable);
> +			audit_log_cap(ab, "old_pe", &axs->old_pcap.effective);
> +			audit_log_cap(ab, "new_pp", &axs->new_pcap.permitted);
> +			audit_log_cap(ab, "new_pi", &axs->new_pcap.inheritable);
> +			audit_log_cap(ab, "new_pe", &axs->new_pcap.effective);
> +			break; }
> +
>  		}
>  		audit_log_end(ab);
>  	}
> @@ -2502,6 +2524,52 @@ 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
> + *
> + * this can fail and we don't care.  See the note in audit.h for
> + * audit_log_bprm_fcaps() for my explaination....
> + *
> + * -Eric
> + */
> +void __audit_log_bprm_fcaps(struct linux_binprm *bprm, kernel_cap_t *pP, kernel_cap_t *pE)
> +{
> +	struct audit_aux_data_bprm_fcaps *ax;
> +	struct audit_context *context = current->audit_context;
> +	struct cpu_vfs_cap_data vcaps;
> +	struct dentry *dentry;
> +
> +	ax = kmalloc(sizeof(*ax), GFP_KERNEL);
> +	if (!ax)
> +		return;
> +
> +	ax->d.type = AUDIT_BPRM_FCAPS;
> +	ax->d.next = context->aux;
> +	context->aux = (void *)ax;
> +
> +	dentry = dget(bprm->file->f_dentry);
> +	get_vfs_caps_from_disk(dentry, &vcaps);
> +	dput(dentry);
> +
> +	ax->fcap.permitted = vcaps.permitted;
> +	ax->fcap.inheritable = vcaps.inheritable;
> +	ax->fcap.fE = !!(vcaps.magic_etc & VFS_CAP_FLAGS_EFFECTIVE);
> +	ax->fcap_ver = (vcaps.magic_etc & VFS_CAP_REVISION_MASK) >> VFS_CAP_REVISION_SHIFT;
> +
> +	ax->old_pcap.permitted = *pP;
> +	ax->old_pcap.inheritable = current->cap_inheritable;
> +	ax->old_pcap.effective = *pE;
> +
> +	ax->new_pcap.permitted = current->cap_permitted;
> +	ax->new_pcap.inheritable = current->cap_inheritable;
> +	ax->new_pcap.effective = current->cap_effective;
> +}
> +
> +/**
>   * audit_core_dumps - record information about processes that end abnormally
>   * @signr: signal value
>   *
> diff --git a/security/commoncap.c b/security/commoncap.c
> index 8bb95ed..0b88160 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>
> @@ -373,6 +374,9 @@ int cap_bprm_set_security (struct linux_binprm *bprm)
> 
>  void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
>  {
> +	kernel_cap_t pP = current->cap_permitted;
> +	kernel_cap_t pE = current->cap_effective;
> +
>  	if (bprm->e_uid != current->uid || bprm->e_gid != current->gid ||
>  	    !cap_issubset(bprm->cap_post_exec_permitted,
>  			  current->cap_permitted)) {
> @@ -406,7 +410,24 @@ void cap_bprm_apply_creds (struct linux_binprm *bprm, int unsafe)
>  			cap_clear(current->cap_effective);
>  	}
> 
> -	/* AUD: Audit candidate if current->cap_effective is set */
> +	/*
> +	 * Audit candidate if current->cap_effective is set
> +	 *
> +	 * We do not bother to audit if 3 things are true:
> +	 *   1) cap_effective has all caps
> +	 *   2) we are root
> +	 *   3) root is supposed to have all caps (SECURE_NOROOT)
> +	 * Since this is just a normal root execing a process.
> +	 *
> +	 * Number 1 above might fail if you don't have a full bset, but I think
> +	 * that is interesting information to audit.
> +	 */
> +	if (!cap_isclear(current->cap_effective)) {
> +		if (!cap_issubset(CAP_FULL_SET, current->cap_effective) ||
> +		    (bprm->e_uid != 0) || (current->uid != 0) ||
> +		    issecure(SECURE_NOROOT))
> +			audit_log_bprm_fcaps(bprm, &pP, &pE);
> +	}
> 
>  	current->securebits &= ~issecure_mask(SECURE_KEEP_CAPS);
>  }

^ permalink raw reply	[flat|nested] 9+ messages in thread

* Re: [PATCH -v3 5/5] AUDIT: emit new record type showing all capset information
  2008-11-07 15:14 ` [PATCH -v3 5/5] AUDIT: emit new record type showing all capset information Eric Paris
@ 2008-11-10 14:55   ` Serge E. Hallyn
  0 siblings, 0 replies; 9+ messages in thread
From: Serge E. Hallyn @ 2008-11-10 14:55 UTC (permalink / raw)
  To: Eric Paris; +Cc: linux-kernel, linux-audit, sgrubb, morgan, viro

Quoting Eric Paris (eparis@redhat.com):
> 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(1225743140.465:76): arch=c000003e syscall=126 success=yes exit=0 a0=17f2014 a1=17f201c a2=80000000 a3=7fff2ab7f060 items=0 ppid=2160 pid=2223 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(1225743140.465:76): pid=0 cap_pi=ffffffffffffffff cap_pp=ffffffffffffffff cap_pe=ffffffffffffffff
> 
> Signed-off-by: Eric Paris <eparis@redhat.com>

Acked-by: Serge Hallyn <serue@us.ibm.com>

> ---
> 
>  include/linux/audit.h |   10 ++++++++++
>  kernel/auditsc.c      |   48 ++++++++++++++++++++++++++++++++++++++++++++++++
>  kernel/capability.c   |    5 +++++
>  3 files changed, 63 insertions(+), 0 deletions(-)
> 
> diff --git a/include/linux/audit.h b/include/linux/audit.h
> index 8cfb9fe..6fbebac 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 void __audit_log_bprm_fcaps(struct linux_binprm *bprm, kernel_cap_t *pP, kernel_cap_t *pE);
> +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)
>  {
> @@ -526,6 +528,13 @@ static inline void audit_log_bprm_fcaps(struct linux_binprm *bprm, kernel_cap_t
>  		__audit_log_bprm_fcaps(bprm, pP, pE);
>  }
> 
> +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
> @@ -558,6 +567,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, p, e) do { ; } while (0)
> +#define audit_log_capset(pid, e, i, p) ({ 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 b8b5a2a..d1d45b4 100644
> --- a/kernel/auditsc.c
> +++ b/kernel/auditsc.c
> @@ -204,6 +204,12 @@ struct audit_aux_data_bprm_fcaps {
>  	struct audit_cap_data	new_pcap;
>  };
> 
> +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];
> @@ -1397,6 +1403,14 @@ static void audit_log_exit(struct audit_context *context, struct task_struct *ts
>  			audit_log_cap(ab, "new_pe", &axs->new_pcap.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, "cap_pi", &axs->cap.inheritable);
> +			audit_log_cap(ab, "cap_pp", &axs->cap.permitted);
> +			audit_log_cap(ab, "cap_pe", &axs->cap.effective);
> +			break; }
> +
>  		}
>  		audit_log_end(ab);
>  	}
> @@ -2570,6 +2584,40 @@ void __audit_log_bprm_fcaps(struct linux_binprm *bprm, kernel_cap_t *pP, kernel_
>  }
> 
>  /**
> + * __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..aaa2262 100644
> --- a/kernel/capability.c
> +++ b/kernel/capability.c
> @@ -7,6 +7,7 @@
>   * 30 May 2002:	Cleanup, Robert M. Love <rml@tech9.net>
>   */
> 
> +#include <linux/audit.h>
>  #include <linux/capability.h>
>  #include <linux/mm.h>
>  #include <linux/module.h>
> @@ -457,6 +458,10 @@ asmlinkage long sys_capset(cap_user_header_t header, const cap_user_data_t data)
>  		i++;
>  	}
> 
> +	ret = audit_log_capset(pid, &effective, &inheritable, &permitted);
> +	if (ret)
> +		return ret;
> +
>  	if (pid && (pid != task_pid_vnr(current)))
>  		ret = do_sys_capset_other_tasks(pid, &effective, &inheritable,
>  						&permitted);

^ permalink raw reply	[flat|nested] 9+ messages in thread

end of thread, other threads:[~2008-11-10 14:56 UTC | newest]

Thread overview: 9+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2008-11-07 15:13 [PATCH -v3 1/5] Capabilities: document the order of arguments to cap_issubset Eric Paris
2008-11-07 15:14 ` [PATCH -v3 2/5] CAPABILITIES: add cpu endian vfs caps structure Eric Paris
2008-11-07 15:14 ` [PATCH -v3 3/5] AUDIT: output permitted and inheritable fcaps in PATH records Eric Paris
2008-11-10 14:46   ` Serge E. Hallyn
2008-11-07 15:14 ` [PATCH -v3 4/5] AUDIT: collect info when execve results in caps in pE Eric Paris
2008-11-10 14:53   ` Serge E. Hallyn
2008-11-07 15:14 ` [PATCH -v3 5/5] AUDIT: emit new record type showing all capset information Eric Paris
2008-11-10 14:55   ` Serge E. Hallyn
2008-11-10 14:28 ` [PATCH -v3 1/5] Capabilities: document the order of arguments to cap_issubset Serge E. Hallyn

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).