[Freeswitch-svn] [commit] r8373 - in freeswitch/trunk/src: . include

Freeswitch SVN anthm at freeswitch.org
Mon May 12 20:17:58 EDT 2008


Author: anthm
Date: Mon May 12 20:17:58 2008
New Revision: 8373

Modified:
   freeswitch/trunk/src/include/switch_stun.h
   freeswitch/trunk/src/switch_rtp.c
   freeswitch/trunk/src/switch_stun.c

Log:
more robust stun packet validation from stkn



Modified: freeswitch/trunk/src/include/switch_stun.h
==============================================================================
--- freeswitch/trunk/src/include/switch_stun.h	(original)
+++ freeswitch/trunk/src/include/switch_stun.h	Mon May 12 20:17:58 2008
@@ -40,6 +40,7 @@
 SWITCH_BEGIN_EXTERN_C
 #define SWITCH_STUN_DEFAULT_PORT 3478
 #define SWITCH_STUN_PACKET_MIN_LEN 20
+#define SWITCH_STUN_ATTRIBUTE_MIN_LEN 8
 	typedef enum {
 	SWITCH_STUN_BINDING_REQUEST = 0x0001,
 	SWITCH_STUN_BINDING_RESPONSE = 0x0101,
@@ -98,13 +99,13 @@
 } switch_stun_type_t;
 
 typedef struct {
-	int16_t type;
-	int16_t length;
+	uint16_t type;
+	uint16_t length;
 	char id[16];
 } switch_stun_packet_header_t;
 
 typedef struct {
-	int16_t type;
+	uint16_t type;
 	uint16_t length;
 	char value[];
 } switch_stun_packet_attribute_t;
@@ -115,10 +116,10 @@
 } switch_stun_packet_t;
 
 typedef struct {
-	int8_t wasted;
-	int8_t family;
-	int16_t port;
-	int32_t address;
+	uint8_t wasted;
+	uint8_t family;
+	uint16_t port;
+	uint32_t address;
 } switch_stun_ip_t;
 
 
@@ -208,6 +209,12 @@
 												   switch_port_t *port, char *stunip, switch_port_t stunport, char **err, switch_memory_pool_t *pool);
 
 
+/*!
+  \brief Obtain the padded length of an attribute's value
+  \param attribute the attribute
+  \return the padded size in bytes
+*/
+#define switch_stun_attribute_padded_length(attribute) ((uint16_t)(attribute->length + (sizeof(uint32_t)-1)) & ~sizeof(uint32_t))
 
 /*!
   \brief set a switch_stun_packet_attribute_t pointer to point at the first attribute in a packet
@@ -221,7 +228,7 @@
   \param attribute the pointer to increment
   \return true or false depending on if there are any more attributes
 */
