In HTTP-over-TCP, handle more than one HTTP message in a TCP segment.
[obnox/wireshark/wip.git] / packet-spnego.c
index dd7cdf4734534ef3d6451f1c35bfe5db5e4df652..10b6f696f412d9c7e7b0010031de58c70e3c3b51 100644 (file)
@@ -1,10 +1,11 @@
 /* packet-spnego.c
  * Routines for the simple and protected GSS-API negotiation mechanism
- * as described in rfc2478.
+ * as described in RFC 2478.
  * Copyright 2002, Tim Potter <tpot@samba.org>
  * Copyright 2002, Richard Sharpe <rsharpe@ns.aus.com>
+ * Copyright 2003, Richard Sharpe <rsharpe@richardsharpe.com>
  *
- * $id$
+ * $Id: packet-spnego.c,v 1.51 2003/07/17 22:17:01 sharpe Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
@@ -39,7 +40,8 @@
 #include "asn1.h"
 #include "format-oid.h"
 #include "packet-gssapi.h"
-#include "epan/conversation.h"
+#include "packet-kerberos.h"
+#include <epan/conversation.h>
 
 #define SPNEGO_negTokenInit 0
 #define SPNEGO_negTokenTarg 1
 #define SPNEGO_negResult_accept_reject 2
 
 static int proto_spnego = -1;
+static int proto_spnego_krb5 = -1;
 
 static int hf_spnego = -1;
 static int hf_spnego_negtokeninit = -1;
 static int hf_spnego_negtokentarg = -1;
 static int hf_spnego_mechtype = -1;
+static int hf_spnego_mechtoken = -1;
 static int hf_spnego_negtokentarg_negresult = -1;
+static int hf_spnego_mechlistmic = -1;
+static int hf_spnego_responsetoken = -1;
+static int hf_spnego_reqflags = -1;
+static int hf_spnego_wraptoken = -1;
+static int hf_spnego_krb5 = -1;
+static int hf_spnego_krb5_tok_id = -1;
+static int hf_spnego_krb5_sgn_alg = -1;
+static int hf_spnego_krb5_seal_alg = -1;
+static int hf_spnego_krb5_snd_seq = -1;
+static int hf_spnego_krb5_sgn_cksum = -1;
+static int hf_spnego_krb5_confounder = -1;
+static int hf_gssapi_reqflags_deleg = -1;
+static int hf_gssapi_reqflags_mutual = -1;
+static int hf_gssapi_reqflags_replay = -1;
+static int hf_gssapi_reqflags_sequence = -1;
+static int hf_gssapi_reqflags_anon = -1;
+static int hf_gssapi_reqflags_conf = -1;
+static int hf_gssapi_reqflags_integ = -1;
 
 static gint ett_spnego = -1;
 static gint ett_spnego_negtokeninit = -1;
 static gint ett_spnego_negtokentarg = -1;
 static gint ett_spnego_mechtype = -1;
-
-/*
- * XXX: Fixme. This thould be made global ...
- */
+static gint ett_spnego_mechtoken = -1;
+static gint ett_spnego_mechlistmic = -1;
+static gint ett_spnego_responsetoken = -1;
+static gint ett_spnego_wraptoken = -1;
+static gint ett_spnego_krb5 = -1;
+static gint ett_spnego_reqflags = -1;
 
 static const value_string spnego_negResult_vals[] = {
   { SPNEGO_negResult_accept_completed,   "Accept Completed" },
   { SPNEGO_negResult_accept_incomplete,  "Accept Incomplete" },
   { SPNEGO_negResult_accept_reject,      "Accept Reject"},
-  { NULL, NULL}
+  { 0, NULL}
 };
 
 /*
- * We need to keep this around for other routines to use.
- * We store it in the per-protocol conversation data and 
- * retrieve it in the main dissector.
+ * These should be in the GSSAPI dissector ... XXX
  */
 
-static dissector_handle_t next_level_dissector = -1;
+static const true_false_string tfs_reqflags_deleg = {
+  "Delegation Requested",
+  "Delegation NOT Requested"
+};
+
+static const true_false_string tfs_reqflags_mutual = {
+  "Mutual Authentication Requested",
+  "Mutual Authentication NOT Requested"
+};
+
+static const true_false_string tfs_reqflags_replay = {
+  "Replay Detection Requested",
+  "Replay Detection NOT Requested"
+};
+
+static const true_false_string tfs_reqflags_sequence = {
+  "Out-of-sequence Detection Requested",
+  "Out-of-sequence Detection NOT Requested"
+};
+
+static const true_false_string tfs_reqflags_anon = {
+  "Anonymous Authentication Requested",
+  "Anonymous Authentication NOT Requested"
+};
+
+static const true_false_string tfs_reqflags_conf = {
+  "Per-message Confidentiality Requested",
+  "Per-message Confidentiality NOT Requested"
+};
+
+static const true_false_string tfs_reqflags_integ = {
+  "Per-message Integrity Requested",
+  "Per-message Integrity NOT Requested"
+};
 
 /* Display an ASN1 parse error.  Taken from packet-snmp.c */
 
@@ -106,20 +161,426 @@ dissect_parse_error(tvbuff_t *tvb, int offset, packet_info *pinfo,
        }
 }
 
