# HG changeset patch
# User Mark Doliner <mark@kingant.net>
# Date 1396939555 25200
#      Mon Apr 07 23:45:55 2014 -0700
# Branch release-2.x.y
# Node ID 6436e14bdb9d997dfd73cc7cea1b300c37fa401d
# Parent  6c4d5b524296264ad20462a89ae02c4db0d965e5
Add bounds checking when parsing emoticon responses in MXit. This fixes
a potential remote crash when parsing a malformed emoticon response.
We'll need to get a CVE ID for this.

Discovered by Yves Younan and Richard Johnson of Sourcefire VRT. Thanks
to Sourcefire VRT for finding this and reporting it to us!

Index: pidgin-2.6.6/libpurple/protocols/mxit/markup.c
===================================================================
--- pidgin-2.6.6.orig/libpurple/protocols/mxit/markup.c
+++ pidgin-2.6.6/libpurple/protocols/mxit/markup.c
@@ -152,16 +152,22 @@ void mxit_add_html_link( struct RXMsgDat
  * Extract an ASN.1 formatted length field from the data.
  *
  *  @param data				The source data
+ *  @param data_len                     Length of data
  *  @param size				The extracted length
  *  @return					The number of bytes extracted
  */
-static unsigned int asn_getlength( const char* data, int* size )
+static unsigned int asn_getlength( const char* data, gsize data_len, int* size )
 {
 	unsigned int	len		= 0;
 	unsigned char	bytes;
 	unsigned char	byte;
 	int				i;
 
+	if ( data_len < 1 ) {
+		/* missing first byte! */
+		return -1;
+	}
+
 	/* first byte specifies the number of bytes in the length */
 	bytes = ( data[0] & ~0x80 );
 	if ( bytes > sizeof( unsigned int ) ) {
@@ -170,6 +176,11 @@ static unsigned int asn_getlength( const
 	}
 	data++;
 
+	if ( data_len - 1 < bytes ) {
+		/* missing length! */
+		return -1;
+	}
+
 	/* parse out the actual length */
 	for ( i = 0; i < bytes; i++ ) {
 		byte = data[i];
@@ -186,15 +197,21 @@ static unsigned int asn_getlength( const
  * Extract an ASN.1 formatted UTF-8 string field from the data.
  *
  *  @param data				The source data
+ *  @param data_len			Length of data
  *  @param type				Expected type of string
  *  @param utf8				The extracted string.  Must be deallocated by caller.
  *  @return					The number of bytes extracted
  */
-static int asn_getUtf8( const char* data, char type, char** utf8 )
+static int asn_getUtf8( const char* data, gsize data_len, char type, char** utf8 )
 {
 	unsigned int len;
 	gchar *out_str;
 
+	if ( data_len < 2 ) {
+		/* missing type or length! */
+		return -1;
+	}
+
 	/* validate the field type [1 byte] */
 	if ( data[0] != type ) {
 		/* this is not a utf-8 string! */
@@ -203,6 +220,10 @@ static int asn_getUtf8( const char* data
 	}
 
 	len = (uint8_t)data[1]; /* length field [1 byte] */
+	if ( data_len - 2 < len ) {
+		/* not enough bytes left in data! */
+		return -1;
+	}
 	out_str = g_malloc(len + 1);
 	if (out_str == NULL) {
 		purple_debug_fatal(MXIT_PLUGIN_ID, "asn_getUtf8: out of memory");
@@ -499,7 +520,7 @@ static void emoticon_returned( PurpleUti
 	pos = 0;
 
 	/* validate the binary data received from the wapsite */
-	if ( memcmp( MXIT_FRAME_MAGIC, &data[pos], strlen( MXIT_FRAME_MAGIC ) ) != 0 ) {
+	if ( len - pos < strlen( MXIT_FRAME_MAGIC ) || memcmp( MXIT_FRAME_MAGIC, &data[pos], strlen( MXIT_FRAME_MAGIC ) ) != 0 ) {
 		/* bad data, magic constant is wrong */
 		purple_debug_error( MXIT_PLUGIN_ID, "Invalid emoticon received from wapsite (bad magic)\n" );
 		goto done;
@@ -515,7 +536,7 @@ static void emoticon_returned( PurpleUti
 	pos++;
 
 	/* get the data length */
-	res = asn_getlength( &data[pos], &em_size );
+	res = asn_getlength( &data[pos], len - pos, &em_size );
 	if ( res <= 0 ) {
 		/* bad frame length */
 		purple_debug_error( MXIT_PLUGIN_ID, "Invalid emoticon received from wapsite (bad frame length)\n" );
@@ -527,7 +548,7 @@ static void emoticon_returned( PurpleUti
 #endif
 
 	/* utf-8 (emoticon name) */
-	res = asn_getUtf8( &data[pos], 0x0C, &str );
+	res = asn_getUtf8( &data[pos], len - pos, 0x0C, &str );
 	if ( res <= 0 ) {
 		/* bad utf-8 string */
 		purple_debug_error( MXIT_PLUGIN_ID, "Invalid emoticon received from wapsite (bad name string)\n" );
@@ -541,7 +562,7 @@ static void emoticon_returned( PurpleUti
 	str = NULL;
 
 	/* utf-8 (emoticon shortcut) */
-	res = asn_getUtf8( &data[pos], 0x81, &str );
+	res = asn_getUtf8( &data[pos], len - pos, 0x81, &str );
 	if ( res <= 0 ) {
 		/* bad utf-8 string */
 		purple_debug_error( MXIT_PLUGIN_ID, "Invalid emoticon received from wapsite (bad shortcut string)\n" );
@@ -554,7 +575,7 @@ static void emoticon_returned( PurpleUti
 	em_id = str;
 
 	/* validate the image data type */
-	if ( data[pos] != '\x82' ) {
+	if ( len - pos < 1 || data[pos] != '\x82' ) {
 		/* bad frame desc */
 		purple_debug_error( MXIT_PLUGIN_ID, "Invalid emoticon received from wapsite (bad data type)\n" );
 		g_free( em_id );
@@ -563,7 +584,7 @@ static void emoticon_returned( PurpleUti
 	pos++;
 
 	/* get the data length */
-	res = asn_getlength( &data[pos], &em_size );
+	res = asn_getlength( &data[pos], len - pos, &em_size );
 	if ( res <= 0 ) {
 		/* bad frame length */
 		purple_debug_error( MXIT_PLUGIN_ID, "Invalid emoticon received from wapsite (bad data length)\n" );
@@ -580,6 +601,13 @@ static void emoticon_returned( PurpleUti
 		goto done;
 	}
 
+	if ( len - pos < em_size ) {
+		/* not enough bytes left in data! */
+		purple_debug_error( MXIT_PLUGIN_ID, "Invalid emoticon received from wapsite (data length too long)\n");
+		g_free( em_id );
+		goto done;
+	}
+
 	/* make a copy of the data */
 	em_data = g_malloc( em_size );
 	memcpy( em_data, &data[pos], em_size );
