* 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;
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 */
#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
#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
#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
#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
#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
{ 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 }
{ 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 },
};
{ 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 },
{ KRB5_ADDR_DECNET, "DECNET"},
{ KRB5_ADDR_APPLETALK, "APPLETALK"},
{ KRB5_ADDR_NETBIOS, "NETBIOS"},
+ { KRB5_ADDR_IPv6, "IPv6"},
{ 0, NULL },
};
{ 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) {
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) {\
#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; \
}
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,
*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;
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 */
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);
}
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 ||
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);
*/
/* 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);
}
/* 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);
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 {
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;
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)
}
break;
+ /* XXX - do KRB5_ADDR_IPv6 */
+
default:
proto_tree_add_text(address_tree, asn1p->tvb, tmp_pos2,
str_len, "Value: %s",
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");
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)
{
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,
&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);
}