+/*
+ * This is the SPNEGO KRB5 dissector. It is not true KRB5, but some ASN.1
+ * wrapped blob with an OID, USHORT token ID, and a Ticket, that is also 
+ * ASN.1 wrapped by the looks of it. It conforms to RFC1964.
+ */ 
+
+#define KRB_TOKEN_AP_REQ               0x0001
+#define KRB_TOKEN_AP_REP               0x0002
+#define KRB_TOKEN_AP_ERR               0x0003
+#define KRB_TOKEN_GETMIC               0x0101
+#define KRB_TOKEN_WRAP                 0x0102
+#define KRB_TOKEN_DELETE_SEC_CONTEXT   0x0201
+
+static const value_string spnego_krb5_tok_id_vals[] = {
+  { KRB_TOKEN_AP_REQ,             "KRB5_AP_REQ"},
+  { KRB_TOKEN_AP_REP,             "KRB5_AP_REP"},
+  { KRB_TOKEN_AP_ERR,             "KRB5_ERROR"},
+  { KRB_TOKEN_GETMIC,             "KRB5_GSS_GetMIC" },
+  { KRB_TOKEN_WRAP,               "KRB5_GSS_Wrap" },
+  { KRB_TOKEN_DELETE_SEC_CONTEXT, "KRB5_GSS_Delete_sec_context" },
+  { 0, NULL}
+};
+
+#define KRB_SGN_ALG_DES_MAC_MD5        0x0000
+#define KRB_SGN_ALG_MD2_5      0x0001
+#define KRB_SGN_ALG_DES_MAC    0x0002
+#define KRB_SGN_ALG_HMAC       0x0011
+
+static const value_string spnego_krb5_sgn_alg_vals[] = {
+  { KRB_SGN_ALG_DES_MAC_MD5, "DES MAC MD5"},
+  { KRB_SGN_ALG_MD2_5,       "MD2.5"},
+  { KRB_SGN_ALG_DES_MAC,     "DES MAC"},
+  { KRB_SGN_ALG_HMAC,        "HMAC"},
+  { 0, NULL}
+};
+
+#define KRB_SEAL_ALG_DES_CBC   0x0000
+#define KRB_SEAL_ALG_RC4       0x0010
+#define KRB_SEAL_ALG_NONE      0xffff
+
+static const value_string spnego_krb5_seal_alg_vals[] = {
+  { KRB_SEAL_ALG_DES_CBC, "DES CBC"},
+  { KRB_SEAL_ALG_RC4,     "RC4"},
+  { KRB_SEAL_ALG_NONE,    "None"},
+  { 0, NULL}
+};
+
+/*
+ * XXX - is this for SPNEGO or just GSS-API?
+ * RFC 1964 is "The Kerberos Version 5 GSS-API Mechanism"; presumably one
+ * can directly designate Kerberos V5 as a mechanism in GSS-API, rather
+ * than designating SPNEGO as the mechanism, offering Kerberos V5, and
+ * getting it accepted.
+ */
+static int
+dissect_spnego_krb5_getmic_base(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree);
+static int
+dissect_spnego_krb5_wrap_base(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree);
+
+static void
+dissect_spnego_krb5(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+       proto_item *item;
+       proto_tree *subtree;
+       int ret, offset = 0;
+       ASN1_SCK hnd;
+       gboolean def;
+       guint len1, cls, con, tag, oid_len, nbytes;
+       guint16 token_id;
+       subid_t *oid;
+       gchar *oid_string;
+       gssapi_oid_value *value;
+       tvbuff_t *krb5_tvb;
+
+       item = proto_tree_add_item(tree, hf_spnego_krb5, tvb, offset, 
+                                  -1, FALSE);
+
+       subtree = proto_item_add_subtree(item, ett_spnego_krb5);
+
+       /*
+        * The KRB5 blob conforms to RFC1964:
+        * [APPLICATION 0] {
+        *   OID,
+        *   USHORT (0x0001 == AP-REQ, 0x0002 == AP-REP, 0x0003 == ERROR),
+        *   OCTET STRING } 
+         *
+         * However, for some protocols, the KRB5 blob starts at the SHORT
+        * and has no DER encoded header etc.
+        *
+        * It appears that for some other protocols the KRB5 blob is just
+        * a Kerberos message, with no [APPLICATION 0] header, no OID,
+        * and no USHORT.
+        *
+        * So:
+        *
+        *      If we see an [APPLICATION 0] HEADER, we show the OID and
+        *      the USHORT, and then dissect the rest as a Kerberos message.
+        *
+        *      If we see an [APPLICATION 14] or [APPLICATION 15] header,
+        *      we assume it's an AP-REQ or AP-REP message, and dissect
+        *      it all as a Kerberos message.
+        *
+        *      Otherwise, we show the USHORT, and then dissect the rest
+        *      as a Kerberos message.
+        */
+
+       asn1_open(&hnd, tvb, offset);
+
+       /*
+        * Get the first header ...
+        */
+
+       ret = asn1_header_decode(&hnd, &cls, &con, &tag, &def, &len1);
+
+       if (ret != ASN1_ERR_NOERROR) {
+               dissect_parse_error(tvb, offset, pinfo, subtree,
+                                   "SPNEGO KRB5 Header", ret);
+               goto done;
+       }
+
+       if (cls == ASN1_APL && con == ASN1_CON) {
+           /*
+            * [APPLICATION <tag>]
+            */
+           switch (tag) {
+
+           case 0:
+               /*
+                * [APPLICATION 0]
+                */
+
+               offset = hnd.offset;
+
+               /* Next, the OID */
+
+               ret = asn1_oid_decode(&hnd, &oid, &oid_len, &nbytes);
+
+               if (ret != ASN1_ERR_NOERROR) {
+                   dissect_parse_error(tvb, offset, pinfo, subtree,
+                                       "SPNEGO supportedMech token", ret);
+                   goto done;
+               }
+
+               oid_string = format_oid(oid, oid_len);
+
+               value = gssapi_lookup_oid(oid, oid_len);
+
+               if (value) 
+                   proto_tree_add_text(subtree, tvb, offset, nbytes, 
+                                       "OID: %s (%s)",
+                                       oid_string, value->comment);
+               else
+                   proto_tree_add_text(subtree, tvb, offset, nbytes,
+                                       "OID: %s",
+                                       oid_string);
+         
+               g_free(oid_string);
+
+               offset += nbytes;
+
+               /* Next, the token ID ... */
+
+               token_id = tvb_get_letohs(tvb, offset);
+               proto_tree_add_uint(subtree, hf_spnego_krb5_tok_id, tvb, offset, 2,
+                                   token_id);
+
+               hnd.offset += 2;
+
+               offset += 2;
+
+               break;
+
+           case 14:    /* [APPLICATION 14] */
+           case 15:    /* [APPLICATION 15] */
+               /*
+                * No token ID - just dissect as a Kerberos message and
+                * return.
+                */
+               krb5_tvb = tvb_new_subset(tvb, offset, -1, -1); 
+               offset = dissect_kerberos_main(krb5_tvb, pinfo, subtree, FALSE);
+               return;
+
+           default:
+               proto_tree_add_text(subtree, tvb, offset, 0,
+                       "Unknown header (cls=%d, con=%d, tag=%d)",
+                       cls, con, tag);
+               goto done;
+           }
+       } else {
+           /* Next, the token ID ... */
+
+           token_id = tvb_get_letohs(tvb, offset);
+           proto_tree_add_uint(subtree, hf_spnego_krb5_tok_id, tvb, offset, 2,
+                               token_id);
+
+           hnd.offset += 2;
+
+           offset += 2;
+       }
+
+       switch (token_id) {
+
+       case KRB_TOKEN_AP_REQ:
+       case KRB_TOKEN_AP_REP:
+       case KRB_TOKEN_AP_ERR:
+         krb5_tvb = tvb_new_subset(tvb, offset, -1, -1); 
+         offset = dissect_kerberos_main(krb5_tvb, pinfo, subtree, FALSE);
+         break;
+
+       case KRB_TOKEN_GETMIC:
+         offset = dissect_spnego_krb5_getmic_base(tvb, offset, pinfo, subtree); 
+         break;
+
+       case KRB_TOKEN_WRAP:
+          offset = dissect_spnego_krb5_wrap_base(tvb, offset, pinfo, subtree);
+         break;
+
+       case KRB_TOKEN_DELETE_SEC_CONTEXT:
+
+         break;
+
+       default:
+
+         break;
+       }
+
+ done:
+       return;
+}
+
+/*
+ * XXX - This is for GSSAPI Wrap tokens ...
+ */
 static int
