lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [day] [month] [year] [list]
Date:   Wed, 24 Apr 2019 15:07:42 -0400
From:   Willem de Bruijn <willemdebruijn.kernel@...il.com>
To:     netdev@...r.kernel.org
Cc:     davem@...emloft.net, David.Laight@...lab.com, idosch@...sch.org,
        Willem de Bruijn <willemb@...gle.com>
Subject: [PATCH net] packet: validate msg_namelen in send directly

From: Willem de Bruijn <willemb@...gle.com>

Packet sockets in datagram mode take a link layer destination address
in sockaddr_ll.sll_addr. For some device types, address length exceeds
this field size, extending beyond the struct.

This addr argument is then passed to dev_hard_header without length
and assumed to be equal to dev->addr_len. Before commit 99137b7888f4
("packet: validate address length") it was possible to pass a shorter
msg_name and msg_namelen, causing an out of bound read.

After that and commit 6b8d95f1795c4 ("packet: validate address length
if non-zero") send() returns EINVAL if sockaddr_ll.sll_halen is
non-zero but less than dev->addr_len (zero length is allowed because
sockets in SOCK_RAW mode ignore sll_addr).

It turns out that legacy applications require more permissive input.
An otherwise legitimate Ethernet application that sets sll_halen > 0,
< ETH_ALEN sees EINVAL, even though ETH_ALEN fits in sockaddr_ll, so
cannot cause the out of bounds read.

Be more permissive. Ignore sll_halen, which is not used elsewhere.
Directly compare msg_namelen to dev->addr_len.

Fixes: 6b8d95f1795c4 ("packet: validate address length if non-zero")
Signed-off-by: Willem de Bruijn <willemb@...gle.com>
---
 net/packet/af_packet.c | 13 +++++++++----
 1 file changed, 9 insertions(+), 4 deletions(-)

diff --git a/net/packet/af_packet.c b/net/packet/af_packet.c
index 9419c5cf4de5e..9ae30bbd00913 100644
--- a/net/packet/af_packet.c
+++ b/net/packet/af_packet.c
@@ -2616,6 +2616,9 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
 		proto	= po->num;
 		addr	= NULL;
 	} else {
+		const int sa_len = offsetof(struct sockaddr_ll, sll_addr);
+		const short sk_type = po->sk.sk_socket->type;
+
 		err = -EINVAL;
 		if (msg->msg_namelen < sizeof(struct sockaddr_ll))
 			goto out;
@@ -2624,9 +2627,9 @@ static int tpacket_snd(struct packet_sock *po, struct msghdr *msg)
 						sll_addr)))
 			goto out;
 		proto	= saddr->sll_protocol;
-		addr	= saddr->sll_halen ? saddr->sll_addr : NULL;
+		addr	= sk_type == SOCK_DGRAM ? saddr->sll_addr : NULL;
 		dev = dev_get_by_index(sock_net(&po->sk), saddr->sll_ifindex);
-		if (addr && dev && saddr->sll_halen < dev->addr_len)
+		if (addr && dev && msg->msg_namelen < (sa_len + dev->addr_len))
 			goto out_put;
 	}
 
@@ -2818,15 +2821,17 @@ static int packet_snd(struct socket *sock, struct msghdr *msg, size_t len)
 		proto	= po->num;
 		addr	= NULL;
 	} else {
+		const int sa_len = offsetof(struct sockaddr_ll, sll_addr);
+
 		err = -EINVAL;
 		if (msg->msg_namelen < sizeof(struct sockaddr_ll))
 			goto out;
 		if (msg->msg_namelen < (saddr->sll_halen + offsetof(struct sockaddr_ll, sll_addr)))
 			goto out;
 		proto	= saddr->sll_protocol;
-		addr	= saddr->sll_halen ? saddr->sll_addr : NULL;
+		addr	= sock->type == SOCK_DGRAM ? saddr->sll_addr : NULL;
 		dev = dev_get_by_index(sock_net(sk), saddr->sll_ifindex);
-		if (addr && dev && saddr->sll_halen < dev->addr_len)
+		if (addr && dev && msg->msg_namelen < (sa_len + dev->addr_len))
 			goto out_unlock;
 	}
 
-- 
2.21.0.593.g511ec345e18-goog

Powered by blists - more mailing lists