dd7cdf4734534ef3d6451f1c35bfe5db5e4df652
[obnox/wireshark/wip.git] / packet-spnego.c
1 /* packet-spnego.c
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>
6  *
7  * $id$
8  *
9  * Ethereal - Network traffic analyzer
10  * By Gerald Combs <gerald@ethereal.com>
11  * Copyright 1998 Gerald Combs
12  *
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.
17  *
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.
22  *
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.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #ifdef HAVE_SYS_TYPES_H
33 # include <sys/types.h>
34 #endif
35
36 #include <glib.h>
37 #include <epan/packet.h>
38
39 #include "asn1.h"
40 #include "format-oid.h"
41 #include "packet-gssapi.h"
42 #include "epan/conversation.h"
43
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
56
57 static int proto_spnego = -1;
58
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;
64
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;
69
70 /*
71  * XXX: Fixme. This thould be made global ...
72  */
73
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"},
78   { NULL, NULL}
79 };
80
81 /*
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.
85  */
86
87 static dissector_handle_t next_level_dissector = -1;
88
89 /* Display an ASN1 parse error.  Taken from packet-snmp.c */
90
91 static dissector_handle_t data_handle;
92
93 static void
94 dissect_parse_error(tvbuff_t *tvb, int offset, packet_info *pinfo,
95                     proto_tree *tree, const char *field_name, int ret)
96 {
97         char *errstr;
98
99         errstr = asn1_err_to_str(ret);
100
101         if (tree != NULL) {
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);
106         }
107 }
108
109 static int
110 dissect_spnego_mechTypes(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
111                          proto_tree *tree, ASN1_SCK *hnd)
112 {
113         proto_item *item;
114         proto_tree *subtree;
115         gboolean def;
116         guint len1, len, cls, con, tag, nbytes;
117         subid_t *oid;
118         gchar *oid_string;
119         proto_item *sub_item;
120         proto_tree *oid_subtree;
121         int ret;
122         int length = tvb_length_remaining(tvb, offset);
123
124         /*
125          * MechTypeList ::= SEQUENCE OF MechType
126          */
127
128         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
129
130         if (ret != ASN1_ERR_NOERROR) {
131           dissect_parse_error(tvb, offset, pinfo, subtree,
132                               "SPNEGO last sequence header", ret);
133           goto done;
134         }
135
136         if (!(cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ)) {
137           proto_tree_add_text(
138                               subtree, tvb, offset, 0,
139                               "Unknown header (cls=%d, con=%d, tag=%d)",
140                               cls, con, tag);
141           goto done;
142         }
143
144         offset = hnd->offset;
145
146         item = proto_tree_add_item( tree, hf_spnego_mechtype, tvb, offset, 
147                                     len1, FALSE);
148         subtree = proto_item_add_subtree(item, ett_spnego_mechtype);
149
150         /*
151          * Now, the object IDs ... We should translate them: FIXME
152          */
153
154         while (len1) {
155
156           ret = asn1_oid_decode(hnd, &oid, &len, &nbytes);
157
158           if (ret != ASN1_ERR_NOERROR) {
159             dissect_parse_error(tvb, offset, pinfo, subtree,
160                                 "GSS-API token", ret);
161             goto done;
162           }
163
164           oid_string = format_oid(oid, len);
165
166           proto_tree_add_text(subtree, tvb, offset, nbytes, "OID: %s",
167                               oid_string);
168
169           offset += nbytes;
170           len1 -= nbytes;
171
172         }
173
174  done:
175
176         return offset;
177
178 }
179
180 static int
181 dissect_spnego_reqFlags(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
182                         proto_tree *tree, ASN1_SCK *hnd)
183 {
184
185   return offset;
186
187 }
188
189 static int
190 dissect_spnego_mechToken(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
191                          proto_tree *tree, ASN1_SCK *hnd)
192 {
193         gboolean def;
194         int ret;
195         guint oid_len, len, cls, con, tag, nbytes;
196         tvbuff_t *token_tvb;
197
198         /*
199          * This appears to be a simple octet string ...
200          */
201
202         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &nbytes);
203
204         if (ret != ASN1_ERR_NOERROR) {
205                 dissect_parse_error(tvb, offset, pinfo, tree,
206                                     "SPNEGO sequence header", ret);
207                 goto done;
208         }
209
210         if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS)) {
211                 proto_tree_add_text(
212                         tree, tvb, offset, 0,
213                         "Unknown header (cls=%d, con=%d, tag=%d)",
214                         cls, con, tag);
215                 goto done;
216         }
217
218         offset = hnd->offset;
219
220         proto_tree_add_text(tree, tvb, offset, nbytes, "mechToken: %s",
221                             tvb_format_text(tvb, offset, nbytes)); 
222
223         /*
224          * Now, we should be able to dispatch after creating a new TVB.
225          */
226
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);
230
231         hnd->offset += nbytes; /* Update this ... */
232
233  done:
234
235   return offset + nbytes;
236
237 }
238
239 static int
240 dissect_spnego_mechListMIC(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
241                            proto_tree *tree, ASN1_SCK *hnd)
242 {
243         guint len1, len, cls, con, tag, nbytes;
244         int ret;
245         gboolean def;
246         int length = tvb_length_remaining(tvb, offset);
247         proto_tree *subtree = NULL;
248
249         /*
250          * Add the mechListMIC [3] Octet String or General String ...
251          */
252         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
253
254         if (ret != ASN1_ERR_NOERROR) {
255                 dissect_parse_error(tvb, offset, pinfo, subtree,
256                                     "SPNEGO sequence header", ret);
257                 goto done;
258         }
259
260         if (!(cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ)) {
261                 proto_tree_add_text(
262                         subtree, tvb, offset, 0,
263                         "Unknown header (cls=%d, con=%d, tag=%d)",
264                         cls, con, tag);
265                 goto done;
266         }
267
268         offset = hnd->offset;
269
270         /* XXX: FIXME, we should dissect this as well */
271
272         /*
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
276          */
277          
278         proto_tree_add_text(tree, tvb, offset + 4, len1 - 4,
279                             "mechListMIC: %s\n",
280                             tvb_format_text(tvb, offset + 4, len1 - 4));
281
282         /* Naughty ... but we have to adjust for what we never took */
283
284         hnd->offset += 4;
285
286
287  done:
288
289         return offset + len1 - 4;
290
291 }
292
293 static int
294 dissect_spnego_negTokenInit(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
295                             proto_tree *tree, ASN1_SCK *hnd)
296 {
297         proto_item *item;
298         proto_tree *subtree;
299         gboolean def;
300         guint len1, len, cls, con, tag, nbytes;
301         subid_t *oid;
302         gssapi_oid_value *value;
303         dissector_handle_t handle;
304         gchar *oid_string;
305         proto_item *sub_item;
306         proto_tree *oid_subtree;
307         int ret;
308         int length = tvb_length_remaining(tvb, offset);
309
310         item = proto_tree_add_item( tree, hf_spnego_negtokeninit, tvb, offset,
311                                     length, FALSE);
312         subtree = proto_item_add_subtree(item, ett_spnego_negtokeninit);
313
314         /*
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 }
321
322          */
323
324         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
325
326         if (ret != ASN1_ERR_NOERROR) {
327                 dissect_parse_error(tvb, offset, pinfo, subtree,
328                                     "SPNEGO sequence header", ret);
329                 goto done;
330         }
331
332         if (!(cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ)) {
333                 proto_tree_add_text(
334                         subtree, tvb, offset, 0,
335                         "Unknown header (cls=%d, con=%d, tag=%d)",
336                         cls, con, tag);
337                 goto done;
338         }
339
340         offset = hnd->offset;
341
342         len1 -= 2; /* Account for the Header above ... */
343
344         while (len1) {
345
346           ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len);
347
348           if (ret != ASN1_ERR_NOERROR) {
349             dissect_parse_error(tvb, offset, pinfo, subtree,
350                                 "SPNEGO context header", ret);
351             goto done;
352           }
353
354           if (!(cls == ASN1_CTX && con == ASN1_CON)) {
355             proto_tree_add_text(
356                                 subtree, tvb, offset, 0,
357                                 "Unknown header (cls=%d, con=%d, tag=%d)",
358                                 cls, con, tag);
359             goto done;
360           }
361
362           /* Should be one of the fields */
363
364           switch (tag) {
365
366           case SPNEGO_mechTypes:
367
368             offset = dissect_spnego_mechTypes(tvb, offset, pinfo,
369                                               subtree, hnd);
370
371             break;
372
373           case SPNEGO_reqFlags:
374
375             break;
376
377           case SPNEGO_mechToken:
378
379             offset = dissect_spnego_mechToken(tvb, offset, pinfo, subtree, 
380                                               hnd);
381             break;
382
383           case SPNEGO_mechListMIC:
384
385             offset = dissect_spnego_mechListMIC(tvb, offset, pinfo, subtree,
386                                                 hnd);
387             break;
388
389           default:
390
391             break;
392           }
393
394           len1 -= (len + 2); /* Account for header */
395
396         }
397
398  done:
399
400         return offset; /* Not sure this is right */
401 }
402
403 static int
404 dissect_spnego_negResult(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
405                             proto_tree *tree, ASN1_SCK *hnd)
406 {
407         gboolean def;
408         int ret;
409         guint len1, len, cls, con, tag, nbytes, val;
410
411         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len);
412
413         if (ret != ASN1_ERR_NOERROR) {
414           dissect_parse_error(tvb, offset, pinfo, tree,
415                               "SPNEGO context header", ret);
416           goto done;
417         }
418
419         if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_ENUM)) {
420           proto_tree_add_text(
421                               tree, tvb, offset, 0,
422                               "Unknown header (cls=%d, con=%d, tag=%d) xxx",
423                               cls, con, tag);
424           goto done;
425         }
426
427         offset = hnd->offset;
428
429         /* Now, get the value */
430
431         ret = asn1_uint32_value_decode(hnd, len, &val);
432
433         if (ret != ASN1_ERR_NOERROR) {
434           dissect_parse_error(tvb, offset, pinfo, tree,
435                               "SPNEGO negResult value", ret);
436           goto done;
437         }
438         
439         proto_tree_add_item(tree, hf_spnego_negtokentarg_negresult, tvb, 
440                             offset, 1, FALSE);
441
442         offset = hnd->offset;
443
444  done:
445         return offset;
446 }
447
448 static int
449 dissect_spnego_supportedMech(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
450                              proto_tree *tree, ASN1_SCK *hnd)
451 {
452         gboolean def;
453         int ret;
454         guint oid_len, len1, len, cls, con, tag, nbytes;
455         subid_t *oid;
456         gchar *p, *oid_string;
457         unsigned int i;
458         gssapi_oid_value *value;
459         conversation_t *conversation;
460
461         /*
462          * Now, get the OID, and find the handle, if any
463          */
464
465         offset = hnd->offset;
466
467         ret = asn1_oid_decode(hnd, &oid, &oid_len, &nbytes);
468
469         if (ret != ASN1_ERR_NOERROR) {
470                 dissect_parse_error(tvb, offset, pinfo, tree,
471                                     "SPNEGO supportedMech token", ret);
472                 goto done;
473         }
474
475         oid_string = format_oid(oid, oid_len);
476
477         proto_tree_add_text(tree, tvb, offset, nbytes, "supportedMech: %s",
478                             oid_string);
479
480         offset += nbytes;
481
482         g_free(oid_string);
483
484         oid_string = g_malloc(oid_len * 22 + 1);
485         p = oid_string;
486         len = sprintf(p, "%lu", (unsigned long)oid[0]);
487         p += len;
488         for (i = 1; i < oid_len;i++) {
489                 len = sprintf(p, ".%lu", (unsigned long)oid[i]);
490                 p += len;
491         }
492
493         value = g_hash_table_lookup(gssapi_oids, oid_string);
494
495         /* Should check for an unrecognized OID ... */
496
497         next_level_dissector = -1;
498
499         if (value) next_level_dissector = find_dissector(value->name);
500
501         /*
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. :-(
509          */
510
511         if (conversation = find_conversation(&pinfo->src, &pinfo->dst,
512                                              pinfo->ptype, pinfo->srcport,
513                                              pinfo->destport, 0)) {
514
515
516           conversation_add_proto_data(conversation, proto_spnego, 
517                                       next_level_dissector);
518         }
519         else {
520
521         }
522
523  done:
524         return offset;
525 }
526
527 static int
528 dissect_spnego_responseToken(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
529                              proto_tree *tree, ASN1_SCK *hnd)
530 {
531         gboolean def;
532         int ret;
533         guint oid_len, len, cls, con, tag, nbytes;
534         tvbuff_t *token_tvb;
535
536         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &nbytes);
537
538         if (ret != ASN1_ERR_NOERROR) {
539                 dissect_parse_error(tvb, offset, pinfo, tree,
540                                     "SPNEGO sequence header", ret);
541                 goto done;
542         }
543
544         if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS)) {
545                 proto_tree_add_text(
546                         tree, tvb, offset, 0,
547                         "Unknown header (cls=%d, con=%d, tag=%d)",
548                         cls, con, tag);
549                 goto done;
550         }
551
552         offset = hnd->offset;
553
554         proto_tree_add_text(tree, tvb, offset, nbytes, "responseToken: %s",
555                             tvb_format_text(tvb, offset, nbytes)); 
556
557         /*
558          * Now, we should be able to dispatch after creating a new TVB.
559          */
560
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);
564
565         hnd->offset += nbytes; /* Update this ... */
566
567  done:
568         return offset + nbytes;
569 }
570
571
572 static int
573 dissect_spnego_negTokenTarg(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
574                             proto_tree *tree, ASN1_SCK *hnd)
575
576 {
577         proto_item *item;
578         proto_tree *subtree;
579         gboolean def;
580         int ret;
581         guint len1, len, cls, con, tag, nbytes;
582         dissector_handle_t handle = NULL;
583
584         int length = tvb_length_remaining(tvb, offset);
585
586         item = proto_tree_add_item( tree, hf_spnego_negtokentarg, tvb, offset,
587                                     length, FALSE);
588         subtree = proto_item_add_subtree(item, ett_spnego_negtokentarg);
589
590         /* 
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 }
600          */
601
602         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
603
604         if (ret != ASN1_ERR_NOERROR) {
605                 dissect_parse_error(tvb, offset, pinfo, subtree,
606                                     "SPNEGO sequence header", ret);
607                 goto done;
608         }
609
610         if (!(cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ)) {
611                 proto_tree_add_text(
612                         subtree, tvb, offset, 0,
613                         "Unknown header (cls=%d, con=%d, tag=%d)",
614                         cls, con, tag);
615                 goto done;
616         }
617
618         offset = hnd->offset;
619
620         len1 -= 2; /* Account for the Header above ... */
621
622         while (len1) {
623
624           ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len);
625
626           if (ret != ASN1_ERR_NOERROR) {
627             dissect_parse_error(tvb, offset, pinfo, subtree,
628                                 "SPNEGO context header", ret);
629             goto done;
630           }
631
632           if (!(cls == ASN1_CTX && con == ASN1_CON)) {
633             proto_tree_add_text(
634                                 subtree, tvb, offset, 0,
635                                 "Unknown header (cls=%d, con=%d, tag=%d)",
636                                 cls, con, tag);
637             goto done;
638           }
639
640           /* Should be one of the fields */
641
642           switch (tag) {
643
644           case SPNEGO_negResult:
645
646             offset = dissect_spnego_negResult(tvb, offset, pinfo, subtree, 
647                                               hnd);
648             break;
649
650           case SPNEGO_supportedMech:
651
652             offset = dissect_spnego_supportedMech(tvb, offset, pinfo, subtree,
653                                                   hnd);
654
655             break;
656
657           case SPNEGO_responseToken:
658
659             offset = dissect_spnego_responseToken(tvb, offset, pinfo, subtree,
660                                                   hnd);
661             break;
662
663           case SPNEGO_mechListMIC:
664
665             offset = dissect_spnego_mechListMIC(tvb, offset, pinfo, subtree, 
666                                                 hnd);
667             break;
668
669           default:
670
671             break;
672           }
673
674           len1 -= (len + 2); /* FIXME: The +2 may be wrong */
675
676         }
677
678  done:
679         return offset;
680
681 }
682
683 static void
684 dissect_spnego(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
685 {
686         proto_item *item;
687         proto_tree *subtree;
688         int length = tvb_length_remaining(tvb, 0);
689         int ret, offset = 0;
690         ASN1_SCK hnd;
691         gboolean def;
692         guint len1, len, cls, con, tag, nbytes;
693         conversation_t *conversation;
694         dissector_handle_t handle;
695
696         /*
697          * We need this later, so lets get it now ...
698          */
699
700         conversation = find_conversation(&pinfo->src, &pinfo->dst,
701                                          pinfo->ptype, pinfo->srcport,
702                                          pinfo->destport, 0);
703
704         if (conversation && 
705             (handle = conversation_get_proto_data(conversation, 
706                                                  proto_spnego))) {
707           next_level_dissector = handle;
708
709         }
710         item = proto_tree_add_item(
711                 tree, hf_spnego, tvb, offset, length, FALSE);
712
713         subtree = proto_item_add_subtree(item, ett_spnego);
714
715         /*
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 }
735          *
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.
740          *
741          * FIXME, the following code is broken so far.
742          */
743         asn1_open(&hnd, tvb, offset);
744
745         /*
746          * Get the first header ...
747          */
748
749         ret = asn1_header_decode(&hnd, &cls, &con, &tag, &def, &len1);
750
751         if (ret != ASN1_ERR_NOERROR) {
752                 dissect_parse_error(tvb, offset, pinfo, subtree,
753                                     "SPNEGO context header", ret);
754                 goto done;
755         }
756
757         if (!(cls == ASN1_CTX && con == ASN1_CON)) {
758                 proto_tree_add_text(
759                         subtree, tvb, offset, 0,
760                         "Unknown header (cls=%d, con=%d, tag=%d)",
761                         cls, con, tag);
762                 goto done;
763         }
764
765         offset = hnd.offset;
766
767         /*
768          * The Tag is one of negTokenInit or negTokenTarg
769          */
770
771         switch (tag) {
772
773         case SPNEGO_negTokenInit:
774
775           offset = dissect_spnego_negTokenInit(tvb, offset, pinfo,
776                                                subtree, &hnd);
777
778           break;
779
780         case SPNEGO_negTokenTarg:
781
782           offset = dissect_spnego_negTokenTarg(tvb, offset, pinfo,
783                                                subtree, &hnd);
784           break;
785
786         default: /* Broken, what to do? */
787
788           break;
789         }
790
791
792  done:
793         asn1_close(&hnd, &offset);
794
795 }
796
797 void
798 proto_register_spnego(void)
799 {
800         static hf_register_info hf[] = {
801                 { &hf_spnego,
802                   { "SPNEGO", "spnego", FT_NONE, BASE_NONE, NULL, 0x0,
803                     "SPNEGO", HFILL }},
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}},
816         };
817
818         static gint *ett[] = {
819                 &ett_spnego,
820                 &ett_spnego_negtokeninit,
821                 &ett_spnego_negtokentarg,
822                 &ett_spnego_mechtype,
823         };
824
825         proto_spnego = proto_register_protocol(
826                 "Spnego", "Spnego", "spnego");
827
828         proto_register_field_array(proto_spnego, hf, array_length(hf));
829         proto_register_subtree_array(ett, array_length(ett));
830
831         register_dissector("spnego", dissect_spnego, proto_spnego);
832 }
833
834 void
835 proto_reg_handoff_spnego(void)
836 {
837         /* Register protocol with GSS-API module */
838
839         gssapi_init_oid("1.3.6.1.5.5.2", proto_spnego, ett_spnego, "spnego");
840 }