Wireshark-dev: [Wireshark-dev] [PATCH] Add support for the CIPSO IPv4 option
From: "Paul Moore" <paul.moore@xxxxxx>
Date: Wed, 17 Jan 2007 14:52:45 -0500
This patch adds support for the IPv4 Commercial IP Security Option (CIPSO) as
defined in the IETF draft, draft-ietf-cipso-ipsecurity-01.txt.  While this
draft has long since expired, it has become a de-facto standard for labeled
networking with support from several commercial Multi-Level Security (MLS)
operating systems such as HP-UX CMW and Trusted Solaris; in addition, Linux
Kernels 2.6.19 and later provide support for CIPSO in conjunction with SELinux.

Copies of the expired CIPSO draft can be found at the NetLabel project page:

 * http://netlabel.sf.net

A sample packet capture demonstrating the three types of CIPSO tags can be
found here:

 * http://free.linux.hp.com/~pmoore/files/tcpdump_out.pcap

This patch is backed against the current SVN sources and has been fuzz tested
using the fuzz-test.sh script.  Please consider it for inclusion into the
wireshark source tree.

Thank you.

---
 epan/dissectors/packet-ip.c |  263 ++++++++++++++++++++++++++++++++++++++++++++
 1 file changed, 263 insertions(+)

Index: wireshark/epan/dissectors/packet-ip.c
===================================================================
--- wireshark.orig/epan/dissectors/packet-ip.c
+++ wireshark/epan/dissectors/packet-ip.c
@@ -13,6 +13,11 @@
  * by   Maria-Luiza Crivat <luizacri@xxxxxxxxx>
  * &    Brice Augustin <bricecotte@xxxxxxxxx>
  *
+ * Wednesday, January 17, 2006
+ * Support for the CIPSO IPv4 option
+ * (http://sourceforge.net/docman/display_doc.php?docid=34650&group_id=174379)
+ * by   Paul Moore <paul.moore@xxxxxx>
+ *
  * This program is free software; you can redistribute it and/or
  * modify it under the terms of the GNU General Public License
  * as published by the Free Software Foundation; either version 2
@@ -124,6 +129,7 @@ static gint ett_ip_options = -1;
 static gint ett_ip_option_sec = -1;
 static gint ett_ip_option_route = -1;
 static gint ett_ip_option_timestamp = -1;
+static gint ett_ip_option_cipso = -1;
 static gint ett_ip_fragments = -1;
 static gint ett_ip_fragment  = -1;
 static gint ett_ip_checksum = -1;
@@ -326,6 +332,7 @@ static gint ett_icmp_mpls_stack_object =
 #define IPOPT_SEC	(2 |IPOPT_CONTROL|IPOPT_COPY)
 #define IPOPT_LSRR	(3 |IPOPT_CONTROL|IPOPT_COPY)
 #define IPOPT_TIMESTAMP	(4 |IPOPT_MEASUREMENT)
+#define IPOPT_CIPSO	(6 |IPOPT_CONTROL|IPOPT_COPY)
 #define IPOPT_RR	(7 |IPOPT_CONTROL)
 #define IPOPT_SID	(8 |IPOPT_CONTROL|IPOPT_COPY)
 #define IPOPT_SSRR	(9 |IPOPT_CONTROL|IPOPT_COPY)
@@ -339,6 +346,7 @@ static gint ett_icmp_mpls_stack_object =
 #define IPOLEN_SID      4
 #define IPOLEN_SSRR_MIN 3
 #define IPOLEN_RA       4
+#define IPOLEN_CIPSO_MIN 10
 
 #define IPSEC_UNCLASSIFIED	0x0000
 #define	IPSEC_CONFIDENTIAL	0xF135
@@ -461,6 +469,252 @@ dissect_ipopt_security(const ip_tcp_opt 
 	      tvb_get_guint8(tvb, offset + 2));
 }
 
