Remove a bunch of duplicate semicolons.
[obnox/wireshark/wip.git] / packet-kerberos.c
index f609b4b1a6abf4ad3458eb96b4a9d0706fceeff5..c5d49c5d3fb69e6a205a86ca8925b6f9a7b89696 100644 (file)
@@ -2,8 +2,19 @@
  * Routines for Kerberos
  * Wes Hardaker (c) 2000
  * wjhardaker@ucdavis.edu
+ * Richard Sharpe (C) 2002, rsharpe@samba.org, modularized a bit more and
+ *                          added AP-REQ and AP-REP dissection
  *
- * $Id: packet-kerberos.c,v 1.31 2002/09/05 03:49:03 sharpe Exp $
+ * See RFC 1510, and various I-Ds and other documents showing additions,
+ * e.g. ones listed under
+ *
+ *     http://www.isi.edu/people/bcn/krb-revisions/
+ *
+ * and
+ *
+ *     http://www.ietf.org/internet-drafts/draft-ietf-krb-wg-kerberos-clarifications-03.txt
+ *
+ * $Id: packet-kerberos.c,v 1.41 2003/07/08 06:39:13 guy Exp $
  *
  * Ethereal - Network traffic analyzer
  * By Gerald Combs <gerald@ethereal.com>
 #include "asn1.h"
 #include "packet-netbios.h"
 #include "packet-gssapi.h"
+#include "packet-tcp.h"
+#include "prefs.h"
 
 #define UDP_PORT_KERBEROS              88
 #define TCP_PORT_KERBEROS              88
 
+/* Desegment Kerberos over TCP messages */
+static gboolean krb_desegment = TRUE;
+
 static gint proto_kerberos = -1;
+static gint hf_krb_rm_reserved = -1;
+static gint hf_krb_rm_reclen = -1;
 
 static gint ett_kerberos = -1;
 static gint ett_preauth = -1;
@@ -56,6 +74,11 @@ static gint ett_ticket = -1;
 static gint ett_encrypted = -1;
 static gint ett_etype = -1;
 static gint ett_additional_tickets = -1;
+static gint ett_recordmark = -1;
+
+/* TCP Record Mark */
+#define        KRB_RM_RESERVED 0x80000000L
+#define        KRB_RM_RECLEN   0x7fffffffL
 
 #define KRB5_MSG_AS_REQ   10   /* AS-REQ type */
 #define KRB5_MSG_AS_REP   11   /* AS-REP type */
@@ -98,6 +121,14 @@ static gint ett_additional_tickets = -1;
 #define KRB5_BODY_ENC_AUTHORIZATION_DATA 10
 #define KRB5_BODY_ADDITIONAL_TICKETS     11
 
+/* TAGs within AP-REQ */
+#define KRB5_AP_REQ_APOPTIONS             2
+#define KRB5_AP_REQ_TICKET                3
+#define KRB5_AP_REQ_ENC_DATA              4
+
+/* TAGs within AP-REP */
+#define KRB5_AP_REP_ENC_DATA              2
+
 /* Type tags within KRB-ERROR */
 #define KRB5_ERROR_PVNO       0
 #define KRB5_ERROR_MSG_TYPE   1
@@ -121,6 +152,7 @@ static gint ett_additional_tickets = -1;
 #define KRB5_ADDR_DECNET     0x0c
 #define KRB5_ADDR_APPLETALK  0x10
 #define KRB5_ADDR_NETBIOS    0x14
+#define KRB5_ADDR_IPv6       0x18
 
 /* encryption type constants */
 #define KRB5_ENCTYPE_NULL                0
@@ -131,10 +163,20 @@ static gint ett_additional_tickets = -1;
 #define KRB5_ENCTYPE_DES3_CBC_SHA        5
 #define KRB5_ENCTYPE_DES3_CBC_RAW        6
 #define KRB5_ENCTYPE_DES_HMAC_SHA1       8
