/* packet-spnego.c * Routines for the simple and protected GSS-API negotiation mechanism * as described in RFC 2478. * Copyright 2002, Tim Potter * Copyright 2002, Richard Sharpe * Copyright 2003, Richard Sharpe * Copyright 2005, Ronnie Sahlberg (krb decryption) * Copyright 2005, Anders Broman (converted to asn2eth generated dissector) * * $Id$ * * Ethereal - Network traffic analyzer * By Gerald Combs * 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. */ /* The heimdal code for decryption of GSSAPI wrappers using heimdal comes from Heimdal 1.6 and has been modified for ethereal's requirements. */ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include "packet-dcerpc.h" #include "packet-gssapi.h" #include "packet-kerberos.h" #include #include #include #include #include #include "packet-ber.h" #define PNAME "Simple Protected Negotiation" #define PSNAME "SPNEGO" #define PFNAME "spnego" /* Initialize the protocol and registered fields */ static int proto_spnego = -1; static int proto_spnego_krb5 = -1; static int hf_spnego = -1; static int hf_spnego_wraptoken = -1; static int hf_spnego_krb5_oid; 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; #include "packet-spnego-hf.c" /* Global variables */ gchar MechType_oid[MAX_OID_STR_LEN]; gssapi_oid_value *next_level_value; gboolean saw_mechanism = FALSE; /* Initialize the subtree pointers */ static gint ett_spnego; static gint ett_spnego_wraptoken; static gint ett_spnego_krb5 = -1; #include "packet-spnego-ett.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; } #include "packet-spnego-fn.c" /* * 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, guint16 token_id); static void dissect_spnego_krb5(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { proto_item *item; proto_tree *subtree; int offset = 0; guint16 token_id; gchar oid[MAX_OID_STR_LEN]; gssapi_oid_value *value; tvbuff_t *krb5_tvb; gint8 class; gboolean pc, ind = 0; gint32 tag; guint32 len; 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. */ /* * Get the first header ... */ offset = dissect_ber_identifier(pinfo, subtree, tvb, offset, &class, &pc, &tag); offset = dissect_ber_length(pinfo, subtree, tvb, offset, &len, &ind); if (class == BER_CLASS_APP && pc == 1) { /* * [APPLICATION ] */ switch (tag) { case 0: /* * [APPLICATION 0] */ /* Next, the OID */ offset=dissect_ber_object_identifier(FALSE, pinfo, subtree, tvb, offset, hf_spnego_krb5_oid, oid); value = gssapi_lookup_oid_str(oid); token_id = tvb_get_letohs(tvb, offset); proto_tree_add_uint(subtree, hf_spnego_krb5_tok_id, tvb, offset, 2, token_id); offset += 2; break; case 14: /* [APPLICATION 14] */ case 15: /* [APPLICATION 15] */ /* * No token ID - just dissect as a Kerberos message and * return. */ offset = dissect_kerberos_main(tvb, pinfo, subtree, FALSE, NULL); return; default: proto_tree_add_text(subtree, tvb, offset, 0, "Unknown header (class=%d, pc=%d, tag=%d)", class, pc, 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); 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, token_id); break; case KRB_TOKEN_DELETE_SEC_CONTEXT: break; default: break; } done: return; } #ifdef HAVE_KERBEROS #include #ifndef KEYTYPE_ARCFOUR_56 # define KEYTYPE_ARCFOUR_56 24 #endif /* XXX - We should probably do a configure-time check for this instead */ #ifndef KRB5_KU_USAGE_SEAL # define KRB5_KU_USAGE_SEAL 22 #endif static int arcfour_mic_key(void *key_data, size_t key_size, int key_type, void *cksum_data, size_t cksum_size, void *key6_data) { guint8 k5_data[16]; guint8 T[4]; memset(T, 0, 4); if (key_type == KEYTYPE_ARCFOUR_56) { guint8 L40[14] = "fortybits"; memcpy(L40 + 10, T, sizeof(T)); md5_hmac( L40, 14, key_data, key_size, k5_data); memset(&k5_data[7], 0xAB, 9); } else { md5_hmac( T, 4, key_data, key_size, k5_data); } md5_hmac( cksum_data, cksum_size, k5_data, 16, key6_data); return 0; } static int usage2arcfour(int usage) { switch (usage) { case 3: /*KRB5_KU_AS_REP_ENC_PART 3 */ case 9: /*KRB5_KU_TGS_REP_ENC_PART_SUB_KEY 9 */ return 8; case 22: /*KRB5_KU_USAGE_SEAL 22 */ return 13; case 23: /*KRB5_KU_USAGE_SIGN 23 */ return 15; case 24: /*KRB5_KU_USAGE_SEQ 24 */ return 0; default : return 0; } } static int arcfour_mic_cksum(guint8 *key_data, int key_length, unsigned usage, u_char sgn_cksum[8], const void *v1, size_t l1, const void *v2, size_t l2, const void *v3, size_t l3) { const guint8 signature[] = "signaturekey"; guint8 ksign_c[16]; unsigned char t[4]; md5_state_t ms; unsigned char digest[16]; int rc4_usage; guint8 cksum[16]; rc4_usage=usage2arcfour(usage); md5_hmac(signature, sizeof(signature), key_data, key_length, ksign_c); md5_init(&ms); t[0] = (rc4_usage >> 0) & 0xFF; t[1] = (rc4_usage >> 8) & 0xFF; t[2] = (rc4_usage >> 16) & 0xFF; t[3] = (rc4_usage >> 24) & 0xFF; md5_append(&ms, t, 4); md5_append(&ms, v1, l1); md5_append(&ms, v2, l2); md5_append(&ms, v3, l3); md5_finish(&ms, digest); md5_hmac(digest, 16, ksign_c, 16, cksum); memcpy(sgn_cksum, cksum, 8); return 0; } /* * Verify padding of a gss wrapped message and return its length. */ static int gssapi_verify_pad(unsigned char *wrapped_data, int wrapped_length, size_t datalen, size_t *padlen) { unsigned char *pad; size_t padlength; int i; pad = wrapped_data + wrapped_length - 1; padlength = *pad; if (padlength > datalen) return 1; for (i = padlength; i > 0 && *pad == padlength; i--, pad--) ; if (i != 0) return 2; *padlen = padlength; return 0; } static int decrypt_arcfour(packet_info *pinfo, guint8 *input_message_buffer, guint8 *output_message_buffer, guint8 *key_value, int key_size, int key_type) { guint8 Klocaldata[16]; int ret; gint32 seq_number; size_t datalen; guint8 k6_data[16], SND_SEQ[8], Confounder[8]; guint8 cksum_data[8]; int cmp; int conf_flag; size_t padlen = 0; datalen = tvb_length(pinfo->gssapi_encrypted_tvb); if(tvb_get_ntohs(pinfo->gssapi_wrap_tvb, 4)==0x1000){ conf_flag=1; } else if (tvb_get_ntohs(pinfo->gssapi_wrap_tvb, 4)==0xffff){ conf_flag=0; } else { return -3; } if(tvb_get_ntohs(pinfo->gssapi_wrap_tvb, 6)!=0xffff){ return -4; } ret = arcfour_mic_key(key_value, key_size, key_type, (void *)tvb_get_ptr(pinfo->gssapi_wrap_tvb, 16, 8), 8, /* SGN_CKSUM */ k6_data); if (ret) { return -5; } { rc4_state_struct rc4_state; crypt_rc4_init(&rc4_state, k6_data, sizeof(k6_data)); memcpy(SND_SEQ, (unsigned char *)tvb_get_ptr(pinfo->gssapi_wrap_tvb, 8, 8), 8); crypt_rc4(&rc4_state, SND_SEQ, 8); memset(k6_data, 0, sizeof(k6_data)); } seq_number=g_ntohl(*((guint32 *)SND_SEQ)); cmp = memcmp(&SND_SEQ[4], "\xff\xff\xff\xff", 4); if(cmp){ cmp = memcmp(&SND_SEQ[4], "\x00\x00\x00\x00", 4); } if (cmp != 0) { return -6; } { int i; for (i = 0; i < 16; i++) Klocaldata[i] = ((u_char *)key_value)[i] ^ 0xF0; } ret = arcfour_mic_key(Klocaldata,sizeof(Klocaldata),key_type, SND_SEQ, 4, k6_data); memset(Klocaldata, 0, sizeof(Klocaldata)); if (ret) { return -7; } if(conf_flag) { rc4_state_struct rc4_state; crypt_rc4_init(&rc4_state, k6_data, sizeof(k6_data)); memcpy(Confounder, (unsigned char *)tvb_get_ptr(pinfo->gssapi_wrap_tvb, 24, 8), 8); crypt_rc4(&rc4_state, Confounder, 8); memcpy(output_message_buffer, input_message_buffer, datalen); crypt_rc4(&rc4_state, output_message_buffer, datalen); } else { memcpy(Confounder, tvb_get_ptr(pinfo->gssapi_wrap_tvb, 24, 8), 8); /* Confounder */ memcpy(output_message_buffer, input_message_buffer, datalen); } memset(k6_data, 0, sizeof(k6_data)); /* only normal (i.e. non DCE style wrapping use padding ? */ if(pinfo->decrypt_gssapi_tvb==DECRYPT_GSSAPI_NORMAL){ ret = gssapi_verify_pad(output_message_buffer,datalen,datalen, &padlen); if (ret) { return -9; } datalen -= padlen; } /* dont know what the checksum looks like for dce style gssapi */ if(pinfo->decrypt_gssapi_tvb==DECRYPT_GSSAPI_NORMAL){ ret = arcfour_mic_cksum(key_value, key_size, KRB5_KU_USAGE_SEAL, cksum_data, tvb_get_ptr(pinfo->gssapi_wrap_tvb, 0, 8), 8, Confounder, sizeof(Confounder), output_message_buffer, datalen + padlen); if (ret) { return -10; } cmp = memcmp(cksum_data, tvb_get_ptr(pinfo->gssapi_wrap_tvb, 16, 8), 8); /* SGN_CKSUM */ if (cmp) { return -11; } } return datalen; } #if defined(HAVE_HEIMDAL_KERBEROS) || defined(HAVE_MIT_KERBEROS) static void decrypt_gssapi_krb_arcfour_wrap(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, int keytype) { int ret; enc_key_t *ek; int length; const guint8 *original_data; static int omb_index=0; static guint8 *omb_arr[4]={NULL,NULL,NULL,NULL}; static guint8 *cryptocopy=NULL; /* workaround for pre-0.6.1 heimdal bug */ guint8 *output_message_buffer; omb_index++; if(omb_index>=4){ omb_index=0; } output_message_buffer=omb_arr[omb_index]; length=tvb_length(pinfo->gssapi_encrypted_tvb); original_data=tvb_get_ptr(pinfo->gssapi_encrypted_tvb, 0, length); /* dont do anything if we are not attempting to decrypt data */ /* if(!krb_decrypt){ return; } */ /* XXX we should only do this for first time, then store somewhere */ /* XXX We also need to re-read the keytab when the preference changes */ cryptocopy=ep_alloc(length); if(output_message_buffer){ g_free(output_message_buffer); output_message_buffer=NULL; } output_message_buffer=g_malloc(length); for(ek=enc_key_list;ek;ek=ek->next){ /* shortcircuit and bail out if enctypes are not matching */ if(ek->keytype!=keytype){ continue; } /* pre-0.6.1 versions of Heimdal would sometimes change the cryptotext data even when the decryption failed. This would obviously not work since we iterate over the keys. So just give it a copy of the crypto data instead. This has been seen for RC4-HMAC blobs. */ memcpy(cryptocopy, original_data, length); ret=decrypt_arcfour(pinfo, cryptocopy, output_message_buffer, ek->keyvalue, ek->keylength, ek->keytype ); if (ret >= 0) { proto_tree_add_text(tree, NULL, 0, 0, "[Decrypted using: %s]", ek->key_origin); pinfo->gssapi_decrypted_tvb=tvb_new_real_data( output_message_buffer, ret, ret); tvb_set_child_real_data_tvbuff(tvb, pinfo->gssapi_decrypted_tvb); add_new_data_source(pinfo, pinfo->gssapi_decrypted_tvb, "Decrypted GSS-Krb5"); return; } } return; } #endif /* HAVE_HEIMDAL_KERBEROS || HAVE_MIT_KERBEROS */ #endif /* * XXX - This is for GSSAPI Wrap tokens ... */ static int dissect_spnego_krb5_wrap_base(tvbuff_t *tvb, int offset, packet_info *pinfo #ifndef HAVE_KERBEROS _U_ #endif , proto_tree *tree, guint16 token_id #ifndef HAVE_KERBEROS _U_ #endif ) { guint16 sgn_alg, seal_alg; #ifdef HAVE_KERBEROS int start_offset=offset; #endif /* * 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; seal_alg = tvb_get_letohs(tvb, offset); proto_tree_add_uint(tree, hf_spnego_krb5_seal_alg, tvb, offset, 2, seal_alg); 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; } /* Is the data encrypted? */ pinfo->gssapi_data_encrypted=(seal_alg!=KRB_SEAL_ALG_NONE); #ifdef HAVE_KERBEROS #define GSS_ARCFOUR_WRAP_TOKEN_SIZE 32 if(pinfo->decrypt_gssapi_tvb){ /* if the caller did not provide a tvb, then we just use whatever is left of our current tvb. */ if(!pinfo->gssapi_encrypted_tvb){ int len; len=tvb_reported_length_remaining(tvb,offset); if(len>tvb_length_remaining(tvb, offset)){ /* no point in trying to decrypt, we dont have the full pdu. */ return offset; } pinfo->gssapi_encrypted_tvb = tvb_new_subset( tvb, offset, len, len); } /* if this is KRB5 wrapped rc4-hmac */ if((token_id==KRB_TOKEN_WRAP) &&(sgn_alg==KRB_SGN_ALG_HMAC) &&(seal_alg==KRB_SEAL_ALG_RC4)){ /* do we need to create a tvb for the wrapper as well ? */ if(!pinfo->gssapi_wrap_tvb){ pinfo->gssapi_wrap_tvb = tvb_new_subset( tvb, start_offset-2, GSS_ARCFOUR_WRAP_TOKEN_SIZE, GSS_ARCFOUR_WRAP_TOKEN_SIZE); } #if defined(HAVE_HEIMDAL_KERBEROS) || defined(HAVE_MIT_KERBEROS) decrypt_gssapi_krb_arcfour_wrap(tree, pinfo, tvb, 23 /* rc4-hmac */); #endif /* HAVE_HEIMDAL_KERBEROS || HAVE_MIT_KERBEROS */ } } #endif /* * 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. * * Note that for DCERPC the GSSAPI blobs comes after the data it wraps, * not before. */ 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; guint16 token_id; 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 ... */ token_id = tvb_get_letohs(tvb, offset); proto_tree_add_uint(subtree, hf_spnego_krb5_tok_id, tvb, offset, 2, token_id); offset += 2; offset = dissect_spnego_krb5_wrap_base(tvb, offset, pinfo, subtree, token_id); /* * 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_wrap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree) { proto_item *item; proto_tree *subtree; int offset = 0; /* * 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. */ 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 code addet to spnego.asn to handle this. */ offset = dissect_spnego_InitialContextToken(FALSE, tvb, offset, pinfo , subtree, -1); return offset; } static void dissect_spnego(tvbuff_t *tvb, packet_info *pinfo, proto_tree *parent_tree) { proto_item *item; proto_tree *subtree; int offset = 0; conversation_t *conversation; /* * 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->fd->num, &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(parent_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. * */ offset = dissect_spnego_NegotiationToken(FALSE, tvb, offset, pinfo, subtree, -1); } /*--- proto_register_spnego -------------------------------------------*/ void proto_register_spnego(void) { /* List of fields */ static hf_register_info hf[] = { { &hf_spnego, { "SPNEGO", "spnego", FT_NONE, BASE_NONE, NULL, 0x0, "SPNEGO", 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_oid, {"KRB5 OID", "spnego.krb5_oid", FT_STRING, BASE_NONE, NULL, 0, "KRB5 OID", 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}}, #include "packet-spnego-hfarr.c" }; /* List of subtrees */ static gint *ett[] = { &ett_spnego, &ett_spnego_wraptoken, &ett_spnego_krb5, #include "packet-spnego-ettarr.c" }; /* Register protocol */ proto_spnego = proto_register_protocol(PNAME, PSNAME, PFNAME); proto_spnego_krb5 = proto_register_protocol("SPNEGO-KRB5", "SPNEGO-KRB5", "spnego-krb5"); /* Register fields and subtrees */ proto_register_field_array(proto_spnego, hf, array_length(hf)); proto_register_subtree_array(ett, array_length(ett)); } /*--- proto_reg_handoff_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 */ 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 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"); }