diff options
author | Paul Moore <paul@paul-moore.com> | 2020-04-28 09:59:02 -0400 |
---|---|---|
committer | Ben Hutchings <ben@decadent.org.uk> | 2020-06-11 19:05:39 +0100 |
commit | 9592b90cdc99f58e0674e64037c422ea7e9aa367 (patch) | |
tree | 754df8d4d3b1c25c0630292a8fb77c6b30b3251f | |
parent | 976dc007c4fde824b46cd40d001a17734ce9e333 (diff) | |
download | linux-stable-9592b90cdc99f58e0674e64037c422ea7e9aa367.tar.gz linux-stable-9592b90cdc99f58e0674e64037c422ea7e9aa367.tar.bz2 linux-stable-9592b90cdc99f58e0674e64037c422ea7e9aa367.zip |
selinux: properly handle multiple messages in selinux_netlink_send()
commit fb73974172ffaaf57a7c42f35424d9aece1a5af6 upstream.
Fix the SELinux netlink_send hook to properly handle multiple netlink
messages in a single sk_buff; each message is parsed and subject to
SELinux access control. Prior to this patch, SELinux only inspected
the first message in the sk_buff.
Cc: stable@vger.kernel.org
Reported-by: Dmitry Vyukov <dvyukov@google.com>
Reviewed-by: Stephen Smalley <stephen.smalley.work@gmail.com>
Signed-off-by: Paul Moore <paul@paul-moore.com>
[bwh: Backported to 3.16: adjust context]
Signed-off-by: Ben Hutchings <ben@decadent.org.uk>
-rw-r--r-- | security/selinux/hooks.c | 68 |
1 files changed, 44 insertions, 24 deletions
diff --git a/security/selinux/hooks.c b/security/selinux/hooks.c index 5b8353183cd1..84f4a4e9e2ba 100644 --- a/security/selinux/hooks.c +++ b/security/selinux/hooks.c @@ -4669,39 +4669,59 @@ static int selinux_tun_dev_open(void *security) static int selinux_nlmsg_perm(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; - err = selinux_nlmsg_lookup(sksec->sclass, nlh->nlmsg_type, &perm); - if (err) { - if (err == -EINVAL) { + /* 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; + + rc = selinux_nlmsg_lookup(sclass, nlh->nlmsg_type, &perm); + if (rc == 0) { + rc = sock_has_perm(current, 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" - " pig=%d comm=%s\n", - sk->sk_protocol, nlh->nlmsg_type, - secclass_map[sksec->sclass - 1].name, - task_pid_nr(current), current->comm); - if (!selinux_enforcing || security_get_allow_unknown()) - 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 (selinux_enforcing && !security_get_allow_unknown()) + 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(current, sk, perm); -out: - return err; + return rc; } #ifdef CONFIG_NETFILTER |