summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorHugo Benichi <hugobenichi@google.com>2016-03-02 13:31:52 +0900
committerAbhisek Devkota <ciwrl@lineageos.org>2017-02-03 23:17:31 +0000
commitef8dca814466776553100c5c51d16f7026eb8c88 (patch)
tree3b31b557b5d2e771e5c9ddd2efb16b7b5b74f2e6
parent012f0762275df7125358e8db352834af60495b43 (diff)
downloadframeworks_base-ef8dca814466776553100c5c51d16f7026eb8c88.zip
frameworks_base-ef8dca814466776553100c5c51d16f7026eb8c88.tar.gz
frameworks_base-ef8dca814466776553100c5c51d16f7026eb8c88.tar.bz2
Do not crash on malformed DHCP packets.
This fixes possible crashes with malformed DHCP packets. It is part of Android Security Bulletin of January 2017 and mitigates CVE CVE-2017-0389. Squash of: Author: Lorenzo Colitti <lorenzo@google.com> Date: Wed Mar 02 13:31:52 2016 +0900 Don't crash if we get a DHCP packet with the wrong port. This should only happen if we get a packet in the small time window between binding the packet socket and programming the BPF filter on it. Bug: 26696823 Change-Id: I481f1bc74bbaeb9646d96e1841d2a69acdb47d62 Author: Hugo Benichi <hugobenichi@google.com> Date: Wed Oct 05 18:33:21 2016 +0900 Catch runtime exceptions when parsing DHCP packets This patch adds a try catch all to DHCP packet parsing so that DhcpClient does not choke on malformed packets, brinding down with it the whole framework. Test: added new unit tests catching the issue fixed in this patch. Bug: 31850211 Change-Id: I3c50a149fed6b2cbc4f40bb4f0e5bb2b56859b44 Author: Hugo Benichi <hugobenichi@google.com> Date: Wed Oct 05 21:07:19 2016 +0900 Reject DHCP packets with no magic cookie This patch adds an explicit check in the DHCP packet parser for rejecting packets without a magic cookie, instead of relying on the top-level try-catch-all in the parser. This allows to add to DHCP error metrics this specific error. It also allows to add two poor man's fuzzing tests that tries to find additional gaps in the DHCP packet parser by - trying to parse all subslices of a valid offer packet. - trying to parse random byte arrays. Test: covered by previously introduced malformed DHCP packet unit tests + additional fuzzing tests. Bug: 31850211 Change-Id: If53c9ba9df78d7604ec018c9d67c237ae59c4833 Change-Id: Ic5a8fa4feb46cca325cb5c47616ee63b22d2e7c8 mh0rst: Backported to cm-13.0.
-rw-r--r--services/net/java/android/net/dhcp/DhcpPacket.java27
-rw-r--r--services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java189
2 files changed, 181 insertions, 35 deletions
diff --git a/services/net/java/android/net/dhcp/DhcpPacket.java b/services/net/java/android/net/dhcp/DhcpPacket.java
index f97df83..820a03d 100644
--- a/services/net/java/android/net/dhcp/DhcpPacket.java
+++ b/services/net/java/android/net/dhcp/DhcpPacket.java
@@ -6,6 +6,7 @@ import android.net.NetworkUtils;
import android.os.Build;
import android.os.SystemProperties;
import android.system.OsConstants;
+import com.android.internal.annotations.VisibleForTesting;
import java.io.UnsupportedEncodingException;
import java.net.Inet4Address;
@@ -13,9 +14,8 @@ import java.net.UnknownHostException;
import java.nio.BufferUnderflowException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
-import java.nio.charset.StandardCharsets;
import java.nio.ShortBuffer;
-
+import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
@@ -701,7 +701,8 @@ abstract class DhcpPacket {
* A subset of the optional parameters are parsed and are stored
* in object fields.
*/
- public static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) throws ParseException
+ @VisibleForTesting
+ static DhcpPacket decodeFullPacket(ByteBuffer packet, int pktType) throws ParseException
{
// bootp parameters
int transactionId;
@@ -809,7 +810,11 @@ abstract class DhcpPacket {
// server-to-server packets, e.g. for relays.
if (!isPacketToOrFromClient(udpSrcPort, udpDstPort) &&
!isPacketServerToServer(udpSrcPort, udpDstPort)) {
- return null;
+ // This should almost never happen because we use SO_ATTACH_FILTER on the packet
+ // socket to drop packets that don't have the right source ports. However, it's
+ // possible that a packet arrives between when the socket is bound and when the
+ // filter is set. http://b/26696823 .
+ throw new ParseException("Unexpected UDP ports %d->%d", udpSrcPort, udpDstPort);
}
}
@@ -860,8 +865,12 @@ abstract class DhcpPacket {
+ 64 // skip server host name (64 chars)
+ 128); // skip boot file name (128 chars)
- int dhcpMagicCookie = packet.getInt();
+ // Ensure this is a DHCP packet with a magic cookie, and not BOOTP. http://b/31850211
+ if (packet.remaining() < 4) {
+ throw new ParseException("DHCP packet without a magic cookie");
+ }
+ int dhcpMagicCookie = packet.getInt();
if (dhcpMagicCookie != DHCP_MAGIC_COOKIE) {
throw new ParseException("Bad magic cookie 0x%08x, should be 0x%08x", dhcpMagicCookie,
DHCP_MAGIC_COOKIE);
@@ -1049,7 +1058,13 @@ abstract class DhcpPacket {
public static DhcpPacket decodeFullPacket(byte[] packet, int length, int pktType)
throws ParseException {
ByteBuffer buffer = ByteBuffer.wrap(packet, 0, length).order(ByteOrder.BIG_ENDIAN);
- return decodeFullPacket(buffer, pktType);
+ try {
+ return decodeFullPacket(buffer, pktType);
+ } catch (ParseException e) {
+ throw e;
+ } catch (Exception e) {
+ throw new ParseException("DHCP parsing error: %s", e.getMessage());
+ }
}
/**
diff --git a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
index 7e60bf1..2639e26 100644
--- a/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
+++ b/services/tests/servicestests/src/android/net/dhcp/DhcpPacketTest.java
@@ -16,23 +16,19 @@
package android.net.dhcp;
-import android.net.NetworkUtils;
import android.net.DhcpResults;
import android.net.LinkAddress;
+import android.net.NetworkUtils;
import android.system.OsConstants;
import android.test.suitebuilder.annotation.SmallTest;
import com.android.internal.util.HexDump;
-
import java.net.Inet4Address;
import java.nio.ByteBuffer;
import java.util.ArrayList;
-
import junit.framework.TestCase;
-import libcore.util.HexEncoding;
import static android.net.dhcp.DhcpPacket.*;
-
public class DhcpPacketTest extends TestCase {
private static Inet4Address SERVER_ADDR = v4Address("192.0.2.1");
@@ -278,7 +274,7 @@ public class DhcpPacketTest extends TestCase {
// TODO: Turn all of these into golden files. This will probably require modifying
// Android.mk appropriately, making this into an AndroidTestCase, and adding code to read
// the golden files from the test APK's assets via mContext.getAssets().
- final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
+ final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
// IP header.
"451001480000000080118849c0a89003c0a89ff7" +
// UDP header.
@@ -297,8 +293,7 @@ public class DhcpPacketTest extends TestCase {
"0000000000000000000000000000000000000000000000000000000000000000" +
// Options
"638253633501023604c0a89003330400001c200104fffff0000304c0a89ffe06080808080808080404" +
- "3a0400000e103b040000189cff00000000000000000000"
- ).toCharArray(), false));
+ "3a0400000e103b040000189cff00000000000000000000"));
DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
assertTrue(offerPacket instanceof DhcpOfferPacket); // Implicitly checks it's non-null.
@@ -309,7 +304,7 @@ public class DhcpPacketTest extends TestCase {
@SmallTest
public void testOffer2() throws Exception {
- final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
+ final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
// IP header.
"450001518d0600004011144dc0a82b01c0a82bf7" +
// UDP header.
@@ -328,8 +323,7 @@ public class DhcpPacketTest extends TestCase {
"0000000000000000000000000000000000000000000000000000000000000000" +
// Options
"638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
- "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff"
- ).toCharArray(), false));
+ "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff"));
assertEquals(337, packet.limit());
DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
@@ -341,8 +335,119 @@ public class DhcpPacketTest extends TestCase {
}
@SmallTest
+ public void testBadIpPacket() throws Exception {
+ final byte[] packet = HexDump.hexStringToByteArray(
+ // IP header.
+ "450001518d0600004011144dc0a82b01c0a82bf7");
+
+ try {
+ DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
+ fail("Dhcp packet parsing should have failed");
+ } catch (DhcpPacket.ParseException expected) {}
+ }
+
+ @SmallTest
+ public void testBadDhcpPacket() throws Exception {
+ final byte[] packet = HexDump.hexStringToByteArray(
+ // IP header.
+ "450001518d0600004011144dc0a82b01c0a82bf7" +
+ // UDP header.
+ "00430044013d9ac7" +
+ // BOOTP header.
+ "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000");
+
+ try {
+ DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
+ fail("Dhcp packet parsing should have failed");
+ } catch (DhcpPacket.ParseException expected) {}
+ }
+
+ @SmallTest
+ public void testBadTruncatedOffer() throws Exception {
+ final byte[] packet = HexDump.hexStringToByteArray(
+ // IP header.
+ "450001518d0600004011144dc0a82b01c0a82bf7" +
+ // UDP header.
+ "00430044013d9ac7" +
+ // BOOTP header.
+ "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
+ // MAC address.
+ "30766ff2a90c00000000000000000000" +
+ // Server name.
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ // File, missing one byte
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "00000000000000000000000000000000000000000000000000000000000000");
+
+ try {
+ DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
+ fail("Dhcp packet parsing should have failed");
+ } catch (DhcpPacket.ParseException expected) {}
+ }
+
+ @SmallTest
+ public void testBadOfferWithoutACookie() throws Exception {
+ final byte[] packet = HexDump.hexStringToByteArray(
+ // IP header.
+ "450001518d0600004011144dc0a82b01c0a82bf7" +
+ // UDP header.
+ "00430044013d9ac7" +
+ // BOOTP header.
+ "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
+ // MAC address.
+ "30766ff2a90c00000000000000000000" +
+ // Server name.
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ // File.
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000"
+ // No options
+ );
+
+ try {
+ DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
+ fail("Dhcp packet parsing should have failed");
+ } catch (DhcpPacket.ParseException expected) {}
+ }
+
+ @SmallTest
+ public void testOfferWithBadCookie() throws Exception {
+ final byte[] packet = HexDump.hexStringToByteArray(
+ // IP header.
+ "450001518d0600004011144dc0a82b01c0a82bf7" +
+ // UDP header.
+ "00430044013d9ac7" +
+ // BOOTP header.
+ "02010600dfc23d1f0002000000000000c0a82bf7c0a82b0100000000" +
+ // MAC address.
+ "30766ff2a90c00000000000000000000" +
+ // Server name.
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ // File.
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ // Bad cookie
+ "DEADBEEF3501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
+ "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff");
+
+ try {
+ DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, packet.length, ENCAP_L3);
+ fail("Dhcp packet parsing should have failed");
+ } catch (DhcpPacket.ParseException expected) {}
+ }
+
+ @SmallTest
public void testBadHwaddrLength() throws Exception {
- final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
+ final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
// IP header.
"450001518d0600004011144dc0a82b01c0a82bf7" +
// UDP header.
@@ -361,8 +466,7 @@ public class DhcpPacketTest extends TestCase {
"0000000000000000000000000000000000000000000000000000000000000000" +
// Options
"638253633501023604c0a82b01330400000e103a04000007083b0400000c4e0104ffffff00" +
- "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff"
- ).toCharArray(), false));
+ "1c04c0a82bff0304c0a82b010604c0a82b012b0f414e44524f49445f4d455445524544ff"));
String expectedClientMac = "30766FF2A90C";
final int hwAddrLenOffset = 20 + 8 + 2;
@@ -419,7 +523,7 @@ public class DhcpPacketTest extends TestCase {
// store any information in the overloaded fields).
//
// For now, we just check that it parses correctly.
- final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
+ final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
// Ethernet header.
"b4cef6000000e80462236e300800" +
// IP header.
@@ -440,8 +544,7 @@ public class DhcpPacketTest extends TestCase {
"0000000000000000000000000000000000000000000000000000000000000000" +
// Options
"638253633501023604010101010104ffff000033040000a8c03401030304ac1101010604ac110101" +
- "0000000000000000000000000000000000000000000000ff000000"
- ).toCharArray(), false));
+ "0000000000000000000000000000000000000000000000ff000000"));
DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
assertTrue(offerPacket instanceof DhcpOfferPacket);
@@ -452,7 +555,7 @@ public class DhcpPacketTest extends TestCase {
@SmallTest
public void testBug2111() throws Exception {
- final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
+ final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
// IP header.
"4500014c00000000ff119beac3eaf3880a3f5d04" +
// UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
@@ -471,8 +574,7 @@ public class DhcpPacketTest extends TestCase {
"0000000000000000000000000000000000000000000000000000000000000000" +
// Options.
"638253633501023604c00002fe33040000bfc60104fffff00003040a3f50010608c0000201c0000202" +
- "0f0f646f6d61696e3132332e636f2e756b0000000000ff00000000"
- ).toCharArray(), false));
+ "0f0f646f6d61696e3132332e636f2e756b0000000000ff00000000"));
DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L3);
assertTrue(offerPacket instanceof DhcpOfferPacket);
@@ -483,7 +585,7 @@ public class DhcpPacketTest extends TestCase {
@SmallTest
public void testBug2136() throws Exception {
- final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
+ final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
// Ethernet header.
"bcf5ac000000d0c7890000000800" +
// IP header.
@@ -504,8 +606,7 @@ public class DhcpPacketTest extends TestCase {
"0000000000000000000000000000000000000000000000000000000000000000" +
// Options.
"6382536335010236040a20ff80330400001c200104fffff00003040a20900106089458413494584135" +
- "0f0b6c616e63732e61632e756b000000000000000000ff00000000"
- ).toCharArray(), false));
+ "0f0b6c616e63732e61632e756b000000000000000000ff00000000"));
DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
assertTrue(offerPacket instanceof DhcpOfferPacket);
@@ -517,7 +618,7 @@ public class DhcpPacketTest extends TestCase {
@SmallTest
public void testUdpServerAnySourcePort() throws Exception {
- final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
+ final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
// Ethernet header.
"9cd917000000001c2e0000000800" +
// IP header.
@@ -539,8 +640,7 @@ public class DhcpPacketTest extends TestCase {
"0000000000000000000000000000000000000000000000000000000000000000" +
// Options.
"6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" +
- "d18180060f0777766d2e6564751c040a0fffffff000000"
- ).toCharArray(), false));
+ "d18180060f0777766d2e6564751c040a0fffffff000000"));
DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
assertTrue(offerPacket instanceof DhcpOfferPacket);
@@ -552,8 +652,40 @@ public class DhcpPacketTest extends TestCase {
}
@SmallTest
+ public void testUdpInvalidDstPort() throws Exception {
+ final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
+ // Ethernet header.
+ "9cd917000000001c2e0000000800" +
+ // IP header.
+ "45a00148000040003d115087d18194fb0a0f7af2" +
+ // UDP header. TODO: fix invalid checksum (due to MAC address obfuscation).
+ // NOTE: The destination port is a non-DHCP port.
+ "0043aaaa01341268" +
+ // BOOTP header.
+ "02010600d628ba8200000000000000000a0f7af2000000000a0fc818" +
+ // MAC address.
+ "9cd91700000000000000000000000000" +
+ // Server name.
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ // File.
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ "0000000000000000000000000000000000000000000000000000000000000000" +
+ // Options.
+ "6382536335010236040a0169fc3304000151800104ffff000003040a0fc817060cd1818003d1819403" +
+ "d18180060f0777766d2e6564751c040a0fffffff000000"));
+
+ try {
+ DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
+ fail("Packet with invalid dst port did not throw ParseException");
+ } catch (ParseException expected) {}
+ }
+
+ @SmallTest
public void testMultipleRouters() throws Exception {
- final ByteBuffer packet = ByteBuffer.wrap(HexEncoding.decode((
+ final ByteBuffer packet = ByteBuffer.wrap(HexDump.hexStringToByteArray(
// Ethernet header.
"fc3d93000000" + "081735000000" + "0800" +
// IP header.
@@ -574,8 +706,7 @@ public class DhcpPacketTest extends TestCase {
"0000000000000000000000000000000000000000000000000000000000000000" +
// Options.
"638253633501023604c0abbd023304000070803a04000038403b04000062700104ffffff00" +
- "0308c0a8bd01ffffff0006080808080808080404ff000000000000"
- ).toCharArray(), false));
+ "0308c0a8bd01ffffff0006080808080808080404ff000000000000"));
DhcpPacket offerPacket = DhcpPacket.decodeFullPacket(packet, ENCAP_L2);
assertTrue(offerPacket instanceof DhcpOfferPacket);