diff options
Diffstat (limited to 'security/selinux/hooks.c')
| -rw-r--r-- | security/selinux/hooks.c | 70 | 
1 files changed, 45 insertions, 25 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 0b4e32161b77..4c037c2545c1 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -5842,40 +5842,60 @@ static unsigned int selinux_ipv6_postroute(void *priv,  static int selinux_netlink_send(struct sock *sk, struct sk_buff *skb)  { -	int err = 0; -	u32 perm; +	int rc = 0; +	unsigned int msg_len; +	unsigned int data_len = skb->len; +	unsigned char *data = skb->data;  	struct nlmsghdr *nlh;  	struct sk_security_struct *sksec = sk->sk_security; +	u16 sclass = sksec->sclass; +	u32 perm; -	if (skb->len < NLMSG_HDRLEN) { -		err = -EINVAL; -		goto out; -	} -	nlh = nlmsg_hdr(skb); +	while (data_len >= nlmsg_total_size(0)) { +		nlh = (struct nlmsghdr *)data; + +		/* NOTE: the nlmsg_len field isn't reliably set by some netlink +		 *       users which means we can't reject skb's with bogus +		 *       length fields; our solution is to follow what +		 *       netlink_rcv_skb() does and simply skip processing at +		 *       messages with length fields that are clearly junk +		 */ +		if (nlh->nlmsg_len < NLMSG_HDRLEN || nlh->nlmsg_len > data_len) +			return 0; -	err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm); -	if (err) { -		if (err == -EINVAL) { +		rc = selinux_nlmsg_lookup(sclass, nlh->nlmsg_type, &perm); +		if (rc == 0) { +			rc = sock_has_perm(sk, perm); +			if (rc) +				return rc; +		} else if (rc == -EINVAL) { +			/* -EINVAL is a missing msg/perm mapping */  			pr_warn_ratelimited("SELinux: unrecognized netlink" -			       " message: protocol=%hu nlmsg_type=%hu sclass=%s" -			       " pid=%d comm=%s\n", -			       sk->sk_protocol, nlh->nlmsg_type, -			       secclass_map[sksec->sclass - 1].name, -			       task_pid_nr(current), current->comm); -			if (!enforcing_enabled(&selinux_state) || -			    security_get_allow_unknown(&selinux_state)) -				err = 0; +				" message: protocol=%hu nlmsg_type=%hu sclass=%s" +				" pid=%d comm=%s\n", +				sk->sk_protocol, nlh->nlmsg_type, +				secclass_map[sclass - 1].name, +				task_pid_nr(current), current->comm); +			if (enforcing_enabled(&selinux_state) && +			    !security_get_allow_unknown(&selinux_state)) +				return rc; +			rc = 0; +		} else if (rc == -ENOENT) { +			/* -ENOENT is a missing socket/class mapping, ignore */ +			rc = 0; +		} else { +			return rc;  		} -		/* Ignore */ -		if (err == -ENOENT) -			err = 0; -		goto out; +		/* move to the next message after applying netlink padding */ +		msg_len = NLMSG_ALIGN(nlh->nlmsg_len); +		if (msg_len >= data_len) +			return 0; +		data_len -= msg_len; +		data += msg_len;  	} -	err = sock_has_perm(sk, perm); -out: -	return err; +	return rc;  }  static void ipc_init_security(struct ipc_security_struct *isec, u16 sclass)  | 
