Ethereal-dev: [Ethereal-dev] PCT record layer for SSL dissector

Note: This archive is from the project's previous web site, ethereal.com. This list is no longer active.

From: Pasi Eronen <pasi.eronen@xxxxxxxx>
Date: Wed, 10 Apr 2002 23:32:22 +0300 (EET DST
Hi,

I was bored so I also added support for the PCT record layer
to the SSL dissector (this is quite similar to the SSLv2
record layer, but with different message numbers).
You can't yet see the contents of individual records
(client hello, server hello, etc.), but at least the types
are shown correctly.

The patch also fixes a couple of bugs in the SSLv2 TCP desegmentation
routines (an off-by-1 error and an off-by-2..3 error :).

Best regards,

Pasi

-- 
Pasi Eronen                         E-mail pasi.eronen@xxxxxxxx
Nixu Oy                             Tel +358 50 5123499
Mäkelänkatu 91, 00610 Helsinki      Fax +358 9 4781030
*** packet-ssl.c.old1	Wed Apr 10 23:28:03 2002
--- packet-ssl.c	Wed Apr 10 23:24:08 2002
***************
*** 90,93 ****
--- 90,94 ----
  static int hf_ssl2_record_padding_length     = -1;
  static int hf_ssl2_msg_type                  = -1;
+ static int hf_pct_msg_type                   = -1;
  static int hf_ssl_change_cipher_spec         = -1;
  static int hf_ssl_alert_message              = -1;
***************
*** 161,164 ****
--- 162,166 ----
  #define SSL_VER_SSLv3                     2
  #define SSL_VER_TLS                       3
+ #define SSL_VER_PCT                       4
  
  /* corresponds to the #defines above */
***************
*** 168,171 ****
--- 170,174 ----
      "SSLv3",
      "TLS",
+     "PCT"
  };
  
***************
*** 197,200 ****
--- 200,211 ----
  #define SSL2_HND_CLIENT_CERTIFICATE    0x08
  
+ #define PCT_VERSION_1		       0x8001
+ 
+ #define PCT_MSG_CLIENT_HELLO           0x01
+ #define PCT_MSG_SERVER_HELLO           0x02
+ #define PCT_MSG_CLIENT_MASTER_KEY      0x03
+ #define PCT_MSG_SERVER_VERIFY          0x04
+ #define PCT_MSG_ERROR                  0x05
+ 
  /*
   * Lookup tables
***************
*** 437,440 ****
--- 448,461 ----
  };
  
+ static const value_string pct_msg_types[] = {
+     { PCT_MSG_CLIENT_HELLO,         "Client Hello" },
+     { PCT_MSG_SERVER_HELLO,         "Server Hello" },
+     { PCT_MSG_CLIENT_MASTER_KEY,    "Client Master Key" },
+     { PCT_MSG_SERVER_VERIFY,        "Server Verify" },
+     { PCT_MSG_ERROR,                "Error" },
+     { 0x00, NULL },
+ };
+ 
+ 
  /*********************************************************************
   *
***************
*** 534,537 ****
--- 555,561 ----
                                                guint32 offset,
                                                guint32 record_length);
+ static int  ssl_looks_like_valid_pct_handshake(tvbuff_t *tvb,
+                                                guint32 offset,
+                                                guint32 record_length);
  
  /*********************************************************************
***************
*** 637,640 ****
--- 661,665 ----
          switch(conv_version) {
          case SSL_VER_SSLv2:
+         case SSL_VER_PCT:
              offset = dissect_ssl2_record(tvb, pinfo, ssl_tree,
                                           offset, &conv_version,
***************
*** 670,674 ****
              if (ssl_looks_like_sslv2(tvb, offset))
              {
!                 /* looks like sslv2 client hello */
                  offset = dissect_ssl2_record(tvb, pinfo, ssl_tree,
                                               offset, &conv_version,
--- 695,699 ----
              if (ssl_looks_like_sslv2(tvb, offset))
              {
!                 /* looks like sslv2 or pct client hello */
                  offset = dissect_ssl2_record(tvb, pinfo, ssl_tree,
                                               offset, &conv_version,
***************
*** 1605,1609 ****
       * record length is two bytes
       */
!     byte = tvb_get_guint8(tvb, offset++);
      record_length_length = (byte & 0x80) ? 2 : 3;
  
--- 1630,1634 ----
       * record length is two bytes
       */
!     byte = tvb_get_guint8(tvb, offset);
      record_length_length = (byte & 0x80) ? 2 : 3;
  
***************
*** 1634,1638 ****
      case 2:                     /* two-byte record length */
          record_length = (byte & 0x7f) << 8;
!         byte = tvb_get_guint8(tvb, offset++);
          record_length += byte;
          break;
--- 1659,1663 ----
      case 2:                     /* two-byte record length */
          record_length = (byte & 0x7f) << 8;
!         byte = tvb_get_guint8(tvb, offset + 1);
          record_length += byte;
          break;
***************
*** 1640,1646 ****
          is_escape = (byte & 0x40) ? TRUE : FALSE;
          record_length = (byte & 0x3f) << 8;
!         byte = tvb_get_guint8(tvb, offset++);
          record_length += byte;
!         byte = tvb_get_guint8(tvb, offset++);
          padding_length = byte;
      }
--- 1665,1671 ----
          is_escape = (byte & 0x40) ? TRUE : FALSE;
          record_length = (byte & 0x3f) << 8;
!         byte = tvb_get_guint8(tvb, offset + 1);
          record_length += byte;
!         byte = tvb_get_guint8(tvb, offset + 2);
          padding_length = byte;
      }
***************
*** 1653,1657 ****
           * Yes - is the record split across segment boundaries?
           */
!         if (available_bytes < record_length) {
              /*
               * Yes.  Tell the TCP dissector where the data for this
--- 1678,1682 ----
           * Yes - is the record split across segment boundaries?
           */
!         if (available_bytes < (record_length_length + record_length)) {
              /*
               * Yes.  Tell the TCP dissector where the data for this
***************
*** 1660,1677 ****
               */
              pinfo->desegment_offset = offset;
!             pinfo->desegment_len = record_length - available_bytes;
              *need_desegmentation = TRUE;
              return offset;
          }
      }
! 
!     /* if we get here, but don't have a version set for the
!      * conversation, then set a version for just this frame
!      * (e.g., on a client hello)
!      */
!     if (check_col(pinfo->cinfo, COL_PROTOCOL))
!     {
!         col_set_str(pinfo->cinfo, COL_PROTOCOL, "SSLv2");
!     }
  
      /* add the record layer subtree header */
--- 1685,1695 ----
               */
              pinfo->desegment_offset = offset;
!             pinfo->desegment_len = (record_length_length + record_length)
! 		                   - available_bytes;
              *need_desegmentation = TRUE;
              return offset;
          }
      }
