2 * Routines for the simple and protected GSS-API negotiation mechanism
3 * as described in rfc2478.
4 * Copyright 2002, Tim Potter <tpot@samba.org>
5 * Copyright 2002, Richard Sharpe <rsharpe@ns.aus.com>
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 "epan/conversation.h"
44 #define SPNEGO_negTokenInit 0
45 #define SPNEGO_negTokenTarg 1
46 #define SPNEGO_mechTypes 0
47 #define SPNEGO_reqFlags 1
48 #define SPNEGO_mechToken 2
49 #define SPNEGO_mechListMIC 3
50 #define SPNEGO_negResult 0
51 #define SPNEGO_supportedMech 1
52 #define SPNEGO_responseToken 2
53 #define SPNEGO_negResult_accept_completed 0
54 #define SPNEGO_negResult_accept_incomplete 1
55 #define SPNEGO_negResult_accept_reject 2
57 static int proto_spnego = -1;
59 static int hf_spnego = -1;
60 static int hf_spnego_negtokeninit = -1;
61 static int hf_spnego_negtokentarg = -1;
62 static int hf_spnego_mechtype = -1;
63 static int hf_spnego_negtokentarg_negresult = -1;
65 static gint ett_spnego = -1;
66 static gint ett_spnego_negtokeninit = -1;
67 static gint ett_spnego_negtokentarg = -1;
68 static gint ett_spnego_mechtype = -1;
71 * XXX: Fixme. This thould be made global ...
74 static const value_string spnego_negResult_vals[] = {
75 { SPNEGO_negResult_accept_completed, "Accept Completed" },
76 { SPNEGO_negResult_accept_incomplete, "Accept Incomplete" },
77 { SPNEGO_negResult_accept_reject, "Accept Reject"},
82 * We need to keep this around for other routines to use.
83 * We store it in the per-protocol conversation data and
84 * retrieve it in the main dissector.
87 static dissector_handle_t next_level_dissector = -1;
89 /* Display an ASN1 parse error. Taken from packet-snmp.c */
91 static dissector_handle_t data_handle;
94 dissect_parse_error(tvbuff_t *tvb, int offset, packet_info *pinfo,
95 proto_tree *tree, const char *field_name, int ret)
99 errstr = asn1_err_to_str(ret);
102 proto_tree_add_text(tree, tvb, offset, 0,
103 "ERROR: Couldn't parse %s: %s", field_name, errstr);
104 call_dissector(data_handle,
105 tvb_new_subset(tvb, offset, -1, -1), pinfo, tree);
110 dissect_spnego_mechTypes(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
111 proto_tree *tree, ASN1_SCK *hnd)
116 guint len1, len, cls, con, tag, nbytes;
119 proto_item *sub_item;
120 proto_tree *oid_subtree;
122 int length = tvb_length_remaining(tvb, offset);
125 * MechTypeList ::= SEQUENCE OF MechType
128 ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
130 if (ret != ASN1_ERR_NOERROR) {
131 dissect_parse_error(tvb, offset, pinfo, subtree,
132 "SPNEGO last sequence header", ret);
136 if (!(cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ)) {
138 subtree, tvb, offset, 0,
139 "Unknown header (cls=%d, con=%d, tag=%d)",
144 offset = hnd->offset;
146 item = proto_tree_add_item( tree, hf_spnego_mechtype, tvb, offset,
148 subtree = proto_item_add_subtree(item, ett_spnego_mechtype);
151 * Now, the object IDs ... We should translate them: FIXME
156 ret = asn1_oid_decode(hnd, &oid, &len, &nbytes);
158 if (ret != ASN1_ERR_NOERROR) {
159 dissect_parse_error(tvb, offset, pinfo, subtree,
160 "GSS-API token", ret);
164 oid_string = format_oid(oid, len);
166 proto_tree_add_text(subtree, tvb, offset, nbytes, "OID: %s",
181 dissect_spnego_reqFlags(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
182 proto_tree *tree, ASN1_SCK *hnd)
190 dissect_spnego_mechToken(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
191 proto_tree *tree, ASN1_SCK *hnd)
195 guint oid_len, len, cls, con, tag, nbytes;
199 * This appears to be a simple octet string ...
202 ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &nbytes);
204 if (ret != ASN1_ERR_NOERROR) {
205 dissect_parse_error(tvb, offset, pinfo, tree,
206 "SPNEGO sequence header", ret);
210 if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS)) {
212 tree, tvb, offset, 0,
213 "Unknown header (cls=%d, con=%d, tag=%d)",
218 offset = hnd->offset;
220 proto_tree_add_text(tree, tvb, offset, nbytes, "mechToken: %s",
221 tvb_format_text(tvb, offset, nbytes));
224 * Now, we should be able to dispatch after creating a new TVB.
227 token_tvb = tvb_new_subset(tvb, offset, nbytes, -1);
228 if (next_level_dissector != -1)
229 call_dissector(next_level_dissector, token_tvb, pinfo, tree);
231 hnd->offset += nbytes; /* Update this ... */
235 return offset + nbytes;
240 dissect_spnego_mechListMIC(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
241 proto_tree *tree, ASN1_SCK *hnd)
243 guint len1, len, cls, con, tag, nbytes;
246 int length = tvb_length_remaining(tvb, offset);
247 proto_tree *subtree = NULL;
250 * Add the mechListMIC [3] Octet String or General String ...
252 ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
254 if (ret != ASN1_ERR_NOERROR) {
255 dissect_parse_error(tvb, offset, pinfo, subtree,
256 "SPNEGO sequence header", ret);
260 if (!(cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ)) {
262 subtree, tvb, offset, 0,
263 "Unknown header (cls=%d, con=%d, tag=%d)",
268 offset = hnd->offset;
270 /* XXX: FIXME, we should dissect this as well */
273 * There seems to be two different forms this can take
274 * One as an Octet string, and one as a general string in a
275 * sequence ... We will have to dissect this later
278 proto_tree_add_text(tree, tvb, offset + 4, len1 - 4,
280 tvb_format_text(tvb, offset + 4, len1 - 4));
282 /* Naughty ... but we have to adjust for what we never took */
289 return offset + len1 - 4;
294 dissect_spnego_negTokenInit(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
295 proto_tree *tree, ASN1_SCK *hnd)
300 guint len1, len, cls, con, tag, nbytes;
302 gssapi_oid_value *value;
303 dissector_handle_t handle;
305 proto_item *sub_item;
306 proto_tree *oid_subtree;
308 int length = tvb_length_remaining(tvb, offset);
310 item = proto_tree_add_item( tree, hf_spnego_negtokeninit, tvb, offset,
312 subtree = proto_item_add_subtree(item, ett_spnego_negtokeninit);
315 * Here is what we need to get ...
316 * NegTokenInit ::= SEQUENCE {
317 * mechTypes [0] MechTypeList OPTIONAL,
318 * reqFlags [1] ContextFlags OPTIONAL,
319 * mechToken [2] OCTET STRING OPTIONAL,
320 * mechListMIC [3] OCTET STRING OPTIONAL }
324 ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
326 if (ret != ASN1_ERR_NOERROR) {
327 dissect_parse_error(tvb, offset, pinfo, subtree,
328 "SPNEGO sequence header", ret);
332 if (!(cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ)) {
334 subtree, tvb, offset, 0,
335 "Unknown header (cls=%d, con=%d, tag=%d)",
340 offset = hnd->offset;
342 len1 -= 2; /* Account for the Header above ... */
346 ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len);
348 if (ret != ASN1_ERR_NOERROR) {
349 dissect_parse_error(tvb, offset, pinfo, subtree,
350 "SPNEGO context header", ret);
354 if (!(cls == ASN1_CTX && con == ASN1_CON)) {
356 subtree, tvb, offset, 0,
357 "Unknown header (cls=%d, con=%d, tag=%d)",
362 /* Should be one of the fields */
366 case SPNEGO_mechTypes:
368 offset = dissect_spnego_mechTypes(tvb, offset, pinfo,
373 case SPNEGO_reqFlags:
377 case SPNEGO_mechToken:
379 offset = dissect_spnego_mechToken(tvb, offset, pinfo, subtree,
383 case SPNEGO_mechListMIC:
385 offset = dissect_spnego_mechListMIC(tvb, offset, pinfo, subtree,
394 len1 -= (len + 2); /* Account for header */
400 return offset; /* Not sure this is right */
404 dissect_spnego_negResult(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
405 proto_tree *tree, ASN1_SCK *hnd)
409 guint len1, len, cls, con, tag, nbytes, val;
411 ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len);
413 if (ret != ASN1_ERR_NOERROR) {
414 dissect_parse_error(tvb, offset, pinfo, tree,
415 "SPNEGO context header", ret);
419 if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_ENUM)) {
421 tree, tvb, offset, 0,
422 "Unknown header (cls=%d, con=%d, tag=%d) xxx",
427 offset = hnd->offset;
429 /* Now, get the value */
431 ret = asn1_uint32_value_decode(hnd, len, &val);
433 if (ret != ASN1_ERR_NOERROR) {
434 dissect_parse_error(tvb, offset, pinfo, tree,
435 "SPNEGO negResult value", ret);
439 proto_tree_add_item(tree, hf_spnego_negtokentarg_negresult, tvb,
442 offset = hnd->offset;
449 dissect_spnego_supportedMech(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
450 proto_tree *tree, ASN1_SCK *hnd)
454 guint oid_len, len1, len, cls, con, tag, nbytes;
456 gchar *p, *oid_string;
458 gssapi_oid_value *value;
459 conversation_t *conversation;
462 * Now, get the OID, and find the handle, if any
465 offset = hnd->offset;
467 ret = asn1_oid_decode(hnd, &oid, &oid_len, &nbytes);
469 if (ret != ASN1_ERR_NOERROR) {
470 dissect_parse_error(tvb, offset, pinfo, tree,
471 "SPNEGO supportedMech token", ret);
475 oid_string = format_oid(oid, oid_len);
477 proto_tree_add_text(tree, tvb, offset, nbytes, "supportedMech: %s",
484 oid_string = g_malloc(oid_len * 22 + 1);
486 len = sprintf(p, "%lu", (unsigned long)oid[0]);
488 for (i = 1; i < oid_len;i++) {
489 len = sprintf(p, ".%lu", (unsigned long)oid[i]);
493 value = g_hash_table_lookup(gssapi_oids, oid_string);
495 /* Should check for an unrecognized OID ... */
497 next_level_dissector = -1;
499 if (value) next_level_dissector = find_dissector(value->name);
502 * Now, we need to save this in per proto info in the
503 * conversation if it exists. We also should create a
504 * conversation if one does not exist. FIXME!
505 * Hmmm, might need to be smarter, because there can be
506 * multiple mechTypes in a negTokenInit with one being the
507 * default used in the Token if present. Then the negTokenTarg
508 * could override that. :-(
511 if (conversation = find_conversation(&pinfo->src, &pinfo->dst,
512 pinfo->ptype, pinfo->srcport,
513 pinfo->destport, 0)) {
516 conversation_add_proto_data(conversation, proto_spnego,
517 next_level_dissector);
528 dissect_spnego_responseToken(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
529 proto_tree *tree, ASN1_SCK *hnd)
533 guint oid_len, len, cls, con, tag, nbytes;
536 ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &nbytes);
538 if (ret != ASN1_ERR_NOERROR) {
539 dissect_parse_error(tvb, offset, pinfo, tree,
540 "SPNEGO sequence header", ret);
544 if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS)) {
546 tree, tvb, offset, 0,
547 "Unknown header (cls=%d, con=%d, tag=%d)",
552 offset = hnd->offset;
554 proto_tree_add_text(tree, tvb, offset, nbytes, "responseToken: %s",
555 tvb_format_text(tvb, offset, nbytes));
558 * Now, we should be able to dispatch after creating a new TVB.
561 token_tvb = tvb_new_subset(tvb, offset, nbytes, -1);
562 if (next_level_dissector != -1)
563 call_dissector(next_level_dissector, token_tvb, pinfo, tree);
565 hnd->offset += nbytes; /* Update this ... */
568 return offset + nbytes;
573 dissect_spnego_negTokenTarg(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
574 proto_tree *tree, ASN1_SCK *hnd)
581 guint len1, len, cls, con, tag, nbytes;
582 dissector_handle_t handle = NULL;
584 int length = tvb_length_remaining(tvb, offset);
586 item = proto_tree_add_item( tree, hf_spnego_negtokentarg, tvb, offset,
588 subtree = proto_item_add_subtree(item, ett_spnego_negtokentarg);
591 * Here is what we need to get ...
592 * NegTokenTarg ::= SEQUENCE {
593 * negResult [0] ENUMERATED {
594 * accept_completed (0),
595 * accept_incomplete (1),
596 * reject (2) } OPTIONAL,
597 * supportedMech [1] MechType OPTIONAL,
598 * responseToken [2] OCTET STRING OPTIONAL,
599 * mechListMIC [3] OCTET STRING OPTIONAL }
602 ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
604 if (ret != ASN1_ERR_NOERROR) {
605 dissect_parse_error(tvb, offset, pinfo, subtree,
606 "SPNEGO sequence header", ret);
610 if (!(cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ)) {
612 subtree, tvb, offset, 0,
613 "Unknown header (cls=%d, con=%d, tag=%d)",
618 offset = hnd->offset;
620 len1 -= 2; /* Account for the Header above ... */
624 ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len);
626 if (ret != ASN1_ERR_NOERROR) {
627 dissect_parse_error(tvb, offset, pinfo, subtree,
628 "SPNEGO context header", ret);
632 if (!(cls == ASN1_CTX && con == ASN1_CON)) {
634 subtree, tvb, offset, 0,
635 "Unknown header (cls=%d, con=%d, tag=%d)",
640 /* Should be one of the fields */
644 case SPNEGO_negResult:
646 offset = dissect_spnego_negResult(tvb, offset, pinfo, subtree,
650 case SPNEGO_supportedMech:
652 offset = dissect_spnego_supportedMech(tvb, offset, pinfo, subtree,
657 case SPNEGO_responseToken:
659 offset = dissect_spnego_responseToken(tvb, offset, pinfo, subtree,
663 case SPNEGO_mechListMIC:
665 offset = dissect_spnego_mechListMIC(tvb, offset, pinfo, subtree,
674 len1 -= (len + 2); /* FIXME: The +2 may be wrong */
684 dissect_spnego(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
688 int length = tvb_length_remaining(tvb, 0);
692 guint len1, len, cls, con, tag, nbytes;
693 conversation_t *conversation;
694 dissector_handle_t handle;
697 * We need this later, so lets get it now ...
700 conversation = find_conversation(&pinfo->src, &pinfo->dst,
701 pinfo->ptype, pinfo->srcport,
705 (handle = conversation_get_proto_data(conversation,
707 next_level_dissector = handle;
710 item = proto_tree_add_item(
711 tree, hf_spnego, tvb, offset, length, FALSE);
713 subtree = proto_item_add_subtree(item, ett_spnego);
716 * The TVB contains a [0] header and a sequence that consists of an
717 * object ID and a blob containing the data ...
718 * Actually, it contains, according to RFC2478:
719 * NegotiationToken ::= CHOICE {
720 * negTokenInit [0] NegTokenInit,
721 * negTokenTarg [1] NegTokenTarg }
722 * NegTokenInit ::= SEQUENCE {
723 * mechTypes [0] MechTypeList OPTIONAL,
724 * reqFlags [1] ContextFlags OPTIONAL,
725 * mechToken [2] OCTET STRING OPTIONAL,
726 * mechListMIC [3] OCTET STRING OPTIONAL }
727 * NegTokenTarg ::= SEQUENCE {
728 * negResult [0] ENUMERATED {
729 * accept_completed (0),
730 * accept_incomplete (1),
731 * reject (2) } OPTIONAL,
732 * supportedMech [1] MechType OPTIONAL,
733 * responseToken [2] OCTET STRING OPTIONAL,
734 * mechListMIC [3] OCTET STRING OPTIONAL }
736 * Windows typically includes mechTypes and mechListMic ('NONE'
737 * in the case of NTLMSSP only).
738 * It seems to duplicate the responseToken into the mechListMic field
739 * as well. Naughty, naughty.
741 * FIXME, the following code is broken so far.
743 asn1_open(&hnd, tvb, offset);
746 * Get the first header ...
749 ret = asn1_header_decode(&hnd, &cls, &con, &tag, &def, &len1);
751 if (ret != ASN1_ERR_NOERROR) {
752 dissect_parse_error(tvb, offset, pinfo, subtree,
753 "SPNEGO context header", ret);
757 if (!(cls == ASN1_CTX && con == ASN1_CON)) {
759 subtree, tvb, offset, 0,
760 "Unknown header (cls=%d, con=%d, tag=%d)",
768 * The Tag is one of negTokenInit or negTokenTarg
773 case SPNEGO_negTokenInit:
775 offset = dissect_spnego_negTokenInit(tvb, offset, pinfo,
780 case SPNEGO_negTokenTarg:
782 offset = dissect_spnego_negTokenTarg(tvb, offset, pinfo,
786 default: /* Broken, what to do? */
793 asn1_close(&hnd, &offset);
798 proto_register_spnego(void)
800 static hf_register_info hf[] = {
802 { "SPNEGO", "spnego", FT_NONE, BASE_NONE, NULL, 0x0,
804 { &hf_spnego_negtokeninit,
805 { "negTokenInit", "spnego.negtokeninit", FT_NONE, BASE_NONE,
806 NULL, 0x0, "SPNEGO negTokenInit", HFILL}},
807 { &hf_spnego_negtokentarg,
808 { "negTokenTarg", "spnego.negtokentarg", FT_NONE, BASE_NONE,
809 NULL, 0x0, "SPNEGO negTokenTarg", HFILL}},
810 { &hf_spnego_mechtype,
811 { "mechType", "spnego.negtokeninit.mechtype", FT_NONE,
812 BASE_NONE, NULL, 0x0, "SPNEGO negTokenInit mechTypes", HFILL}},
813 { &hf_spnego_negtokentarg_negresult,
814 { "negResult", "spnego.negtokeninit.negresult", FT_UINT16,
815 BASE_HEX, VALS(spnego_negResult_vals), 0, "negResult", HFILL}},
818 static gint *ett[] = {
820 &ett_spnego_negtokeninit,
821 &ett_spnego_negtokentarg,
822 &ett_spnego_mechtype,
825 proto_spnego = proto_register_protocol(
826 "Spnego", "Spnego", "spnego");
828 proto_register_field_array(proto_spnego, hf, array_length(hf));
829 proto_register_subtree_array(ett, array_length(ett));
831 register_dissector("spnego", dissect_spnego, proto_spnego);
835 proto_reg_handoff_spnego(void)
837 /* Register protocol with GSS-API module */
839 gssapi_init_oid("1.3.6.1.5.5.2", proto_spnego, ett_spnego, "spnego");