[patch 008/133] Fix audit control message checks

akpm at osdl.org akpm at osdl.org
Fri Jan 21 05:29:48 UTC 2005


From: "Serge E. Hallyn" <hallyn at CS.WM.EDU>

The audit control messages are sent over netlink.  Permission checks are
done on the process receiving the message, which may not be the same as the
process sending the message.  This patch switches the netlink_send security
hooks to calculate the effective capabilities based on the sender.  Then
audit_receive_msg performs capability checks based on that.

It also introduces the CAP_AUDIT_WRITE and CAP_AUDIT_CONTROL capabilities,
and replaces the previous CAP_SYS_ADMIN checks in audit code with the
appropriate checks.

- Simplified dummy_netlink_send given that dummy now keeps track of
  capabilities.

- Many fixes based on feedback from <linux-audit at redhat.com> list.

- Removed the netlink_msg_type helper function.

- Switch to using CAP_AUDIT_WRITE and CAP_AUDIT_CONTROL.

Signed-off-by: Serge Hallyn <serue at us.ibm.com>
Signed-off-by: Stephen Smalley <sds at epoch.ncsc.mil>
Signed-off-by: Chris Wright <chrisw at osdl.org>
Signed-off-by: Andrew Morton <akpm at osdl.org>
---

 25-akpm/include/linux/capability.h |    4 ++
 25-akpm/kernel/audit.c             |   51 +++++++++++++++++++++++++++++++------
 25-akpm/kernel/auditsc.c           |    2 -
 25-akpm/security/dummy.c           |    5 ---
 25-akpm/security/selinux/hooks.c   |   18 +++++++++----
 5 files changed, 62 insertions(+), 18 deletions(-)

diff -puN include/linux/capability.h~fix-audit-control-message-checks include/linux/capability.h
--- 25/include/linux/capability.h~fix-audit-control-message-checks	2005-01-20 20:55:57.000000000 -0800
+++ 25-akpm/include/linux/capability.h	2005-01-20 20:55:57.575163664 -0800
@@ -284,6 +284,10 @@ typedef __u32 kernel_cap_t;
 
 #define CAP_LEASE            28
 
+#define CAP_AUDIT_WRITE      29
+
+#define CAP_AUDIT_CONTROL    30
+
 #ifdef __KERNEL__
 /* 
  * Bounding set
diff -puN kernel/audit.c~fix-audit-control-message-checks kernel/audit.c
--- 25/kernel/audit.c~fix-audit-control-message-checks	2005-01-20 20:55:57.000000000 -0800
+++ 25-akpm/kernel/audit.c	2005-01-20 20:56:04.000000000 -0800
@@ -300,21 +300,55 @@ nlmsg_failure:			/* Used by NLMSG_PUT */
 		kfree_skb(skb);
 }
 
+/*
+ * Check for appropriate CAP_AUDIT_ capabilities on incoming audit
+ * control messages.
+ */
+static int audit_netlink_ok(kernel_cap_t eff_cap, u16 msg_type)
+{
+	int err = 0;
+
+	switch (msg_type) {
+	case AUDIT_GET:
+	case AUDIT_LIST:
+	case AUDIT_SET:
+	case AUDIT_LOGIN:
+	case AUDIT_ADD:
+	case AUDIT_DEL:
+		if (!cap_raised(eff_cap, CAP_AUDIT_CONTROL))
+			err = -EPERM;
+		break;
+	case AUDIT_USER:
+		if (!cap_raised(eff_cap, CAP_AUDIT_WRITE))
+			err = -EPERM;
+		break;
+	default:  /* bad msg */
+		err = -EINVAL;
+	}
+
+	return err;
+}
+
 static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
 	u32			uid, pid, seq;
 	void			*data;
 	struct audit_status	*status_get, status_set;
 	struct audit_login	*login;
-	int			err = 0;
+	int			err;
 	struct audit_buffer	*ab;
+	u16			msg_type = nlh->nlmsg_type;
+
+	err = audit_netlink_ok(NETLINK_CB(skb).eff_cap, msg_type);
+	if (err)
+		return err;
 
 	pid  = NETLINK_CREDS(skb)->pid;
 	uid  = NETLINK_CREDS(skb)->uid;
 	seq  = nlh->nlmsg_seq;
 	data = NLMSG_DATA(nlh);
 
