--- 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;