Package: mimms
Version: 0.0.9-1
Severity: grave
Justification: user security hole
Tags: security patch

According to the patch attached in this report, it has many possible buffer
overflows.
For example,
- memcpy(buf, data, length) without bounding the limit of "length",
  while "length" depend on the input data incoming from the internet.
- read(s, data, BUF_SIZE) in main(), where BUF_SIZE is much greater than
  sizeof(data) which is only 1024 chars allocated in main(), while
  BUF_SIZE is defined as 1024*128.


-- System Information:
Debian Release: testing/unstable
Architecture: i386 (i686)
Kernel: Linux 2.6.10-5-386
Locale: LANG=C, LC_CTYPE=thai

Versions of packages mimms depends on:
ii  libc6                   2.3.5-1ubuntu12  GNU C Library: Shared libraries an
ii  libgcc1                 1:3.4.2-2ubuntu1 GCC support library
ii  libpopt0                1.7-4            lib for parsing cmdline parameters
ii  libstdc++5              1:3.3.4-9ubuntu5 The GNU Standard C++ Library v3
ii  libuuid1                1.35-6ubuntu1    Universally unique id library

-- no debconf information


__________________________________________________
Do You Yahoo!?
Tired of spam?  Yahoo! Mail has the best spam protection around 
http://mail.yahoo.com 
--- mimms-0.0.9.orig/mimms.cpp	2004-10-22 21:38:33.000000000 +0700
+++ mimms-0.0.9/mimms.cpp	2006-05-13 21:27:57.168095728 +0700
@@ -66,6 +66,9 @@
 int stream_ids[20];
 int output_fh;
 