-#define KRB5_ENCTYPE_DES3_CBC_SHA1          0x10
+#define KRB5_ENCTYPE_DES3_CBC_SHA1       16
+#define KERB_ENCTYPE_RC4_HMAC            23 
+#define KERB_ENCTYPE_RC4_HMAC_EXP        24
 #define KRB5_ENCTYPE_UNKNOWN                0x1ff
 #define KRB5_ENCTYPE_LOCAL_DES3_HMAC_SHA1   0x7007
 
+/*
+ * For KERB_ENCTYPE_RC4_HMAC and KERB_ENCTYPE_RC4_HMAC_EXP, see
+ *
+ *     http://www.ietf.org/internet-drafts/draft-brezak-win2k-krb-rc4-hmac-04.txt
+ *
+ * unless it's expired.
+ */
+
 /* pre-authentication type constants */
 #define KRB5_PA_TGS_REQ                1
 #define KRB5_PA_ENC_TIMESTAMP          2
@@ -146,10 +188,18 @@ static gint ett_additional_tickets = -1;
 #define KRB5_PA_OSF_DCE                8
 #define KRB5_PA_CYBERSAFE_SECUREID     9
 #define KRB5_PA_AFS3_SALT              10
-#define KRB5_PA_ENCTYPE_INFO             11
+#define KRB5_PA_ENCTYPE_INFO           11
 #define KRB5_PA_SAM_CHALLENGE          12
 #define KRB5_PA_SAM_RESPONSE           13
 #define KRB5_PA_DASS                   16
+#define KRB5_PA_USE_SPECIFIED_KVNO     20
+#define KRB5_PA_SAM_REDIRECT           21
+#define KRB5_PA_GET_FROM_TYPED_DATA    22
+#define KRB5_PA_SAM_ETYPE_INFO         23
+#define KRB5_PA_ALT_PRINC              24
+#define KRB5_PA_SAM_CHALLENGE2         30
+#define KRB5_PA_SAM_RESPONSE2          31
+#define KRB5_PA_PAC_REQUEST            128
 
 /* Type tags within Ticket */
 #define KRB5_TKT_TKT_VNO  0
@@ -214,6 +264,7 @@ static gint ett_additional_tickets = -1;
 #define KRB5_ET_KRB5KRB_AP_ERR_METHOD                    48
 #define KRB5_ET_KRB5KRB_AP_ERR_BADSEQ                    49
 #define KRB5_ET_KRB5KRB_AP_ERR_INAPP_CKSUM               50
+#define KRB5_ET_KRB5KRB_ERR_RESPONSE_TOO_BIG             52
 #define KRB5_ET_KRB5KRB_ERR_GENERIC                      60
 #define KRB5_ET_KRB5KRB_ERR_FIELD_TOOLONG                61
 
