[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]

Two netlink patches



Hi,

Is there any objection to my sending the two netlink patches I recently
sent out to lkml?  Just to refresh memory, the one (audit-fix-
permchecks.diff) adds some message length checks and moves audit control
message authorization to netlink message send, while the other (audit-
loginuid.patch) changes the SET_LOGINUID behavior to set loginuid for
the sending process (as expected) rather than whichever process happens
to end up handling the message.

thanks,
-serge
-- 
Serge Hallyn <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-03 08:52:19.104513648 -0600
@@ -518,6 +518,18 @@
 	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;
@@ -1511,6 +1523,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.000000000 -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.000000000 -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)
Index: linux-2.6.9/include/linux/audit.h
===================================================================
--- linux-2.6.9.orig/include/linux/audit.h	2004-12-08 22:31:52.000000000 -0600
+++ linux-2.6.9/include/linux/audit.h	2004-12-09 12:18:55.600110496 -0600
@@ -183,10 +183,14 @@
 					     const char *prefix,
 					     struct dentry *dentry,
 					     struct vfsmount *vfsmnt);
-extern int		    audit_set_rate_limit(int limit);
-extern int		    audit_set_backlog_limit(int limit);
-extern int		    audit_set_enabled(int state);
-extern int		    audit_set_failure(int state);
+extern int		    audit_set_rate_limit(struct task_struct *p,
+						int limit);
+extern int		    audit_set_backlog_limit(struct task_struct *p,
+						int limit);
+extern int		    audit_set_enabled(struct task_struct *p,
+						int state);
+extern int		    audit_set_failure(struct task_struct *p,
+						int state);
 
 				/* Private API (for auditsc.c only) */
 extern void		    audit_send_reply(int pid, int seq, int type,
Index: linux-2.6.9/kernel/audit.c
===================================================================
--- linux-2.6.9.orig/kernel/audit.c	2004-12-08 22:31:52.000000000 -0600
+++ linux-2.6.9/kernel/audit.c	2004-12-09 12:21:16.412703728 -0600
@@ -231,36 +231,36 @@
 
 }
 