-dissect_spnego_mechTypes(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
-                        proto_tree *tree, ASN1_SCK *hnd)
+dissect_spnego_krb5_wrap_base(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
+{
+       guint16 sgn_alg;
+
+       /*
+        * The KRB5 blob conforms to RFC1964:
+        *   USHORT (0x0102 == GSS_Wrap)
+        *   and so on } 
+        */
+
+       /* Now, the sign and seal algorithms ... */
+
+       sgn_alg = tvb_get_letohs(tvb, offset);
+       proto_tree_add_uint(tree, hf_spnego_krb5_sgn_alg, tvb, offset, 2,
+                           sgn_alg);
+
+       offset += 2;
+
+       proto_tree_add_item(tree, hf_spnego_krb5_seal_alg, tvb, offset, 2,
+                           TRUE);
+
+       offset += 2;
+
+       /* Skip the filler */
+
+       offset += 2;
+
+       /* Encrypted sequence number */
+
+       proto_tree_add_item(tree, hf_spnego_krb5_snd_seq, tvb, offset, 8,
+                           TRUE);
+
+       offset += 8;
+
+       /* Checksum of plaintext padded data */
+
+       proto_tree_add_item(tree, hf_spnego_krb5_sgn_cksum, tvb, offset, 8,
+                           TRUE);
+
+       offset += 8;
+
+       /*
+        * At least according to draft-brezak-win2k-krb-rc4-hmac-04,
+        * if the signing algorithm is KRB_SGN_ALG_HMAC, there's an
+        * extra 8 bytes of "Random confounder" after the checksum.
+        * It certainly confounds code expecting all Kerberos 5
+        * GSS_Wrap() tokens to look the same....
+        */
+       if (sgn_alg == KRB_SGN_ALG_HMAC) {
+         proto_tree_add_item(tree, hf_spnego_krb5_confounder, tvb, offset, 8,
+                             TRUE);
+
+         offset += 8;
+       }
+
+       /*
+        * Return the offset past the checksum, so that we know where
+        * the data we're wrapped around starts.  Also, set the length
+        * of our top-level item to that offset, so it doesn't cover
+        * the data we're wrapped around.
+        */
+       return offset;
+}
+
+/*
+ * XXX - This is for GSSAPI GetMIC tokens ...
+ */
+static int
+dissect_spnego_krb5_getmic_base(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
+{
+       guint16 sgn_alg;
+
+       /*
+        * The KRB5 blob conforms to RFC1964:
+        *   USHORT (0x0101 == GSS_GetMIC)
+        *   and so on } 
+        */
+
+       /* Now, the sign algorithm ... */
+
+       sgn_alg = tvb_get_letohs(tvb, offset);
+       proto_tree_add_uint(tree, hf_spnego_krb5_sgn_alg, tvb, offset, 2,
+                           sgn_alg);
+
+       offset += 2;
+
+       /* Skip the filler */
+
+       offset += 4;
+
+       /* Encrypted sequence number */
+
+       proto_tree_add_item(tree, hf_spnego_krb5_snd_seq, tvb, offset, 8,
+                           TRUE);
+
+       offset += 8;
+
+       /* Checksum of plaintext padded data */
+
+       proto_tree_add_item(tree, hf_spnego_krb5_sgn_cksum, tvb, offset, 8,
+                           TRUE);
+
+       offset += 8;
+
+       /*
+        * At least according to draft-brezak-win2k-krb-rc4-hmac-04,
+        * if the signing algorithm is KRB_SGN_ALG_HMAC, there's an
+        * extra 8 bytes of "Random confounder" after the checksum.
+        * It certainly confounds code expecting all Kerberos 5
+        * GSS_Wrap() tokens to look the same....
+        */
+       if (sgn_alg == KRB_SGN_ALG_HMAC) {
+         proto_tree_add_item(tree, hf_spnego_krb5_confounder, tvb, offset, 8,
+                             TRUE);
+
+         offset += 8;
+       }
+
+       /*
+        * Return the offset past the checksum, so that we know where
+        * the data we're wrapped around starts.  Also, set the length
+        * of our top-level item to that offset, so it doesn't cover
+        * the data we're wrapped around.
+        */
+
+       return offset;
+}
+
+/*
+ * XXX - is this for SPNEGO or just GSS-API?
+ * RFC 1964 is "The Kerberos Version 5 GSS-API Mechanism"; presumably one
+ * can directly designate Kerberos V5 as a mechanism in GSS-API, rather
+ * than designating SPNEGO as the mechanism, offering Kerberos V5, and
+ * getting it accepted.
+ */
+static int
+dissect_spnego_krb5_wrap(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
 {
        proto_item *item;
        proto_tree *subtree;
+       int offset = 0;
+
+       item = proto_tree_add_item(tree, hf_spnego_krb5, tvb, 0, -1, FALSE);
+
+       subtree = proto_item_add_subtree(item, ett_spnego_krb5);
+
+       /*
+        * The KRB5 blob conforms to RFC1964:
+        *   USHORT (0x0102 == GSS_Wrap)
+        *   and so on } 
+        */
+
+       /* First, the token ID ... */
+
+       proto_tree_add_item(subtree, hf_spnego_krb5_tok_id, tvb, offset, 2,
+                           TRUE);
+
+       offset += 2;
+
+        offset = dissect_spnego_krb5_wrap_base(tvb, offset, pinfo, subtree);
+
+        /*
+        * Return the offset past the checksum, so that we know where
+        * the data we're wrapped around starts.  Also, set the length
+        * of our top-level item to that offset, so it doesn't cover
+        * the data we're wrapped around.
+        */
+       proto_item_set_len(item, offset);
+       return offset;
+}
+
+/* Spnego stuff from here */
+
+static int
+dissect_spnego_mechTypes(tvbuff_t *tvb, int offset, packet_info *pinfo,
+                        proto_tree *tree, ASN1_SCK *hnd,
+                        gssapi_oid_value **next_level_value_p)
+{
+       proto_item *item = NULL;
+       proto_tree *subtree = NULL;
        gboolean def;
        guint len1, len, cls, con, tag, nbytes;
        subid_t *oid;
        gchar *oid_string;
-       proto_item *sub_item;
-       proto_tree *oid_subtree;
        int ret;
-       int length = tvb_length_remaining(tvb, offset);
+       gboolean saw_mechanism = FALSE;
 
        /*
         * MechTypeList ::= SEQUENCE OF MechType
@@ -143,8 +604,8 @@ dissect_spnego_mechTypes(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
 
        offset = hnd->offset;
 
-       item = proto_tree_add_item( tree, hf_spnego_mechtype, tvb, offset, 
-                                   len1, FALSE);
+       item = proto_tree_add_item(tree, hf_spnego_mechtype, tvb, offset, 
+                                  len1, FALSE);
        subtree = proto_item_add_subtree(item, ett_spnego_mechtype);
 
        /*
@@ -152,19 +613,43 @@ dissect_spnego_mechTypes(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
         */
 
        while (len1) {
+         gssapi_oid_value *value;
 
          ret = asn1_oid_decode(hnd, &oid, &len, &nbytes);
 
          if (ret != ASN1_ERR_NOERROR) {
            dissect_parse_error(tvb, offset, pinfo, subtree,
-                               "GSS-API token", ret);
+                               "SPNEGO mechTypes token", ret);
            goto done;
          }
 
          oid_string = format_oid(oid, len);
-
-         proto_tree_add_text(subtree, tvb, offset, nbytes, "OID: %s",
-                             oid_string);
+         value = gssapi_lookup_oid(oid, len);
+         if (value)
+           proto_tree_add_text(subtree, tvb, offset, nbytes, "OID: %s (%s)",
+                               oid_string, value->comment);
+         else
+           proto_tree_add_text(subtree, tvb, offset, nbytes, "OID: %s",
+                               oid_string);
+
+         g_free(oid_string);
+
+         /*
+          * Tell our caller the first mechanism we see, so that if
+          * this is a negTokenInit with a mechToken, it can interpret
+          * the mechToken according to the first mechType.  (There
+          * might not have been any indication of the mechType
+          * in prior frames, so we can't necessarily use the
+          * mechanism from the conversation; i.e., a negTokenInit
+          * can contain the initial security token for the desired
+          * mechanism of the initiator - that's the first mechanism
+          * in the list.)
+          */
+         if (!saw_mechanism) {
+           if (value)
+             *next_level_value_p = value;
+           saw_mechanism = TRUE;
+         }
 
          offset += nbytes;
          len1 -= nbytes;
@@ -181,18 +666,68 @@ static int
 dissect_spnego_reqFlags(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
                        proto_tree *tree, ASN1_SCK *hnd)
 {
+       gboolean def;
+       guint len1, cls, con, tag, flags;
+       int ret;
+        proto_item *item;
+        proto_tree *subtree;
 
-  return offset;
+       ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
+
+       if (ret != ASN1_ERR_NOERROR) {
+               dissect_parse_error(tvb, offset, pinfo, tree,
+                                   "SPNEGO reqFlags header", ret);
+               goto done;
+       }
+
+       if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_BTS)) {
+               proto_tree_add_text(
+                       tree, tvb, offset, 0,
+                       "Unknown header (cls=%d, con=%d, tag=%d)",
+                       cls, con, tag);
+               goto done;
+       }
+
+       /* We must have a Bit String ... insert it */ 
+
+       offset = hnd->offset;
+
+        flags = tvb_get_guint8(tvb, offset);
+
+       item = proto_tree_add_item(tree, hf_spnego_reqflags, tvb, offset, len1,
+            FALSE);
+
+        subtree = proto_item_add_subtree(item, ett_spnego_reqflags);
+
+        /*
+         * Now, the bits. XXX: Assume 8 bits. FIXME.
+         */
+
+        proto_tree_add_boolean(subtree, hf_gssapi_reqflags_deleg, tvb, offset, len1, flags);
+        proto_tree_add_boolean(subtree, hf_gssapi_reqflags_mutual, tvb, offset, len1, flags);
+        proto_tree_add_boolean(subtree, hf_gssapi_reqflags_replay, tvb, offset, len1, flags);
+        proto_tree_add_boolean(subtree, hf_gssapi_reqflags_sequence, tvb, offset, len1, flags);
+        proto_tree_add_boolean(subtree, hf_gssapi_reqflags_anon, tvb, offset, len1, flags);
+        proto_tree_add_boolean(subtree, hf_gssapi_reqflags_conf, tvb, offset, len1, flags);
+        proto_tree_add_boolean(subtree, hf_gssapi_reqflags_integ, tvb, offset, len1, flags);
+
+       hnd->offset += len1;
+
+ done:
+       return offset + len1;
 
 }
 
 static int
 dissect_spnego_mechToken(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
-                        proto_tree *tree, ASN1_SCK *hnd)
+                        proto_tree *tree, ASN1_SCK *hnd,
+                        dissector_handle_t next_level_dissector)
 {
+        proto_item *item;
+       proto_tree *subtree;
        gboolean def;
        int ret;
-       guint oid_len, len, cls, con, tag, nbytes;
+       guint cls, con, tag, nbytes;
        tvbuff_t *token_tvb;
 
        /*
@@ -217,16 +752,17 @@ dissect_spnego_mechToken(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
 
        offset = hnd->offset;
 
-       proto_tree_add_text(tree, tvb, offset, nbytes, "mechToken: %s",
-                           tvb_format_text(tvb, offset, nbytes)); 
+       item = proto_tree_add_item(tree, hf_spnego_mechtoken, tvb, offset, 
+                                  nbytes, FALSE);
+       subtree = proto_item_add_subtree(item, ett_spnego_mechtoken);
 
        /*
         * Now, we should be able to dispatch after creating a new TVB.
         */
 
        token_tvb = tvb_new_subset(tvb, offset, nbytes, -1);
-       if (next_level_dissector != -1)
-         call_dissector(next_level_dissector, token_tvb, pinfo, tree);
+       if (next_level_dissector)
+         call_dissector(next_level_dissector, token_tvb, pinfo, subtree);
 
        hnd->offset += nbytes; /* Update this ... */
 
@@ -238,12 +774,12 @@ dissect_spnego_mechToken(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
 
 static int
 dissect_spnego_mechListMIC(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
-                          proto_tree *tree, ASN1_SCK *hnd)
+                          proto_tree *tree, ASN1_SCK *hnd,
+                          dissector_handle_t next_level_dissector)
 {
-       guint len1, len, cls, con, tag, nbytes;
+       guint len1, cls, con, tag;
        int ret;
        gboolean def;
-       int length = tvb_length_remaining(tvb, offset);
        proto_tree *subtree = NULL;
 
        /*
@@ -257,58 +793,74 @@ dissect_spnego_mechListMIC(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
                goto done;
        }
 
-       if (!(cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ)) {
-               proto_tree_add_text(
-                       subtree, tvb, offset, 0,
-                       "Unknown header (cls=%d, con=%d, tag=%d)",
-                       cls, con, tag);
-               goto done;
-       }
-
        offset = hnd->offset;
 
-       /* XXX: FIXME, we should dissect this as well */
+       if (cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ) {
+
+         /*
+          * There seems to be two different forms this can take
+          * One as an Octet string, and one as a general string in a 
+          * sequence ... We will have to dissect this later
+          */
+        
+         proto_tree_add_text(tree, tvb, offset + 4, len1 - 4,
+                             "mechListMIC: %s",
+                             tvb_format_text(tvb, offset + 4, len1 - 4));
 
+         /* Naughty ... but we have to adjust for what we never took */
+
+         hnd->offset += len1;
+         offset += len1;
+
+       }
+       else if (cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS) {
+         tvbuff_t *token_tvb;
+         proto_item *item;
+         proto_tree *subtree;
+
+         item = proto_tree_add_item(tree, hf_spnego_mechlistmic, tvb, offset, 
+                             len1, FALSE); 
+         subtree = proto_item_add_subtree(item, ett_spnego_mechlistmic);
+         
        /*
-        * There seems to be two different forms this can take
-        * One as an Octet string, and one as a general string in a 
-        * sequence ... We will have to dissect this later
+        * Now, we should be able to dispatch after creating a new TVB.
         */
-        
-       proto_tree_add_text(tree, tvb, offset + 4, len1 - 4,
-                           "mechListMIC: %s\n",
-                           tvb_format_text(tvb, offset + 4, len1 - 4));
 
-       /* Naughty ... but we have to adjust for what we never took */
+         token_tvb = tvb_new_subset(tvb, offset, len1, -1);
+         if (next_level_dissector)
+           call_dissector(next_level_dissector, token_tvb, pinfo, subtree);
 
-       hnd->offset += 4;
+         hnd->offset += len1; /* Update this ... */
+         offset += len1;
 
+       }
+       else {
+
+         proto_tree_add_text(subtree, tvb, offset, 0,
+                             "Unknown header (cls=%d, con=%d, tag=%d)",
+                             cls, con, tag);
+         goto done;
+       }
 
  done:
 
-       return offset + len1 - 4;
+       return offset;
 
 }
 
 static int
 dissect_spnego_negTokenInit(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
-                           proto_tree *tree, ASN1_SCK *hnd)
+                           proto_tree *tree, ASN1_SCK *hnd,
+                           gssapi_oid_value **next_level_value_p)
 {
        proto_item *item;
        proto_tree *subtree;
        gboolean def;
-       guint len1, len, cls, con, tag, nbytes;
-       subid_t *oid;
-       gssapi_oid_value *value;
-       dissector_handle_t handle;
-       gchar *oid_string;
-       proto_item *sub_item;
-       proto_tree *oid_subtree;
+       guint len1, len, cls, con, tag;
        int ret;
-       int length = tvb_length_remaining(tvb, offset);
 
        item = proto_tree_add_item( tree, hf_spnego_negtokeninit, tvb, offset,
-                                   length, FALSE);
+                                   -1, FALSE);
        subtree = proto_item_add_subtree(item, ett_spnego_negtokeninit);
 
        /*
@@ -339,9 +891,10 @@ dissect_spnego_negTokenInit(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
 
        offset = hnd->offset;
 
-       len1 -= 2; /* Account for the Header above ... */
-
        while (len1) {
+         int hdr_ofs;
+
+         hdr_ofs = hnd->offset;
 
          ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len);
 
@@ -352,13 +905,16 @@ dissect_spnego_negTokenInit(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
          }
 
          if (!(cls == ASN1_CTX && con == ASN1_CON)) {
-           proto_tree_add_text(
-                               subtree, tvb, offset, 0,
+           proto_tree_add_text(subtree, tvb, offset, 0,
                                "Unknown header (cls=%d, con=%d, tag=%d)",
                                cls, con, tag);
            goto done;
          }
 
+         /* Adjust for the length of the header */
+
+         len1 -= (hnd->offset - hdr_ofs);
+
          /* Should be one of the fields */
 
          switch (tag) {
@@ -366,24 +922,27 @@ dissect_spnego_negTokenInit(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
          case SPNEGO_mechTypes:
 
            offset = dissect_spnego_mechTypes(tvb, offset, pinfo,
-                                             subtree, hnd);
+                                             subtree, hnd,
+                                             next_level_value_p);
 
            break;
 
          case SPNEGO_reqFlags:
 
+           offset = dissect_spnego_reqFlags(tvb, offset, pinfo, subtree, hnd);
+
            break;
 
          case SPNEGO_mechToken:
 
            offset = dissect_spnego_mechToken(tvb, offset, pinfo, subtree, 
-                                             hnd);
+                                             hnd, (*next_level_value_p)->handle);
            break;
 
          case SPNEGO_mechListMIC:
 
            offset = dissect_spnego_mechListMIC(tvb, offset, pinfo, subtree,
-                                               hnd);
+                                               hnd, (*next_level_value_p)->handle);
            break;
 
          default:
@@ -391,7 +950,7 @@ dissect_spnego_negTokenInit(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
            break;
          }
 
-         len1 -= (len + 2); /* Account for header */
+         len1 -= len;
 
        }
 
