Blame SOURCES/freeradius-FR-GV-301-handle-malformed-WiMAX-attributes.patch

5fa452
From c4685b85f61f28ea0d5cf4e41cb8feaa5193d9dd Mon Sep 17 00:00:00 2001
5fa452
From: "Alan T. DeKok" <aland@freeradius.org>
5fa452
Date: Tue, 27 Jun 2017 21:49:20 -0400
5fa452
Subject: [PATCH] FR-GV-301 - handle malformed WiMAX attributes
5fa452
5fa452
---
5fa452
 src/lib/radius.c         | 177 ++++++++++++++++++++++++++++++++++-------------
5fa452
 src/tests/unit/wimax.txt |  12 ++++
5fa452
 2 files changed, 142 insertions(+), 47 deletions(-)
5fa452
5fa452
diff --git a/src/lib/radius.c b/src/lib/radius.c
5fa452
index 7114e1650..ea4cbf06b 100644
5fa452
--- a/src/lib/radius.c
5fa452
+++ b/src/lib/radius.c
5fa452
@@ -3229,7 +3229,7 @@ static ssize_t data2vp_extended(TALLOC_CTX *ctx, RADIUS_PACKET *packet,
5fa452
 	return end - data;
5fa452
 }
5fa452
 
5fa452
-/** Convert a Vendor-Specific WIMAX to vps
5fa452
+/** Convert a Vendor-Specific WIMAX to VPs
5fa452
  *
5fa452
  * @note Called ONLY for Vendor-Specific
5fa452
  */
5fa452
@@ -3241,25 +3241,54 @@ static ssize_t data2vp_wimax(TALLOC_CTX *ctx,
5fa452
 			     VALUE_PAIR **pvp)