+/* USHRT_MAX can hold at most 5 (base 10) digits (6 for the NULL byte) */
+#define USHRT_MAX_STRLEN 6
+
+/* Maximum CIPSO tag length:
+ * (IP hdr max)60 - (IPv4 hdr std)20 - (CIPSO base)6 = 34 */
+#define CIPSO_TAG_LEN_MAX 34
+
+/* The Commercial IP Security Option (CIPSO) is defined in IETF draft
+ * draft-ietf-cipso-ipsecurity-01.txt and FIPS 188, a copy of both documents
+ * can be found at the NetLabel project page, http://netlabel.sf.net. */
+static void
+dissect_ipopt_cipso(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
+                    guint optlen, packet_info *pinfo _U_, proto_tree *opt_tree)
+{
+  proto_tree *field_tree = NULL;
+  proto_item *tf;
+  guint      tagtype, taglen;
+  int        offset_max = offset + optlen;
+
+  tf = proto_tree_add_text(opt_tree, tvb, offset, optlen, "%s", optp->name);
+  field_tree = proto_item_add_subtree(tf, *optp->subtree_index);
+  offset += 2;
+
+  proto_tree_add_text(field_tree, tvb, offset, 4, "DOI: %u",
+		      tvb_get_ntohl(tvb, offset));
+  offset += 4;
+
+  /* loop through all of the tags in the CIPSO option */
+  while (offset < offset_max) {
+    tagtype = tvb_get_guint8(tvb, offset);
+
+    if ((offset + 1) < offset_max)
+      taglen = tvb_get_guint8(tvb, offset + 1);
+    else
+      taglen = 1;
+
+    switch (tagtype) {
+    case 0:
+      /* padding - skip this tag */
+      offset += 1;
+      continue;
+      break;
+    case 1:
+      /* restrictive bitmap, see CIPSO draft section 3.4.2 for tag format */
+      if ((taglen < 4) || (taglen > CIPSO_TAG_LEN_MAX) ||
+	  ((offset + (int)taglen - 1) > offset_max)) {
+	proto_tree_add_text(field_tree, tvb, offset, offset_max - offset,
+			    "Malformed CIPSO tag");
+        return;
+      }
+
+      proto_tree_add_text(field_tree, tvb, offset, 1,
+                          "Tag Type: Restrictive Category Bitmap (%u)",
+			  tagtype);
+
+      /* skip past alignment octet */
+      offset += 3;
+
+      proto_tree_add_text(field_tree, tvb, offset, 1, "Sensitivity Level: %u",
+			  tvb_get_guint8(tvb, offset));
+      offset += 1;
+
+      if (taglen > 4) {
+	guint bit_spot = 0;
+	guint byte_spot = 0;
+	unsigned char bitmask;
+	gchar *cat_str;
+	gchar *cat_str_tmp = ep_alloc(USHRT_MAX_STRLEN);
+	size_t cat_str_len;
+	const guint8 *val_ptr = tvb_get_ptr(tvb, offset, taglen - 4);
+
+	/* this is just a guess regarding string size, but we grow it below
+	 * if needed */
+	cat_str_len = 256;
+	cat_str = ep_alloc0(cat_str_len);
+
+	/* we checked the length above so the highest category value
+	 * possibile here is 240 */
+	while (byte_spot < (taglen - 4)) {
+	  bitmask = 0x80;
+	  bit_spot = 0;
+	  while (bit_spot < 8) {
+	    if (val_ptr[byte_spot] & bitmask) {
+	      g_snprintf(cat_str_tmp, USHRT_MAX_STRLEN, "%u",
+			 byte_spot * 8 + bit_spot);
+	      cat_str_tmp[USHRT_MAX_STRLEN - 1] = '\0';
+	      if (cat_str_len < (strlen(cat_str) + 2 + USHRT_MAX_STRLEN)) {
+		gchar *cat_str_new;
+		cat_str_len += cat_str_len;
+		cat_str_new = ep_alloc(cat_str_len);
+		g_stpcpy(cat_str_new, cat_str);
+		cat_str = cat_str_new;
+	      }
+	      if (cat_str[0] != '\0')
+		g_strlcat(cat_str, ",", cat_str_len);
+	      g_strlcat(cat_str, cat_str_tmp, cat_str_len);
+	    }
+	    bit_spot++;
+	    bitmask >>= 1;
+	  }
+	  byte_spot++;
+	}
+
+	if (cat_str)
+	  proto_tree_add_text(field_tree, tvb, offset, taglen - 4,
+			      "Categories: %s", cat_str);
+	else
+	  proto_tree_add_text(field_tree, tvb, offset, taglen - 4,
+			      "Categories: ERROR PARSING CATEGORIES");
+
+        offset += taglen - 4;
+      }
+      break;
+    case 2:
+      /* enumerated categories, see CIPSO draft section 3.4.3 for tag format */
+      if ((taglen < 4) || (taglen > CIPSO_TAG_LEN_MAX) ||
+	  ((offset + (int)taglen - 1) > offset_max)) {
+	proto_tree_add_text(field_tree, tvb, offset, offset_max - offset,
+			    "Malformed CIPSO tag");
+        return;
+      }
+
+      proto_tree_add_text(field_tree, tvb, offset, 1,
+                          "Tag Type: Enumerated Categories (%u)", tagtype);
+
+      /* skip past alignment octet */
+      offset += 3;
+
+      /* sensitvity level */
+      proto_tree_add_text(field_tree, tvb, offset, 1, "Sensitivity Level: %u",
+			  tvb_get_guint8(tvb, offset));
+      offset += 1;
+
+      if (taglen > 4) {
+	int offset_max_cat = offset + taglen - 4;
+	gchar *cat_str = ep_alloc0(USHRT_MAX_STRLEN * 15);
+	gchar *cat_str_tmp = ep_alloc(USHRT_MAX_STRLEN);
+
+	while ((offset + 2) <= offset_max_cat) {
+	  g_snprintf(cat_str_tmp, USHRT_MAX_STRLEN, "%u",
+		     tvb_get_ntohs(tvb, offset));
+	  offset += 2;
+	  cat_str_tmp[USHRT_MAX_STRLEN - 1] = '\0';
+	  if (cat_str[0] != '\0')
+	    g_strlcat(cat_str, ",", USHRT_MAX_STRLEN * 15);
+	  g_strlcat(cat_str, cat_str_tmp, USHRT_MAX_STRLEN * 15);
+	}
+
+	proto_tree_add_text(field_tree, tvb, offset - taglen + 4, taglen - 4,
+			    "Categories: %s", cat_str);
+      }
+      break;
+    case 5:
+      /* ranged categories, see CIPSO draft section 3.4.4 for tag format */
+      if ((taglen < 4) || (taglen > CIPSO_TAG_LEN_MAX) ||
+	  ((offset + (int)taglen - 1) > offset_max)) {
+	proto_tree_add_text(field_tree, tvb, offset, offset_max - offset,
+			    "Malformed CIPSO tag");
+        return;
+      }
+
+      proto_tree_add_text(field_tree, tvb, offset, 1,
+                          "Tag Type: Ranged Categories (%u)", tagtype);
+
+      /* skip past alignment octet */
+      offset += 3;
+
+      /* sensitvity level */
+      proto_tree_add_text(field_tree, tvb, offset, 1, "Sensitivity Level: %u",
+			  tvb_get_guint8(tvb, offset));
+      offset += 1;
+
+      if (taglen > 4) {
+	guint16 cat_low, cat_high;
+	int offset_max_cat = offset + taglen - 4;
+	gchar *cat_str = ep_alloc0(USHRT_MAX_STRLEN * 16);
+	gchar *cat_str_tmp = ep_alloc(USHRT_MAX_STRLEN * 2);
+
+	while ((offset + 2) <= offset_max_cat) {
+	  cat_high = tvb_get_ntohs(tvb, offset);
+	  if ((offset + 4) <= offset_max_cat) {
+	    cat_low = tvb_get_ntohs(tvb, offset + 2);
+	    offset += 4;
+	  } else {
+	    cat_low = 0;
+	    offset += 2;
+	  }
+	  if (cat_low != cat_high)
+	    g_snprintf(cat_str_tmp, USHRT_MAX_STRLEN * 2, "%u-%u",
+		       cat_high, cat_low);
+	  else
+	    g_snprintf(cat_str_tmp, USHRT_MAX_STRLEN * 2, "%u", cat_high);
+	  if (cat_str[0] != '\0')
+	    g_strlcat(cat_str, ",", USHRT_MAX_STRLEN * 16);
+	  g_strlcat(cat_str, cat_str_tmp, USHRT_MAX_STRLEN * 16);
+	}
+
+	proto_tree_add_text(field_tree, tvb, offset - taglen + 4, taglen - 4,
+			    "Categories: %s", cat_str);
+      }
+      break;
+    case 6:
+      /* permissive categories, see FIPS 188 section 6.9 for tag format */
+      if ((taglen < 4) || (taglen > CIPSO_TAG_LEN_MAX) ||
+	  ((offset + (int)taglen - 1) > offset_max)) {
+	proto_tree_add_text(field_tree, tvb, offset, offset_max - offset,
+			    "Malformed CIPSO tag");
+        return;
+      }
+
+      proto_tree_add_text(field_tree, tvb, offset, 1,
+                          "Tag Type: Permissive Categories (%u)", tagtype);
+      proto_tree_add_text(field_tree, tvb, offset + 2, taglen - 2, "Tag data");
+      offset += taglen;
+      break;
+    case 7:
+      /* free form, see FIPS 188 section 6.10 for tag format */
+      if ((taglen < 2) || (taglen > CIPSO_TAG_LEN_MAX) ||
+	  ((offset + (int)taglen - 1) > offset_max)) {
+	proto_tree_add_text(field_tree, tvb, offset, offset_max - offset,
+			    "Malformed CIPSO tag");
+        return;
+      }
+
+      proto_tree_add_text(field_tree, tvb, offset, 1,
+                          "Tag Type: Free Form (%u)", tagtype);
+      proto_tree_add_text(field_tree, tvb, offset + 2, taglen - 2, "Tag data");
+      offset += taglen;
+      break;
+    default:
+      /* unknown tag - stop parsing this IPv4 option */
+      if ((offset + 1) <= offset_max) {
+	taglen = tvb_get_guint8(tvb, offset + 1);
+	proto_tree_add_text(field_tree, tvb, offset, 1,
+			    "Tag Type: Unknown (%u) (%u bytes)",
+			    tagtype, taglen);
+	return;
+      }
+      proto_tree_add_text(field_tree, tvb, offset, 1,
+			  "Tag Type: Unknown (%u) (invalid format)",
+			  tagtype);
+      return;
+    }
+  }
+}
+
 static void
 dissect_ipopt_route(const ip_tcp_opt *optp, tvbuff_t *tvb, int offset,
 			guint optlen, packet_info *pinfo _U_,
@@ -644,6 +898,14 @@ static const ip_tcp_opt ipopts[] = {
     dissect_ipopt_route
   },
   {
+    IPOPT_CIPSO,
+    "Commercial IP security option",
+    &ett_ip_option_cipso,
+    VARIABLE_LENGTH,
+    IPOLEN_CIPSO_MIN,
+    dissect_ipopt_cipso
+  },
+  {
     IPOPT_RR,
     "Record route",
     &ett_ip_option_route,
@@ -2124,6 +2386,7 @@ proto_register_ip(void)
 		&ett_ip_option_sec,
 		&ett_ip_option_route,
 		&ett_ip_option_timestamp,
+		&ett_ip_option_cipso,
 		&ett_ip_fragments,
 		&ett_ip_fragment,
         &ett_ip_checksum,

--
paul moore
linux security @ hp