!     offset += record_length_length;
  
      /* add the record layer subtree header */
***************
*** 1686,1694 ****
       * this to sslv2
       */
!     if (*conv_version == SSL_VER_UNKNOWN
!         && msg_type >= 2 && msg_type <= 8)
      {
!         *conv_version = SSL_VER_SSLv2;
!         ssl_set_conv_version(pinfo, *conv_version);
      }
  
--- 1704,1731 ----
       * this to sslv2
       */
!     if (*conv_version == SSL_VER_UNKNOWN)
!     {
!         if (ssl_looks_like_valid_pct_handshake(tvb,
!                                                (initial_offset +
!                                                 record_length_length),
!                                                record_length)) {
!             *conv_version = SSL_VER_PCT;
!             ssl_set_conv_version(pinfo, *conv_version);
!         }
!         else if (msg_type >= 2 && msg_type <= 8)
!         {
!             *conv_version = SSL_VER_SSLv2;
!             ssl_set_conv_version(pinfo, *conv_version);
!         }
!     }
!     
!     /* if we get here, but don't have a version set for the
!      * conversation, then set a version for just this frame
!      * (e.g., on a client hello)
!      */
!     if (check_col(pinfo->cinfo, COL_PROTOCOL))
      {
!         col_set_str(pinfo->cinfo, COL_PROTOCOL,
!                     (*conv_version == SSL_VER_PCT) ? "PCT" : "SSLv2");
      }
  
***************
*** 1696,1708 ****
       * probably encrypted, so note that fact and bail
       */