-#define switch_stun_packet_next_attribute(attribute, end) (attribute && (attribute = (switch_stun_packet_attribute_t *) (attribute->value + attribute->length)) && ((void *)attribute < end) && attribute->length && ((void *)(attribute + attribute->length) < end))
+#define switch_stun_packet_next_attribute(attribute, end) (attribute && (attribute = (switch_stun_packet_attribute_t *) (attribute->value +  switch_stun_attribute_padded_length(attribute))) && ((void *)attribute < end) && attribute->length && ((void *)(attribute +  switch_stun_attribute_padded_length(attribute)) < end))
 
 /*!
   \brief Obtain the correct length in bytes of a stun packet

Modified: freeswitch/trunk/src/switch_rtp.c
==============================================================================
--- freeswitch/trunk/src/switch_rtp.c	(original)
+++ freeswitch/trunk/src/switch_rtp.c	Mon May 12 20:17:58 2008
@@ -261,7 +261,12 @@
 
 	memcpy(buf, data, cpylen);
 	packet = switch_stun_packet_parse(buf, sizeof(buf));
-	end_buf = buf + sizeof(buf);
+	if (!packet) {
+		switch_log_printf(SWITCH_CHANNEL_LOG, SWITCH_LOG_ERROR, "Invalid STUN/ICE packet received\n");
+		goto end;
+	}
+	end_buf = buf + ((sizeof(buf) > packet->header.length) ? packet->header.length : sizeof(buf));
+
 	rtp_session->last_stun = switch_time_now();
 
 	switch_stun_packet_first_attribute(packet, attr);

Modified: freeswitch/trunk/src/switch_stun.c
==============================================================================
--- freeswitch/trunk/src/switch_stun.c	(original)
+++ freeswitch/trunk/src/switch_stun.c	Mon May 12 20:17:58 2008
@@ -113,10 +113,12 @@
 	}
 }
 
+
 SWITCH_DECLARE(switch_stun_packet_t *) switch_stun_packet_parse(uint8_t * buf, uint32_t len)
 {
 	switch_stun_packet_t *packet;
 	switch_stun_packet_attribute_t *attr;
+	int32_t bytes_left = len;
 	void *end_buf = buf + len;
 
 	if (len < SWITCH_STUN_PACKET_MIN_LEN) {
@@ -126,23 +128,181 @@
 	packet = (switch_stun_packet_t *) buf;
 	packet->header.type = ntohs(packet->header.type);
 	packet->header.length = ntohs(packet->header.length);
+	bytes_left -= packet->header.length + 20;
+
+	/*
+	 * Check packet type (RFC3489(bis?) values)
+	 */
+	switch(packet->header.type) {
+	case SWITCH_STUN_BINDING_REQUEST:
+	case SWITCH_STUN_BINDING_RESPONSE:
+	case SWITCH_STUN_BINDING_ERROR_RESPONSE:
+	case SWITCH_STUN_SHARED_SECRET_REQUEST:
+	case SWITCH_STUN_SHARED_SECRET_RESPONSE:
+	case SWITCH_STUN_SHARED_SECRET_ERROR_RESPONSE:
+	case SWITCH_STUN_ALLOCATE_REQUEST:
+	case SWITCH_STUN_ALLOCATE_RESPONSE:
+	case SWITCH_STUN_ALLOCATE_ERROR_RESPONSE:
+	case SWITCH_STUN_SEND_REQUEST:
+	case SWITCH_STUN_SEND_RESPONSE:
+	case SWITCH_STUN_SEND_ERROR_RESPONSE:
+	case SWITCH_STUN_DATA_INDICATION:
+		/* Valid */
+		break;
+
+	default:
+		/* Invalid value */
+		return NULL;
+	}
+
+	/*
+	 * Check for length overflow
+	 */
+	if (bytes_left <= 0) {
+		/* Invalid */
+		return NULL;
+	}
+
+	/*
+	 * No payload?
+	 */
+	if (packet->header.length == 0) {
+		/* Invalid?! */
+		return NULL;
+	}
+
+	/* check if we have enough bytes left for an attribute */
+	if (bytes_left < SWITCH_STUN_ATTRIBUTE_MIN_LEN) {
+		return NULL;
+	}
+
 	switch_stun_packet_first_attribute(packet, attr);
 	do {
 		attr->length = ntohs(attr->length);
 		attr->type = ntohs(attr->type);
-		if (!attr->length) {
-			break;
+		bytes_left -= 4;	/* attribute header consumed */
+
+		if (!attr->length || switch_stun_attribute_padded_length(attr) > bytes_left) {
+			/*
+			 * Note we simply don't "break" here out of the loop anymore because
+			 * we don't want the upper layers to have to deal with attributes without a value
+			 * (or worse: invalid length)
+			 */
+			return NULL;
 		}
+
+		/*
+		 * Handle STUN attributes
+		 */
 		switch (attr->type) {
-		case SWITCH_STUN_ATTR_MAPPED_ADDRESS:
-			if (attr->type) {
+		case SWITCH_STUN_ATTR_MAPPED_ADDRESS:		/* Address, we only care about this one, but parse the others too */
+		case SWITCH_STUN_ATTR_RESPONSE_ADDRESS:
+		case SWITCH_STUN_ATTR_SOURCE_ADDRESS:
+		case SWITCH_STUN_ATTR_CHANGED_ADDRESS:
+		case SWITCH_STUN_ATTR_REFLECTED_FROM:
+		case SWITCH_STUN_ATTR_ALTERNATE_SERVER:
+		case SWITCH_STUN_ATTR_DESTINATION_ADDRESS:
+		case SWITCH_STUN_ATTR_SOURCE_ADDRESS2:
+			{
 				switch_stun_ip_t *ip;
+				uint32_t addr_length = 0;
 				ip = (switch_stun_ip_t *) attr->value;
+
+				switch (ip->family) {
+				case 0x01:		/* IPv4 */
+					addr_length = 4;
+					break;
+
+				case 0x02:		/* IPv6 */
+					addr_length = 16;
+					break;
+
+				default:		/* Invalid */
+					return NULL;
+				}
+
+				/* attribute payload length must be == address length + size of other payload fields (family...) */
+				if (attr->length != addr_length + 4) {
+					/* Invalid */
+					return NULL;
+				}
+
 				ip->port = ntohs(ip->port);
 			}
 			break;
+
+		case SWITCH_STUN_ATTR_CHANGE_REQUEST:		/* UInt32 */
+		case SWITCH_STUN_ATTR_LIFETIME:
+		case SWITCH_STUN_ATTR_BANDWIDTH:
+		case SWITCH_STUN_ATTR_OPTIONS:
+			{
+				uint32_t *val = (uint32_t *) attr->value;
+
+				if (attr->length != sizeof(uint32_t)) {
+					/* Invalid */
+					return NULL;
+				}
+
+				*val = ntohl(*val);	/* should we do this here? */
+			}
+			break;
+
+		case SWITCH_STUN_ATTR_USERNAME:			/* ByteString, multiple of 4 bytes */
+		case SWITCH_STUN_ATTR_PASSWORD:			/* ByteString, multiple of 4 bytes */
+			if (attr->length % 4 != 0) {
+				/* Invalid */
+				return NULL;
+			}
+			break;
+
+		case SWITCH_STUN_ATTR_DATA:			/* ByteString */
+		case SWITCH_STUN_ATTR_ERROR_CODE:		/* ErrorCode */
+		case SWITCH_STUN_ATTR_TRANSPORT_PREFERENCES:	/* TransportPrefs */
+			/*
+			 * No length checking here, since we already checked against the padded length
+			 * before
+			 */
+			break;
+
+		case SWITCH_STUN_ATTR_MESSAGE_INTEGRITY:	/* ByteString, 20 bytes */
+			if (attr->length != 20) {
+				/* Invalid */
+				return NULL;
+			}
+			break;
+
+		case SWITCH_STUN_ATTR_MAGIC_COOKIE:		/* ByteString, 4 bytes */
+			if (attr->length != 4) {
+				/* Invalid */
+				return NULL;
+			}
+			break;
+
+		case SWITCH_STUN_ATTR_UNKNOWN_ATTRIBUTES:	/* UInt16List (= multiple of 2 bytes) */
+			if (attr->length % 2 != 0) {
+				return NULL;
+			}
+			break;
+
+		default:
+			/* Mandatory attribute range? => invalid */
+			if (attr->type <= 0x7FFF) {
+				return NULL;
+			}
+			break;
 		}
-	} while (switch_stun_packet_next_attribute(attr, end_buf));
+		bytes_left -= switch_stun_attribute_padded_length(attr);	/* attribute value consumed, substract padded length */
+
+	} while (bytes_left >= SWITCH_STUN_ATTRIBUTE_MIN_LEN && switch_stun_packet_next_attribute(attr, end_buf));
+
+	if ((packet->header.length + 20) > (len - bytes_left)) {
+		/*
+		 * the packet length is longer than the length of all attributes?
+		 * for now simply decrease the packet size
+		 */
+		packet->header.length = (len - bytes_left) - 20;
+	}
+
 	return packet;
 }
 
@@ -337,9 +497,13 @@
 	switch_socket_close(sock);
 
 	packet = switch_stun_packet_parse(buf, sizeof(buf));
-	end_buf = buf + sizeof(buf);
-	switch_stun_packet_first_attribute(packet, attr);
+	if (!packet) {
+		*err = "Invalid STUN/ICE packet";
+		return SWITCH_STATUS_FALSE;
+	}
+	end_buf = buf + ((sizeof(buf) > packet->header.length) ? packet->header.length : sizeof(buf));
 
+	switch_stun_packet_first_attribute(packet, attr);
 	do {
 		switch (attr->type) {
 		case SWITCH_STUN_ATTR_MAPPED_ADDRESS:



More information about the Freeswitch-svn mailing list