Commit the changes to allow us to dissect GSSAPI GetMIC tokens.
[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 RFC 2478.
4  * Copyright 2002, Tim Potter <tpot@samba.org>
5  * Copyright 2002, Richard Sharpe <rsharpe@ns.aus.com>
6  *
7  * $Id: packet-spnego.c,v 1.44 2003/05/23 18:34:58 sharpe Exp $
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 "packet-kerberos.h"
43 #include <epan/conversation.h>
44
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
57
58 static int proto_spnego = -1;
59 static int proto_spnego_krb5 = -1;
60
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;
78
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;
88
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"},
93   { 0, NULL}
94 };
95
96 /* Display an ASN1 parse error.  Taken from packet-snmp.c */
97
98 static dissector_handle_t data_handle;
99
100 static void
101 dissect_parse_error(tvbuff_t *tvb, int offset, packet_info *pinfo,
102                     proto_tree *tree, const char *field_name, int ret)
103 {
104         char *errstr;
105
106         errstr = asn1_err_to_str(ret);
107
108         if (tree != NULL) {
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);
113         }
114 }
115
116 /*
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.
120  */ 
121
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
128
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" },
136   { 0, NULL}
137 };
138
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
143
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"},
149   { 0, NULL}
150 };
151
152 #define KRB_SEAL_ALG_DES_CBC    0x0000
153 #define KRB_SEAL_ALG_RC4        0x0010
154 #define KRB_SEAL_ALG_NONE       0xffff
155
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"},
160   { 0, NULL}
161 };
162
163 /*
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.
169  */
170 static int
171 dissect_spnego_krb5_getmic_base(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree);
172 static void
173 dissect_spnego_krb5(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
174 {
175         proto_item *item;
176         proto_tree *subtree;
177         int ret, offset = 0;
178         ASN1_SCK hnd;
179         gboolean def;
180         guint len1, cls, con, tag, oid_len, nbytes;
181         guint16 token_id = 0;
182         subid_t *oid;
183         gchar *oid_string;
184         gssapi_oid_value *value;
185         tvbuff_t *krb5_tvb;
186
187         item = proto_tree_add_item(tree, hf_spnego_krb5, tvb, offset, 
188                                    -1, FALSE);
189
190         subtree = proto_item_add_subtree(item, ett_spnego_krb5);
191
192         /*
193          * The KRB5 blob conforms to RFC1964:
194          * [APPLICATION 0] {
195          *   OID,
196          *   USHORT (0x0001 == AP-REQ, 0x0002 == AP-REP, 0x0003 == ERROR),
197          *   OCTET STRING } 
198          *
199          * However, for some protocols, the KRB5 blob starts at the SHORT
200          * and has no DER encoded header etc.
201          *
202          * It appears that for some other protocols the KRB5 blob is just
203          * a Kerberos message, with no [APPLICATION 0] header, no OID,
204          * and no USHORT.
205          *
206          * So:
207          *
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.
210          *
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.
214          *
215          *      Otherwise, we show the USHORT, and then dissect the rest
216          *      as a Kerberos message.
217          */
218
219         asn1_open(&hnd, tvb, offset);
220
221         /*
222          * Get the first header ...
223          */
224
225         ret = asn1_header_decode(&hnd, &cls, &con, &tag, &def, &len1);
226
227         if (ret != ASN1_ERR_NOERROR) {
228                 dissect_parse_error(tvb, offset, pinfo, subtree,
229                                     "SPNEGO KRB5 Header", ret);
230                 goto done;
231         }
232
233         if (cls == ASN1_APL && con == ASN1_CON) {
234             /*
235              * [APPLICATION <tag>]
236              */
237             switch (tag) {
238
239             case 0:
240                 /*
241                  * [APPLICATION 0]
242                  */
243
244                 offset = hnd.offset;
245
246                 /* Next, the OID */
247
248                 ret = asn1_oid_decode(&hnd, &oid, &oid_len, &nbytes);
249
250                 if (ret != ASN1_ERR_NOERROR) {
251                     dissect_parse_error(tvb, offset, pinfo, subtree,
252                                         "SPNEGO supportedMech token", ret);
253                     goto done;
254                 }
255
256                 oid_string = format_oid(oid, oid_len);
257
258                 value = gssapi_lookup_oid(oid, oid_len);
259
260                 if (value) 
261                     proto_tree_add_text(subtree, tvb, offset, nbytes, 
262                                         "OID: %s (%s)",
263                                         oid_string, value->comment);
264                 else
265                     proto_tree_add_text(subtree, tvb, offset, nbytes,
266                                         "OID: %s",
267                                         oid_string);
268           
269                 g_free(oid_string);
270
271                 offset += nbytes;
272
273                 /* Next, the token ID ... */
274
275                 token_id = tvb_get_letohs(tvb, offset);
276                 proto_tree_add_item(subtree, hf_spnego_krb5_tok_id, tvb, offset, 2,
277                                     TRUE);
278
279                 hnd.offset += 2;
280
281                 offset += 2;
282
283                 break;
284
285             case 14:    /* [APPLICATION 14] */
286             case 15:    /* [APPLICATION 15] */
287                 break;
288
289             default:
290                 proto_tree_add_text(subtree, tvb, offset, 0,
291                         "Unknown header (cls=%d, con=%d, tag=%d)",
292                         cls, con, tag);
293                 goto done;
294             }
295         } else {
296             /* Next, the token ID ... */
297
298             token_id = tvb_get_letohs(tvb, offset);
299             proto_tree_add_item(subtree, hf_spnego_krb5_tok_id, tvb, offset, 2,
300                                 TRUE);
301
302             hnd.offset += 2;
303
304             offset += 2;
305         }
306
307         switch (token_id) {
308
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);
314           break;
315
316         case KRB_TOKEN_GETMIC:
317
318           offset = dissect_spnego_krb5_getmic_base(tvb, offset, pinfo, subtree); 
319           break;
320
321         case KRB_TOKEN_WRAP:
322
323           break;
324
325         case KRB_TOKEN_DELETE_SEC_CONTEXT:
326
327           break;
328
329         default:
330
331           break;
332         }
333
334  done:
335         return;
336 }
337
338 /*
339  * XXX - This is for GSSAPI GetMIC tokens ...
340  */
341 static int
342 dissect_spnego_krb5_getmic_base(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
343 {
344         guint16 sgn_alg;
345
346         /*
347          * The KRB5 blob conforms to RFC1964:
348          *   USHORT (0x0101 == GSS_GetMIC)
349          *   and so on } 
350          */
351
352         /* Now, the sign algorithm ... */
353
354         sgn_alg = tvb_get_letohs(tvb, offset);
355         proto_tree_add_uint(tree, hf_spnego_krb5_sgn_alg, tvb, offset, 2,
356                             sgn_alg);
357
358         offset += 2;
359
360         /* Skip the filler */
361
362         offset += 4;
363
364         /* Encrypted sequence number */
365
366         proto_tree_add_item(tree, hf_spnego_krb5_snd_seq, tvb, offset, 8,
367                             TRUE);
368
369         offset += 8;
370
371         /* Checksum of plaintext padded data */
372
373         proto_tree_add_item(tree, hf_spnego_krb5_sgn_cksum, tvb, offset, 8,
374                             TRUE);
375
376         offset += 8;
377
378         /*
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....
384          */
385         if (sgn_alg == KRB_SGN_ALG_HMAC) {
386           proto_tree_add_item(tree, hf_spnego_krb5_confounder, tvb, offset, 8,
387                               TRUE);
388
389           offset += 8;
390         }
391
392         /*
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.
397          */
398
399         return offset;
400 }
401
402 /*
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.
408  */
409 static int
410 dissect_spnego_krb5_wrap(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
411 {
412         proto_item *item;
413         proto_tree *subtree;
414         int offset = 0;
415         guint16 sgn_alg;
416
417         item = proto_tree_add_item(tree, hf_spnego_krb5, tvb, 0, -1, FALSE);
418
419         subtree = proto_item_add_subtree(item, ett_spnego_krb5);
420
421         /*
422          * The KRB5 blob conforms to RFC1964:
423          *   USHORT (0x0102 == GSS_Wrap)
424          *   and so on } 
425          */
426
427         /* First, the token ID ... */
428
429         proto_tree_add_item(subtree, hf_spnego_krb5_tok_id, tvb, offset, 2,
430                             TRUE);
431
432         offset += 2;
433
434         /* Now, the sign and seal algorithms ... */
435
436         sgn_alg = tvb_get_letohs(tvb, offset);
437         proto_tree_add_uint(subtree, hf_spnego_krb5_sgn_alg, tvb, offset, 2,
438                             sgn_alg);
439
440         offset += 2;
441
442         proto_tree_add_item(subtree, hf_spnego_krb5_seal_alg, tvb, offset, 2,
443                             TRUE);
444
445         offset += 2;
446
447         /* Skip the filler */
448
449         offset += 2;
450
451         /* Encrypted sequence number */
452
453         proto_tree_add_item(subtree, hf_spnego_krb5_snd_seq, tvb, offset, 8,
454                             TRUE);
455
456         offset += 8;
457
458         /* Checksum of plaintext padded data */
459
460         proto_tree_add_item(subtree, hf_spnego_krb5_sgn_cksum, tvb, offset, 8,
461                             TRUE);
462
463         offset += 8;
464
465         /*
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....
471          */
472         if (sgn_alg == KRB_SGN_ALG_HMAC) {
473           proto_tree_add_item(subtree, hf_spnego_krb5_confounder, tvb, offset, 8,
474                               TRUE);
475
476           offset += 8;
477         }
478
479         /*
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.
484          */
485         proto_item_set_len(item, offset);
486         return offset;
487 }
488
489 /* Spnego stuff from here */
490
491 static int
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)
495 {
496         proto_item *item = NULL;
497         proto_tree *subtree = NULL;
498         gboolean def;
499         guint len1, len, cls, con, tag, nbytes;
500         subid_t *oid;
501         gchar *oid_string;
502         int ret;
503         gboolean saw_mechanism = FALSE;
504
505         /*
506          * MechTypeList ::= SEQUENCE OF MechType
507          */
508
509         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
510
511         if (ret != ASN1_ERR_NOERROR) {
512           dissect_parse_error(tvb, offset, pinfo, subtree,
513                               "SPNEGO last sequence header", ret);
514           goto done;
515         }
516
517         if (!(cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ)) {
518           proto_tree_add_text(
519                               subtree, tvb, offset, 0,
520                               "Unknown header (cls=%d, con=%d, tag=%d)",
521                               cls, con, tag);
522           goto done;
523         }
524
525         offset = hnd->offset;
526
527         item = proto_tree_add_item(tree, hf_spnego_mechtype, tvb, offset, 
528                                    len1, FALSE);
529         subtree = proto_item_add_subtree(item, ett_spnego_mechtype);
530
531         /*
532          * Now, the object IDs ... We should translate them: FIXME
533          */
534
535         while (len1) {
536           gssapi_oid_value *value;
537
538           ret = asn1_oid_decode(hnd, &oid, &len, &nbytes);
539
540           if (ret != ASN1_ERR_NOERROR) {
541             dissect_parse_error(tvb, offset, pinfo, subtree,
542                                 "SPNEGO mechTypes token", ret);
543             goto done;
544           }
545
546           oid_string = format_oid(oid, len);
547           value = gssapi_lookup_oid(oid, len);
548           if (value)
549             proto_tree_add_text(subtree, tvb, offset, nbytes, "OID: %s (%s)",
550                                 oid_string, value->comment);
551           else
552             proto_tree_add_text(subtree, tvb, offset, nbytes, "OID: %s",
553                                 oid_string);
554
555           g_free(oid_string);
556
557           /*
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
566            * in the list.)
567            */
568           if (!saw_mechanism) {
569             if (value)
570               *next_level_value_p = value;
571             saw_mechanism = TRUE;
572           }
573
574           offset += nbytes;
575           len1 -= nbytes;
576
577         }
578
579  done:
580
581         return offset;
582
583 }
584
585 static int
586 dissect_spnego_reqFlags(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
587                         proto_tree *tree, ASN1_SCK *hnd)
588 {
589         gboolean def;
590         guint len1, cls, con, tag;
591         int ret;
592
593         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
594
595         if (ret != ASN1_ERR_NOERROR) {
596                 dissect_parse_error(tvb, offset, pinfo, tree,
597                                     "SPNEGO reqFlags header", ret);
598                 goto done;
599         }
600
601         if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_BTS)) {
602                 proto_tree_add_text(
603                         tree, tvb, offset, 0,
604                         "Unknown header (cls=%d, con=%d, tag=%d)",
605                         cls, con, tag);
606                 goto done;
607         }
608
609         /* We must have a Bit String ... insert it */ 
610
611         offset = hnd->offset;
612
613         proto_tree_add_item(tree, hf_spnego_reqflags, tvb, offset, len1,
614                             FALSE);
615
616         hnd->offset += len1;
617
618  done:
619         return offset + len1;
620
621 }
622
623 static int
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)
627 {
628         proto_item *item;
629         proto_tree *subtree;
630         gboolean def;
631         int ret;
632         guint cls, con, tag, nbytes;
633         tvbuff_t *token_tvb;
634
635         /*
636          * This appears to be a simple octet string ...
637          */
638
639         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &nbytes);
640
641         if (ret != ASN1_ERR_NOERROR) {
642                 dissect_parse_error(tvb, offset, pinfo, tree,
643                                     "SPNEGO sequence header", ret);
644                 goto done;
645         }
646
647         if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS)) {
648                 proto_tree_add_text(
649                         tree, tvb, offset, 0,
650                         "Unknown header (cls=%d, con=%d, tag=%d)",
651                         cls, con, tag);
652                 goto done;
653         }
654
655         offset = hnd->offset;
656
657         item = proto_tree_add_item(tree, hf_spnego_mechtoken, tvb, offset, 
658                                    nbytes, FALSE);
659         subtree = proto_item_add_subtree(item, ett_spnego_mechtoken);
660
661         /*
662          * Now, we should be able to dispatch after creating a new TVB.
663          */
664
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);
668
669         hnd->offset += nbytes; /* Update this ... */
670
671  done:
672
673   return offset + nbytes;
674
675 }
676
677 static int
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)
681 {
682         guint len1, cls, con, tag;
683         int ret;
684         gboolean def;
685         proto_tree *subtree = NULL;
686
687         /*
688          * Add the mechListMIC [3] Octet String or General String ...
689          */
690         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
691
692         if (ret != ASN1_ERR_NOERROR) {
693                 dissect_parse_error(tvb, offset, pinfo, subtree,
694                                     "SPNEGO sequence header", ret);
695                 goto done;
696         }
697
698         offset = hnd->offset;
699
700         if (cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ) {
701
702           /*
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
706            */
707          
708           proto_tree_add_text(tree, tvb, offset + 4, len1 - 4,
709                               "mechListMIC: %s",
710                               tvb_format_text(tvb, offset + 4, len1 - 4));
711
712           /* Naughty ... but we have to adjust for what we never took */
713
714           hnd->offset += len1;
715           offset += len1;
716
717         }
718         else if (cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS) {
719           tvbuff_t *token_tvb;
720           proto_item *item;
721           proto_tree *subtree;
722
723           item = proto_tree_add_item(tree, hf_spnego_mechlistmic, tvb, offset, 
724                               len1, FALSE); 
725           subtree = proto_item_add_subtree(item, ett_spnego_mechlistmic);
726           
727         /*
728          * Now, we should be able to dispatch after creating a new TVB.
729          */
730
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);
734
735           hnd->offset += len1; /* Update this ... */
736           offset += len1;
737
738         }
739         else {
740
741           proto_tree_add_text(subtree, tvb, offset, 0,
742                               "Unknown header (cls=%d, con=%d, tag=%d)",
743                               cls, con, tag);
744           goto done;
745         }
746
747  done:
748
749         return offset;
750
751 }
752
753 static int
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)
757 {
758         proto_item *item;
759         proto_tree *subtree;
760         gboolean def;
761         guint len1, len, cls, con, tag;
762         int ret;
763
764         item = proto_tree_add_item( tree, hf_spnego_negtokeninit, tvb, offset,
765                                     -1, FALSE);
766         subtree = proto_item_add_subtree(item, ett_spnego_negtokeninit);
767
768         /*
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 }
775
776          */
777
778         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
779
780         if (ret != ASN1_ERR_NOERROR) {
781                 dissect_parse_error(tvb, offset, pinfo, subtree,
782                                     "SPNEGO sequence header", ret);
783                 goto done;
784         }
785
786         if (!(cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ)) {
787                 proto_tree_add_text(
788                         subtree, tvb, offset, 0,
789                         "Unknown header (cls=%d, con=%d, tag=%d)",
790                         cls, con, tag);
791                 goto done;
792         }
793
794         offset = hnd->offset;
795
796         while (len1) {
797           int hdr_ofs;
798
799           hdr_ofs = hnd->offset;
800
801           ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len);
802
803           if (ret != ASN1_ERR_NOERROR) {
804             dissect_parse_error(tvb, offset, pinfo, subtree,
805                                 "SPNEGO context header", ret);
806             goto done;
807           }
808
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)",
812                                 cls, con, tag);
813             goto done;
814           }
815
816           /* Adjust for the length of the header */
817
818           len1 -= (hnd->offset - hdr_ofs);
819
820           /* Should be one of the fields */
821
822           switch (tag) {
823
824           case SPNEGO_mechTypes:
825
826             offset = dissect_spnego_mechTypes(tvb, offset, pinfo,
827                                               subtree, hnd,
828                                               next_level_value_p);
829
830             break;
831
832           case SPNEGO_reqFlags:
833
834             offset = dissect_spnego_reqFlags(tvb, offset, pinfo, subtree, hnd);
835
836             break;
837
838           case SPNEGO_mechToken:
839
840             offset = dissect_spnego_mechToken(tvb, offset, pinfo, subtree, 
841                                               hnd, (*next_level_value_p)->handle);
842             break;
843
844           case SPNEGO_mechListMIC:
845
846             offset = dissect_spnego_mechListMIC(tvb, offset, pinfo, subtree,
847                                                 hnd, (*next_level_value_p)->handle);
848             break;
849
850           default:
851
852             break;
853           }
854
855           len1 -= len;
856
857         }
858
859  done:
860
861         return offset; /* Not sure this is right */
862 }
863
864 static int
865 dissect_spnego_negResult(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
866                             proto_tree *tree, ASN1_SCK *hnd)
867 {
868         gboolean def;
869         int ret;
870         guint len, cls, con, tag, val;
871
872         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len);
873
874         if (ret != ASN1_ERR_NOERROR) {
875           dissect_parse_error(tvb, offset, pinfo, tree,
876                               "SPNEGO context header", ret);
877           goto done;
878         }
879
880         if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_ENUM)) {
881           proto_tree_add_text(
882                               tree, tvb, offset, 0,
883                               "Unknown header (cls=%d, con=%d, tag=%d) xxx",
884                               cls, con, tag);
885           goto done;
886         }
887
888         offset = hnd->offset;
889
890         /* Now, get the value */
891
892         ret = asn1_uint32_value_decode(hnd, len, &val);
893
894         if (ret != ASN1_ERR_NOERROR) {
895           dissect_parse_error(tvb, offset, pinfo, tree,
896                               "SPNEGO negResult value", ret);
897           goto done;
898         }
899         
900         proto_tree_add_item(tree, hf_spnego_negtokentarg_negresult, tvb, 
901                             offset, 1, FALSE);
902
903         offset = hnd->offset;
904
905  done:
906         return offset;
907 }
908
909 static int
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)
913 {
914         int ret;
915         guint oid_len, nbytes;
916         subid_t *oid;
917         gchar *oid_string;
918         gssapi_oid_value *value;
919         conversation_t *conversation;
920
921         /*
922          * Now, get the OID, and find the handle, if any
923          */
924
925         offset = hnd->offset;
926
927         ret = asn1_oid_decode(hnd, &oid, &oid_len, &nbytes);
928
929         if (ret != ASN1_ERR_NOERROR) {
930                 dissect_parse_error(tvb, offset, pinfo, tree,
931                                     "SPNEGO supportedMech token", ret);
932                 goto done;
933         }
934
935         oid_string = format_oid(oid, oid_len);
936         value = gssapi_lookup_oid(oid, oid_len);
937
938         if (value)
939           proto_tree_add_text(tree, tvb, offset, nbytes, 
940                               "supportedMech: %s (%s)",
941                               oid_string, value->comment);
942         else
943           proto_tree_add_text(tree, tvb, offset, nbytes, "supportedMech: %s",
944                               oid_string);
945
946         g_free(oid_string);
947
948         offset += nbytes;
949
950         /* Should check for an unrecognized OID ... */
951
952         if (value)
953           *next_level_value_p = value;
954
955         /*
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. :-(
963          */
964
965         if ((conversation = find_conversation(&pinfo->src, &pinfo->dst,
966                                              pinfo->ptype, pinfo->srcport,
967                                              pinfo->destport, 0))) {
968
969
970           conversation_add_proto_data(conversation, proto_spnego, 
971                                       *next_level_value_p);
972         }
973         else {
974
975         }
976
977  done:
978         return offset;
979 }
980
981 static int
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)
985 {
986         gboolean def;
987         int ret;
988         guint cls, con, tag, nbytes;
989         tvbuff_t *token_tvb;
990         proto_item *item;
991         proto_tree *subtree;
992
993         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &nbytes);
994
995         if (ret != ASN1_ERR_NOERROR) {
996                 dissect_parse_error(tvb, offset, pinfo, tree,
997                                     "SPNEGO sequence header", ret);
998                 goto done;
999         }
1000
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)",
1005                         cls, con, tag);
1006                 goto done;
1007         }
1008
1009         offset = hnd->offset;
1010
1011         item = proto_tree_add_item(tree, hf_spnego_responsetoken, tvb, offset, 
1012                                    nbytes, FALSE); 
1013
1014         subtree = proto_item_add_subtree(item, ett_spnego_responsetoken);
1015
1016         /*
1017          * Now, we should be able to dispatch after creating a new TVB.
1018          */
1019
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);
1023
1024         hnd->offset += nbytes; /* Update this ... */
1025
1026  done:
1027         return offset + nbytes;
1028 }
1029
1030 static int
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)
1034
1035 {
1036         proto_item *item;
1037         proto_tree *subtree;
1038         gboolean def;
1039         int ret;
1040         guint len1, len, cls, con, tag;
1041
1042         item = proto_tree_add_item( tree, hf_spnego_negtokentarg, tvb, offset,
1043                                     -1, FALSE);
1044         subtree = proto_item_add_subtree(item, ett_spnego_negtokentarg);
1045
1046         /* 
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 }
1056          */
1057
1058         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
1059
1060         if (ret != ASN1_ERR_NOERROR) {
1061                 dissect_parse_error(tvb, offset, pinfo, subtree,
1062                                     "SPNEGO sequence header", ret);
1063                 goto done;
1064         }
1065
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)",
1070                         cls, con, tag);
1071                 goto done;
1072         }
1073
1074         offset = hnd->offset;
1075
1076         while (len1) {
1077           int hdr_ofs;
1078
1079           hdr_ofs = hnd->offset; 
1080
1081           ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len);
1082
1083           if (ret != ASN1_ERR_NOERROR) {
1084             dissect_parse_error(tvb, offset, pinfo, subtree,
1085                                 "SPNEGO context header", ret);
1086             goto done;
1087           }
1088
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)",
1093                                 cls, con, tag);
1094             goto done;
1095           }
1096
1097           /* Adjust for the length of the header */
1098
1099           len1 -= (hnd->offset - hdr_ofs);
1100
1101           /* Should be one of the fields */
1102
1103           switch (tag) {
1104
1105           case SPNEGO_negResult:
1106
1107             offset = dissect_spnego_negResult(tvb, offset, pinfo, subtree, 
1108                                               hnd);
1109             break;
1110
1111           case SPNEGO_supportedMech:
1112
1113             offset = dissect_spnego_supportedMech(tvb, offset, pinfo, subtree,
1114                                                   hnd, next_level_value_p);
1115
1116             break;
1117
1118           case SPNEGO_responseToken:
1119
1120             offset = dissect_spnego_responseToken(tvb, offset, pinfo, subtree,
1121                                                   hnd,
1122                                                   (*next_level_value_p != NULL) ?
1123                                                       (*next_level_value_p)->handle :
1124                                                       NULL);
1125             break;
1126
1127           case SPNEGO_mechListMIC:
1128
1129             offset = dissect_spnego_mechListMIC(tvb, offset, pinfo, subtree, 
1130                                                 hnd,
1131                                                 (*next_level_value_p != NULL) ?
1132                                                     (*next_level_value_p)->handle :
1133                                                     NULL);
1134             break;
1135
1136           default:
1137
1138             break;
1139           }
1140
1141           len1 -= len;
1142
1143         }
1144
1145  done:
1146         return offset;
1147
1148 }
1149
1150 static void
1151 dissect_spnego(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1152 {
1153         proto_item *item;
1154         proto_tree *subtree;
1155         int ret, offset = 0;
1156         ASN1_SCK hnd;
1157         gboolean def;
1158         guint len1, cls, con, tag;
1159         conversation_t *conversation;
1160         gssapi_oid_value *next_level_value;
1161
1162         /*
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.
1166          */
1167
1168         next_level_value = p_get_proto_data(pinfo->fd, proto_spnego);
1169         if (!next_level_value && !pinfo->fd->flags.visited) {
1170             /*
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.
1175              */
1176             conversation = find_conversation(&pinfo->src, &pinfo->dst,
1177                                              pinfo->ptype, pinfo->srcport,
1178                                              pinfo->destport, 0);
1179
1180             if (conversation) {
1181                 next_level_value = conversation_get_proto_data(conversation, 
1182                                                                proto_spnego);
1183                 if (next_level_value)
1184                     p_add_proto_data(pinfo->fd, proto_spnego, next_level_value);
1185             }
1186         }
1187
1188         item = proto_tree_add_item(tree, hf_spnego, tvb, offset, 
1189                                    -1, FALSE);
1190
1191         subtree = proto_item_add_subtree(item, ett_spnego);
1192
1193         /*
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 }
1213          *
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.
1218          *
1219          */
1220
1221         asn1_open(&hnd, tvb, offset);
1222
1223         /*
1224          * Get the first header ...
1225          */
1226
1227         ret = asn1_header_decode(&hnd, &cls, &con, &tag, &def, &len1);
1228
1229         if (ret != ASN1_ERR_NOERROR) {
1230                 dissect_parse_error(tvb, offset, pinfo, subtree,
1231                                     "SPNEGO context header", ret);
1232                 goto done;
1233         }
1234
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)",
1239                         cls, con, tag);
1240                 goto done;
1241         }
1242
1243         offset = hnd.offset;
1244
1245         /*
1246          * The Tag is one of negTokenInit or negTokenTarg
1247          */
1248
1249         switch (tag) {
1250
1251         case SPNEGO_negTokenInit:
1252
1253           offset = dissect_spnego_negTokenInit(tvb, offset, pinfo,
1254                                                subtree, &hnd,
1255                                                &next_level_value);
1256
1257           break;
1258
1259         case SPNEGO_negTokenTarg:
1260
1261           offset = dissect_spnego_negTokenTarg(tvb, offset, pinfo,
1262                                                subtree, &hnd,
1263                                                &next_level_value);
1264           break;
1265
1266         default: /* Broken, what to do? */
1267
1268           break;
1269         }
1270
1271
1272  done:
1273         asn1_close(&hnd, &offset);
1274
1275 }
1276
1277 static int
1278 dissect_spnego_wrap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1279 {
1280         proto_item *item;
1281         proto_tree *subtree;
1282         int ret, offset = 0;
1283         int return_offset;
1284         ASN1_SCK hnd;
1285         gboolean def;
1286         guint len1, cls, con, tag, nbytes;
1287         guint oid_len;
1288         subid_t *oid;
1289         gchar *oid_string;
1290         conversation_t *conversation;
1291         gssapi_oid_value *next_level_value;
1292         tvbuff_t *token_tvb;
1293         int len;
1294
1295         /*
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.
1299          */
1300
1301         next_level_value = p_get_proto_data(pinfo->fd, proto_spnego);
1302         if (!next_level_value && !pinfo->fd->flags.visited) {
1303             /*
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.
1308              */
1309             conversation = find_conversation(&pinfo->src, &pinfo->dst,
1310                                              pinfo->ptype, pinfo->srcport,
1311                                              pinfo->destport, 0);
1312
1313             if (conversation) {
1314                 next_level_value = conversation_get_proto_data(conversation, 
1315                                                                proto_spnego);
1316                 if (next_level_value)
1317                     p_add_proto_data(pinfo->fd, proto_spnego, next_level_value);
1318             }
1319         }
1320
1321         item = proto_tree_add_item(tree, hf_spnego, tvb, offset, 
1322                                    -1, FALSE);
1323
1324         subtree = proto_item_add_subtree(item, ett_spnego);
1325
1326         /*
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.
1331          */
1332
1333         asn1_open(&hnd, tvb, offset);
1334
1335         /*
1336          * Get the first header ...
1337          */
1338
1339         ret = asn1_header_decode(&hnd, &cls, &con, &tag, &def, &len1);
1340
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);
1345                 goto done;
1346         }
1347
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)",
1352                         cls, con, tag);
1353                 return_offset = tvb_length(tvb);
1354                 goto done;
1355         }
1356
1357         offset = hnd.offset;
1358
1359         /*
1360          * Get the OID, and find the handle, if any
1361          */
1362
1363         ret = asn1_oid_decode(&hnd, &oid, &oid_len, &nbytes);
1364
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);
1369                 goto done;
1370         }
1371
1372         oid_string = format_oid(oid, oid_len);
1373         next_level_value = gssapi_lookup_oid(oid, oid_len);
1374
1375         /*
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.)
1381          */
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);
1386         else
1387           proto_tree_add_text(tree, tvb, offset, nbytes, "thisMech: %s",
1388                               oid_string);
1389
1390         g_free(oid_string);
1391
1392         offset += nbytes;
1393
1394         /*
1395          * Now dissect the GSS_Wrap token; it's assumed to be in the
1396          * rest of the tvbuff.
1397          */
1398         item = proto_tree_add_item(tree, hf_spnego_wraptoken, tvb, offset, 
1399                                    -1, FALSE); 
1400
1401         subtree = proto_item_add_subtree(item, ett_spnego_wraptoken);
1402
1403         /*
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.
1408          */
1409
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);
1413           if (len == 0)
1414             return_offset = tvb_length(tvb);
1415           else
1416             return_offset = offset + len;
1417         } else
1418           return_offset = tvb_length(tvb);
1419  done:
1420         asn1_close(&hnd, &offset);
1421
1422         return return_offset;
1423 }
1424
1425 void
1426 proto_register_spnego(void)
1427 {
1428         static hf_register_info hf[] = {
1429                 { &hf_spnego,
1430                   { "SPNEGO", "spnego", FT_NONE, BASE_NONE, NULL, 0x0,
1431                     "SPNEGO", HFILL }},
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",
1450                     HFILL}},
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",
1460                     HFILL}},
1461                 { &hf_spnego_krb5,
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}},
1482         };
1483
1484         static gint *ett[] = {
1485                 &ett_spnego,
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,
1493                 &ett_spnego_krb5,
1494         };
1495
1496         proto_spnego = proto_register_protocol(
1497                 "Spnego", "Spnego", "spnego");
1498         proto_spnego_krb5 = proto_register_protocol("SPNEGO-KRB5",
1499                                                     "SPNEGO-KRB5",
1500                                                     "spnego-krb5");
1501
1502         proto_register_field_array(proto_spnego, hf, array_length(hf));
1503         proto_register_subtree_array(ett, array_length(ett));
1504 }
1505
1506 void
1507 proto_reg_handoff_spnego(void)
1508 {
1509         dissector_handle_t spnego_handle, spnego_wrap_handle;
1510         dissector_handle_t spnego_krb5_handle, spnego_krb5_wrap_handle;
1511
1512         /* Register protocol with GSS-API module */
1513
1514         spnego_handle = create_dissector_handle(dissect_spnego, proto_spnego);
1515         spnego_wrap_handle = new_create_dissector_handle(dissect_spnego_wrap,
1516                                                          proto_spnego);
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");
1520
1521         /* Register both the one MS created and the real one */
1522         /*
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.
1529          */
1530         spnego_krb5_handle = create_dissector_handle(dissect_spnego_krb5,
1531                                                      proto_spnego_krb5);
1532         spnego_krb5_wrap_handle = new_create_dissector_handle(dissect_spnego_krb5_wrap,
1533                                                               proto_spnego_krb5);
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");
1540 }