Audit perms check on recv (Re: Two netlink patches)

Serge E. Hallyn serue at us.ibm.com
Tue Dec 21 04:15:14 UTC 2004


The attached patch implements the permissions check the way I think
Chris and Stephen were suggesting.  It does not yet set the netlink
eff_caps in selinux_netlink_send().  Other than that, does this patch
seem reasonable?  Is this preferable to the sender side check?  Do
we want to add some audit read checks, and split CAP_AUDIT into two
or three capabilities?

thanks,
-serge

Quoting Stephen Smalley (sds at epoch.ncsc.mil):
> On Wed, 2004-12-15 at 17:26, Chris Wright wrote:
> > I really dislike duplicating code.  I agree it should be put in a
> > central location.  Does it really need to be broken out into the
> > security framework?  Why not place it in audit itself?
> > 
> > Just a simple helper:
> > 
> > int audit_netlink_ok(struct nlmsghdr *nlh)
> > {
> > 	int err = -EINVAL;
> > 
> > 	if (audit_bad_header(nlh))
> > 		goto out;
> > 
> > 	err = 0;
> > 	switch() {
> > 		ok:
> > 			break;
> > 		capable:
> > 			if (!capable())
> > 				err = -EPERM;
> > 			break;
> > 		default:
> > 			err = -EINVAL;
> > 			break;
> > 	}
> > out:
> > 	return err;
> > }
> > 
> > audit_recieve_msg(skb, nlh)
> > {
> > 	...
> > 	err = audit_netlink_ok(nlh);
> > 	if (err)
> > 		return err;
> > 	...
> > }
> 
> That is still on the receive path, where you can't use capable() because
> it is based on current which isn't necessarily the same as the sender.
> 
> Now, it is true that one could replace the existing capable() checks in
> the audit code with explicit checks of NETLINK_CREDS(skb)->eff_cap. 
> That would allow you to keep the checks in the audit receive-side code. 
> We would need to change selinux_netlink_send to set the entire
> capability set rather than just CAP_NET_ADMIN, but that would be easy to
> do.
> 
> -- 
> Stephen Smalley <sds at epoch.ncsc.mil>
> National Security Agency
> 
> 
-------------- next part --------------
--- linux-2.6.9/include/linux/capability.h	2004-10-18 16:53:44.000000000 -0500
+++ linux-2.6.9-audit-permcheck/include/linux/capability.h	2004-12-20 21:55:01.058388800 -0600
@@ -284,6 +284,10 @@ typedef __u32 kernel_cap_t;
 
 #define CAP_LEASE            28
 
+/* Allow control of audit subsystem */
+
+#define CAP_AUDIT	     29
+
 #ifdef __KERNEL__
 /* 
  * Bounding set
--- linux-2.6.9/include/linux/netlink.h	2004-10-18 16:55:06.000000000 -0500
+++ linux-2.6.9-audit-permcheck/include/linux/netlink.h	2004-12-20 21:55:01.098446400 -0600
@@ -119,6 +119,7 @@ extern int netlink_attach(int unit, int 
 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,
--- linux-2.6.9/kernel/audit.c	2004-10-18 16:55:36.000000000 -0500
+++ linux-2.6.9-audit-permcheck/kernel/audit.c	2004-12-20 21:55:01.168547200 -0600
@@ -300,6 +300,43 @@ nlmsg_failure:			/* Used by NLMSG_PUT */
 		kfree_skb(skb);
 }
 
+/*
+ * This function causes two checks on the incoming audit control
+ * message.  1. netlink_get_msgtype() will perform a length check,
+ * and 2. we check for CAP_AUDIT perms on a message which might
+ * change audit params.
+ */
+int audit_netlink_ok(struct sk_buff *skb)
+{
+	int err = 0;
+	int msgtype;
+	
+	msgtype = netlink_get_msgtype(skb);
+
+	switch(msgtype) {
+		case 0:  /* not an audit msg */
+
+		case AUDIT_GET:
+		case AUDIT_LIST:
+			break;
+
+		case AUDIT_SET:
+		case AUDIT_USER:
+		case AUDIT_LOGIN:
+
+		case AUDIT_ADD:
+		case AUDIT_DEL:
+			if (!cap_raised(NETLINK_CB (skb).eff_cap, CAP_AUDIT))
+				err = -EPERM;
+			break;
+
+		default:  /* permission denied: bad msg */
+			err = -EINVAL;
+	}
+
+	return err;
+}
+
 static int audit_receive_msg(struct sk_buff *skb, struct nlmsghdr *nlh)
 {
 	u32			uid, pid, seq;
@@ -309,6 +346,10 @@ static int audit_receive_msg(struct sk_b
 	int			err = 0;
 	struct audit_buffer	*ab;
 
+	err = audit_netlink_ok(skb);
+	if (err)
+		return err;
+
 	pid  = NETLINK_CREDS(skb)->pid;
 	uid  = NETLINK_CREDS(skb)->uid;
 	seq  = nlh->nlmsg_seq;
@@ -327,8 +368,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 +405,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 +425,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);
@@ -394,7 +438,7 @@ static int audit_receive_msg(struct sk_b
 		err = -EOPNOTSUPP;
 #endif
 		break;
-	default:
+	default:  /* no longer needed... */
 		err = -EINVAL;
 		break;
 	}
--- linux-2.6.9/kernel/auditsc.c	2004-10-18 16:54:54.000000000 -0500
+++ linux-2.6.9-audit-permcheck/kernel/auditsc.c	2004-12-20 21:55:01.218619200 -0600
@@ -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)) {
--- linux-2.6.9/net/netlink/af_netlink.c	2004-10-18 16:54:08.000000000 -0500
+++ linux-2.6.9-audit-permcheck/net/netlink/af_netlink.c	2004-12-20 21:55:01.268691200 -0600
@@ -389,6 +389,18 @@ static int netlink_connect(struct socket
 	return err;
 }
 
+int netlink_get_msgtype(struct sk_buff *skb)
+{
+	struct nlmsghdr *nlh = (struct nlmsghdr *)skb->data;
+
+	if (skb->len < NLMSG_SPACE(0))
+		return -EINVAL;
+
+	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;
@@ -1239,6 +1251,7 @@ MODULE_LICENSE("GPL");
 
 MODULE_ALIAS_NETPROTO(PF_NETLINK);
 
+EXPORT_SYMBOL(netlink_get_msgtype);
 EXPORT_SYMBOL(netlink_ack);
 EXPORT_SYMBOL(netlink_broadcast);
 EXPORT_SYMBOL(netlink_dump_start);
--- linux-2.6.9/security/dummy.c	2004-10-18 16:53:43.000000000 -0500
+++ linux-2.6.9-audit-permcheck/security/dummy.c	2004-12-20 21:55:01.318763200 -0600
@@ -723,7 +723,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);
+		cap_raise (NETLINK_CB (skb).eff_cap, CAP_NET_ADMIN | CAP_AUDIT);
 	else
 		NETLINK_CB (skb).eff_cap = 0;
 	return 0;


More information about the Linux-audit mailing list