diff options
author | Kenneth R Westerback <krw@cvs.openbsd.org> | 2016-02-03 14:48:37 +0000 |
---|---|---|
committer | Kenneth R Westerback <krw@cvs.openbsd.org> | 2016-02-03 14:48:37 +0000 |
commit | 7e871a95369ad244fffbec74bb209cd54b343e0f (patch) | |
tree | 4f35a5afa925b320970726a4db8ce905d76d0d43 /sbin | |
parent | d6dd156a051c0b53bf4854b36e8cff0596194f49 (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.c | 33 |
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 && |