2 * Routines for the simple and protected GSS-API negotiation mechanism
3 * as described in RFC 2478.
4 * Copyright 2002, Tim Potter <tpot@samba.org>
5 * Copyright 2002, Richard Sharpe <rsharpe@ns.aus.com>
7 * $Id: packet-spnego.c,v 1.44 2003/05/23 18:34:58 sharpe Exp $
9 * Ethereal - Network traffic analyzer
10 * By Gerald Combs <gerald@ethereal.com>
11 * Copyright 1998 Gerald Combs
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
32 #ifdef HAVE_SYS_TYPES_H
33 # include <sys/types.h>
37 #include <epan/packet.h>
40 #include "format-oid.h"
41 #include "packet-gssapi.h"
42 #include "packet-kerberos.h"
43 #include <epan/conversation.h>
45 #define SPNEGO_negTokenInit 0
46 #define SPNEGO_negTokenTarg 1
47 #define SPNEGO_mechTypes 0
48 #define SPNEGO_reqFlags 1
49 #define SPNEGO_mechToken 2
50 #define SPNEGO_mechListMIC 3
51 #define SPNEGO_negResult 0
52 #define SPNEGO_supportedMech 1
53 #define SPNEGO_responseToken 2
54 #define SPNEGO_negResult_accept_completed 0
55 #define SPNEGO_negResult_accept_incomplete 1
56 #define SPNEGO_negResult_accept_reject 2
58 static int proto_spnego = -1;
59 static int proto_spnego_krb5 = -1;
61 static int hf_spnego = -1;
62 static int hf_spnego_negtokeninit = -1;
63 static int hf_spnego_negtokentarg = -1;
64 static int hf_spnego_mechtype = -1;
65 static int hf_spnego_mechtoken = -1;
66 static int hf_spnego_negtokentarg_negresult = -1;
67 static int hf_spnego_mechlistmic = -1;
68 static int hf_spnego_responsetoken = -1;
69 static int hf_spnego_reqflags = -1;
70 static int hf_spnego_wraptoken = -1;
71 static int hf_spnego_krb5 = -1;
72 static int hf_spnego_krb5_tok_id = -1;
73 static int hf_spnego_krb5_sgn_alg = -1;
74 static int hf_spnego_krb5_seal_alg = -1;
75 static int hf_spnego_krb5_snd_seq = -1;
76 static int hf_spnego_krb5_sgn_cksum = -1;
77 static int hf_spnego_krb5_confounder = -1;
79 static gint ett_spnego = -1;
80 static gint ett_spnego_negtokeninit = -1;
81 static gint ett_spnego_negtokentarg = -1;
82 static gint ett_spnego_mechtype = -1;
83 static gint ett_spnego_mechtoken = -1;
84 static gint ett_spnego_mechlistmic = -1;
85 static gint ett_spnego_responsetoken = -1;
86 static gint ett_spnego_wraptoken = -1;
87 static gint ett_spnego_krb5 = -1;
89 static const value_string spnego_negResult_vals[] = {
90 { SPNEGO_negResult_accept_completed, "Accept Completed" },
91 { SPNEGO_negResult_accept_incomplete, "Accept Incomplete" },
92 { SPNEGO_negResult_accept_reject, "Accept Reject"},
96 /* Display an ASN1 parse error. Taken from packet-snmp.c */
98 static dissector_handle_t data_handle;
101 dissect_parse_error(tvbuff_t *tvb, int offset, packet_info *pinfo,
102 proto_tree *tree, const char *field_name, int ret)
106 errstr = asn1_err_to_str(ret);
109 proto_tree_add_text(tree, tvb, offset, 0,
110 "ERROR: Couldn't parse %s: %s", field_name, errstr);
111 call_dissector(data_handle,
112 tvb_new_subset(tvb, offset, -1, -1), pinfo, tree);
117 * This is the SPNEGO KRB5 dissector. It is not true KRB5, but some ASN.1
118 * wrapped blob with an OID, USHORT token ID, and a Ticket, that is also
119 * ASN.1 wrapped by the looks of it. It conforms to RFC1964.
122 #define KRB_TOKEN_AP_REQ 0x0001
123 #define KRB_TOKEN_AP_REP 0x0002
124 #define KRB_TOKEN_AP_ERR 0x0003
125 #define KRB_TOKEN_GETMIC 0x0101
126 #define KRB_TOKEN_WRAP 0x0102
127 #define KRB_TOKEN_DELETE_SEC_CONTEXT 0x0201
129 static const value_string spnego_krb5_tok_id_vals[] = {
130 { KRB_TOKEN_AP_REQ, "KRB5_AP_REQ"},
131 { KRB_TOKEN_AP_REP, "KRB5_AP_REP"},
132 { KRB_TOKEN_AP_ERR, "KRB5_ERROR"},
133 { KRB_TOKEN_GETMIC, "KRB5_GSS_GetMIC" },
134 { KRB_TOKEN_WRAP, "KRB5_GSS_Wrap" },
135 { KRB_TOKEN_DELETE_SEC_CONTEXT, "KRB5_GSS_Delete_sec_context" },
139 #define KRB_SGN_ALG_DES_MAC_MD5 0x0000
140 #define KRB_SGN_ALG_MD2_5 0x0001
141 #define KRB_SGN_ALG_DES_MAC 0x0002
142 #define KRB_SGN_ALG_HMAC 0x0011
144 static const value_string spnego_krb5_sgn_alg_vals[] = {
145 { KRB_SGN_ALG_DES_MAC_MD5, "DES MAC MD5"},
146 { KRB_SGN_ALG_MD2_5, "MD2.5"},
147 { KRB_SGN_ALG_DES_MAC, "DES MAC"},
148 { KRB_SGN_ALG_HMAC, "HMAC"},
152 #define KRB_SEAL_ALG_DES_CBC 0x0000
153 #define KRB_SEAL_ALG_RC4 0x0010
154 #define KRB_SEAL_ALG_NONE 0xffff
156 static const value_string spnego_krb5_seal_alg_vals[] = {
157 { KRB_SEAL_ALG_DES_CBC, "DES CBC"},
158 { KRB_SEAL_ALG_RC4, "RC4"},
159 { KRB_SEAL_ALG_NONE, "None"},
164 * XXX - is this for SPNEGO or just GSS-API?
165 * RFC 1964 is "The Kerberos Version 5 GSS-API Mechanism"; presumably one
166 * can directly designate Kerberos V5 as a mechanism in GSS-API, rather
167 * than designating SPNEGO as the mechanism, offering Kerberos V5, and
168 * getting it accepted.
171 dissect_spnego_krb5_getmic_base(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree);
173 dissect_spnego_krb5(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
180 guint len1, cls, con, tag, oid_len, nbytes;
181 guint16 token_id = 0;
184 gssapi_oid_value *value;
187 item = proto_tree_add_item(tree, hf_spnego_krb5, tvb, offset,
190 subtree = proto_item_add_subtree(item, ett_spnego_krb5);
193 * The KRB5 blob conforms to RFC1964:
196 * USHORT (0x0001 == AP-REQ, 0x0002 == AP-REP, 0x0003 == ERROR),
199 * However, for some protocols, the KRB5 blob starts at the SHORT
200 * and has no DER encoded header etc.
202 * It appears that for some other protocols the KRB5 blob is just
203 * a Kerberos message, with no [APPLICATION 0] header, no OID,
208 * If we see an [APPLICATION 0] HEADER, we show the OID and
209 * the USHORT, and then dissect the rest as a Kerberos message.
211 * If we see an [APPLICATION 14] or [APPLICATION 15] header,
212 * we assume it's an AP-REQ or AP-REP message, and dissect
213 * it all as a Kerberos message.
215 * Otherwise, we show the USHORT, and then dissect the rest
216 * as a Kerberos message.
219 asn1_open(&hnd, tvb, offset);
222 * Get the first header ...
225 ret = asn1_header_decode(&hnd, &cls, &con, &tag, &def, &len1);
227 if (ret != ASN1_ERR_NOERROR) {
228 dissect_parse_error(tvb, offset, pinfo, subtree,
229 "SPNEGO KRB5 Header", ret);
233 if (cls == ASN1_APL && con == ASN1_CON) {
235 * [APPLICATION <tag>]
248 ret = asn1_oid_decode(&hnd, &oid, &oid_len, &nbytes);
250 if (ret != ASN1_ERR_NOERROR) {
251 dissect_parse_error(tvb, offset, pinfo, subtree,
252 "SPNEGO supportedMech token", ret);
256 oid_string = format_oid(oid, oid_len);
258 value = gssapi_lookup_oid(oid, oid_len);
261 proto_tree_add_text(subtree, tvb, offset, nbytes,
263 oid_string, value->comment);
265 proto_tree_add_text(subtree, tvb, offset, nbytes,
273 /* Next, the token ID ... */
275 token_id = tvb_get_letohs(tvb, offset);
276 proto_tree_add_item(subtree, hf_spnego_krb5_tok_id, tvb, offset, 2,
285 case 14: /* [APPLICATION 14] */
286 case 15: /* [APPLICATION 15] */
290 proto_tree_add_text(subtree, tvb, offset, 0,
291 "Unknown header (cls=%d, con=%d, tag=%d)",
296 /* Next, the token ID ... */
298 token_id = tvb_get_letohs(tvb, offset);
299 proto_tree_add_item(subtree, hf_spnego_krb5_tok_id, tvb, offset, 2,
309 case KRB_TOKEN_AP_REQ:
310 case KRB_TOKEN_AP_REP:
311 case KRB_TOKEN_AP_ERR:
312 krb5_tvb = tvb_new_subset(tvb, offset, -1, -1);
313 offset = dissect_kerberos_main(krb5_tvb, pinfo, subtree, FALSE);
316 case KRB_TOKEN_GETMIC:
318 offset = dissect_spnego_krb5_getmic_base(tvb, offset, pinfo, subtree);
325 case KRB_TOKEN_DELETE_SEC_CONTEXT:
339 * XXX - This is for GSSAPI GetMIC tokens ...
342 dissect_spnego_krb5_getmic_base(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
347 * The KRB5 blob conforms to RFC1964:
348 * USHORT (0x0101 == GSS_GetMIC)
352 /* Now, the sign algorithm ... */
354 sgn_alg = tvb_get_letohs(tvb, offset);
355 proto_tree_add_uint(tree, hf_spnego_krb5_sgn_alg, tvb, offset, 2,
360 /* Skip the filler */
364 /* Encrypted sequence number */
366 proto_tree_add_item(tree, hf_spnego_krb5_snd_seq, tvb, offset, 8,
371 /* Checksum of plaintext padded data */
373 proto_tree_add_item(tree, hf_spnego_krb5_sgn_cksum, tvb, offset, 8,
379 * At least according to draft-brezak-win2k-krb-rc4-hmac-04,
380 * if the signing algorithm is KRB_SGN_ALG_HMAC, there's an
381 * extra 8 bytes of "Random confounder" after the checksum.
382 * It certainly confounds code expecting all Kerberos 5
383 * GSS_Wrap() tokens to look the same....
385 if (sgn_alg == KRB_SGN_ALG_HMAC) {
386 proto_tree_add_item(tree, hf_spnego_krb5_confounder, tvb, offset, 8,
393 * Return the offset past the checksum, so that we know where
394 * the data we're wrapped around starts. Also, set the length
395 * of our top-level item to that offset, so it doesn't cover
396 * the data we're wrapped around.
403 * XXX - is this for SPNEGO or just GSS-API?
404 * RFC 1964 is "The Kerberos Version 5 GSS-API Mechanism"; presumably one
405 * can directly designate Kerberos V5 as a mechanism in GSS-API, rather
406 * than designating SPNEGO as the mechanism, offering Kerberos V5, and
407 * getting it accepted.
410 dissect_spnego_krb5_wrap(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
417 item = proto_tree_add_item(tree, hf_spnego_krb5, tvb, 0, -1, FALSE);
419 subtree = proto_item_add_subtree(item, ett_spnego_krb5);
422 * The KRB5 blob conforms to RFC1964:
423 * USHORT (0x0102 == GSS_Wrap)
427 /* First, the token ID ... */
429 proto_tree_add_item(subtree, hf_spnego_krb5_tok_id, tvb, offset, 2,
434 /* Now, the sign and seal algorithms ... */
436 sgn_alg = tvb_get_letohs(tvb, offset);
437 proto_tree_add_uint(subtree, hf_spnego_krb5_sgn_alg, tvb, offset, 2,
442 proto_tree_add_item(subtree, hf_spnego_krb5_seal_alg, tvb, offset, 2,
447 /* Skip the filler */
451 /* Encrypted sequence number */
453 proto_tree_add_item(subtree, hf_spnego_krb5_snd_seq, tvb, offset, 8,
458 /* Checksum of plaintext padded data */
460 proto_tree_add_item(subtree, hf_spnego_krb5_sgn_cksum, tvb, offset, 8,
466 * At least according to draft-brezak-win2k-krb-rc4-hmac-04,
467 * if the signing algorithm is KRB_SGN_ALG_HMAC, there's an
468 * extra 8 bytes of "Random confounder" after the checksum.
469 * It certainly confounds code expecting all Kerberos 5
470 * GSS_Wrap() tokens to look the same....
472 if (sgn_alg == KRB_SGN_ALG_HMAC) {
473 proto_tree_add_item(subtree, hf_spnego_krb5_confounder, tvb, offset, 8,
480 * Return the offset past the checksum, so that we know where
481 * the data we're wrapped around starts. Also, set the length
482 * of our top-level item to that offset, so it doesn't cover
483 * the data we're wrapped around.
485 proto_item_set_len(item, offset);
489 /* Spnego stuff from here */
492 dissect_spnego_mechTypes(tvbuff_t *tvb, int offset, packet_info *pinfo,
493 proto_tree *tree, ASN1_SCK *hnd,
494 gssapi_oid_value **next_level_value_p)
496 proto_item *item = NULL;
497 proto_tree *subtree = NULL;
499 guint len1, len, cls, con, tag, nbytes;
503 gboolean saw_mechanism = FALSE;
506 * MechTypeList ::= SEQUENCE OF MechType
509 ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
511 if (ret != ASN1_ERR_NOERROR) {
512 dissect_parse_error(tvb, offset, pinfo, subtree,
513 "SPNEGO last sequence header", ret);
517 if (!(cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ)) {
519 subtree, tvb, offset, 0,
520 "Unknown header (cls=%d, con=%d, tag=%d)",
525 offset = hnd->offset;
527 item = proto_tree_add_item(tree, hf_spnego_mechtype, tvb, offset,
529 subtree = proto_item_add_subtree(item, ett_spnego_mechtype);
532 * Now, the object IDs ... We should translate them: FIXME
536 gssapi_oid_value *value;
538 ret = asn1_oid_decode(hnd, &oid, &len, &nbytes);
540 if (ret != ASN1_ERR_NOERROR) {
541 dissect_parse_error(tvb, offset, pinfo, subtree,
542 "SPNEGO mechTypes token", ret);
546 oid_string = format_oid(oid, len);
547 value = gssapi_lookup_oid(oid, len);
549 proto_tree_add_text(subtree, tvb, offset, nbytes, "OID: %s (%s)",
550 oid_string, value->comment);
552 proto_tree_add_text(subtree, tvb, offset, nbytes, "OID: %s",
558 * Tell our caller the first mechanism we see, so that if
559 * this is a negTokenInit with a mechToken, it can interpret
560 * the mechToken according to the first mechType. (There
561 * might not have been any indication of the mechType
562 * in prior frames, so we can't necessarily use the
563 * mechanism from the conversation; i.e., a negTokenInit
564 * can contain the initial security token for the desired
565 * mechanism of the initiator - that's the first mechanism
568 if (!saw_mechanism) {
570 *next_level_value_p = value;
571 saw_mechanism = TRUE;
586 dissect_spnego_reqFlags(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
587 proto_tree *tree, ASN1_SCK *hnd)
590 guint len1, cls, con, tag;
593 ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
595 if (ret != ASN1_ERR_NOERROR) {
596 dissect_parse_error(tvb, offset, pinfo, tree,
597 "SPNEGO reqFlags header", ret);
601 if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_BTS)) {
603 tree, tvb, offset, 0,
604 "Unknown header (cls=%d, con=%d, tag=%d)",
609 /* We must have a Bit String ... insert it */
611 offset = hnd->offset;
613 proto_tree_add_item(tree, hf_spnego_reqflags, tvb, offset, len1,
619 return offset + len1;
624 dissect_spnego_mechToken(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
625 proto_tree *tree, ASN1_SCK *hnd,
626 dissector_handle_t next_level_dissector)
632 guint cls, con, tag, nbytes;
636 * This appears to be a simple octet string ...
639 ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &nbytes);
641 if (ret != ASN1_ERR_NOERROR) {
642 dissect_parse_error(tvb, offset, pinfo, tree,
643 "SPNEGO sequence header", ret);
647 if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS)) {
649 tree, tvb, offset, 0,
650 "Unknown header (cls=%d, con=%d, tag=%d)",
655 offset = hnd->offset;
657 item = proto_tree_add_item(tree, hf_spnego_mechtoken, tvb, offset,
659 subtree = proto_item_add_subtree(item, ett_spnego_mechtoken);
662 * Now, we should be able to dispatch after creating a new TVB.
665 token_tvb = tvb_new_subset(tvb, offset, nbytes, -1);
666 if (next_level_dissector)
667 call_dissector(next_level_dissector, token_tvb, pinfo, subtree);
669 hnd->offset += nbytes; /* Update this ... */
673 return offset + nbytes;
678 dissect_spnego_mechListMIC(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
679 proto_tree *tree, ASN1_SCK *hnd,
680 dissector_handle_t next_level_dissector)
682 guint len1, cls, con, tag;
685 proto_tree *subtree = NULL;
688 * Add the mechListMIC [3] Octet String or General String ...
690 ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
692 if (ret != ASN1_ERR_NOERROR) {
693 dissect_parse_error(tvb, offset, pinfo, subtree,
694 "SPNEGO sequence header", ret);
698 offset = hnd->offset;
700 if (cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ) {
703 * There seems to be two different forms this can take
704 * One as an Octet string, and one as a general string in a
705 * sequence ... We will have to dissect this later
708 proto_tree_add_text(tree, tvb, offset + 4, len1 - 4,
710 tvb_format_text(tvb, offset + 4, len1 - 4));
712 /* Naughty ... but we have to adjust for what we never took */
718 else if (cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS) {
723 item = proto_tree_add_item(tree, hf_spnego_mechlistmic, tvb, offset,
725 subtree = proto_item_add_subtree(item, ett_spnego_mechlistmic);
728 * Now, we should be able to dispatch after creating a new TVB.
731 token_tvb = tvb_new_subset(tvb, offset, len1, -1);
732 if (next_level_dissector)
733 call_dissector(next_level_dissector, token_tvb, pinfo, subtree);
735 hnd->offset += len1; /* Update this ... */
741 proto_tree_add_text(subtree, tvb, offset, 0,
742 "Unknown header (cls=%d, con=%d, tag=%d)",
754 dissect_spnego_negTokenInit(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
755 proto_tree *tree, ASN1_SCK *hnd,
756 gssapi_oid_value **next_level_value_p)
761 guint len1, len, cls, con, tag;
764 item = proto_tree_add_item( tree, hf_spnego_negtokeninit, tvb, offset,
766 subtree = proto_item_add_subtree(item, ett_spnego_negtokeninit);
769 * Here is what we need to get ...
770 * NegTokenInit ::= SEQUENCE {
771 * mechTypes [0] MechTypeList OPTIONAL,
772 * reqFlags [1] ContextFlags OPTIONAL,
773 * mechToken [2] OCTET STRING OPTIONAL,
774 * mechListMIC [3] OCTET STRING OPTIONAL }
778 ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
780 if (ret != ASN1_ERR_NOERROR) {
781 dissect_parse_error(tvb, offset, pinfo, subtree,
782 "SPNEGO sequence header", ret);
786 if (!(cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ)) {
788 subtree, tvb, offset, 0,
789 "Unknown header (cls=%d, con=%d, tag=%d)",
794 offset = hnd->offset;
799 hdr_ofs = hnd->offset;
801 ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len);
803 if (ret != ASN1_ERR_NOERROR) {
804 dissect_parse_error(tvb, offset, pinfo, subtree,
805 "SPNEGO context header", ret);
809 if (!(cls == ASN1_CTX && con == ASN1_CON)) {
810 proto_tree_add_text(subtree, tvb, offset, 0,
811 "Unknown header (cls=%d, con=%d, tag=%d)",
816 /* Adjust for the length of the header */
818 len1 -= (hnd->offset - hdr_ofs);
820 /* Should be one of the fields */
824 case SPNEGO_mechTypes:
826 offset = dissect_spnego_mechTypes(tvb, offset, pinfo,
832 case SPNEGO_reqFlags:
834 offset = dissect_spnego_reqFlags(tvb, offset, pinfo, subtree, hnd);
838 case SPNEGO_mechToken:
840 offset = dissect_spnego_mechToken(tvb, offset, pinfo, subtree,
841 hnd, (*next_level_value_p)->handle);
844 case SPNEGO_mechListMIC:
846 offset = dissect_spnego_mechListMIC(tvb, offset, pinfo, subtree,
847 hnd, (*next_level_value_p)->handle);
861 return offset; /* Not sure this is right */
865 dissect_spnego_negResult(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
866 proto_tree *tree, ASN1_SCK *hnd)
870 guint len, cls, con, tag, val;
872 ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len);
874 if (ret != ASN1_ERR_NOERROR) {
875 dissect_parse_error(tvb, offset, pinfo, tree,
876 "SPNEGO context header", ret);
880 if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_ENUM)) {
882 tree, tvb, offset, 0,
883 "Unknown header (cls=%d, con=%d, tag=%d) xxx",
888 offset = hnd->offset;
890 /* Now, get the value */
892 ret = asn1_uint32_value_decode(hnd, len, &val);
894 if (ret != ASN1_ERR_NOERROR) {
895 dissect_parse_error(tvb, offset, pinfo, tree,
896 "SPNEGO negResult value", ret);
900 proto_tree_add_item(tree, hf_spnego_negtokentarg_negresult, tvb,
903 offset = hnd->offset;
910 dissect_spnego_supportedMech(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
911 proto_tree *tree, ASN1_SCK *hnd,
912 gssapi_oid_value **next_level_value_p)
915 guint oid_len, nbytes;
918 gssapi_oid_value *value;
919 conversation_t *conversation;
922 * Now, get the OID, and find the handle, if any
925 offset = hnd->offset;
927 ret = asn1_oid_decode(hnd, &oid, &oid_len, &nbytes);
929 if (ret != ASN1_ERR_NOERROR) {
930 dissect_parse_error(tvb, offset, pinfo, tree,
931 "SPNEGO supportedMech token", ret);
935 oid_string = format_oid(oid, oid_len);
936 value = gssapi_lookup_oid(oid, oid_len);
939 proto_tree_add_text(tree, tvb, offset, nbytes,
940 "supportedMech: %s (%s)",
941 oid_string, value->comment);
943 proto_tree_add_text(tree, tvb, offset, nbytes, "supportedMech: %s",
950 /* Should check for an unrecognized OID ... */
953 *next_level_value_p = value;
956 * Now, we need to save this in per proto info in the
957 * conversation if it exists. We also should create a
958 * conversation if one does not exist. FIXME!
959 * Hmmm, might need to be smarter, because there can be
960 * multiple mechTypes in a negTokenInit with one being the
961 * default used in the Token if present. Then the negTokenTarg
962 * could override that. :-(
965 if ((conversation = find_conversation(&pinfo->src, &pinfo->dst,
966 pinfo->ptype, pinfo->srcport,
967 pinfo->destport, 0))) {
970 conversation_add_proto_data(conversation, proto_spnego,
971 *next_level_value_p);
982 dissect_spnego_responseToken(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
983 proto_tree *tree, ASN1_SCK *hnd,
984 dissector_handle_t next_level_dissector)
988 guint cls, con, tag, nbytes;
993 ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &nbytes);
995 if (ret != ASN1_ERR_NOERROR) {
996 dissect_parse_error(tvb, offset, pinfo, tree,
997 "SPNEGO sequence header", ret);
1001 if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS)) {
1002 proto_tree_add_text(
1003 tree, tvb, offset, 0,
1004 "Unknown header (cls=%d, con=%d, tag=%d)",
1009 offset = hnd->offset;
1011 item = proto_tree_add_item(tree, hf_spnego_responsetoken, tvb, offset,
1014 subtree = proto_item_add_subtree(item, ett_spnego_responsetoken);
1017 * Now, we should be able to dispatch after creating a new TVB.
1020 token_tvb = tvb_new_subset(tvb, offset, nbytes, -1);
1021 if (next_level_dissector)
1022 call_dissector(next_level_dissector, token_tvb, pinfo, subtree);
1024 hnd->offset += nbytes; /* Update this ... */
1027 return offset + nbytes;
1031 dissect_spnego_negTokenTarg(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
1032 proto_tree *tree, ASN1_SCK *hnd,
1033 gssapi_oid_value **next_level_value_p)
1037 proto_tree *subtree;
1040 guint len1, len, cls, con, tag;
1042 item = proto_tree_add_item( tree, hf_spnego_negtokentarg, tvb, offset,
1044 subtree = proto_item_add_subtree(item, ett_spnego_negtokentarg);
1047 * Here is what we need to get ...
1048 * NegTokenTarg ::= SEQUENCE {
1049 * negResult [0] ENUMERATED {
1050 * accept_completed (0),
1051 * accept_incomplete (1),
1052 * reject (2) } OPTIONAL,
1053 * supportedMech [1] MechType OPTIONAL,
1054 * responseToken [2] OCTET STRING OPTIONAL,
1055 * mechListMIC [3] OCTET STRING OPTIONAL }
1058 ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
1060 if (ret != ASN1_ERR_NOERROR) {
1061 dissect_parse_error(tvb, offset, pinfo, subtree,
1062 "SPNEGO sequence header", ret);
1066 if (!(cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ)) {
1067 proto_tree_add_text(
1068 subtree, tvb, offset, 0,
1069 "Unknown header (cls=%d, con=%d, tag=%d)",
1074 offset = hnd->offset;
1079 hdr_ofs = hnd->offset;
1081 ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len);
1083 if (ret != ASN1_ERR_NOERROR) {
1084 dissect_parse_error(tvb, offset, pinfo, subtree,
1085 "SPNEGO context header", ret);
1089 if (!(cls == ASN1_CTX && con == ASN1_CON)) {
1090 proto_tree_add_text(
1091 subtree, tvb, offset, 0,
1092 "Unknown header (cls=%d, con=%d, tag=%d)",
1097 /* Adjust for the length of the header */
1099 len1 -= (hnd->offset - hdr_ofs);
1101 /* Should be one of the fields */
1105 case SPNEGO_negResult:
1107 offset = dissect_spnego_negResult(tvb, offset, pinfo, subtree,
1111 case SPNEGO_supportedMech:
1113 offset = dissect_spnego_supportedMech(tvb, offset, pinfo, subtree,
1114 hnd, next_level_value_p);
1118 case SPNEGO_responseToken:
1120 offset = dissect_spnego_responseToken(tvb, offset, pinfo, subtree,
1122 (*next_level_value_p != NULL) ?
1123 (*next_level_value_p)->handle :
1127 case SPNEGO_mechListMIC:
1129 offset = dissect_spnego_mechListMIC(tvb, offset, pinfo, subtree,
1131 (*next_level_value_p != NULL) ?
1132 (*next_level_value_p)->handle :
1151 dissect_spnego(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1154 proto_tree *subtree;
1155 int ret, offset = 0;
1158 guint len1, cls, con, tag;
1159 conversation_t *conversation;
1160 gssapi_oid_value *next_level_value;
1163 * We need this later, so lets get it now ...
1164 * It has to be per-frame as there can be more than one GSS-API
1165 * negotiation in a conversation.
1168 next_level_value = p_get_proto_data(pinfo->fd, proto_spnego);
1169 if (!next_level_value && !pinfo->fd->flags.visited) {
1171 * No handle attached to this frame, but it's the first
1172 * pass, so it'd be attached to the conversation.
1173 * If we have a conversation, try to get the handle,
1174 * and if we get one, attach it to the frame.
1176 conversation = find_conversation(&pinfo->src, &pinfo->dst,
1177 pinfo->ptype, pinfo->srcport,
1178 pinfo->destport, 0);
1181 next_level_value = conversation_get_proto_data(conversation,
1183 if (next_level_value)
1184 p_add_proto_data(pinfo->fd, proto_spnego, next_level_value);
1188 item = proto_tree_add_item(tree, hf_spnego, tvb, offset,
1191 subtree = proto_item_add_subtree(item, ett_spnego);
1194 * The TVB contains a [0] header and a sequence that consists of an
1195 * object ID and a blob containing the data ...
1196 * Actually, it contains, according to RFC2478:
1197 * NegotiationToken ::= CHOICE {
1198 * negTokenInit [0] NegTokenInit,
1199 * negTokenTarg [1] NegTokenTarg }
1200 * NegTokenInit ::= SEQUENCE {
1201 * mechTypes [0] MechTypeList OPTIONAL,
1202 * reqFlags [1] ContextFlags OPTIONAL,
1203 * mechToken [2] OCTET STRING OPTIONAL,
1204 * mechListMIC [3] OCTET STRING OPTIONAL }
1205 * NegTokenTarg ::= SEQUENCE {
1206 * negResult [0] ENUMERATED {
1207 * accept_completed (0),
1208 * accept_incomplete (1),
1209 * reject (2) } OPTIONAL,
1210 * supportedMech [1] MechType OPTIONAL,
1211 * responseToken [2] OCTET STRING OPTIONAL,
1212 * mechListMIC [3] OCTET STRING OPTIONAL }
1214 * Windows typically includes mechTypes and mechListMic ('NONE'
1215 * in the case of NTLMSSP only).
1216 * It seems to duplicate the responseToken into the mechListMic field
1217 * as well. Naughty, naughty.
1221 asn1_open(&hnd, tvb, offset);
1224 * Get the first header ...
1227 ret = asn1_header_decode(&hnd, &cls, &con, &tag, &def, &len1);
1229 if (ret != ASN1_ERR_NOERROR) {
1230 dissect_parse_error(tvb, offset, pinfo, subtree,
1231 "SPNEGO context header", ret);
1235 if (!(cls == ASN1_CTX && con == ASN1_CON)) {
1236 proto_tree_add_text(
1237 subtree, tvb, offset, 0,
1238 "Unknown header (cls=%d, con=%d, tag=%d)",
1243 offset = hnd.offset;
1246 * The Tag is one of negTokenInit or negTokenTarg
1251 case SPNEGO_negTokenInit:
1253 offset = dissect_spnego_negTokenInit(tvb, offset, pinfo,
1259 case SPNEGO_negTokenTarg:
1261 offset = dissect_spnego_negTokenTarg(tvb, offset, pinfo,
1266 default: /* Broken, what to do? */
1273 asn1_close(&hnd, &offset);
1278 dissect_spnego_wrap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1281 proto_tree *subtree;
1282 int ret, offset = 0;
1286 guint len1, cls, con, tag, nbytes;
1290 conversation_t *conversation;
1291 gssapi_oid_value *next_level_value;
1292 tvbuff_t *token_tvb;
1296 * We need this later, so lets get it now ...
1297 * It has to be per-frame as there can be more than one GSS-API
1298 * negotiation in a conversation.
1301 next_level_value = p_get_proto_data(pinfo->fd, proto_spnego);
1302 if (!next_level_value && !pinfo->fd->flags.visited) {
1304 * No handle attached to this frame, but it's the first
1305 * pass, so it'd be attached to the conversation.
1306 * If we have a conversation, try to get the handle,
1307 * and if we get one, attach it to the frame.
1309 conversation = find_conversation(&pinfo->src, &pinfo->dst,
1310 pinfo->ptype, pinfo->srcport,
1311 pinfo->destport, 0);
1314 next_level_value = conversation_get_proto_data(conversation,
1316 if (next_level_value)
1317 p_add_proto_data(pinfo->fd, proto_spnego, next_level_value);
1321 item = proto_tree_add_item(tree, hf_spnego, tvb, offset,
1324 subtree = proto_item_add_subtree(item, ett_spnego);
1327 * The TVB contains a [0] header and a sequence that consists of an
1328 * object ID and a blob containing the data ...
1329 * XXX - is this RFC 2743's "Mechanism-Independent Token Format",
1330 * with the "optional" "use in non-initial tokens" being chosen.
1333 asn1_open(&hnd, tvb, offset);
1336 * Get the first header ...
1339 ret = asn1_header_decode(&hnd, &cls, &con, &tag, &def, &len1);
1341 if (ret != ASN1_ERR_NOERROR) {
1342 dissect_parse_error(tvb, offset, pinfo, subtree,
1343 "SPNEGO context header", ret);
1344 return_offset = tvb_length(tvb);
1348 if (!(cls == ASN1_APL && con == ASN1_CON && tag == 0)) {
1349 proto_tree_add_text(
1350 subtree, tvb, offset, 0,
1351 "Unknown header (cls=%d, con=%d, tag=%d)",
1353 return_offset = tvb_length(tvb);
1357 offset = hnd.offset;
1360 * Get the OID, and find the handle, if any
1363 ret = asn1_oid_decode(&hnd, &oid, &oid_len, &nbytes);
1365 if (ret != ASN1_ERR_NOERROR) {
1366 dissect_parse_error(tvb, offset, pinfo, tree,
1367 "SPNEGO wrap token", ret);
1368 return_offset = tvb_length(tvb);
1372 oid_string = format_oid(oid, oid_len);
1373 next_level_value = gssapi_lookup_oid(oid, oid_len);
1376 * XXX - what should we do if this doesn't match the value
1377 * attached to the frame or conversation? (That would be
1378 * bogus, but that's not impossible - some broken implementation
1379 * might negotiate some security mechanism but put the OID
1380 * for some other security mechanism in GSS_Wrap tokens.)
1382 if (next_level_value)
1383 proto_tree_add_text(tree, tvb, offset, nbytes,
1384 "thisMech: %s (%s)",
1385 oid_string, next_level_value->comment);
1387 proto_tree_add_text(tree, tvb, offset, nbytes, "thisMech: %s",
1395 * Now dissect the GSS_Wrap token; it's assumed to be in the
1396 * rest of the tvbuff.
1398 item = proto_tree_add_item(tree, hf_spnego_wraptoken, tvb, offset,
1401 subtree = proto_item_add_subtree(item, ett_spnego_wraptoken);
1404 * Now, we should be able to dispatch after creating a new TVB.
1405 * The subdissector must return the length of the part of the
1406 * token it dissected, so we can return the length of the part
1407 * we (and it) dissected.
1410 token_tvb = tvb_new_subset(tvb, offset, -1, -1);
1411 if (next_level_value->wrap_handle) {
1412 len = call_dissector(next_level_value->wrap_handle, token_tvb, pinfo, subtree);
1414 return_offset = tvb_length(tvb);
1416 return_offset = offset + len;
1418 return_offset = tvb_length(tvb);
1420 asn1_close(&hnd, &offset);
1422 return return_offset;
1426 proto_register_spnego(void)
1428 static hf_register_info hf[] = {
1430 { "SPNEGO", "spnego", FT_NONE, BASE_NONE, NULL, 0x0,
1432 { &hf_spnego_negtokeninit,
1433 { "negTokenInit", "spnego.negtokeninit", FT_NONE, BASE_NONE,
1434 NULL, 0x0, "SPNEGO negTokenInit", HFILL}},
1435 { &hf_spnego_negtokentarg,
1436 { "negTokenTarg", "spnego.negtokentarg", FT_NONE, BASE_NONE,
1437 NULL, 0x0, "SPNEGO negTokenTarg", HFILL}},
1438 { &hf_spnego_mechtype,
1439 { "mechType", "spnego.negtokeninit.mechtype", FT_NONE,
1440 BASE_NONE, NULL, 0x0, "SPNEGO negTokenInit mechTypes", HFILL}},
1441 { &hf_spnego_mechtoken,
1442 { "mechToken", "spnego.negtokeninit.mechtoken", FT_NONE,
1443 BASE_NONE, NULL, 0x0, "SPNEGO negTokenInit mechToken", HFILL}},
1444 { &hf_spnego_mechlistmic,
1445 { "mechListMIC", "spnego.mechlistmic", FT_NONE,
1446 BASE_NONE, NULL, 0x0, "SPNEGO mechListMIC", HFILL}},
1447 { &hf_spnego_responsetoken,
1448 { "responseToken", "spnego.negtokentarg.responsetoken",
1449 FT_NONE, BASE_NONE, NULL, 0x0, "SPNEGO responseToken",
1451 { &hf_spnego_negtokentarg_negresult,
1452 { "negResult", "spnego.negtokeninit.negresult", FT_UINT16,
1453 BASE_HEX, VALS(spnego_negResult_vals), 0, "negResult", HFILL}},
1454 { &hf_spnego_reqflags,
1455 { "reqFlags", "spnego.negtokeninit.reqflags", FT_BYTES,
1456 BASE_HEX, NULL, 0, "reqFlags", HFILL }},
1457 { &hf_spnego_wraptoken,
1458 { "wrapToken", "spnego.wraptoken",
1459 FT_NONE, BASE_NONE, NULL, 0x0, "SPNEGO wrapToken",
1462 { "krb5_blob", "spnego.krb5.blob", FT_BYTES,
1463 BASE_NONE, NULL, 0, "krb5_blob", HFILL }},
1464 { &hf_spnego_krb5_tok_id,
1465 { "krb5_tok_id", "spnego.krb5.tok_id", FT_UINT16, BASE_HEX,
1466 VALS(spnego_krb5_tok_id_vals), 0, "KRB5 Token Id", HFILL}},
1467 { &hf_spnego_krb5_sgn_alg,
1468 { "krb5_sgn_alg", "spnego.krb5.sgn_alg", FT_UINT16, BASE_HEX,
1469 VALS(spnego_krb5_sgn_alg_vals), 0, "KRB5 Signing Algorithm", HFILL}},
1470 { &hf_spnego_krb5_seal_alg,
1471 { "krb5_seal_alg", "spnego.krb5.seal_alg", FT_UINT16, BASE_HEX,
1472 VALS(spnego_krb5_seal_alg_vals), 0, "KRB5 Sealing Algorithm", HFILL}},
1473 { &hf_spnego_krb5_snd_seq,
1474 { "krb5_snd_seq", "spnego.krb5.snd_seq", FT_BYTES, BASE_NONE,
1475 NULL, 0, "KRB5 Encrypted Sequence Number", HFILL}},
1476 { &hf_spnego_krb5_sgn_cksum,
1477 { "krb5_sgn_cksum", "spnego.krb5.sgn_cksum", FT_BYTES, BASE_NONE,
1478 NULL, 0, "KRB5 Data Checksum", HFILL}},
1479 { &hf_spnego_krb5_confounder,
1480 { "krb5_confounder", "spnego.krb5.confounder", FT_BYTES, BASE_NONE,
1481 NULL, 0, "KRB5 Confounder", HFILL}},
1484 static gint *ett[] = {
1486 &ett_spnego_negtokeninit,
1487 &ett_spnego_negtokentarg,
1488 &ett_spnego_mechtype,
1489 &ett_spnego_mechtoken,
1490 &ett_spnego_mechlistmic,
1491 &ett_spnego_responsetoken,
1492 &ett_spnego_wraptoken,
1496 proto_spnego = proto_register_protocol(
1497 "Spnego", "Spnego", "spnego");
1498 proto_spnego_krb5 = proto_register_protocol("SPNEGO-KRB5",
1502 proto_register_field_array(proto_spnego, hf, array_length(hf));
1503 proto_register_subtree_array(ett, array_length(ett));
1507 proto_reg_handoff_spnego(void)
1509 dissector_handle_t spnego_handle, spnego_wrap_handle;
1510 dissector_handle_t spnego_krb5_handle, spnego_krb5_wrap_handle;
1512 /* Register protocol with GSS-API module */
1514 spnego_handle = create_dissector_handle(dissect_spnego, proto_spnego);
1515 spnego_wrap_handle = new_create_dissector_handle(dissect_spnego_wrap,
1517 gssapi_init_oid("1.3.6.1.5.5.2", proto_spnego, ett_spnego,
1518 spnego_handle, spnego_wrap_handle,
1519 "SPNEGO - Simple Protected Negotiation");
1521 /* Register both the one MS created and the real one */
1523 * Thanks to Jean-Baptiste Marchand and Richard B Ward, the
1524 * mystery of the MS KRB5 OID is cleared up. It was due to a library
1525 * that did not handle OID components greater than 16 bits, and was
1526 * fixed in Win2K SP2 as well as WinXP.
1527 * See the archive of <ietf-krb-wg@anl.gov> for the thread topic
1528 * SPNEGO implementation issues. 3-Dec-2002.
1530 spnego_krb5_handle = create_dissector_handle(dissect_spnego_krb5,
1532 spnego_krb5_wrap_handle = new_create_dissector_handle(dissect_spnego_krb5_wrap,
1534 gssapi_init_oid("1.2.840.48018.1.2.2", proto_spnego_krb5, ett_spnego_krb5,
1535 spnego_krb5_handle, spnego_krb5_wrap_handle,
1536 "MS KRB5 - Microsoft Kerberos 5");
1537 gssapi_init_oid("1.2.840.113554.1.2.2", proto_spnego_krb5, ett_spnego_krb5,
1538 spnego_krb5_handle, spnego_krb5_wrap_handle,
1539 "KRB5 - Kerberos 5");