@@ -406,7 +965,7 @@ dissect_spnego_negResult(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
 {
         gboolean def;
        int ret;
-       guint len1, len, cls, con, tag, nbytes, val;
+       guint len, cls, con, tag, val;
 
        ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len);
 
@@ -447,14 +1006,13 @@ dissect_spnego_negResult(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
 
 static int
 dissect_spnego_supportedMech(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
-                            proto_tree *tree, ASN1_SCK *hnd)
+                            proto_tree *tree, ASN1_SCK *hnd,
+                            gssapi_oid_value **next_level_value_p)
 {
-       gboolean def;
        int ret;
-       guint oid_len, len1, len, cls, con, tag, nbytes;
+       guint oid_len, nbytes;
        subid_t *oid;
-       gchar *p, *oid_string;
-       unsigned int i;
+       gchar *oid_string;
        gssapi_oid_value *value;
        conversation_t *conversation;
 
@@ -473,30 +1031,24 @@ dissect_spnego_supportedMech(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
        }
 
        oid_string = format_oid(oid, oid_len);
-
-       proto_tree_add_text(tree, tvb, offset, nbytes, "supportedMech: %s",
-                           oid_string);
-
-       offset += nbytes;
+       value = gssapi_lookup_oid(oid, oid_len);
+
+       if (value)
+         proto_tree_add_text(tree, tvb, offset, nbytes, 
+                             "supportedMech: %s (%s)",
+                             oid_string, value->comment);
+       else
+         proto_tree_add_text(tree, tvb, offset, nbytes, "supportedMech: %s",
+                             oid_string);
 
        g_free(oid_string);
 
-       oid_string = g_malloc(oid_len * 22 + 1);
-       p = oid_string;
-       len = sprintf(p, "%lu", (unsigned long)oid[0]);
-       p += len;
-       for (i = 1; i < oid_len;i++) {
-               len = sprintf(p, ".%lu", (unsigned long)oid[i]);
-               p += len;
-       }
-
-       value = g_hash_table_lookup(gssapi_oids, oid_string);
+       offset += nbytes;
 
        /* Should check for an unrecognized OID ... */
 
-       next_level_dissector = -1;
-
-       if (value) next_level_dissector = find_dissector(value->name);
+       if (value)
+         *next_level_value_p = value;
 
        /*
         * Now, we need to save this in per proto info in the
@@ -508,13 +1060,13 @@ dissect_spnego_supportedMech(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
         * could override that. :-(
         */
 
-       if (conversation = find_conversation(&pinfo->src, &pinfo->dst,
+       if ((conversation = find_conversation(&pinfo->src, &pinfo->dst,
                                             pinfo->ptype, pinfo->srcport,
-                                            pinfo->destport, 0)) {
+                                            pinfo->destport, 0))) {
 
 
          conversation_add_proto_data(conversation, proto_spnego, 
-                                     next_level_dissector);
+                                     *next_level_value_p);
        }
        else {
 
@@ -526,12 +1078,15 @@ dissect_spnego_supportedMech(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
 
 static int
 dissect_spnego_responseToken(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
-                            proto_tree *tree, ASN1_SCK *hnd)
+                            proto_tree *tree, ASN1_SCK *hnd,
+                            dissector_handle_t next_level_dissector)
 {
        gboolean def;
        int ret;
-       guint oid_len, len, cls, con, tag, nbytes;
+       guint cls, con, tag, nbytes;
        tvbuff_t *token_tvb;
+       proto_item *item;
+       proto_tree *subtree;
 
        ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &nbytes);
 
@@ -551,40 +1106,46 @@ dissect_spnego_responseToken(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
 
        offset = hnd->offset;
 
-       proto_tree_add_text(tree, tvb, offset, nbytes, "responseToken: %s",
-                           tvb_format_text(tvb, offset, nbytes)); 
+       item = proto_tree_add_item(tree, hf_spnego_responsetoken, tvb, offset -2 , 
+                                  nbytes + 2, FALSE); 
+
+       subtree = proto_item_add_subtree(item, ett_spnego_responsetoken);
+
 
        /*
         * Now, we should be able to dispatch after creating a new TVB.
+        * However, we should make sure that there is something in the 
+        * response token ...
         */
 
-       token_tvb = tvb_new_subset(tvb, offset, nbytes, -1);
-       if (next_level_dissector != -1)
-         call_dissector(next_level_dissector, token_tvb, pinfo, tree);
-
+       if (nbytes) {
+         token_tvb = tvb_new_subset(tvb, offset, nbytes, -1);
+         if (next_level_dissector)
+           call_dissector(next_level_dissector, token_tvb, pinfo, subtree);
+       }
+       else {
+         proto_tree_add_text(subtree, tvb, offset-2, 2, "<Empty String>");
+       }
        hnd->offset += nbytes; /* Update this ... */
 
  done:
        return offset + nbytes;
 }
 
-
 static int
 dissect_spnego_negTokenTarg(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
-                           proto_tree *tree, ASN1_SCK *hnd)
+                           proto_tree *tree, ASN1_SCK *hnd,
+                           gssapi_oid_value **next_level_value_p)
 
 {
        proto_item *item;
        proto_tree *subtree;
        gboolean def;
        int ret;
-       guint len1, len, cls, con, tag, nbytes;
-       dissector_handle_t handle = NULL;
-
-       int length = tvb_length_remaining(tvb, offset);
+       guint len1, len, cls, con, tag;
 
        item = proto_tree_add_item( tree, hf_spnego_negtokentarg, tvb, offset,
-                                   length, FALSE);
+                                   -1, FALSE);
        subtree = proto_item_add_subtree(item, ett_spnego_negtokentarg);
 
        /* 
@@ -617,9 +1178,10 @@ dissect_spnego_negTokenTarg(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
 
        offset = hnd->offset;
 
-       len1 -= 2; /* Account for the Header above ... */
-
        while (len1) {
+         int hdr_ofs;
+
+         hdr_ofs = hnd->offset; 
 
          ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len);
 
@@ -637,6 +1199,10 @@ dissect_spnego_negTokenTarg(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
            goto done;
          }
 
+         /* Adjust for the length of the header */
+
+         len1 -= (hnd->offset - hdr_ofs);
+
          /* Should be one of the fields */
 
          switch (tag) {
@@ -650,20 +1216,26 @@ dissect_spnego_negTokenTarg(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
          case SPNEGO_supportedMech:
 
            offset = dissect_spnego_supportedMech(tvb, offset, pinfo, subtree,
-                                                 hnd);
+                                                 hnd, next_level_value_p);
 
            break;
 
          case SPNEGO_responseToken:
 
            offset = dissect_spnego_responseToken(tvb, offset, pinfo, subtree,
-                                                 hnd);
+                                                 hnd,
+                                                 (*next_level_value_p != NULL) ?
+                                                     (*next_level_value_p)->handle :
+                                                     NULL);
            break;
 
          case SPNEGO_mechListMIC:
 
            offset = dissect_spnego_mechListMIC(tvb, offset, pinfo, subtree, 
-                                               hnd);
+                                               hnd,
+                                               (*next_level_value_p != NULL) ?
+                                                   (*next_level_value_p)->handle :
+                                                   NULL);
            break;
 
          default:
@@ -671,7 +1243,7 @@ dissect_spnego_negTokenTarg(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
            break;
          }
 
-         len1 -= (len + 2); /* FIXME: The +2 may be wrong */
+         len1 -= len;
 
        }
 
