diff options
Diffstat (limited to 'epan/dissectors/packet-spnego.c')
-rw-r--r-- | epan/dissectors/packet-spnego.c | 1694 |
1 files changed, 1694 insertions, 0 deletions
diff --git a/epan/dissectors/packet-spnego.c b/epan/dissectors/packet-spnego.c new file mode 100644 index 0000000000..70eace5ff2 --- /dev/null +++ b/epan/dissectors/packet-spnego.c @@ -0,0 +1,1694 @@ +/* packet-spnego.c + * Routines for the simple and protected GSS-API negotiation mechanism + * 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$ + * + * Ethereal - Network traffic analyzer + * By Gerald Combs <gerald@ethereal.com> + * Copyright 1998 Gerald Combs + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + */ + +#ifdef HAVE_CONFIG_H +# include "config.h" +#endif + +#ifdef HAVE_SYS_TYPES_H +# include <sys/types.h> +#endif + +#include <glib.h> +#include <epan/packet.h> + +#include "asn1.h" +#include "format-oid.h" +#include "packet-gssapi.h" +#include "packet-kerberos.h" +#include <epan/conversation.h> + +#define SPNEGO_negTokenInit 0 +#define SPNEGO_negTokenTarg 1 +#define SPNEGO_mechTypes 0 +#define SPNEGO_reqFlags 1 +#define SPNEGO_mechToken 2 +#define SPNEGO_mechListMIC 3 +#define SPNEGO_negResult 0 +#define SPNEGO_supportedMech 1 +#define SPNEGO_responseToken 2 +#define SPNEGO_negResult_accept_completed 0 +#define SPNEGO_negResult_accept_incomplete 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; +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"}, + { 0, NULL} +}; + +/* + * These should be in the GSSAPI dissector ... XXX + */ + +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 */ + +static dissector_handle_t data_handle; + +static dissector_handle_t +gssapi_dissector_handle(gssapi_oid_value *next_level_value) { + if (next_level_value == NULL) { + return NULL; + } + return next_level_value->handle; +} + +static void +dissect_parse_error(tvbuff_t *tvb, int offset, packet_info *pinfo, + proto_tree *tree, const char *field_name, int ret) +{ + char *errstr; + + errstr = asn1_err_to_str(ret); + + if (tree != NULL) { + proto_tree_add_text(tree, tvb, offset, 0, + "ERROR: Couldn't parse %s: %s", field_name, errstr); + call_dissector(data_handle, + tvb_new_subset(tvb, offset, -1, -1), pinfo, tree); + } +} + +/* + * 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, NULL); + 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, NULL); + 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_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; + int ret; + gboolean saw_mechanism = FALSE; + + /* + * MechTypeList ::= SEQUENCE OF MechType + */ + + ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1); + + if (ret != ASN1_ERR_NOERROR) { + dissect_parse_error(tvb, offset, pinfo, subtree, + "SPNEGO last sequence header", ret); + 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; + + item = proto_tree_add_item(tree, hf_spnego_mechtype, tvb, offset, + len1, FALSE); + subtree = proto_item_add_subtree(item, ett_spnego_mechtype); + + /* + * Now, the object IDs ... We should translate them: FIXME + */ + + 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, + "SPNEGO mechTypes token", ret); + goto done; + } + + oid_string = format_oid(oid, len); + 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; + + } + + done: + + return offset; + +} + +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; + + 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, + dissector_handle_t next_level_dissector) +{ + proto_item *item; + proto_tree *subtree; + gboolean def; + int ret; + guint cls, con, tag, nbytes; + gint length_remaining, reported_length_remaining; + tvbuff_t *token_tvb; + + /* + * This appears to be a simple octet string ... + */ + + ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &nbytes); + + if (ret != ASN1_ERR_NOERROR) { + dissect_parse_error(tvb, offset, pinfo, tree, + "SPNEGO sequence header", ret); + goto done; + } + + if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS)) { + proto_tree_add_text( + tree, tvb, offset, 0, + "Unknown header (cls=%d, con=%d, tag=%d)", + cls, con, tag); + goto done; + } + + offset = hnd->offset; + + + /* Dont try to create an item with more bytes than remains in the + * frame or we will not even attempt to dissect those bytes we + * do have. (since there will be an exception) + * + * We use "tvb_ensure_length_remaining()" so that we throw + * an exception if there's nothing to dissect. + */ + length_remaining = tvb_ensure_length_remaining(tvb,offset); + reported_length_remaining = tvb_reported_length_remaining(tvb,offset); + if ((guint)length_remaining > nbytes) + length_remaining = nbytes; + if ((guint)reported_length_remaining > nbytes) + reported_length_remaining = nbytes; + item = proto_tree_add_item(tree, hf_spnego_mechtoken, tvb, offset, + length_remaining, 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, length_remaining, + reported_length_remaining); + if (next_level_dissector) + call_dissector(next_level_dissector, token_tvb, pinfo, subtree); + + hnd->offset += nbytes; /* Update this ... */ + +done: + return offset + nbytes; +} + +static int +dissect_spnego_mechListMIC(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, + proto_tree *tree, ASN1_SCK *hnd, + dissector_handle_t next_level_dissector) +{ + guint len1, cls, con, tag; + int ret; + gboolean def; + proto_tree *subtree = NULL; + + /* + * Add the mechListMIC [3] Octet String or General String ... + */ + ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1); + + if (ret != ASN1_ERR_NOERROR) { + dissect_parse_error(tvb, offset, pinfo, subtree, + "SPNEGO sequence header", ret); + goto done; + } + + offset = hnd->offset; + + 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); + + /* + * Now, we should be able to dispatch after creating a new TVB. + */ + + token_tvb = tvb_new_subset(tvb, offset, len1, -1); + if (next_level_dissector) + call_dissector(next_level_dissector, token_tvb, pinfo, subtree); + + 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; + +} + +static int +dissect_spnego_negTokenInit(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, + 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; + int ret; + + item = proto_tree_add_item( tree, hf_spnego_negtokeninit, tvb, offset, + -1, FALSE); + subtree = proto_item_add_subtree(item, ett_spnego_negtokeninit); + + /* + * Here is what we need to get ... + * NegTokenInit ::= SEQUENCE { + * mechTypes [0] MechTypeList OPTIONAL, + * reqFlags [1] ContextFlags OPTIONAL, + * mechToken [2] OCTET STRING OPTIONAL, + * mechListMIC [3] OCTET STRING OPTIONAL } + + */ + + ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1); + + if (ret != ASN1_ERR_NOERROR) { + dissect_parse_error(tvb, offset, pinfo, subtree, + "SPNEGO sequence header", ret); + 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; + + while (len1) { + int hdr_ofs; + + hdr_ofs = hnd->offset; + + ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len); + + if (ret != ASN1_ERR_NOERROR) { + dissect_parse_error(tvb, offset, pinfo, subtree, + "SPNEGO context header", ret); + goto done; + } + + if (!(cls == ASN1_CTX && con == ASN1_CON)) { + 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) { + + case SPNEGO_mechTypes: + + offset = dissect_spnego_mechTypes(tvb, offset, pinfo, + 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, gssapi_dissector_handle(*next_level_value_p)); + break; + + case SPNEGO_mechListMIC: + + offset = dissect_spnego_mechListMIC(tvb, offset, pinfo, subtree, + hnd, gssapi_dissector_handle(*next_level_value_p)); + break; + + default: + + break; + } + + len1 -= len; + + } + + done: + + return offset; /* Not sure this is right */ +} + +static int +dissect_spnego_negResult(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, + proto_tree *tree, ASN1_SCK *hnd) +{ + gboolean def; + int ret; + guint len, cls, con, tag, val; + + ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len); + + if (ret != ASN1_ERR_NOERROR) { + dissect_parse_error(tvb, offset, pinfo, tree, + "SPNEGO context header", ret); + goto done; + } + + if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_ENUM)) { + proto_tree_add_text( + tree, tvb, offset, 0, + "Unknown header (cls=%d, con=%d, tag=%d) xxx", + cls, con, tag); + goto done; + } + + offset = hnd->offset; + + /* Now, get the value */ + + ret = asn1_uint32_value_decode(hnd, len, &val); + + if (ret != ASN1_ERR_NOERROR) { + dissect_parse_error(tvb, offset, pinfo, tree, + "SPNEGO negResult value", ret); + goto done; + } + + proto_tree_add_item(tree, hf_spnego_negtokentarg_negresult, tvb, + offset, 1, FALSE); + + offset = hnd->offset; + + done: + return offset; +} + +static int +dissect_spnego_supportedMech(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, + proto_tree *tree, ASN1_SCK *hnd, + gssapi_oid_value **next_level_value_p) +{ + int ret; + guint oid_len, nbytes; + subid_t *oid; + gchar *oid_string; + gssapi_oid_value *value; + conversation_t *conversation; + + /* + * Now, get the OID, and find the handle, if any + */ + + offset = hnd->offset; + + ret = asn1_oid_decode(hnd, &oid, &oid_len, &nbytes); + + if (ret != ASN1_ERR_NOERROR) { + dissect_parse_error(tvb, offset, pinfo, tree, + "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(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); + + offset += nbytes; + + /* Should check for an unrecognized OID ... */ + + if (value) + *next_level_value_p = value; + + /* + * Now, we need to save this in per proto info in the + * conversation if it exists. We also should create a + * conversation if one does not exist. FIXME! + * Hmmm, might need to be smarter, because there can be + * multiple mechTypes in a negTokenInit with one being the + * default used in the Token if present. Then the negTokenTarg + * could override that. :-( + */ + + if ((conversation = find_conversation(&pinfo->src, &pinfo->dst, + pinfo->ptype, pinfo->srcport, + pinfo->destport, 0))) { + + + conversation_add_proto_data(conversation, proto_spnego, + *next_level_value_p); + } + else { + + } + + done: + return offset; +} + +static int +dissect_spnego_responseToken(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, + proto_tree *tree, ASN1_SCK *hnd, + dissector_handle_t next_level_dissector) +{ + gboolean def; + int ret; + 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); + + if (ret != ASN1_ERR_NOERROR) { + dissect_parse_error(tvb, offset, pinfo, tree, + "SPNEGO sequence header", ret); + goto done; + } + + if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS)) { + proto_tree_add_text( + tree, tvb, offset, 0, + "Unknown header (cls=%d, con=%d, tag=%d)", + cls, con, tag); + goto done; + } + + offset = hnd->offset; + + 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 ... + */ + + 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, + gssapi_oid_value **next_level_value_p) + +{ + proto_item *item; + proto_tree *subtree; + gboolean def; + int ret; + guint len1, len, cls, con, tag; + + item = proto_tree_add_item( tree, hf_spnego_negtokentarg, tvb, offset, + -1, FALSE); + subtree = proto_item_add_subtree(item, ett_spnego_negtokentarg); + + /* + * Here is what we need to get ... + * NegTokenTarg ::= SEQUENCE { + * negResult [0] ENUMERATED { + * accept_completed (0), + * accept_incomplete (1), + * reject (2) } OPTIONAL, + * supportedMech [1] MechType OPTIONAL, + * responseToken [2] OCTET STRING OPTIONAL, + * mechListMIC [3] OCTET STRING OPTIONAL } + */ + + ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1); + + if (ret != ASN1_ERR_NOERROR) { + dissect_parse_error(tvb, offset, pinfo, subtree, + "SPNEGO sequence header", ret); + 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; + + while (len1) { + int hdr_ofs; + + hdr_ofs = hnd->offset; + + ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len); + + if (ret != ASN1_ERR_NOERROR) { + dissect_parse_error(tvb, offset, pinfo, subtree, + "SPNEGO context header", ret); + goto done; + } + + if (!(cls == ASN1_CTX && con == ASN1_CON)) { + 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) { + + case SPNEGO_negResult: + + offset = dissect_spnego_negResult(tvb, offset, pinfo, subtree, + hnd); + break; + + case SPNEGO_supportedMech: + + offset = dissect_spnego_supportedMech(tvb, offset, pinfo, subtree, + hnd, next_level_value_p); + + break; + + case SPNEGO_responseToken: + + offset = dissect_spnego_responseToken(tvb, offset, pinfo, subtree, + hnd, gssapi_dissector_handle(*next_level_value_p)); + break; + + case SPNEGO_mechListMIC: + + offset = dissect_spnego_mechListMIC(tvb, offset, pinfo, subtree, + hnd, gssapi_dissector_handle(*next_level_value_p)); + break; + + default: + + break; + } + + len1 -= len; + + } + + done: + return offset; + +} + +static void +dissect_spnego(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; + conversation_t *conversation; + 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. + */ + + 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 ... + * Actually, it contains, according to RFC2478: + * NegotiationToken ::= CHOICE { + * negTokenInit [0] NegTokenInit, + * negTokenTarg [1] NegTokenTarg } + * NegTokenInit ::= SEQUENCE { + * mechTypes [0] MechTypeList OPTIONAL, + * reqFlags [1] ContextFlags OPTIONAL, + * mechToken [2] OCTET STRING OPTIONAL, + * mechListMIC [3] OCTET STRING OPTIONAL } + * NegTokenTarg ::= SEQUENCE { + * negResult [0] ENUMERATED { + * accept_completed (0), + * accept_incomplete (1), + * reject (2) } OPTIONAL, + * supportedMech [1] MechType OPTIONAL, + * responseToken [2] OCTET STRING OPTIONAL, + * mechListMIC [3] OCTET STRING OPTIONAL } + * + * Windows typically includes mechTypes and mechListMic ('NONE' + * in the case of NTLMSSP only). + * It seems to duplicate the responseToken into the mechListMic field + * as well. Naughty, naughty. + * + */ + + 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); + goto done; + } + + if (!(cls == ASN1_CTX && con == ASN1_CON)) { + proto_tree_add_text( + subtree, tvb, offset, 0, + "Unknown header (cls=%d, con=%d, tag=%d)", + cls, con, tag); + goto done; + } + + offset = hnd.offset; + + /* + * The Tag is one of negTokenInit or negTokenTarg + */ + + switch (tag) { + + case SPNEGO_negTokenInit: + + offset = dissect_spnego_negTokenInit(tvb, offset, pinfo, + subtree, &hnd, + &next_level_value); + + break; + + case SPNEGO_negTokenTarg: + + offset = dissect_spnego_negTokenTarg(tvb, offset, pinfo, + subtree, &hnd, + &next_level_value); + break; + + default: /* Broken, what to do? */ + + break; + } + + + done: + asn1_close(&hnd, &offset); + +} + +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) +{ + static hf_register_info hf[] = { + { &hf_spnego, + { "SPNEGO", "spnego", FT_NONE, BASE_NONE, NULL, 0x0, + "SPNEGO", HFILL }}, + { &hf_spnego_negtokeninit, + { "negTokenInit", "spnego.negtokeninit", FT_NONE, BASE_NONE, + NULL, 0x0, "SPNEGO negTokenInit", HFILL}}, + { &hf_spnego_negtokentarg, + { "negTokenTarg", "spnego.negtokentarg", FT_NONE, BASE_NONE, + NULL, 0x0, "SPNEGO negTokenTarg", HFILL}}, + { &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[] = { + &ett_spnego, + &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)); +} + +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 */ + + 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"); +} |