From Laurent Rabret:
authorguy <guy@f5534014-38df-0310-8fa8-9805f1628bb7>
Thu, 15 May 2003 06:35:02 +0000 (06:35 +0000)
committerguy <guy@f5534014-38df-0310-8fa8-9805f1628bb7>
Thu, 15 May 2003 06:35:02 +0000 (06:35 +0000)
fix a bug where bad IPv4 and IPv6 prefix lengths could cause a
buffer overflow;

check the checksum in LSP packets.

git-svn-id: http://anonsvn.wireshark.org/wireshark/trunk@7675 f5534014-38df-0310-8fa8-9805f1628bb7

packet-isis-lsp.c
packet-osi.c
packet-osi.h

index 4eafc949a928b794a2fe137bc09dd0afcef7eee6..b75ddf9867efe4943fcb9d846ee1df5070e0e94b 100644 (file)
@@ -1,7 +1,7 @@
 /* packet-isis-lsp.c
  * Routines for decoding isis lsp packets and their CLVs
  *
- * $Id: packet-isis-lsp.c,v 1.42 2003/04/29 16:57:05 guy Exp $
+ * $Id: packet-isis-lsp.c,v 1.43 2003/05/15 06:35:02 guy Exp $
  * Stuart Stanley <stuarts@mxmail.net>
  *
  * Ethereal - Network traffic analyzer
@@ -55,6 +55,7 @@ static int hf_isis_lsp_hippity = -1;
 static int hf_isis_lsp_is_type = -1;
 
 static gint ett_isis_lsp = -1;
+static gint ett_isis_lsp_checksum = -1;
 static gint ett_isis_lsp_info = -1;
 static gint ett_isis_lsp_att = -1;
 static gint ett_isis_lsp_clv_area_addr = -1;
@@ -713,7 +714,12 @@ dissect_lsp_ext_ip_reachability_clv(tvbuff_t *tvb, proto_tree *tree,
                ctrl_info = tvb_get_guint8(tvb, offset+4);
                bit_length = ctrl_info & 0x3f;
                byte_length = (bit_length + 7) / 8;
-               tvb_memcpy (tvb, prefix, offset+5, byte_length);
+               if (byte_length > sizeof(prefix)) {
+                        isis_dissect_unknown(tvb, tree, offset,
+                               "IPv4 prefix has an invalid length: %d bytes", byte_length );
+                       return;
+               }
+               tvb_memcpy (tvb, prefix, offset+5, byte_length);
                metric = tvb_get_ntohl(tvb, offset);
                subclvs_len = 0;
                if ((ctrl_info & 0x40) != 0)
@@ -806,13 +812,17 @@ dissect_lsp_ipv6_reachability_clv(tvbuff_t *tvb, proto_tree *tree, int offset,
 
        if (!tree) return;
 
-       memset (prefix.s6_addr, 0, 16);
-
        while (length > 0) {
+       memset (prefix.s6_addr, 0, 16);
                ctrl_info = tvb_get_guint8(tvb, offset+4);
                bit_length = tvb_get_guint8(tvb, offset+5);
                byte_length = (bit_length + 7) / 8;
-               tvb_memcpy (tvb, prefix.s6_addr, offset+6, byte_length);
+               if (byte_length > sizeof(prefix)) {
+                       isis_dissect_unknown(tvb, tree, offset,
+                               "IPv6 prefix has an invalid length: %d bytes", byte_length );
+                       return;
+               }
+               tvb_memcpy (tvb, prefix.s6_addr, offset+6, byte_length);
                metric = tvb_get_ntohl(tvb, offset);
                subclvs_len = 0;
                if ((ctrl_info & 0x20) != 0)
@@ -1753,11 +1763,11 @@ void
 isis_dissect_isis_lsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int offset,
        int lsp_type, int header_length, int id_length)
 {
-       proto_item      *ti, *to, *ta;
-       proto_tree      *lsp_tree = NULL, *info_tree, *att_tree;
-       guint16         pdu_length;
+       proto_item      *ti, *to, *ta, *tc;
+       proto_tree      *lsp_tree = NULL, *info_tree, *att_tree, *check_tree;
+       guint16         pdu_length, checksum, cacl_checksum=0;
        guint8          lsp_info, lsp_att;
-       int             len;
+       int             len, offset_checksum;
 
        if (tree) {
                ti = proto_tree_add_text(tree, tvb, offset, -1,
@@ -1778,6 +1788,7 @@ isis_dissect_isis_lsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int o
                                     tvb_get_ntohs(tvb, offset));
        }
        offset += 2;
+       offset_checksum = offset;
 
        if (tree) {
                proto_tree_add_text(lsp_tree, tvb, offset, id_length + 2,
@@ -1804,9 +1815,31 @@ isis_dissect_isis_lsp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int o
        offset += 4;
 
        if (tree) {
-               /* XXX -> we could validate the cksum here! */
-               proto_tree_add_uint(lsp_tree, hf_isis_lsp_checksum, tvb,
-                       offset, 2, tvb_get_ntohs(tvb, offset));
+               checksum = tvb_get_ntohs(tvb, offset);
+               tc = proto_tree_add_uint(lsp_tree, hf_isis_lsp_checksum, tvb, offset, 2, tvb_get_ntohs(tvb, offset));
+               check_tree = proto_item_add_subtree(tc, ett_isis_lsp_checksum);
+
+               switch (check_and_get_checksum(tvb, offset_checksum, pdu_length-12, checksum, offset, &cacl_checksum)) {
+               case NO_CKSUM :
+                       proto_tree_add_text(check_tree, tvb, offset, 2,
+                               "Checksum control disabled");
+                       break;
+               case DATA_MISSING :
+                       isis_dissect_unknown(tvb, tree, offset,
+                               "packet length %d went beyond packet",
+                               tvb_length_remaining(tvb, offset_checksum));
+                       break;
+               case CKSUM_NOT_OK :
+                       proto_tree_add_text(check_tree, tvb, offset, 2,
+                               "Checksum error: 0x%04x expected", cacl_checksum);
+                       break;
+               case CKSUM_OK :
+                       proto_tree_add_text(check_tree, tvb, offset, 2,
+                               "Checksum OK");
+                       break;
+               default :
+                       g_message("'check_and_get_checksum' returned an invalid value");
+               }
        }
        offset += 2;
 