+// There's currently no use of put_32() that num_bytes depend on input and
+// cause buffer overflow.
+// Use put_32() with care, it can cause buffer overflow.
 static void put_32 (command_t *cmd, uint32_t value) {
   for (int i=0; i<4; i++) {
     cmd->buf[cmd->num_bytes++] = (value >> (8*i)) & 0xff;
@@ -81,6 +84,8 @@
   return value;
 }
 
+#define min(a, b) (a) < (b) ? (a) : (b)
+#define max(a, b) (a) > (b) ? (a) : (b)
 static void send_command (int s, int command, uint32_t switches, 
 			  uint32_t extra, int length,
 			  char *data) {
@@ -107,8 +112,24 @@
   put_32 (&cmd, switches);
   put_32 (&cmd, extra);
 
-  memcpy (&cmd.buf[48], data, length);
-  if (length & 7)
+  // buffer overflow, when the length depend on input, such as, url length
+  //memcpy (&cmd.buf[48], data, length);
+  // Use min() to limit the upperbound length.
+  //memcpy (&cmd.buf[48], data, min(length, sizeof cmd.buf - 48));
+  // But the negative length can also cause buffer overflow, in case size_t is
+  // unsigned and length is casted to size_t.
+  // length can be negative in the line,
+  //   send_command (s, 0x33, num_stream_ids, 
+  //		0xFFFF | stream_ids[0] << 16, 
+  //		(num_stream_ids-1)*6+2 , data);
+  // in main() below, where num_stream_ids is zero.
+  // The solution is to cast length to size_t before min() compare.
+  memcpy (&cmd.buf[48], data,
+	  min((size_t)length, sizeof cmd.buf - 48*(sizeof cmd.buf[0])));
+  if (length & 7 &&
+    // to avoid buffer overflow
+    (48 + length)*(sizeof cmd.buf[0]) + 8 - (length & 7) <= sizeof cmd.buf
+  )
     memset(&cmd.buf[48 + length], 0, 8 - (length & 7));
 
   if (send (s, cmd.buf, len8*8+48, 0) != (len8*8+48)) {
@@ -152,18 +173,37 @@
 
 }
 
-static void string_utf16(char *dest, const char *src, int len) {
+// At this time, dest_size has unit in byte, which specify the length in bytes
+// of the buffer pointed to by dest.
+static void string_utf16(char *dest, size_t dest_size, const char *src, int len) {
   int i;
 
   memset (dest, 0, 1000);
 
-  for (i=0; i<len; i++) {
+  //for (i=0; i<len && /* avoid buffer overflow */ i*2+1 < dest_size; i++)
+  for (i=0;
+       i<len &&
+       /* avoid buffer overflow */
+       // For generic stuff.
+       // This can be buffer overflow at ref_note{string_utf16#1} if
+       // sizeof dest[0] > 1
+       //(i*2+1)*(sizeof dest[0]) < dest_size;
+       // So, advance 1 step
+       (i*2+2)*(sizeof dest[0]) <= dest_size;
+       i++) {
     dest[i*2] = src[i];
-    dest[i*2+1] = 0;
+    dest[i*2+1] = 0; // ref_note{string_utf16#1}
   }
+  //if (i*2+1 >= dest_size) return; // avoid buffer overflow
+  // For generic stuff.
+  // This can be buffer overflow at ref_note{string_utf16#2} if
+  // sizeof dest[0] > 1
+  //if ((i*2+1)*(sizeof dest[0]) >= dest_size) return; // avoid buffer overflow
+  // So, advance 1 step
+  if ((i*2+2)*(sizeof dest[0]) > dest_size) return; // avoid buffer overflow
 
   dest[i*2] = 0;
-  dest[i*2+1] = 0;
+  dest[i*2+1] = 0; // ref_note{string_utf16#2}
 }
 
 static void print_answer (char *data, int len) {
@@ -201,7 +241,10 @@
   while (command == 0x1b) {
     int len;
 
-    len = recv (s, data, BUF_SIZE, 0) ;
+    //len = recv (s, data, BUF_SIZE, 0) ;
+    // For generic stuff
+    //len = recv (s, data, BUF_SIZE*(sizeof data[0]), 0) ;
+    len = recv (s, data, sizeof data, 0) ;
     if (!len) {
       dprintf ("\nalert! eof\n");
       return;
@@ -214,14 +257,41 @@
   }
 }
 
-static int get_data (int s, char *buf, size_t count) {
+// This will continue to recv() data, even though buf_size is reached.
+// Both buf_size and count have unit in byte.
+// To allow buf_size <= 0 to not put any data in buf[]?
+static int get_data (int s, char *buf, size_t/*int*/ buf_size, size_t count) {
 
   ssize_t  len;
   size_t total = 0;
 
+  // buf_size = min(count, buf_size);
+  if (buf_size > count) buf_size = count;
+
   while (total < count) {
 
-    len = recv (s, &buf[total], count-total, 0);
+    char data[1024];
+    //len = recv (s, &buf[total], count-total, 0); // can cause buffer overflow
+    if (total*(sizeof buf[0]) < buf_size)
+      // buf_size is already set to min(count, buf_size), no need to use min()
+      // here.
+      //len = recv (s, &buf[total], min(count-total, buf_size-total), 0);
+      // For the sake of generic programming habit, use total*(sizeof buf[0])
+      // to cover the case that sizeof buf[0] != 1
+      // :)
+      // At this time, this generic consideration just focus on avoiding buffer
+      // overflow only.
+      // It doesn't care if the other program logic is generic or not.
+      // The other program logic, such as, &buf[total], it can rather be
+      // &buf[total/(sizeof buf[0])], to be more generic and logically correct.
+      // But whether it is logically correct or not, this doesn't affect the
+      // buffer overflow, so it is ignored.
+      // This is to make the program secure in generic way, not to make the
+      // program work correctly in generic way :)
+      len = recv (s, &buf[total], buf_size-total*(sizeof buf[0]), 0);
+    else
+      // cast to size_t before min() compare
+      len = recv (s, data, min(size_t(count-total), sizeof data), 0);
 
     if (len>0) {
        dprintf ("[%d/%d]", total, count);
@@ -243,7 +313,18 @@
 
 }
 
-static int get_header (int s, uint8_t *header) {
+// header_size should has the unit, which specify the number of elements of
+// that data type pointed to by header?
+// For example,
+//    uint8_t header[80];
+// should has header_size of (sizeof header)/(sizeof header[0]) rather than
+// sizeof header?  And the get_header() should aware that header_size is the
+// multiple of sizeof header rather than multiple of byte unit.
+// (When thinking in a highly generic and portable way :) )
+//
+// But at this time, header_size has unit in byte, which specify the length in
+// bytes of the buffer pointed to by header.
+static int get_header (int s, uint8_t *header, size_t header_size) {
 
   unsigned char  pre_header[8];
   int            i, header_len;
@@ -252,7 +333,7 @@
 
   while (1) {
 
-    if (!get_data (s, (char *)pre_header, 8)) {
+    if (!get_data (s, (char *)pre_header, sizeof pre_header, 8)) {
       fprintf (stderr,_("pre-header read failed\n"));
       return 0;
     }
@@ -270,7 +351,17 @@
       dprintf (_("asf header packet detected, len=%d\n"),
 	      packet_len);
 
-      if (!get_data (s, (char *)&header[header_len], packet_len)) {
+      if (!get_data (s, (char *)&header[header_len],
+		     // casting negative to size_t may cause buffer overflow
+		     //header_size - header_len,
+		     // So, select non-negative value
+		     //max(header_size - header_len, 0),
+		     // This is equivalent to using max() above.
+		     //header_size > header_len ? header_size - header_len : 0,
+		     // For generic stuff.
+		     header_size > header_len*(sizeof header[0]) ?
+		       header_size - header_len*(sizeof header[0]) : 0,
+		     packet_len)) {
 	fprintf (stderr,_("header data read failed\n"));
 	return 0;
       }
@@ -292,7 +383,7 @@
       int packet_len, command;
       char data[BUF_SIZE];
 
-      if (!get_data (s, (char *)&packet_len, 4)) {
+      if (!get_data (s, (char *)&packet_len, sizeof packet_len, 4)) {
 	fprintf (stderr,_("packet_len read failed\n"));
 	return 0;
       }
@@ -302,7 +393,7 @@
       dprintf (_("command packet detected, len=%d\n"),
 	      packet_len);
       
-      if (!get_data (s, data, packet_len)) {
+      if (!get_data (s, data, sizeof data, packet_len)) {
 	fprintf (stderr,_("command data read failed\n"));
 	return 0;
       }
@@ -373,8 +464,10 @@
 
       dprintf (_("stream object, stream id: %d\n"), stream_id);
 
-      stream_ids[num_stream_ids] = stream_id;
-      num_stream_ids++;
+      if (num_stream_ids < sizeof stream_ids / sizeof stream_ids[0]) {
+        stream_ids[num_stream_ids] = stream_id;
+        num_stream_ids++;
+      }
       
 
       /*
@@ -402,7 +495,7 @@
   int            i;
   char           data[BUF_SIZE];
 
-  if (!get_data (s, (char *)pre_header, 8)) {
+  if (!get_data (s, (char *)pre_header, sizeof pre_header, 8)) {
     fprintf (stderr,_("pre-header read failed\n"));
     return 0;
   }
@@ -420,7 +513,7 @@
     dprintf (_("asf media packet detected, len=%d\n"),
 	    packet_len);
 
-    if (!get_data (s, data, packet_len)) {
+    if (!get_data (s, data, sizeof data, packet_len)) {
       fprintf (stderr,_("media data read failed\n"));
       return 0;
     }
@@ -431,7 +524,7 @@
 
     int packet_len, command;
 
-    if (!get_data (s, (char *)&packet_len, 4)) {
+    if (!get_data (s, (char *)&packet_len, sizeof packet_len, 4)) {
       dprintf (_("packet_len read failed\n"));
       return 0;
     }
@@ -441,7 +534,7 @@
     dprintf (_("command packet detected, len=%d\n"),
 	    packet_len);
 
-    if (!get_data (s, data, packet_len)) {
+    if (!get_data (s, data, sizeof data, packet_len)) {
       fprintf (stderr,_("command data read failed\n"));
       return 0;
     }
@@ -669,32 +762,36 @@
   char uuid_string[37];
   uuid_unparse(client_uuid, uuid_string);
   
-  sprintf (str,
+  snprintf (str, sizeof str,
 	   "\034\003NSPlayer/9.0.0.2800; "
 	   "{%s}; "
 	   "Host: %s", uuid_string, host);
-  string_utf16 (data, str, strlen(str)+2);
+           // long host[] can cause buffer overflow if use sprintf()
+  string_utf16 (data, sizeof data, str, strlen(str)+2);
 
   send_command (s, 1, 0, 0x0004000b, strlen(str) * 2+8, data);
 
-  len = read (s, data, BUF_SIZE) ;
+  len = read (s, data, sizeof data) ;
+  //len = read (s, data, BUF_SIZE) ; // buffer overflow
   if (len)
     print_answer (data, len);
   
   /* cmd2 */
 
-  string_utf16 (&data[8], "\002\000\\\\192.168.0.129\\TCP\\1037\0000", 
+  string_utf16 (&data[8], sizeof data - 8*(sizeof data[0]),
+		"\002\000\\\\192.168.0.129\\TCP\\1037\0000", 
 		28);
   memset (data, 0, 8);
   send_command (s, 2, 0, 0, 28*2+8, data);
 
-  len = read (s, data, BUF_SIZE) ;
+  len = read (s, data, sizeof data) ;
+  //len = read (s, data, BUF_SIZE) ; // buffer overflow
   if (len)
     print_answer (data, len);
 
   /* 0x5 */
 
-  string_utf16 (&data[8], path, strlen(path));
+  string_utf16 (&data[8], sizeof data - 8*(sizeof data[0]), path, strlen(path));
   memset (data, 0, 8);
   send_command (s, 5, 0, 0, strlen(path)*2+12, data);
 
@@ -710,13 +807,17 @@
   num_stream_ids = 0;
   /* get_headers(s, asf_header);  */
 
-  asf_header_len = get_header (s, asf_header);
+  asf_header_len = get_header (s, asf_header, sizeof asf_header);
   packet_length = interp_header (asf_header, asf_header_len);
 
   /* 0x33 */
 
   memset (data, 0, 40);
 
+  // num_stream_ids is limited to size of stream_ids array (in interp_header())
+  // which is 20.
+  // (20-1)*6 == 114 < sizeof data == 1024
+  // No buffer overflow here :)
   for (i=1; i<num_stream_ids; i++) {
     data [ (i-1) * 6 + 2 ] = 0xFF;
     data [ (i-1) * 6 + 3 ] = 0xFF;

Reply via email to