5fa452
 {
5fa452
 	ssize_t rcode;
5fa452
-	size_t fraglen;
5fa452
-	bool last_frag;
5fa452
+	size_t wimax_len;
5fa452
+	bool more;
5fa452
 	uint8_t *head, *tail;
5fa452
-	uint8_t const *frag, *end;
5fa452
+	uint8_t const *attr, *end;
5fa452
 	DICT_ATTR const *child;
5fa452
 
5fa452
-	if (attrlen < 8) return -1;
5fa452
+	/*
5fa452
+	 *	data = VID VID VID VID WiMAX-Attr WimAX-Len Continuation ...
5fa452
+	 */
5fa452
+
5fa452
+	/*
5fa452
+	 *	Not enough room for WiMAX Vendor + Wimax attr + length
5fa452
+	 *	+ continuation, it's a bad attribute.
5fa452
+	 */
5fa452
+	if (attrlen < 8) {
5fa452
+	raw:		
5fa452
+		/*
5fa452
+		 *	It's not a Vendor-Specific, it's unknown...
5fa452
+		 */
5fa452
+		child = dict_unknown_afrom_fields(ctx, PW_VENDOR_SPECIFIC, 0);
5fa452
+		if (!child) {
5fa452
+			fr_strerror_printf("Internal sanity check %d", __LINE__);
5fa452
+			return -1;
5fa452
+		}
5fa452
+
5fa452
+		rcode = data2vp(ctx, packet, original, secret, child,
5fa452
+				data, attrlen, attrlen, pvp);
5fa452
+		if (rcode < 0) return rcode;
5fa452
+		return attrlen;
5fa452
+	}
5fa452
 
5fa452
-	if (((size_t) (data[5] + 4)) != attrlen) return -1;
5fa452
+	if (data[5] < 3) goto raw;		/* WiMAX-Length is too small */
5fa452
 
5fa452
 	child = dict_attrbyvalue(data[4], vendor);
5fa452
-	if (!child) return -1;
5fa452
+	if (!child) goto raw;
5fa452
 
5fa452
+	/*
5fa452
+	 *	No continued data, just decode the attribute in place.
5fa452
+	 */
5fa452
 	if ((data[6] & 0x80) == 0) {
5fa452
+		if ((data[5] + 4) != attrlen) goto raw; /* WiMAX attribute doesn't fill Vendor-Specific */
5fa452
+
5fa452
 		rcode = data2vp(ctx, packet, original, secret, child,
5fa452
 				data + 7, data[5] - 3, data[5] - 3,
5fa452
 				pvp);
5fa452
-		if (rcode < 0) return -1;
5fa452
-		return 7 + rcode;
5fa452
+
5fa452
+		if ((rcode < 0) || (((size_t) rcode + 7) != attrlen)) goto raw; /* didn't decode all of the data */
5fa452
+		return attrlen;
5fa452
 	}
5fa452
 
5fa452
 	/*
5fa452
@@ -3268,61 +3297,115 @@ static ssize_t data2vp_wimax(TALLOC_CTX *ctx,
5fa452
 	 *	MUST be all of the same VSA, WiMAX, and WiMAX-attr.
5fa452
 	 *
5fa452
 	 *	The first fragment doesn't have a RADIUS attribute
5fa452
-	 *	header, so it needs to be treated a little special.
5fa452
+	 *	header.
5fa452
 	 */
5fa452
-	fraglen = data[5] - 3;
5fa452
-	frag = data + attrlen;
5fa452
+	wimax_len = 0;
5fa452
+	attr = data + 4;
5fa452
 	end = data + packetlen;
5fa452
-	last_frag = false;
5fa452
 
5fa452
-	while (frag < end) {
5fa452
-		if (last_frag ||
5fa452
-		    (frag[0] != PW_VENDOR_SPECIFIC) ||
5fa452
-		    (frag[1] < 9) ||		       /* too short for wimax */
5fa452
-		    ((frag + frag[1]) > end) ||		/* overflow */
5fa452
-		    (memcmp(frag + 2, data, 4) != 0) || /* not wimax */
5fa452
-		    (frag[6] != data[4]) || /* not the same wimax attr */
5fa452
-		    ((frag[7] + 6) != frag[1])) { /* doesn't fill the attr */
5fa452
-			end = frag;
5fa452
-			break;
5fa452
-		}
5fa452
+	while (attr < end) {
5fa452
+		/*
5fa452
+		 *	Not enough room for Attribute + length +
5fa452
+		 *	continuation, it's bad.
5fa452
+		 */
5fa452
+		if ((end - attr) < 3) goto raw;
5fa452
 
5fa452
-		last_frag = ((frag[8] & 0x80) == 0);
5fa452
+		/*
5fa452
+		 *	Must have non-zero data in the attribute.
5fa452
+		 */
5fa452
+		if (attr[1] <= 3) goto raw;
5fa452
 
5fa452
-		fraglen += frag[7] - 3;
5fa452
-		frag += frag[1];
5fa452
-	}
5fa452
+		/*
5fa452
+		 *	If the WiMAX attribute overflows the packet,
5fa452
+		 *	it's bad.
5fa452
+		 */
5fa452
+		if ((attr + attr[1]) > end) goto raw;
5fa452
 
5fa452
-	head = tail = malloc(fraglen);
5fa452
-	if (!head) return -1;
5fa452
+		/*
5fa452
+		 *	Check the continuation flag.
5fa452
+		 */
5fa452
+		more = ((attr[2] & 0x80) != 0);
5fa452
+
5fa452
+		/*
5fa452
+		 *	Or, there's no more data, in which case we
5fa452
+		 *	shorten "end" to finish at this attribute.
5fa452
+		 */
5fa452
+		if (!more) end = attr + attr[1];
5fa452
+
5fa452
+		/*
5fa452
+		 *	There's more data, but we're at the end of the
5fa452
+		 *	packet.  The attribute is malformed!
5fa452
+		 */
5fa452
+		if (more && ((attr + attr[1]) == end)) goto raw;
5fa452
+
5fa452
+		/*
5fa452
+		 *	Add in the length of the data we need to
5fa452
+		 *	concatenate together.
5fa452
+		 */
5fa452
+		wimax_len += attr[1] - 3;
5fa452
+
5fa452
+		/*
5fa452
+		 *	Go to the next attribute, and stop if there's
5fa452
+		 *	no more.
5fa452
+		 */
5fa452
+		attr += attr[1];
5fa452
+		if (!more) break;
5fa452
+
5fa452
+		/*
5fa452
+		 *	data = VID VID VID VID WiMAX-Attr WimAX-Len Continuation ...
5fa452
+		 *
5fa452
+		 *	attr = Vendor-Specific VSA-Length VID VID VID VID WiMAX-Attr WimAX-Len Continuation ...
5fa452
+		 *
5fa452
+		 */
5fa452
+
5fa452
+		/*
5fa452
+		 *	No room for Vendor-Specific + length +
5fa452
+		 *	Vendor(4) + attr + length + continuation + data
5fa452
+		 */
5fa452
+		if ((end - attr) < 9) goto raw;
5fa452
+
5fa452
+		if (attr[0] != PW_VENDOR_SPECIFIC) goto raw;
5fa452
+		if (attr[1] < 9) goto raw;
5fa452
+		if ((attr + attr[1]) > end) goto raw;
5fa452
+		if (memcmp(data, attr + 2, 4) != 0) goto raw; /* not WiMAX Vendor ID */
5fa452
+
5fa452
+		if (attr[1] != (attr[7] + 6)) goto raw; /* WiMAX attr doesn't exactly fill the VSA */
5fa452
+
5fa452
+		if (data[4] != attr[6]) goto raw; /* different WiMAX attribute */
5fa452
+
5fa452
+		/*
5fa452
+		 *	Skip over the Vendor-Specific header, and
5fa452
+		 *	continue with the WiMAX attributes.
5fa452
+		 */
5fa452
+		attr += 6;
5fa452
+	}
5fa452
 
5fa452
 	/*
5fa452
-	 *	And again, but faster and looser.
5fa452
-	 *
5fa452
-	 *	We copy the first fragment, followed by the rest of
5fa452
-	 *	the fragments.
5fa452
+	 *	No data in the WiMAX attribute, make a "raw" one.
5fa452
 	 */
5fa452
-	frag = data;
5fa452
+	if (!wimax_len) goto raw;
5fa452
 
5fa452
-	memcpy(tail, frag + 4 + 3, frag[4 + 1] - 3);
5fa452
-	tail += frag[4 + 1] - 3;
5fa452
-	frag += attrlen;	/* should be frag[1] - 7 */
5fa452
+	head = tail = malloc(wimax_len);
5fa452
+	if (!head) return -1;
5fa452
 
5fa452
 	/*
5fa452
-	 *	frag now points to RADIUS attributes
5fa452
+	 *	Copy the data over, this time trusting the attribute
5fa452
+	 *	contents.
5fa452
 	 */
5fa452
-	do {
5fa452
-		memcpy(tail, frag + 2 + 4 + 3, frag[2 + 4 + 1] - 3);
5fa452
-		tail += frag[2 + 4 + 1] - 3;
5fa452
-		frag += frag[1];
5fa452
-	} while (frag < end);
5fa452
+	attr = data;
5fa452
+	while (attr < end) {
5fa452
+		memcpy(tail, attr + 4 + 3, attr[4 + 1] - 3);
5fa452
+		tail += attr[4 + 1] - 3;
5fa452
+		attr += 4 + attr[4 + 1]; /* skip VID+WiMax header */
5fa452
+		attr += 2;		 /* skip Vendor-Specific header */
5fa452
+	}
5fa452
 
5fa452
-	VP_HEXDUMP("wimax fragments", head, fraglen);
5fa452
+	VP_HEXDUMP("wimax fragments", head, wimax_len);
5fa452
 
5fa452
 	rcode = data2vp(ctx, packet, original, secret, child,
5fa452
-			head, fraglen, fraglen, pvp);
5fa452
+			head, wimax_len, wimax_len, pvp);
5fa452
 	free(head);