@@ -1932,6 +1965,7 @@ isis_register_lsp(int proto_isis) {
        };
        static gint *ett[] = {
                &ett_isis_lsp,
+               &ett_isis_lsp_checksum,
                &ett_isis_lsp_info,
                &ett_isis_lsp_att,
                &ett_isis_lsp_clv_area_addr,
index c9782714e2f90f28d97697a06de6abe99cddeb86..5f1011b824294ea27891dd2ce6daa17718626430 100644 (file)
@@ -2,7 +2,7 @@
  * Routines for ISO/OSI network and transport protocol packet disassembly
  * Main entrance point and common functions
  *
- * $Id: packet-osi.c,v 1.60 2003/04/29 17:56:48 guy Exp $
+ * $Id: packet-osi.c,v 1.61 2003/05/15 06:35:02 guy Exp $
  * Laurent Deniel <laurent.deniel@free.fr>
  * Ralf Schneider <Ralf.Schneider@t-online.de>
  *
@@ -91,12 +91,105 @@ calc_checksum( tvbuff_t *tvb, int offset, guint len, guint checksum) {
     len -= seglen;
   }
   if (c0 != 0 || c1 != 0)
-    return( CKSUM_NOT_OK );    /* XXX - what should the checksum be? */
+    return( CKSUM_NOT_OK );    /* XXX - what should the checksum field be? */
   else
     return( CKSUM_OK );
 }
 
 
+cksum_status_t
+check_and_get_checksum( tvbuff_t *tvb, int offset, guint len, guint checksum, int offset_check, guint16* result) {
+  const gchar *buffer;
+  guint   available_len;
+  const guint8 *p;
+  guint8 discard = 0;
+  guint32 c0, c1, factor;
+  guint   seglen, initlen = len;
+  guint   i;
+  int     block, x, y;
+
+  if ( 0 == checksum )
+    return( NO_CKSUM );
+
+  available_len = tvb_length_remaining( tvb, offset );
+  offset_check -= offset;
+  if ( ( available_len < len ) || ( offset_check < 0 ) || ( (guint)(offset_check+2) > len ) )
+    return( DATA_MISSING );
+
+  buffer = tvb_get_ptr( tvb, offset, len );
+  block  = offset_check / 5803;
+
+  /*
+   * The maximum values of c0 and c1 will occur if all bytes have the
+   * value 255; if so, then c0 will be len*255 and c1 will be
+   * (len*255 + (len-1)*255 + ... + 255), which is
+   * (len + (len - 1) + ... + 1)*255, or 255*(len*(len + 1))/2.
+   * This means it can overflow if "len" is 5804 or greater.
+   *
+   * (A+B) mod 255 = ((A mod 255) + (B mod 255) mod 255, so
+   * we can solve this by taking c0 and c1 mod 255 every
+   * 5803 bytes.
+   */
+  p = buffer;
+  c0 = 0;
+  c1 = 0;
+
+  while (len != 0) {
+    seglen = len;
+    if ( block-- == 0 ) {
+      seglen = offset_check % 5803;
+      discard = 1;
+    } else if ( seglen > 5803 )
+      seglen = 5803;
+    for (i = 0; i < seglen; i++) {
+      c0 = c0 + *(p++);
+      c1 += c0;
+    }
+    if ( discard ) {
+      /*
+       * This works even if (offset_check % 5803) == 5802
+       */
+      p += 2;
+      c1 += 2*c0;
+      len -= 2;
+      discard = 0;
+    }
+
+    c0 = c0 % 255;
+    c1 = c1 % 255;
+
+    len -= seglen;
+  }
+
+  factor = ( initlen - offset_check ) * c0;
+  x = factor - c0 - c1;
+  y = c1 - factor - 1;
+
+  /*
+   * This algorithm uses the 8 bits one's complement arithmetic.
+   * Therefore, we must correct an effect produced
+   * by the "standard" arithmetic (two's complement)
+   */
+
+  if (x < 0 ) x--;
+  if (y > 0 ) y++;
+
+  x %= 255;
+  y %= 255;
+
+  if (x == 0) x = 0xFF;
+  if (y == 0) y = 0x01;
+
+  *result = ( x << 8 ) | ( y & 0xFF );
+
+  if (*result != checksum)
+    return( CKSUM_NOT_OK );    /* XXX - what should the checksum field be? */
+  else
+    return( CKSUM_OK );
+}
+
+
+
 /* main entry point */
 
 /*
index 427010b61dd2a45ec3b242665d3a160142fdfd5a..8b389aab9ae50afd776287f83713030be2b8a2fc 100644 (file)
@@ -1,6 +1,6 @@
 /* packet-osi.h
  *
- * $Id: packet-osi.h,v 1.13 2003/04/29 17:56:48 guy Exp $
+ * $Id: packet-osi.h,v 1.14 2003/05/15 06:35:02 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
@@ -86,5 +86,6 @@ typedef enum {
 } cksum_status_t;
 
 extern cksum_status_t calc_checksum(tvbuff_t *, int, guint, guint);
+extern cksum_status_t check_and_get_checksum( tvbuff_t *, int, guint, guint, int, guint16*);
 
 #endif /* _PACKET_OSI_H */