!     msg_type_str = match_strval(msg_type, ssl_20_msg_types);
      if (!msg_type_str
!         || !ssl_looks_like_valid_v2_handshake(tvb, initial_offset
!                                               + record_length_length,
!                                               record_length))
      {
          if (ssl_record_tree)
          {
!             proto_item_set_text(ssl_record_tree, "SSLv2 Record Layer: %s",
                                  "Encrypted Data");
          }
--- 1733,1754 ----
       * probably encrypted, so note that fact and bail
       */
!     msg_type_str = match_strval(msg_type,
!                                 (*conv_version == SSL_VER_PCT)
! 				? pct_msg_types : ssl_20_msg_types);
      if (!msg_type_str
!         || ((*conv_version != SSL_VER_PCT) &&
! 	    !ssl_looks_like_valid_v2_handshake(tvb, initial_offset
! 					       + record_length_length,
! 					       record_length))
! 	|| ((*conv_version == SSL_VER_PCT) &&
! 	    !ssl_looks_like_valid_pct_handshake(tvb, initial_offset
! 						+ record_length_length,
! 						record_length)))
      {
          if (ssl_record_tree)
          {
!             proto_item_set_text(ssl_record_tree, "%s Record Layer: %s",
!                                 (*conv_version == SSL_VER_PCT)
!                                 ? "PCT" : "SSLv2",
                                  "Encrypted Data");
          }
***************
*** 1718,1722 ****
          if (ssl_record_tree)
          {
!             proto_item_set_text(ssl_record_tree, "SSLv2 Record Layer: %s",
                                  msg_type_str);
          }
--- 1764,1770 ----
          if (ssl_record_tree)
          {
!             proto_item_set_text(ssl_record_tree, "%s Record Layer: %s",
!                                 (*conv_version == SSL_VER_PCT)
!                                 ? "PCT" : "SSLv2",
                                  msg_type_str);
          }
***************
*** 1758,1795 ****
      if (ssl_record_tree)
      {
!         proto_tree_add_item(ssl_record_tree, hf_ssl2_msg_type, tvb,
!                             offset, 1, 0);
      }
      offset++;                   /* move past msg_type byte */
  
  
!     /* dissect the message (only handle client hello right now) */
!     switch (msg_type) {
!     case SSL2_HND_CLIENT_HELLO:
!         dissect_ssl2_hnd_client_hello(tvb, ssl_record_tree, offset);
!         break;
! 
!     case SSL2_HND_CLIENT_MASTER_KEY:
!         dissect_ssl2_hnd_client_master_key(tvb, ssl_record_tree, offset);
!         break;
  
!     case SSL2_HND_SERVER_HELLO:
!         dissect_ssl2_hnd_server_hello(tvb, ssl_record_tree, offset);
!         break;
  
!     case SSL2_HND_ERROR:
!     case SSL2_HND_CLIENT_FINISHED:
!     case SSL2_HND_SERVER_VERIFY:
!     case SSL2_HND_SERVER_FINISHED:
!     case SSL2_HND_REQUEST_CERTIFICATE:
!     case SSL2_HND_CLIENT_CERTIFICATE:
!         /* unimplemented */
!         break;
  
!     default:                    /* unknown */
!         break;
      }
  
! 
      return (initial_offset + record_length_length + record_length);
  }
--- 1806,1861 ----
      if (ssl_record_tree)
      {
!         proto_tree_add_item(ssl_record_tree,
!                             (*conv_version == SSL_VER_PCT)
!                             ? hf_pct_msg_type : hf_ssl2_msg_type,
!                             tvb, offset, 1, 0);
      }
      offset++;                   /* move past msg_type byte */
  
+     if (*conv_version != SSL_VER_PCT)
+     {
+         /* dissect the message (only handle client hello right now) */
+         switch (msg_type) {
+         case SSL2_HND_CLIENT_HELLO:
+             dissect_ssl2_hnd_client_hello(tvb, ssl_record_tree, offset);
+             break;
  
!         case SSL2_HND_CLIENT_MASTER_KEY:
!             dissect_ssl2_hnd_client_master_key(tvb, ssl_record_tree, offset);
!             break;
  
!         case SSL2_HND_SERVER_HELLO:
!             dissect_ssl2_hnd_server_hello(tvb, ssl_record_tree, offset);
!             break;
  
!         case SSL2_HND_ERROR:
!         case SSL2_HND_CLIENT_FINISHED:
!         case SSL2_HND_SERVER_VERIFY:
!         case SSL2_HND_SERVER_FINISHED:
!         case SSL2_HND_REQUEST_CERTIFICATE:
!         case SSL2_HND_CLIENT_CERTIFICATE:
!             /* unimplemented */
!             break;
  
!         default:                    /* unknown */
!             break;
!         }
      }
+     else
+     {
+         /* dissect the message */ 
+         switch (msg_type) {
+         case PCT_MSG_CLIENT_HELLO:
+         case PCT_MSG_SERVER_HELLO:
+         case PCT_MSG_CLIENT_MASTER_KEY:
+         case PCT_MSG_SERVER_VERIFY:
+         case PCT_MSG_ERROR:
+             /* unimplemented */
+             break;
  
!         default:                    /* unknown */
!             break;
!         }
!     }
      return (initial_offset + record_length_length + record_length);
  }
***************
*** 2238,2241 ****
--- 2304,2309 ----
      case SSL2_HND_CLIENT_MASTER_KEY:
      case SSL2_HND_SERVER_HELLO:
+     case PCT_MSG_CLIENT_MASTER_KEY:
+     case PCT_MSG_ERROR:
          return 1;
      }