@@ -681,34 +1253,45 @@ dissect_spnego_negTokenTarg(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
 }
 
 static void
-dissect_spnego(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
+dissect_spnego(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 {
        proto_item *item;
        proto_tree *subtree;
-       int length = tvb_length_remaining(tvb, 0);
        int ret, offset = 0;
        ASN1_SCK hnd;
        gboolean def;
-       guint len1, len, cls, con, tag, nbytes;
+       guint len1, cls, con, tag;
        conversation_t *conversation;
-       dissector_handle_t handle;
+       gssapi_oid_value *next_level_value;
 
        /*
         * We need this later, so lets get it now ...
+        * It has to be per-frame as there can be more than one GSS-API
+        * negotiation in a conversation.
         */
 
-       conversation = find_conversation(&pinfo->src, &pinfo->dst,
-                                        pinfo->ptype, pinfo->srcport,
-                                        pinfo->destport, 0);
-
-       if (conversation && 
-           (handle = conversation_get_proto_data(conversation, 
-                                                proto_spnego))) {
-         next_level_dissector = handle;
-
+       next_level_value = p_get_proto_data(pinfo->fd, proto_spnego);
+       if (!next_level_value && !pinfo->fd->flags.visited) {
+           /*
+            * No handle attached to this frame, but it's the first
+            * pass, so it'd be attached to the conversation.
+            * If we have a conversation, try to get the handle,
+            * and if we get one, attach it to the frame.
+            */
+           conversation = find_conversation(&pinfo->src, &pinfo->dst,
+                                            pinfo->ptype, pinfo->srcport,
+                                            pinfo->destport, 0);
+
+           if (conversation) {
+               next_level_value = conversation_get_proto_data(conversation, 
+                                                              proto_spnego);
+               if (next_level_value)
+                   p_add_proto_data(pinfo->fd, proto_spnego, next_level_value);
+           }
        }
-       item = proto_tree_add_item(
-               tree, hf_spnego, tvb, offset, length, FALSE);
+
+       item = proto_tree_add_item(tree, hf_spnego, tvb, offset, 
+                                  -1, FALSE);
 
        subtree = proto_item_add_subtree(item, ett_spnego);
 
@@ -738,8 +1321,8 @@ dissect_spnego(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
          * It seems to duplicate the responseToken into the mechListMic field
          * as well. Naughty, naughty.
          *
-         * FIXME, the following code is broken so far.
         */
+
        asn1_open(&hnd, tvb, offset);
 
        /*
@@ -773,14 +1356,16 @@ dissect_spnego(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
        case SPNEGO_negTokenInit:
 
          offset = dissect_spnego_negTokenInit(tvb, offset, pinfo,
-                                              subtree, &hnd);
+                                              subtree, &hnd,
+                                              &next_level_value);
 
          break;
 
        case SPNEGO_negTokenTarg:
 
          offset = dissect_spnego_negTokenTarg(tvb, offset, pinfo,
-                                              subtree, &hnd);
+                                              subtree, &hnd,
+                                              &next_level_value);
          break;
 
        default: /* Broken, what to do? */
@@ -794,6 +1379,154 @@ dissect_spnego(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
 
 }
 
+static int
+dissect_spnego_wrap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+       proto_item *item;
+       proto_tree *subtree;
+       int ret, offset = 0;
+       int return_offset;
+       ASN1_SCK hnd;
+       gboolean def;
+       guint len1, cls, con, tag, nbytes;
+       guint oid_len;
+       subid_t *oid;
+       gchar *oid_string;
+       conversation_t *conversation;
+       gssapi_oid_value *next_level_value;
+       tvbuff_t *token_tvb;
+       int len;
+
+       /*
+        * We need this later, so lets get it now ...
+        * It has to be per-frame as there can be more than one GSS-API
+        * negotiation in a conversation.
+        */
+
+       next_level_value = p_get_proto_data(pinfo->fd, proto_spnego);
+       if (!next_level_value && !pinfo->fd->flags.visited) {
+           /*
+            * No handle attached to this frame, but it's the first
+            * pass, so it'd be attached to the conversation.
+            * If we have a conversation, try to get the handle,
+            * and if we get one, attach it to the frame.
+            */
+           conversation = find_conversation(&pinfo->src, &pinfo->dst,
+                                            pinfo->ptype, pinfo->srcport,
+                                            pinfo->destport, 0);
+
+           if (conversation) {
+               next_level_value = conversation_get_proto_data(conversation, 
+                                                              proto_spnego);
+               if (next_level_value)
+                   p_add_proto_data(pinfo->fd, proto_spnego, next_level_value);
+           }
+       }
+
+       item = proto_tree_add_item(tree, hf_spnego, tvb, offset, 
+                                  -1, FALSE);
+
+       subtree = proto_item_add_subtree(item, ett_spnego);
+
+       /*
+        * The TVB contains a [0] header and a sequence that consists of an
+        * object ID and a blob containing the data ...
+        * XXX - is this RFC 2743's "Mechanism-Independent Token Format",
+        * with the "optional" "use in non-initial tokens" being chosen.
+        */
+
+       asn1_open(&hnd, tvb, offset);
+
+       /*
+        * Get the first header ...
+        */
+
+       ret = asn1_header_decode(&hnd, &cls, &con, &tag, &def, &len1);
+
+       if (ret != ASN1_ERR_NOERROR) {
+               dissect_parse_error(tvb, offset, pinfo, subtree,
+                                   "SPNEGO context header", ret);
+               return_offset = tvb_length(tvb);
+               goto done;
+       }
+
+       if (!(cls == ASN1_APL && con == ASN1_CON && tag == 0)) {
+               proto_tree_add_text(
+                       subtree, tvb, offset, 0,
+                       "Unknown header (cls=%d, con=%d, tag=%d)",
+                       cls, con, tag);
+               return_offset = tvb_length(tvb);
+               goto done;
+       }
+
+       offset = hnd.offset;
+
+       /*
+        * Get the OID, and find the handle, if any
+        */
+
+       ret = asn1_oid_decode(&hnd, &oid, &oid_len, &nbytes);
+
+       if (ret != ASN1_ERR_NOERROR) {
+               dissect_parse_error(tvb, offset, pinfo, tree,
+                                   "SPNEGO wrap token", ret);
+               return_offset = tvb_length(tvb);
+               goto done;
+       }
+
+       oid_string = format_oid(oid, oid_len);
+       next_level_value = gssapi_lookup_oid(oid, oid_len);
+
+       /*
+        * XXX - what should we do if this doesn't match the value
+        * attached to the frame or conversation?  (That would be
+        * bogus, but that's not impossible - some broken implementation
+        * might negotiate some security mechanism but put the OID
+        * for some other security mechanism in GSS_Wrap tokens.)
+        */
+       if (next_level_value)
+         proto_tree_add_text(tree, tvb, offset, nbytes, 
+                             "thisMech: %s (%s)",
+                             oid_string, next_level_value->comment);
+       else
+         proto_tree_add_text(tree, tvb, offset, nbytes, "thisMech: %s",
+                             oid_string);
+
+       g_free(oid_string);
+
+       offset += nbytes;
+
+       /*
+        * Now dissect the GSS_Wrap token; it's assumed to be in the
+        * rest of the tvbuff.
+        */
+       item = proto_tree_add_item(tree, hf_spnego_wraptoken, tvb, offset, 
+                                  -1, FALSE); 
+
+       subtree = proto_item_add_subtree(item, ett_spnego_wraptoken);
+
+       /*
+        * Now, we should be able to dispatch after creating a new TVB.
+        * The subdissector must return the length of the part of the
+        * token it dissected, so we can return the length of the part
+        * we (and it) dissected.
+        */
+
+       token_tvb = tvb_new_subset(tvb, offset, -1, -1);
+       if (next_level_value->wrap_handle) {
+         len = call_dissector(next_level_value->wrap_handle, token_tvb, pinfo, subtree);
+         if (len == 0)
+           return_offset = tvb_length(tvb);
+         else
+           return_offset = offset + len;
+       } else
+         return_offset = tvb_length(tvb);
+ done:
+       asn1_close(&hnd, &offset);
+
+       return return_offset;
+}
+
 void
 proto_register_spnego(void)
 {
@@ -810,9 +1543,72 @@ proto_register_spnego(void)
                { &hf_spnego_mechtype,
                  { "mechType", "spnego.negtokeninit.mechtype", FT_NONE,
                    BASE_NONE, NULL, 0x0, "SPNEGO negTokenInit mechTypes", HFILL}},
+               { &hf_spnego_mechtoken,
+                 { "mechToken", "spnego.negtokeninit.mechtoken", FT_NONE,
+                   BASE_NONE, NULL, 0x0, "SPNEGO negTokenInit mechToken", HFILL}},
+               { &hf_spnego_mechlistmic,
+                 { "mechListMIC", "spnego.mechlistmic", FT_NONE,
+                   BASE_NONE, NULL, 0x0, "SPNEGO mechListMIC", HFILL}}, 
+               { &hf_spnego_responsetoken,
+                 { "responseToken", "spnego.negtokentarg.responsetoken",
+                   FT_NONE, BASE_NONE, NULL, 0x0, "SPNEGO responseToken",
+                   HFILL}},
                { &hf_spnego_negtokentarg_negresult,
                  { "negResult", "spnego.negtokeninit.negresult", FT_UINT16,
                    BASE_HEX, VALS(spnego_negResult_vals), 0, "negResult", HFILL}},
+               { &hf_spnego_reqflags, 
+                 { "reqFlags", "spnego.negtokeninit.reqflags", FT_BYTES,
+                   BASE_HEX, NULL, 0, "reqFlags", HFILL }},
+                { &hf_gssapi_reqflags_deleg,
+                  { "Delegation", "gssapi.reqflags.deleg", FT_BOOLEAN, 8,
+                    TFS(&tfs_reqflags_deleg), 0x01, "Delegation", HFILL }},
+                { &hf_gssapi_reqflags_mutual,
+                  { "Mutual Authentication", "gssapi.reqflags.mutual", FT_BOOLEAN,
+                    8, TFS(&tfs_reqflags_mutual), 0x02, "Mutual Authentication", HFILL}},
+                { &hf_gssapi_reqflags_replay,
+                  { "Replay Detection", "gssapi.reqflags.replay", FT_BOOLEAN,
+                    8, TFS(&tfs_reqflags_replay), 0x04, "Replay Detection", HFILL}},
+                { &hf_gssapi_reqflags_sequence,
+                  { "Out-of-sequence Detection", "gssapi.reqflags.sequence",
+                    FT_BOOLEAN, 8, TFS(&tfs_reqflags_sequence), 0x08, 
+                    "Out-of-sequence Detection", HFILL}},
+                { &hf_gssapi_reqflags_anon,
+                  { "Anonymous Authentication", "gssapi.reqflags.anon", 
+                    FT_BOOLEAN, 8, TFS(&tfs_reqflags_anon), 0x10,
+                    "Anonymous Authentication", HFILL}},
+                { &hf_gssapi_reqflags_conf,
+                  { "Per-message Confidentiality", "gssapi.reqflags.conf",
+                    FT_BOOLEAN, 8, TFS(&tfs_reqflags_conf), 0x20, 
+                    "Per-message Confidentiality", HFILL}},
+                { &hf_gssapi_reqflags_integ,
+                  { "Per-message Integrity", "gssapi.reqflags.integ", 
+                    FT_BOOLEAN, 8, TFS(&tfs_reqflags_integ), 0x40,
+                    "Per-message Integrity", HFILL}},
+               { &hf_spnego_wraptoken,
+                 { "wrapToken", "spnego.wraptoken",
+                   FT_NONE, BASE_NONE, NULL, 0x0, "SPNEGO wrapToken",
+                   HFILL}},
+               { &hf_spnego_krb5,
+                 { "krb5_blob", "spnego.krb5.blob", FT_BYTES,
+                   BASE_NONE, NULL, 0, "krb5_blob", HFILL }},
+               { &hf_spnego_krb5_tok_id,
+                 { "krb5_tok_id", "spnego.krb5.tok_id", FT_UINT16, BASE_HEX,
+                   VALS(spnego_krb5_tok_id_vals), 0, "KRB5 Token Id", HFILL}},
+               { &hf_spnego_krb5_sgn_alg,
+                 { "krb5_sgn_alg", "spnego.krb5.sgn_alg", FT_UINT16, BASE_HEX,
+                   VALS(spnego_krb5_sgn_alg_vals), 0, "KRB5 Signing Algorithm", HFILL}},
+               { &hf_spnego_krb5_seal_alg,
+                 { "krb5_seal_alg", "spnego.krb5.seal_alg", FT_UINT16, BASE_HEX,
+                   VALS(spnego_krb5_seal_alg_vals), 0, "KRB5 Sealing Algorithm", HFILL}},
+               { &hf_spnego_krb5_snd_seq,
+                 { "krb5_snd_seq", "spnego.krb5.snd_seq", FT_BYTES, BASE_NONE,
+                   NULL, 0, "KRB5 Encrypted Sequence Number", HFILL}},
+               { &hf_spnego_krb5_sgn_cksum,
+                 { "krb5_sgn_cksum", "spnego.krb5.sgn_cksum", FT_BYTES, BASE_NONE,
+                   NULL, 0, "KRB5 Data Checksum", HFILL}},
+               { &hf_spnego_krb5_confounder,
+                 { "krb5_confounder", "spnego.krb5.confounder", FT_BYTES, BASE_NONE,
+                   NULL, 0, "KRB5 Confounder", HFILL}},
        };
 
        static gint *ett[] = {
@@ -820,21 +1616,63 @@ proto_register_spnego(void)
                &ett_spnego_negtokeninit,
                &ett_spnego_negtokentarg,
                &ett_spnego_mechtype,
+               &ett_spnego_mechtoken,
+               &ett_spnego_mechlistmic,
+               &ett_spnego_responsetoken,
+               &ett_spnego_wraptoken,
+               &ett_spnego_krb5,
        };
 
        proto_spnego = proto_register_protocol(
                "Spnego", "Spnego", "spnego");
+       proto_spnego_krb5 = proto_register_protocol("SPNEGO-KRB5",
+                                                   "SPNEGO-KRB5",
+                                                   "spnego-krb5");
 
        proto_register_field_array(proto_spnego, hf, array_length(hf));
        proto_register_subtree_array(ett, array_length(ett));
-
-       register_dissector("spnego", dissect_spnego, proto_spnego);
 }
 
 void
 proto_reg_handoff_spnego(void)
 {
+       dissector_handle_t spnego_handle, spnego_wrap_handle;
+       dissector_handle_t spnego_krb5_handle, spnego_krb5_wrap_handle;
+
        /* Register protocol with GSS-API module */
 
-       gssapi_init_oid("1.3.6.1.5.5.2", proto_spnego, ett_spnego, "spnego");
+       spnego_handle = create_dissector_handle(dissect_spnego, proto_spnego);
+       spnego_wrap_handle = new_create_dissector_handle(dissect_spnego_wrap,
+                                                        proto_spnego);
+       gssapi_init_oid("1.3.6.1.5.5.2", proto_spnego, ett_spnego,
+           spnego_handle, spnego_wrap_handle,
+           "SPNEGO - Simple Protected Negotiation");
+
+       /* Register both the one MS created and the real one */
+       /*
+        * Thanks to Jean-Baptiste Marchand and Richard B Ward, the
+        * mystery of the MS KRB5 OID is cleared up. It was due to a library
+        * that did not handle OID components greater than 16 bits, and was
+        * fixed in Win2K SP2 as well as WinXP.
+        * See the archive of <ietf-krb-wg@anl.gov> for the thread topic
+        * SPNEGO implementation issues. 3-Dec-2002.
+        */
+       spnego_krb5_handle = create_dissector_handle(dissect_spnego_krb5,
+                                                    proto_spnego_krb5);
+       spnego_krb5_wrap_handle = new_create_dissector_handle(dissect_spnego_krb5_wrap,
+                                                             proto_spnego_krb5);
+       gssapi_init_oid("1.2.840.48018.1.2.2", proto_spnego_krb5, ett_spnego_krb5,
+                       spnego_krb5_handle, spnego_krb5_wrap_handle,
+                       "MS KRB5 - Microsoft Kerberos 5");
+       gssapi_init_oid("1.2.840.113554.1.2.2", proto_spnego_krb5, ett_spnego_krb5,
+                       spnego_krb5_handle, spnego_krb5_wrap_handle,
+                       "KRB5 - Kerberos 5");
+       gssapi_init_oid("1.2.840.113554.1.2.2.3", proto_spnego_krb5, ett_spnego_krb5,
+                       spnego_krb5_handle, spnego_krb5_wrap_handle,
+                       "KRB5 - Kerberos 5 - User to User");
+
+       /*
+        * Find the data handle for some calls
+        */
+       data_handle = find_dissector("data");
 }