-int audit_set_rate_limit(int limit)
+int audit_set_rate_limit(struct task_struct *p, int limit)
 {
 	int old		 = audit_rate_limit;
 	audit_rate_limit = limit;
-	audit_log(current->audit_context, "audit_rate_limit=%d old=%d",
+	audit_log(p->audit_context, "audit_rate_limit=%d old=%d",
 		  audit_rate_limit, old);
 	return old;
 }
 
-int audit_set_backlog_limit(int limit)
+int audit_set_backlog_limit(struct task_struct *p, int limit)
 {
 	int old		 = audit_backlog_limit;
 	audit_backlog_limit = limit;
-	audit_log(current->audit_context, "audit_backlog_limit=%d old=%d",
+	audit_log(p->audit_context, "audit_backlog_limit=%d old=%d",
 		  audit_backlog_limit, old);
 	return old;
 }
 
-int audit_set_enabled(int state)
+int audit_set_enabled(struct task_struct *p, int state)
 {
 	int old		 = audit_enabled;
 	if (state != 0 && state != 1)
 		return -EINVAL;
 	audit_enabled = state;
-	audit_log(current->audit_context, "audit_enabled=%d old=%d",
+	audit_log(p->audit_context, "audit_enabled=%d old=%d",
 		  audit_enabled, old);
 	return old;
 }
 
-int audit_set_failure(int state)
+int audit_set_failure(struct task_struct *p, int state)
 {
 	int old		 = audit_failure;
 	if (state != AUDIT_FAIL_SILENT
@@ -268,7 +268,7 @@
 	    && state != AUDIT_FAIL_PANIC)
 		return -EINVAL;
 	audit_failure = state;
-	audit_log(current->audit_context, "audit_failure=%d old=%d",
+	audit_log(p->audit_context, "audit_failure=%d old=%d",
 		  audit_failure, old);
 	return old;
 }
@@ -308,8 +308,21 @@
 	struct audit_login	*login;
 	int			err = 0;
 	struct audit_buffer	*ab;
+	struct task_struct	*task;
 
 	pid  = NETLINK_CREDS(skb)->pid;
+
+	if (pid < 0)
+		pid = 0;
+
+	read_lock(&tasklist_lock);
+	task = find_task_by_pid(pid);
+	if (task)
+		get_task_struct(task);
+	read_unlock(&tasklist_lock);
+	if (!task)
+		return -ENOENT;
+
 	uid  = NETLINK_CREDS(skb)->uid;
 	seq  = nlh->nlmsg_seq;
 	data = NLMSG_DATA(nlh);
@@ -327,27 +340,29 @@
 				 &status_set, sizeof(status_set));
 		break;
 	case AUDIT_SET:
-		if (nlh->nlmsg_len < sizeof(struct audit_status))
-			return -EINVAL;
+		if (nlh->nlmsg_len < sizeof(struct audit_status)) {
+			err = -EINVAL;
+			goto out;
+		}
 		status_get   = (struct audit_status *)data;
 		if (status_get->mask & AUDIT_STATUS_ENABLED) {
-			err = audit_set_enabled(status_get->enabled);
-			if (err < 0) return err;
+			err = audit_set_enabled(task, status_get->enabled);
+			if (err < 0) goto out;
 		}
 		if (status_get->mask & AUDIT_STATUS_FAILURE) {
-			err = audit_set_failure(status_get->failure);
-			if (err < 0) return err;
+			err = audit_set_failure(task, status_get->failure);
+			if (err < 0) goto out;
 		}
 		if (status_get->mask & AUDIT_STATUS_PID) {
 			int old   = audit_pid;
 			audit_pid = status_get->pid;
-			audit_log(current->audit_context,
+			audit_log(task->audit_context,
 				  "audit_pid=%d old=%d", audit_pid, old);
 		}
 		if (status_get->mask & AUDIT_STATUS_RATE_LIMIT)
-			audit_set_rate_limit(status_get->rate_limit);
+			audit_set_rate_limit(task, status_get->rate_limit);
 		if (status_get->mask & AUDIT_STATUS_BACKLOG_LIMIT)
-			audit_set_backlog_limit(status_get->backlog_limit);
+			audit_set_backlog_limit(task, status_get->backlog_limit);
 		break;
 	case AUDIT_USER:
 		ab = audit_log_start(NULL);
@@ -364,8 +379,10 @@
 		audit_log_end(ab);
 		break;
 	case AUDIT_LOGIN:
-		if (nlh->nlmsg_len < sizeof(struct audit_login))
-			return -EINVAL;
+		if (nlh->nlmsg_len < sizeof(struct audit_login)) {
+			err = -EINVAL;
+			goto out;
+		}
 		login = (struct audit_login *)data;
 		ab = audit_log_start(NULL);
 		if (ab) {
@@ -380,14 +397,16 @@
 			audit_log_end(ab);
 		}
 #ifdef CONFIG_AUDITSYSCALL
-		err = audit_set_loginuid(current->audit_context,
+		err = audit_set_loginuid(task->audit_context,
 					 login->loginuid);
 #endif
 		break;
 	case AUDIT_ADD:
 	case AUDIT_DEL:
-		if (nlh->nlmsg_len < sizeof(struct audit_rule))
-			return -EINVAL;
+		if (nlh->nlmsg_len < sizeof(struct audit_rule)) {
+			err = -EINVAL;
+			goto out;
+		}
 		/* fallthrough */
 	case AUDIT_LIST:
 #ifdef CONFIG_AUDITSYSCALL
@@ -402,6 +421,8 @@
 		break;
 	}
 
+out:
+	put_task_struct(task);
 	return err < 0 ? err : 0;
 }
 

[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]