***************
*** 2335,2338 ****
--- 2403,2478 ----
  }
  
+ /* applies a heuristic to determine whether
+  * or not the data beginning at offset looks
+  * like a valid, unencrypted v2 handshake message.
+  * since it isn't possible to completely tell random
+  * data apart from a valid message without state,
+  * we try to help the odds.
+  */
+ static int
+ ssl_looks_like_valid_pct_handshake(tvbuff_t *tvb, guint32 offset,
+ 				   guint32 record_length)
+ {
+     /* first byte should be a msg_type.
+      *
+      *   - we know we only see client_hello, client_master_key,
+      *     and server_hello in the clear, so check to see if
+      *     msg_type is one of those (this gives us a 3 in 2^8
+      *     chance of saying yes with random payload)
+      *
+      *   - for those three types that we know about, do some
+      *     further validation to reduce the chance of an error
+      */
+     guint8 msg_type;
+     guint16 version;
+     guint32 sum;
+ 
+     /* fetch the msg_type */
+     msg_type = tvb_get_guint8(tvb, offset);
+ 
+     switch (msg_type) {
+     case PCT_MSG_CLIENT_HELLO:
+         /* version follows msg byte, so verify that this is valid */
+         version = tvb_get_ntohs(tvb, offset+1);
+         return version == PCT_VERSION_1;
+         break;
+ 
+     case PCT_MSG_SERVER_HELLO:
+         /* version is one byte after msg_type */
+         version = tvb_get_ntohs(tvb, offset+2);
+         return version == PCT_VERSION_1;
+         break;
+ 
+     case PCT_MSG_CLIENT_MASTER_KEY:
+         /* sum of various length fields must be less than record length */
+         sum  = tvb_get_ntohs(tvb, offset + 6); /* clear_key_length */
+         sum += tvb_get_ntohs(tvb, offset + 8); /* encrypted_key_length */
+         sum += tvb_get_ntohs(tvb, offset + 10); /* key_arg_length */
+         sum += tvb_get_ntohs(tvb, offset + 12); /* verify_prelude_length */
+         sum += tvb_get_ntohs(tvb, offset + 14); /* client_cert_length */
+         sum += tvb_get_ntohs(tvb, offset + 16); /* response_length */
+         if (sum > record_length)
+         {
+             return 0;
+         }
+         return 1;
+         break;
+ 
+     case PCT_MSG_SERVER_VERIFY:
+ 	/* record is 36 bytes longer than response_length */
+ 	sum = tvb_get_ntohs(tvb, offset + 34); /* response_length */
+ 	if ((sum + 36) == record_length)
+ 	    return 1;
+ 	else
+ 	    return 0;
+ 	break;
+ 
+     default:
+         return 0;
+     }
+     return 0;
+ }
+ 
+ 
  /*********************************************************************
   *
***************
*** 2361,2364 ****
--- 2501,2509 ----
              "SSLv2 handshake message type", HFILL}
          },
+         { &hf_pct_msg_type,
+           { "Handshake Message Type", "ssl.pct_handshake.type",
+             FT_UINT8, BASE_DEC, VALS(pct_msg_types), 0x0,
+             "PCT handshake message type", HFILL}
+         },
          { &hf_ssl_record_version,
            { "Version", "ssl.record.version",
***************
*** 2377,2383 ****
          },
          { & hf_ssl2_record,
!           { "SSLv2 Record Header", "ssl.record",
              FT_NONE, BASE_DEC, NULL, 0x0,
!             "SSLv2 record data", HFILL }
          },
          { &hf_ssl2_record_is_escape,
--- 2522,2528 ----
          },
          { & hf_ssl2_record,
!           { "SSLv2/PCT Record Header", "ssl.record",
              FT_NONE, BASE_DEC, NULL, 0x0,
!             "SSLv2/PCT record data", HFILL }
          },
          { &hf_ssl2_record_is_escape,
This message has been 'sanitized'.  This means that potentially
dangerous content has been rewritten or removed.  The following
log describes which actions were taken.

Sanitizer (start="1018470747"):
  Split unusually long Date: header.
  Writer (pos="917"):
    Total modifications so far: 1

  Part (pos="1174"):
    SanitizeFile (filename="unnamed.txt", mimetype="TEXT/PLAIN"):
      Match (rule="2"):
        Enforced policy: accept

  Part (pos="1931"):
    SanitizeFile (filename="packet-ssl.diff2", mimetype="TEXT/PLAIN"):
      Match (rule="default"):
        Enforced policy: accept


Anomy 0.0.0 : Sanitizer.pm
$Id: Sanitizer.pm,v 1.32 2001/10/11 19:27:15 bre Exp $