5fa452
-	if (rcode < 0) return rcode;
5fa452
+	if (rcode < 0) goto raw;
5fa452
 
5fa452
 	return end - data;
5fa452
 }
5fa452
diff --git a/src/tests/unit/wimax.txt b/src/tests/unit/wimax.txt
5fa452
index 191b37e25..6e373d59a 100644
5fa452
--- a/src/tests/unit/wimax.txt
5fa452
+++ b/src/tests/unit/wimax.txt
5fa452
@@ -7,6 +7,12 @@ data 1a 0e 00 00 60 b5 01 08 00 01 05 31 2e 30
5fa452
 decode -
5fa452
 data WiMAX-Release = "1.0"
5fa452
 
5fa452
+decode 1a 08 00 00 60 b5 01 02
5fa452
+data Attr-26 = 0x000060b50102
5fa452
+
5fa452
+decode 1a 0a 00 00 60 b5 01 04 00 01
5fa452
+data Attr-26.24757.1 = 0x01
5fa452
+
5fa452
 encode WiMAX-Accounting-Capabilities = 1
5fa452
 data 1a 0c 00 00 60 b5 01 06 00 02 03 01
5fa452
 
5fa452
@@ -143,3 +149,9 @@ data 1a ff 00 00 60 b5 01 f9 80 01 ff 45 45 45 45 45 45 45 45 45 45 45 45 45 45
5fa452
 
5fa452
 decode -
5fa452
 data WiMAX-Release = "EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE", WiMAX-Idle-Mode-Notification-Cap = Supported
5fa452
+
5fa452
+#
5fa452
+#  Continuation is set, but there's no continued data.
5fa452
+decode 1a 0b 00 00 60 b5 31 05 80 00 00
5fa452
+data Attr-26 = 0x000060b53105800000
5fa452
+
5fa452
-- 
5fa452
2.13.2
5fa452