-	switch (nlh->nlmsg_type) {
+	switch (msg_type) {
 	case AUDIT_GET:
 		status_set.enabled	 = audit_enabled;
 		status_set.failure	 = audit_failure;
@@ -327,8 +361,8 @@ static int audit_receive_msg(struct sk_b
 				 &status_set, sizeof(status_set));
 		break;
 	case AUDIT_SET:
-		if (!capable(CAP_SYS_ADMIN))
-			return -EPERM;
+		if (nlh->nlmsg_len < sizeof(struct audit_status))
+			return -EINVAL;
 		status_get   = (struct audit_status *)data;
 		if (status_get->mask & AUDIT_STATUS_ENABLED) {
 			err = audit_set_enabled(status_get->enabled);
@@ -364,8 +398,8 @@ static int audit_receive_msg(struct sk_b
 		audit_log_end(ab);
 		break;
 	case AUDIT_LOGIN:
-		if (!capable(CAP_SYS_ADMIN))
-			return -EPERM;
+		if (nlh->nlmsg_len < sizeof(struct audit_login))
+			return -EINVAL;
 		login = (struct audit_login *)data;
 		ab = audit_log_start(NULL);
 		if (ab) {
@@ -384,9 +418,12 @@ static int audit_receive_msg(struct sk_b
 					 login->loginuid);
 #endif
 		break;
-	case AUDIT_LIST:
 	case AUDIT_ADD:
 	case AUDIT_DEL:
+		if (nlh->nlmsg_len < sizeof(struct audit_rule))
+			return -EINVAL;
+		/* fallthrough */
+	case AUDIT_LIST:
 #ifdef CONFIG_AUDITSYSCALL
 		err = audit_receive_filter(nlh->nlmsg_type, pid, uid, seq,
 					   data);
diff -puN kernel/auditsc.c~fix-audit-control-message-checks kernel/auditsc.c
--- 25/kernel/auditsc.c~fix-audit-control-message-checks	2005-01-20 20:55:57.000000000 -0800
+++ 25-akpm/kernel/auditsc.c	2005-01-20 20:55:57.000000000 -0800
@@ -250,8 +250,6 @@ int audit_receive_filter(int type, int p
 		audit_send_reply(pid, seq, AUDIT_LIST, 1, 1, NULL, 0);
 		break;
 	case AUDIT_ADD:
-		if (!capable(CAP_SYS_ADMIN))
-			return -EPERM;
 		if (!(entry = kmalloc(sizeof(*entry), GFP_KERNEL)))
 			return -ENOMEM;
 		if (audit_copy_rule(&entry->rule, data)) {
diff -puN security/dummy.c~fix-audit-control-message-checks security/dummy.c
--- 25/security/dummy.c~fix-audit-control-message-checks	2005-01-20 20:55:57.000000000 -0800
+++ 25-akpm/security/dummy.c	2005-01-20 20:55:57.000000000 -0800
@@ -685,10 +685,7 @@ static int dummy_sem_semop (struct sem_a
 
 static int dummy_netlink_send (struct sock *sk, struct sk_buff *skb)
 {
-	if (current->euid == 0)
-		cap_raise (NETLINK_CB (skb).eff_cap, CAP_NET_ADMIN);
-	else
-		NETLINK_CB (skb).eff_cap = 0;
+	NETLINK_CB(skb).eff_cap = current->cap_effective;
 	return 0;
 }
 
diff -puN security/selinux/hooks.c~fix-audit-control-message-checks security/selinux/hooks.c
--- 25/security/selinux/hooks.c~fix-audit-control-message-checks	2005-01-20 20:55:57.000000000 -0800
+++ 25-akpm/security/selinux/hooks.c	2005-01-20 21:29:17.245167616 -0800
@@ -3502,12 +3502,20 @@ static inline int selinux_nlmsg_perm(str
 
 static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)
 {
-	int err = 0;
+	struct task_security_struct *tsec;
+	struct av_decision avd;
+	int err;
 
-	if (capable(CAP_NET_ADMIN))
-		cap_raise (NETLINK_CB (skb).eff_cap, CAP_NET_ADMIN);
-	else
-		NETLINK_CB(skb).eff_cap = 0;
+	err = secondary_ops->netlink_send(sk, skb);
+	if (err)
+		return err;
+
+	tsec = current->security;
+
+	avd.allowed = 0;
+	avc_has_perm_noaudit(tsec->sid, tsec->sid,
+				SECCLASS_CAPABILITY, ~0, &avd);
+	cap_mask(NETLINK_CB(skb).eff_cap, avd.allowed);
 
 	if (policydb_loaded_version >= POLICYDB_VERSION_NLCLASS)
 		err = selinux_nlmsg_perm(sk, skb);
_




More information about the Linux-audit mailing list