LWN.net Logo

Audit control kernel patch

From:  Serge Hallyn <serue@us.ibm.com>
To:  Steve Grubb <sgrubb@redhat.com>, Stephen Smalley <sds@epoch.ncsc.mil>, Chris Wright <chrisw@osdl.org>
Subject:  Audit control kernel patch
Date:  Thu, 02 Dec 2004 18:25:56 -0600
Cc:  LSM Mailing List <linux-security-module@wirex.com>
Archive-link:  Article, Thread

Hi,

Attached is a slight modification of a patch I sent out a while ago.  In
the current (at least up to 2.6.10-rc2-bk13) audit code, permission for
things like creating an AUDIT_ADD message are checked at the netlink
message receive.  Stephen Smalley had pointed out that since netlink is
asynchronous, it is possible to end up checking the permissions of the
wrong process.

This patch moves the permission checks to the netlink send side.  The
netlink_get_msgtype function prototyped in include/linux/netlink.h can
be used during security_netlink_send by any security module to implement
different checks, which presumably should be enough to implement an
actual audit role.  The patch also adds some message length checks which
seemed lacking.  Please let me know if I'm wrong about those.

thanks,
-serge
-- 
=======================================================
Serge Hallyn
Security Software Engineer, IBM Linux Technology Center
serue@us.ibm.com

Index: linux-2.6.9/include/linux/netlink.h
===================================================================
--- linux-2.6.9.orig/include/linux/netlink.h	2004-12-02 16:08:23.000000000
-0600
+++ linux-2.6.9/include/linux/netlink.h	2004-12-02 16:14:17.000000000 -0600
@@ -120,6 +120,7 @@
 extern void netlink_detach(int unit);
 extern int netlink_post(int unit, struct sk_buff *skb);
 extern struct sock *netlink_kernel_create(int unit, void (*input)(struct sock *sk, int
len));
+extern int netlink_get_msgtype(struct sk_buff *skb);
 extern void netlink_ack(struct sk_buff *in_skb, struct nlmsghdr *nlh, int
err);
 extern int netlink_unicast(struct sock *ssk, struct sk_buff *skb, __u32 pid, int
nonblock);
 extern int netlink_broadcast(struct sock *ssk, struct sk_buff *skb, __u32
pid,
Index: linux-2.6.9/kernel/audit.c
===================================================================
--- linux-2.6.9.orig/kernel/audit.c	2004-12-02 16:02:22.000000000 -0600
+++ linux-2.6.9/kernel/audit.c	2004-12-02 17:44:06.000000000 -0600
@@ -327,8 +327,8 @@
 				 &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 +364,8 @@
 		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 +384,12 @@
 					 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);
Index: linux-2.6.9/kernel/auditsc.c
===================================================================
--- linux-2.6.9.orig/kernel/auditsc.c	2004-12-02 16:02:22.000000000 -0600
+++ linux-2.6.9/kernel/auditsc.c	2004-12-02 16:14:17.000000000 -0600
@@ -250,8 +250,6 @@
 		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)) {
Index: linux-2.6.9/net/netlink/af_netlink.c
===================================================================
--- linux-2.6.9.orig/net/netlink/af_netlink.c	2004-12-02 16:09:27.000000000
-0600
+++ linux-2.6.9/net/netlink/af_netlink.c	2004-12-02 16:14:17.000000000 -0600
@@ -518,6 +518,15 @@
 	return err;
 }
 
+int netlink_get_msgtype(struct sk_buff *skb)
+{
+	struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data;
+
+	if (nlh->nlmsg_len < sizeof(*nlh) || skb->len < nlh->nlmsg_len)
+		return -EINVAL;
+	return nlh->nlmsg_type;
+}
+
 static int netlink_getname(struct socket *sock, struct sockaddr *addr, int *addr_len, int
peer)
 {
 	struct sock *sk = sock->sk;
@@ -1511,6 +1520,7 @@
 
 MODULE_ALIAS_NETPROTO(PF_NETLINK);
 
+EXPORT_SYMBOL(netlink_get_msgtype);
 EXPORT_SYMBOL(netlink_ack);
 EXPORT_SYMBOL(netlink_broadcast);
 EXPORT_SYMBOL(netlink_dump_start);
Index: linux-2.6.9/security/commoncap.c
===================================================================
--- linux-2.6.9.orig/security/commoncap.c	2004-12-02 16:08:24.000000000 -0600
+++ linux-2.6.9/security/commoncap.c	2004-12-02 18:13:34.150122064 -0600
@@ -23,11 +23,41 @@
 #include <linux/ptrace.h>
 #include <linux/xattr.h>
 #include <linux/hugetlb.h>
+#include <net/sock.h>
+
+static int cap_netlink_audit_check (struct sk_buff *skb)
+{
+	int msgtype = netlink_get_msgtype(skb);
+
+	switch(msgtype) {
+		case 0:  /* not an audit msg */
+
+		case AUDIT_LIST:
+			return 0;
+
+		case AUDIT_SET:
+		case AUDIT_USER:
+		case AUDIT_LOGIN:
+
+		case AUDIT_ADD:
+		case AUDIT_DEL:
+			if (!capable(CAP_SYS_ADMIN))
+				return -EPERM;
+			return 0;
+
+		default:  /* permission denied: bad msg */
+			return msgtype;
+	}
+}
+
+EXPORT_SYMBOL(cap_netlink_audit_check);
 
 int cap_netlink_send(struct sock *sk, struct sk_buff *skb)
 {
 	NETLINK_CB(skb).eff_cap = current->cap_effective;
-	return 0;
+	if (sk->sk_protocol != NETLINK_AUDIT)
+		return 0;
+	return cap_netlink_audit_check(skb);
 }
 
 EXPORT_SYMBOL(cap_netlink_send);
Index: linux-2.6.9/security/dummy.c
===================================================================
--- linux-2.6.9.orig/security/dummy.c	2004-12-02 16:08:24.000000000 -0600
+++ linux-2.6.9/security/dummy.c	2004-12-02 18:08:52.966868416 -0600
@@ -726,13 +726,40 @@
 	return 0;
 }
 
+static int dummy_netlink_audit_check (struct sk_buff *skb)
+{
+	int msgtype = netlink_get_msgtype(skb);
+
+	switch(msgtype) {
+		case 0:  /* not an audit msg */
+
+		case AUDIT_LIST:
+			return 0;
+
+		case AUDIT_SET:
+		case AUDIT_USER:
+		case AUDIT_LOGIN:
+
+		case AUDIT_ADD:
+		case AUDIT_DEL:
+			if (!capable(CAP_SYS_ADMIN))
+				return -EPERM;
+			return 0;
+
+		default:  /* permission denied: bad msg */
+			return msgtype;
+	}
+}
+
 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;
-	return 0;
+	if (sk->sk_protocol != NETLINK_AUDIT)
+		return 0;
+	return dummy_netlink_audit_check(skb);
 }
 
 static int dummy_netlink_recv (struct sk_buff *skb)


Copyright © 2004, Eklektix, Inc.
Comments and public postings are copyrighted by their creators.
Linux is a registered trademark of Linus Torvalds