summaryrefslogtreecommitdiff
path: root/sbin
diff options
context:
space:
mode:
authorKenneth R Westerback <krw@cvs.openbsd.org>2016-02-03 14:48:37 +0000
committerKenneth R Westerback <krw@cvs.openbsd.org>2016-02-03 14:48:37 +0000
commit7e871a95369ad244fffbec74bb209cd54b343e0f (patch)
tree4f35a5afa925b320970726a4db8ce905d76d0d43 /sbin
parentd6dd156a051c0b53bf4854b36e8cff0596194f49 (diff)
be very careful accepting packets via bpf. First check that the
fixed part of the IP header is completely present before using its header length field. Then use the data in the IP header to ensure the entire IP packet is present. Then check that the entire UDP header is present. Then use the data in the UDP header to ensure all the data it thinks is present is actually present. Started when tj@ and a few others noticed ISC "DHCP CVE-2015-8605: UDP payload length not properly checked". ok sthen@ henning@
Diffstat (limited to 'sbin')
-rw-r--r--sbin/dhclient/packet.c33
1 files changed, 26 insertions, 7 deletions
diff --git a/sbin/dhclient/packet.c b/sbin/dhclient/packet.c
index 38e327456ee..64704d05d7a 100644
--- a/sbin/dhclient/packet.c
+++ b/sbin/dhclient/packet.c
@@ -1,4 +1,4 @@
-/* $OpenBSD: packet.c,v 1.28 2014/10/25 03:18:13 lteo Exp $ */
+/* $OpenBSD: packet.c,v 1.29 2016/02/03 14:48:36 krw Exp $ */
/* Packet assembly code, originally contributed by Archie Cobbs. */
@@ -108,7 +108,7 @@ decode_udp_ip_header(unsigned char *buf, int bufix, struct sockaddr_in *from,
struct ip *ip;
struct udphdr *udp;
unsigned char *data;
- u_int32_t ip_len = (buf[bufix] & 0xf) << 2;
+ u_int32_t ip_len;
u_int32_t sum, usum;
static int ip_packets_seen;
static int ip_packets_bad_checksum;
@@ -118,11 +118,16 @@ decode_udp_ip_header(unsigned char *buf, int bufix, struct sockaddr_in *from,
static int udp_packets_length_overflow;
int len;
+ /* Assure that an entire IP header is within the buffer. */
+ if (sizeof(*ip) > buflen)
+ return (-1);
+ ip_len = (buf[bufix] & 0xf) << 2;
+ if (ip_len > buflen)
+ return (-1);
ip = (struct ip *)(buf + bufix);
- udp = (struct udphdr *)(buf + bufix + ip_len);
+ ip_packets_seen++;
/* Check the IP header checksum - it should be zero. */
- ip_packets_seen++;
if (wrapsum(checksum(buf + bufix, ip_len, 0)) != 0) {
ip_packets_bad_checksum++;
if (ip_packets_seen > 4 && ip_packets_bad_checksum != 0 &&
@@ -134,22 +139,36 @@ decode_udp_ip_header(unsigned char *buf, int bufix, struct sockaddr_in *from,
return (-1);
}
+ memcpy(&from->sin_addr, &ip->ip_src, sizeof(from->sin_addr));
+
#ifdef DEBUG
if (ntohs(ip->ip_len) != buflen)
debug("ip length %hu disagrees with bytes received %d.",
ntohs(ip->ip_len), buflen);
#endif
- memcpy(&from->sin_addr, &ip->ip_src, sizeof(from->sin_addr));
+ /* Assure that the entire IP packet is within the buffer. */
+ if (ntohs(ip->ip_len) > buflen)
+ return (-1);
+
+ /* Assure that the UDP header is within the buffer. */
+ if (ip_len + sizeof(*udp) > buflen)
+ return (-1);
+ udp = (struct udphdr *)(buf + bufix + ip_len);
+ udp_packets_seen++;
+
+ /* Assure that the entire UDP packet is within the buffer. */
+ if (ip_len + ntohs(udp->uh_ulen) > buflen)
+ return (-1);
+ data = buf + bufix + ip_len + sizeof(*udp);
/*
* Compute UDP checksums, including the ``pseudo-header'', the
* UDP header and the data. If the UDP checksum field is zero,
* we're not supposed to do a checksum.
*/
- data = buf + bufix + ip_len + sizeof(*udp);
- len = ntohs(udp->uh_ulen) - sizeof(*udp);
udp_packets_length_checked++;
+ len = ntohs(udp->uh_ulen) - sizeof(*udp);
if ((len < 0) || (len + data > buf + bufix + buflen)) {
udp_packets_length_overflow++;
if (udp_packets_length_checked > 4 &&