@@ -265,6 +316,7 @@ static const value_string krb5_error_codes[] = {
        { KRB5_ET_KRB5KRB_AP_ERR_METHOD, "KRB5KRB_AP_ERR_METHOD" },
        { KRB5_ET_KRB5KRB_AP_ERR_BADSEQ, "KRB5KRB_AP_ERR_BADSEQ" },
        { KRB5_ET_KRB5KRB_AP_ERR_INAPP_CKSUM, "KRB5KRB_AP_ERR_INAPP_CKSUM" },
+       { KRB5_ET_KRB5KRB_ERR_RESPONSE_TOO_BIG, "KRB5KRB_ERR_RESPONSE_TOO_BIG"},
        { KRB5_ET_KRB5KRB_ERR_GENERIC, "KRB5KRB_ERR_GENERIC" },
        { KRB5_ET_KRB5KRB_ERR_FIELD_TOOLONG, "KRB5KRB_ERR_FIELD_TOOLONG" },
        { 0, NULL }
@@ -296,6 +348,14 @@ static const value_string krb5_preauthentication_types[] = {
     { KRB5_PA_SAM_CHALLENGE        , "PA-SAM-CHALLENGE" },
     { KRB5_PA_SAM_RESPONSE         , "PA-SAM-RESPONSE" },
     { KRB5_PA_DASS                 , "PA-DASS" },
+    { KRB5_PA_USE_SPECIFIED_KVNO   , "PA-USE-SPECIFIED-KVNO" },
+    { KRB5_PA_SAM_REDIRECT         , "PA-SAM-REDIRECT" },
+    { KRB5_PA_GET_FROM_TYPED_DATA  , "PA-GET-FROM-TYPED-DATA" },
+    { KRB5_PA_SAM_ETYPE_INFO       , "PA-SAM-ETYPE-INFO" },
+    { KRB5_PA_ALT_PRINC            , "PA-ALT-PRINC" },
+    { KRB5_PA_SAM_CHALLENGE2       , "PA-SAM-CHALLENGE2" },
+    { KRB5_PA_SAM_RESPONSE2        , "PA-SAM-RESPONSE2" },
+    { KRB5_PA_PAC_REQUEST          , "PA-PAC-REQUEST" },
     { 0                            , NULL },
 };
 
@@ -309,6 +369,8 @@ static const value_string krb5_encryption_types[] = {
     { KRB5_ENCTYPE_DES3_CBC_RAW   , "des3-cbc-raw" },
     { KRB5_ENCTYPE_DES_HMAC_SHA1  , "des-hmac-sha1" },
     { KRB5_ENCTYPE_DES3_CBC_SHA1  , "des3-cbc-sha1" },
+    { KERB_ENCTYPE_RC4_HMAC       , "rc4-hmac" },
+    { KERB_ENCTYPE_RC4_HMAC_EXP   , "rc4-hmac-exp" },
     { KRB5_ENCTYPE_UNKNOWN        , "unknown" },
     { KRB5_ENCTYPE_LOCAL_DES3_HMAC_SHA1    , "local-des3-hmac-sha1" },
     { 0                            , NULL },
@@ -322,6 +384,7 @@ static const value_string krb5_address_types[] = {
     { KRB5_ADDR_DECNET,                "DECNET"},
     { KRB5_ADDR_APPLETALK,     "APPLETALK"},
     { KRB5_ADDR_NETBIOS,       "NETBIOS"},
+    { KRB5_ADDR_IPv6,          "IPv6"},
     { 0,                        NULL },
 };
 
@@ -339,16 +402,31 @@ static const value_string krb5_msg_types[] = {
         { 0,                    NULL },
 };
 
+static struct { char *set; char *unset; } bitval = { "Set", "Not set" };
+
 static int dissect_PrincipalName(char *title, ASN1_SCK *asn1p,
                                  packet_info *pinfo, proto_tree *tree,
                                  int start_offset);
-int dissect_Ticket(ASN1_SCK *asn1p, packet_info *pinfo,
+static int dissect_Ticket(ASN1_SCK *asn1p, packet_info *pinfo,
                           proto_tree *tree, int start_offset);
 static int dissect_EncryptedData(char *title, ASN1_SCK *asn1p,
                                 packet_info *pinfo, proto_tree *tree,
                                 int start_offset);
 static int dissect_Addresses(ASN1_SCK *asn1p, packet_info *pinfo,
                              proto_tree *tree, int start_offset);
+static void dissect_kerberos_udp(tvbuff_t *tvb, packet_info *pinfo,
+                                proto_tree *tree);
+static void dissect_kerberos_tcp(tvbuff_t *tvb, packet_info *pinfo,
+                                proto_tree *tree);
+static gint dissect_kerberos_common(tvbuff_t *tvb, packet_info *pinfo,
+                                       proto_tree *tree, int do_col_info,
+                                       gboolean have_rm);
+static void show_krb_recordmark(proto_tree *kerberos_tree, tvbuff_t *tvb,
+                               gint start, guint32 krb_rm);
+static gint kerberos_rm_to_reclen(guint krb_rm);
+static void dissect_kerberos_tcp_pdu(tvbuff_t *tvb, packet_info *pinfo,
+                               proto_tree *tree);
+static guint get_krb_pdu_len(tvbuff_t *tvb, int offset);
 
 static const char *
 to_error_str(int ret) {
@@ -396,8 +474,8 @@ krb_proto_tree_add_time(proto_tree *tree, tvbuff_t *tvb, int offset,
    ret = asn1_header_decode (asn1p, &cls, &con, &tag, &def, &item_len); \
    if (ret != ASN1_ERR_NOERROR) {\
        if (check_col(pinfo->cinfo, COL_INFO)) \
-           col_add_fstr(pinfo->cinfo, COL_INFO, "ERROR: Problem at %s: %s", \
-                    token, to_error_str(ret)); \
+           col_add_fstr(pinfo->cinfo, COL_INFO, "ERROR: Problem at %s: %s, offset: %d", \
+                    token, to_error_str(ret), start); \
        return -1; \
    } \
    if (!def) {\
@@ -425,8 +503,8 @@ krb_proto_tree_add_time(proto_tree *tree, tvbuff_t *tvb, int offset,
 #define DIE_WITH_BAD_TYPE(token, expected_tag) \
     { \
       if (check_col(pinfo->cinfo, COL_INFO)) \
-         col_add_fstr(pinfo->cinfo, COL_INFO, "ERROR: Problem at %s: %s (tag=%d exp=%d)", \
-                      token, to_error_str(ASN1_ERR_WRONG_TYPE), tag, expected_tag); \
+         col_add_fstr(pinfo->cinfo, COL_INFO, "ERROR: Problem at %s: %s (tag=%d exp=%d, con=%d, cls=%d, offset=%0x)", \
+                      token, to_error_str(ASN1_ERR_WRONG_TYPE), tag, expected_tag, con, cls, start); \
       return -1; \
     }
 
@@ -487,6 +565,24 @@ krb_proto_tree_add_time(proto_tree *tree, tvbuff_t *tvb, int offset,
 
     which is all over the place in krb5 */
 
+#if 0
+
+/*
+ * Dissect Kerberos 5 flags, which seems to be encoded as an ASN.1 
+ * bit field ... but, there is a one byte padding field (why the f***
+ * they did that I don't know ...
+ *
+ * We will use this routine to dissect several different types of flags
+ * so we will pass in the ETT value to build the flags etc
+ */
+static void 
+dissect_ap_options(tvbuff_t *tvb, int offset)
+{
+
+}
+
+#endif
 static void
 dissect_type_value_pair(ASN1_SCK *asn1p, int *inoff,
                         guint32 *type, int *type_len, int *type_off,
@@ -536,8 +632,87 @@ dissect_type_value_pair(ASN1_SCK *asn1p, int *inoff,
     *inoff = offset + *val_len;
 }
 
-static gboolean
-dissect_kerberos_main(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+gint
+dissect_kerberos_main(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, int do_col_info)
+{
+    return (dissect_kerberos_common(tvb, pinfo, tree, do_col_info, FALSE));
+}
+
+static void
+dissect_kerberos_udp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+    if (check_col(pinfo->cinfo, COL_PROTOCOL))
+        col_set_str(pinfo->cinfo, COL_PROTOCOL, "KRB5");
+
+    (void)dissect_kerberos_common(tvb, pinfo, tree, TRUE, FALSE);
+}
+
+static gint
+kerberos_rm_to_reclen(guint krb_rm)
+{
+    return (krb_rm & KRB_RM_RECLEN);
+}
+
+static guint
+get_krb_pdu_len(tvbuff_t *tvb, int offset)
+{
+    guint krb_rm;
+    gint pdulen;
+
+    krb_rm = tvb_get_ntohl(tvb, offset);
+    pdulen = kerberos_rm_to_reclen(krb_rm);
+    return (pdulen + 4);
+}
+
+static void
+dissect_kerberos_tcp_pdu(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+    pinfo->fragmented = TRUE;
+    if (dissect_kerberos_common(tvb, pinfo, tree, TRUE, TRUE) < 0) {
+       /*
+        * The dissector failed to recognize this as a valid
+        * Kerberos message.  Mark it as a continuation packet.
+        */
+       if (check_col(pinfo->cinfo, COL_INFO)) {
+               col_set_str(pinfo->cinfo, COL_INFO, "Continuation");
+       }
+    }
+}
+
+static void
+dissect_kerberos_tcp(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
+{
+    if (check_col(pinfo->cinfo, COL_PROTOCOL))
+        col_set_str(pinfo->cinfo, COL_PROTOCOL, "KRB5");
+
+    tcp_dissect_pdus(tvb, pinfo, tree, krb_desegment, 4, get_krb_pdu_len,
+       dissect_kerberos_tcp_pdu);
+}
+
+/*
+ * Display the TCP record mark.
+ */
+static void
+show_krb_recordmark(proto_tree *tree, tvbuff_t *tvb, gint start, guint32 krb_rm)
+{
+    gint rec_len;
+    proto_item *rm_item;
+    proto_tree *rm_tree;
+
+    if (tree == NULL)
+       return;
+
+    rec_len = kerberos_rm_to_reclen(krb_rm);
+    rm_item = proto_tree_add_text(tree, tvb, start, 4,
+       "Record Mark: %u %s", rec_len, plurality(rec_len, "byte", "bytes"));
+    rm_tree = proto_item_add_subtree(rm_item, ett_recordmark);
+    proto_tree_add_boolean(rm_tree, hf_krb_rm_reserved, tvb, start, 4, krb_rm);
+    proto_tree_add_uint(rm_tree, hf_krb_rm_reclen, tvb, start, 4, krb_rm);
+}
+
+static gint
+dissect_kerberos_common(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
+    int do_col_info, gboolean have_rm)
 {
     int offset = 0;
     proto_tree *kerberos_tree = NULL;
@@ -569,16 +744,36 @@ dissect_kerberos_main(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     guchar *str;
     int tmp_pos1, tmp_pos2;
 
-    asn1_open(&asn1, tvb, 0);
+    /* TCP record mark and length */
+    guint32 krb_rm = 0;
+    gint krb_reclen = 0;
+
+    if (have_rm) {
+       krb_rm = tvb_get_ntohl(tvb, offset);
+       krb_reclen = kerberos_rm_to_reclen(krb_rm);
+
+       /*
+        * What is a reasonable size limit?
+        */
+       if (krb_reclen > 10 * 1024 * 1024) {
+           return (-1);
+       }
+       offset += 4;
+    }
+
+    asn1_open(&asn1, tvb, offset);
 
     /* top header */
     KRB_HEAD_DECODE_OR_DIE("top");
     protocol_message_type = tag;
     if (tree) {
-        item = proto_tree_add_item(tree, proto_kerberos, tvb, offset,
-                                   item_len, FALSE);
+        item = proto_tree_add_item(tree, proto_kerberos, tvb, 0, -1, FALSE);
         kerberos_tree = proto_item_add_subtree(item, ett_kerberos);
     }
+    /*
+     * If item_len and krb_reclen disagree, which should we trust?  Stick
+     * with item_len for now.
+     */
     message_end = asn1p->offset + item_len;
 
     /* second header */
@@ -589,6 +784,9 @@ dissect_kerberos_main(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     KRB_DECODE_UINT32_OR_DIE("version", version);
 
     if (kerberos_tree) {
+       if (have_rm) {
+           show_krb_recordmark(kerberos_tree, tvb, 0, krb_rm);
+       }
         proto_tree_add_text(kerberos_tree, tvb, offset, length,
                             "Version: %d",
                             version);
@@ -607,10 +805,10 @@ dissect_kerberos_main(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     }
     offset += length;
 
-    if (check_col(pinfo->cinfo, COL_INFO))
+    if (do_col_info & check_col(pinfo->cinfo, COL_INFO))
         col_add_str(pinfo->cinfo, COL_INFO, val_to_str(msg_type, krb5_msg_types,
                                              "Unknown msg type %#x"));
-
+    
         /* is preauthentication present? */
     KRB_HEAD_DECODE_OR_DIE("padata-or-body");
     if (((protocol_message_type == KRB5_MSG_AS_REQ ||
@@ -621,7 +819,7 @@ dissect_kerberos_main(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
          tag == KRB5_KDC_REP_PADATA)) {
         /* pre-authentication supplied */
 
-        if (tree) {
+        if (kerberos_tree) {
             item = proto_tree_add_text(kerberos_tree, tvb, offset,
                                        item_len, "Pre-Authentication");
             preauth_tree = proto_item_add_subtree(item, ett_preauth);
@@ -686,7 +884,7 @@ dissect_kerberos_main(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
 */
         /* request body */
         KRB_HEAD_DECODE_OR_DIE("body-sequence");
-        if (tree) {
+        if (kerberos_tree) {
             item = proto_tree_add_text(kerberos_tree, tvb, offset,
                                        item_len, "Request");
             request_tree = proto_item_add_subtree(item, ett_request);
@@ -819,7 +1017,7 @@ dissect_kerberos_main(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
         }
 
         /* additional-tickets supplied */
-        if (tree) {
+        if (kerberos_tree) {
             item = proto_tree_add_text(kerberos_tree, tvb, offset,
                                        item_len, "Additional Tickets");
             additional_tickets_tree = proto_item_add_subtree(item, ett_additional_tickets);
@@ -883,6 +1081,45 @@ dissect_kerberos_main(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
         offset += length;
         break;
 
+    case KRB5_MSG_AP_REQ:
+
+        /* ap options */
+        /* We pulled the header above */
+        
+        KRB_HEAD_DECODE_OR_DIE("ap options:bits");
+
+        if (kerberos_tree) {
+                proto_tree_add_text(kerberos_tree, tvb, offset, item_len,
+                                    "APOptions: %s",
+                                    tvb_bytes_to_str(asn1p->tvb, asn1p->offset,
+                                                     item_len));
+        }
+        offset += item_len;
+        asn1p->offset += item_len;
+
+        KRB_DECODE_CONTEXT_HEAD_OR_DIE("ticket", KRB5_AP_REQ_TICKET);
+        length = dissect_Ticket(asn1p, pinfo, kerberos_tree, offset);
+        if (length == -1)
+            return -1;
+        offset += length;
+
+        KRB_DECODE_CONTEXT_HEAD_OR_DIE("authenticator",
+                                              KRB5_AP_REQ_ENC_DATA);
+        length = dissect_EncryptedData("Authenticator", asn1p, pinfo,
+                                       kerberos_tree, offset);
+        if (length == -1)
+            return -1;
+        offset += length;
+       break;
+
+    case KRB5_MSG_AP_REP:
+        length = dissect_EncryptedData("EncPart", asn1p, pinfo,
+                                       kerberos_tree, offset);
+        if (length == -1)
+            return -1;
+        offset += length;
+       break;
+
     case KRB5_MSG_ERROR:
 /*
   KRB-ERROR ::=   [APPLICATION 30] SEQUENCE {
@@ -1011,10 +1248,10 @@ dissect_kerberos_main(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
            KRB_DECODE_OCTET_STRING_OR_DIE("e-data", data, data_len, item_len);
 
            if (kerberos_tree) {
-               proto_tree_add_text(kerberos_tree, tvb, offset, data_len,
-                            "Error Data: %s", bytes_to_str(data, item_len));
+               proto_tree_add_text(kerberos_tree, tvb, offset, item_len,
+                            "Error Data: %s", bytes_to_str(data, data_len));
            }
-           offset += data_len;
+           offset += item_len;
        }
 
         break;
@@ -1022,15 +1259,6 @@ dissect_kerberos_main(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
     return offset;
 }
 
-static void
-dissect_kerberos(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
-{
-    if (check_col(pinfo->cinfo, COL_PROTOCOL))
-        col_set_str(pinfo->cinfo, COL_PROTOCOL, "KRB5");
-
-    dissect_kerberos_main(tvb, pinfo, tree);
-}
-
 static int
 dissect_PrincipalName(char *title, ASN1_SCK *asn1p, packet_info *pinfo,
                        proto_tree *tree, int start_offset)
@@ -1186,6 +1414,8 @@ dissect_Addresses(ASN1_SCK *asn1p, packet_info *pinfo,
                     }
                            break;
 
+                /* XXX - do KRB5_ADDR_IPv6 */
+
                 default:
                     proto_tree_add_text(address_tree, asn1p->tvb, tmp_pos2,
                                         str_len, "Value: %s",
@@ -1249,7 +1479,7 @@ dissect_EncryptedData(char *title, ASN1_SCK *asn1p, packet_info *pinfo,
       KRB_DECODE_UINT32_OR_DIE("kvno", val);
       if (encr_tree) {
           proto_tree_add_text(encr_tree, asn1p->tvb, offset, length,
-                              "KVNO: %d", val);
+                              "KVNO: %u", val);
       }
       offset += length;
       KRB_HEAD_DECODE_OR_DIE("cipher-wrap");
@@ -1259,15 +1489,15 @@ dissect_EncryptedData(char *title, ASN1_SCK *asn1p, packet_info *pinfo,
     KRB_DECODE_OCTET_STRING_OR_DIE("cipher", data, data_len, item_len);
 
     if (encr_tree) {
-        proto_tree_add_text(encr_tree, asn1p->tvb, offset, data_len,
-                            "CipherText: %s", bytes_to_str(data, item_len));
+        proto_tree_add_text(encr_tree, asn1p->tvb, offset, item_len,
+                            "CipherText: %s", bytes_to_str(data, data_len));
     }
-    offset += data_len;
+    offset += item_len;
 
     return offset - start_offset;
 }
 
-int
+static int
 dissect_Ticket(ASN1_SCK *asn1p, packet_info *pinfo,
               proto_tree *tree, int start_offset)
 {
@@ -1348,11 +1578,17 @@ dissect_Ticket(ASN1_SCK *asn1p, packet_info *pinfo,
 
 
 void
-proto_register_kerberos(void) {
-/*
+proto_register_kerberos(void)
+{
     static hf_register_info hf[] = {
+       { &hf_krb_rm_reserved, {
+           "Reserved", "kerberos.rm.reserved", FT_BOOLEAN, 32,
+           &bitval, KRB_RM_RESERVED, "Record mark reserved bit", HFILL }},
+       { &hf_krb_rm_reclen, {
+           "Record Length", "kerberos.rm.length", FT_UINT32, BASE_DEC,
+           NULL, KRB_RM_RECLEN, "Record length", HFILL }},
     };
-*/
+
     static gint *ett[] = {
         &ett_kerberos,
         &ett_preauth,
@@ -1363,22 +1599,34 @@ proto_register_kerberos(void) {
         &ett_addresses,
         &ett_etype,
         &ett_additional_tickets,
+        &ett_recordmark,
     };
+    module_t *krb_module;
+
     proto_kerberos = proto_register_protocol("Kerberos", "KRB5", "kerberos");
-/*
     proto_register_field_array(proto_kerberos, hf, array_length(hf));
-*/
     proto_register_subtree_array(ett, array_length(ett));
+
+    /* Register preferences */
+    krb_module = prefs_register_protocol(proto_kerberos, NULL);
+    prefs_register_bool_preference(krb_module, "desegment",
+       "Desegment Kerberos over TCP messages",
+       "Whether the dissector should desegment "
+       "multi-segment Kerberos messages", &krb_desegment);
 }
 
 void
 proto_reg_handoff_kerberos(void)
 {
-    dissector_handle_t kerberos_handle;
-
-    kerberos_handle = create_dissector_handle(dissect_kerberos, proto_kerberos);
-    dissector_add("udp.port", UDP_PORT_KERBEROS, kerberos_handle);
-    dissector_add("tcp.port", TCP_PORT_KERBEROS, kerberos_handle);
+    dissector_handle_t kerberos_handle_udp;
+    dissector_handle_t kerberos_handle_tcp;
+
+    kerberos_handle_udp = create_dissector_handle(dissect_kerberos_udp,
+       proto_kerberos);
+    kerberos_handle_tcp = create_dissector_handle(dissect_kerberos_tcp,
+       proto_kerberos);
+    dissector_add("udp.port", UDP_PORT_KERBEROS, kerberos_handle_udp);
+    dissector_add("tcp.port", TCP_PORT_KERBEROS, kerberos_handle_tcp);
 
 }