The basic approach seems reasonable to me. The untested patch below
should change SELinux to fit with this approach; it calls the secondary
Thanks. The attached patch includes this patch, but I won't be able to
test the SELinux part until I get back to the office after next week. In
the meantime I'll also look into taking the audit checks out of
selinux_netlink_send() as you mentioned in the other email.
(And by then I'll be able to move back to a more modern kernel too)
-serge
--- linux-2.6.8.1/include/linux/capability.h 2004-08-14 05:55:09.000000000 -0500
+++ linux-2.6.8.1-audit/include/linux/capability.h 2004-12-23 13:19:02.000000000 -0600
@@ -284,6 +284,14 @@ typedef __u32 kernel_cap_t;
#define CAP_LEASE 28
+/* Allow reading of audit subsystem parameters */
+
+#define CAP_AUDIT_READ 29
+
+/* Allow control of audit subsystem */
+
+#define CAP_AUDIT_WRITE 30
+
#ifdef __KERNEL__
/*
* Bounding set
--- linux-2.6.8.1/include/linux/netlink.h 2004-08-14 05:56:00.000000000 -0500
+++ linux-2.6.8.1-audit/include/linux/netlink.h 2004-12-20 03:40:10.000000000 -0600
@@ -118,6 +118,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.8.1/kernel/audit.c 2004-08-14 05:56:24.000000000 -0500
+++ linux-2.6.8.1-audit/kernel/audit.c 2004-12-23 13:30:17.000000000 -0600
@@ -300,15 +300,60 @@ 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:
+ if (!cap_raised(NETLINK_CB (skb).eff_cap,
+ CAP_AUDIT_READ))
+ err = -EPERM;
+ 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_WRITE))
+ 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;
void *data;
struct audit_status *status_get, status_set;
struct audit_login *login;
- int err = 0;
+ int err;
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 +372,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 +409,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 +429,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 +442,7 @@ static int audit_receive_msg(struct sk_b
err = -EOPNOTSUPP;
#endif
break;
- default:
+ default: /* no longer needed... */
err = -EINVAL;
break;
}
--- linux-2.6.8.1/kernel/auditsc.c 2004-08-14 05:55:48.000000000 -0500
+++ linux-2.6.8.1-audit/kernel/auditsc.c 2004-12-20 03:40:10.000000000 -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.8.1/net/netlink/af_netlink.c 2004-08-14 05:55:32.000000000 -0500
+++ linux-2.6.8.1-audit/net/netlink/af_netlink.c 2004-12-20 03:40:11.000000000 -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;
@@ -1218,6 +1230,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_broadcast_deliver);
--- linux-2.6.8.1/security/dummy.c 2004-08-14 05:54:51.000000000 -0500
+++ linux-2.6.8.1-audit/security/dummy.c 2004-12-22 03:01:50.000000000 -0600
@@ -722,10 +722,12 @@ static int dummy_sem_semop (struct sem_a
static int dummy_netlink_send (struct sock *sk, struct sk_buff *skb)
{
+ NETLINK_CB (skb).eff_cap = 0;
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 |= (~0 & ~CAP_TO_MASK(CAP_SETPCAP)
+ & ~CAP_FS_MASK);
+ if (current->fsuid == 0)
+ NETLINK_CB (skb).eff_cap |= CAP_FS_MASK;
return 0;
}
--- linux-2.6.8.1/security/selinux/hooks.c 2004-08-14 05:56:22.000000000 -0500
+++ linux-2.6.8.1-audit/security/selinux/hooks.c 2004-12-22 02:30:41.000000000 -0600
@@ -3585,12 +3585,21 @@ 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;
+ err = avc_has_perm_noaudit(tsec->sid, tsec->sid, SECCLASS_CAPABILITY,
+ ~0, &avd);
+ if (err)
+ return 0;
+
+ cap_mask(NETLINK_CB(skb).eff_cap, avd.allowed);
if (policydb_loaded_version >= POLICYDB_VERSION_NLCLASS)
err = selinux_nlmsg_perm(sk, skb);