connectionless cancel PDU's don't have a dg_server_accepting_cancels field
[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  * Copyright 2003, Richard Sharpe <rsharpe@richardsharpe.com>
7  *
8  * $Id: packet-spnego.c,v 1.55 2004/05/11 02:02:44 gerald Exp $
9  *
10  * Ethereal - Network traffic analyzer
11  * By Gerald Combs <gerald@ethereal.com>
12  * Copyright 1998 Gerald Combs
13  *
14  * This program is free software; you can redistribute it and/or
15  * modify it under the terms of the GNU General Public License
16  * as published by the Free Software Foundation; either version 2
17  * of the License, or (at your option) any later version.
18  *
19  * This program is distributed in the hope that it will be useful,
20  * but WITHOUT ANY WARRANTY; without even the implied warranty of
21  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
22  * GNU General Public License for more details.
23  *
24  * You should have received a copy of the GNU General Public License
25  * along with this program; if not, write to the Free Software
26  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
27  */
28
29 #ifdef HAVE_CONFIG_H
30 # include "config.h"
31 #endif
32
33 #ifdef HAVE_SYS_TYPES_H
34 # include <sys/types.h>
35 #endif
36
37 #include <glib.h>
38 #include <epan/packet.h>
39
40 #include "asn1.h"
41 #include "format-oid.h"
42 #include "packet-gssapi.h"
43 #include "packet-kerberos.h"
44 #include <epan/conversation.h>
45
46 #define SPNEGO_negTokenInit 0
47 #define SPNEGO_negTokenTarg 1
48 #define SPNEGO_mechTypes 0
49 #define SPNEGO_reqFlags 1
50 #define SPNEGO_mechToken 2
51 #define SPNEGO_mechListMIC 3
52 #define SPNEGO_negResult 0
53 #define SPNEGO_supportedMech 1
54 #define SPNEGO_responseToken 2
55 #define SPNEGO_negResult_accept_completed 0
56 #define SPNEGO_negResult_accept_incomplete 1
57 #define SPNEGO_negResult_accept_reject 2
58
59 static int proto_spnego = -1;
60 static int proto_spnego_krb5 = -1;
61
62 static int hf_spnego = -1;
63 static int hf_spnego_negtokeninit = -1;
64 static int hf_spnego_negtokentarg = -1;
65 static int hf_spnego_mechtype = -1;
66 static int hf_spnego_mechtoken = -1;
67 static int hf_spnego_negtokentarg_negresult = -1;
68 static int hf_spnego_mechlistmic = -1;
69 static int hf_spnego_responsetoken = -1;
70 static int hf_spnego_reqflags = -1;
71 static int hf_spnego_wraptoken = -1;
72 static int hf_spnego_krb5 = -1;
73 static int hf_spnego_krb5_tok_id = -1;
74 static int hf_spnego_krb5_sgn_alg = -1;
75 static int hf_spnego_krb5_seal_alg = -1;
76 static int hf_spnego_krb5_snd_seq = -1;
77 static int hf_spnego_krb5_sgn_cksum = -1;
78 static int hf_spnego_krb5_confounder = -1;
79 static int hf_gssapi_reqflags_deleg = -1;
80 static int hf_gssapi_reqflags_mutual = -1;
81 static int hf_gssapi_reqflags_replay = -1;
82 static int hf_gssapi_reqflags_sequence = -1;
83 static int hf_gssapi_reqflags_anon = -1;
84 static int hf_gssapi_reqflags_conf = -1;
85 static int hf_gssapi_reqflags_integ = -1;
86
87 static gint ett_spnego = -1;
88 static gint ett_spnego_negtokeninit = -1;
89 static gint ett_spnego_negtokentarg = -1;
90 static gint ett_spnego_mechtype = -1;
91 static gint ett_spnego_mechtoken = -1;
92 static gint ett_spnego_mechlistmic = -1;
93 static gint ett_spnego_responsetoken = -1;
94 static gint ett_spnego_wraptoken = -1;
95 static gint ett_spnego_krb5 = -1;
96 static gint ett_spnego_reqflags = -1;
97
98 static const value_string spnego_negResult_vals[] = {
99   { SPNEGO_negResult_accept_completed,   "Accept Completed" },
100   { SPNEGO_negResult_accept_incomplete,  "Accept Incomplete" },
101   { SPNEGO_negResult_accept_reject,      "Accept Reject"},
102   { 0, NULL}
103 };
104
105 /*
106  * These should be in the GSSAPI dissector ... XXX
107  */
108
109 static const true_false_string tfs_reqflags_deleg = {
110   "Delegation Requested",
111   "Delegation NOT Requested"
112 };
113
114 static const true_false_string tfs_reqflags_mutual = {
115   "Mutual Authentication Requested",
116   "Mutual Authentication NOT Requested"
117 };
118
119 static const true_false_string tfs_reqflags_replay = {
120   "Replay Detection Requested",
121   "Replay Detection NOT Requested"
122 };
123
124 static const true_false_string tfs_reqflags_sequence = {
125   "Out-of-sequence Detection Requested",
126   "Out-of-sequence Detection NOT Requested"
127 };
128
129 static const true_false_string tfs_reqflags_anon = {
130   "Anonymous Authentication Requested",
131   "Anonymous Authentication NOT Requested"
132 };
133
134 static const true_false_string tfs_reqflags_conf = {
135   "Per-message Confidentiality Requested",
136   "Per-message Confidentiality NOT Requested"
137 };
138
139 static const true_false_string tfs_reqflags_integ = {
140   "Per-message Integrity Requested",
141   "Per-message Integrity NOT Requested"
142 };
143
144 /* Display an ASN1 parse error.  Taken from packet-snmp.c */
145
146 static dissector_handle_t data_handle;
147
148 static dissector_handle_t
149 gssapi_dissector_handle(gssapi_oid_value *next_level_value) {
150         if (next_level_value == NULL) {
151                 return NULL;
152         }
153         return next_level_value->handle;
154 }
155
156 static void
157 dissect_parse_error(tvbuff_t *tvb, int offset, packet_info *pinfo,
158                     proto_tree *tree, const char *field_name, int ret)
159 {
160         char *errstr;
161
162         errstr = asn1_err_to_str(ret);
163
164         if (tree != NULL) {
165                 proto_tree_add_text(tree, tvb, offset, 0,
166                     "ERROR: Couldn't parse %s: %s", field_name, errstr);
167                 call_dissector(data_handle,
168                     tvb_new_subset(tvb, offset, -1, -1), pinfo, tree);
169         }
170 }
171
172 /*
173  * This is the SPNEGO KRB5 dissector. It is not true KRB5, but some ASN.1
174  * wrapped blob with an OID, USHORT token ID, and a Ticket, that is also 
175  * ASN.1 wrapped by the looks of it. It conforms to RFC1964.
176  */ 
177
178 #define KRB_TOKEN_AP_REQ                0x0001
179 #define KRB_TOKEN_AP_REP                0x0002
180 #define KRB_TOKEN_AP_ERR                0x0003
181 #define KRB_TOKEN_GETMIC                0x0101
182 #define KRB_TOKEN_WRAP                  0x0102
183 #define KRB_TOKEN_DELETE_SEC_CONTEXT    0x0201
184
185 static const value_string spnego_krb5_tok_id_vals[] = {
186   { KRB_TOKEN_AP_REQ,             "KRB5_AP_REQ"},
187   { KRB_TOKEN_AP_REP,             "KRB5_AP_REP"},
188   { KRB_TOKEN_AP_ERR,             "KRB5_ERROR"},
189   { KRB_TOKEN_GETMIC,             "KRB5_GSS_GetMIC" },
190   { KRB_TOKEN_WRAP,               "KRB5_GSS_Wrap" },
191   { KRB_TOKEN_DELETE_SEC_CONTEXT, "KRB5_GSS_Delete_sec_context" },
192   { 0, NULL}
193 };
194
195 #define KRB_SGN_ALG_DES_MAC_MD5 0x0000
196 #define KRB_SGN_ALG_MD2_5       0x0001
197 #define KRB_SGN_ALG_DES_MAC     0x0002
198 #define KRB_SGN_ALG_HMAC        0x0011
199
200 static const value_string spnego_krb5_sgn_alg_vals[] = {
201   { KRB_SGN_ALG_DES_MAC_MD5, "DES MAC MD5"},
202   { KRB_SGN_ALG_MD2_5,       "MD2.5"},
203   { KRB_SGN_ALG_DES_MAC,     "DES MAC"},
204   { KRB_SGN_ALG_HMAC,        "HMAC"},
205   { 0, NULL}
206 };
207
208 #define KRB_SEAL_ALG_DES_CBC    0x0000
209 #define KRB_SEAL_ALG_RC4        0x0010
210 #define KRB_SEAL_ALG_NONE       0xffff
211
212 static const value_string spnego_krb5_seal_alg_vals[] = {
213   { KRB_SEAL_ALG_DES_CBC, "DES CBC"},
214   { KRB_SEAL_ALG_RC4,     "RC4"},
215   { KRB_SEAL_ALG_NONE,    "None"},
216   { 0, NULL}
217 };
218
219 /*
220  * XXX - is this for SPNEGO or just GSS-API?
221  * RFC 1964 is "The Kerberos Version 5 GSS-API Mechanism"; presumably one
222  * can directly designate Kerberos V5 as a mechanism in GSS-API, rather
223  * than designating SPNEGO as the mechanism, offering Kerberos V5, and
224  * getting it accepted.
225  */
226 static int
227 dissect_spnego_krb5_getmic_base(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree);
228 static int
229 dissect_spnego_krb5_wrap_base(tvbuff_t *tvb, int offset, packet_info *pinfo, proto_tree *tree);
230
231 static void
232 dissect_spnego_krb5(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
233 {
234         proto_item *item;
235         proto_tree *subtree;
236         int ret, offset = 0;
237         ASN1_SCK hnd;
238         gboolean def;
239         guint len1, cls, con, tag, oid_len, nbytes;
240         guint16 token_id;
241         subid_t *oid;
242         gchar *oid_string;
243         gssapi_oid_value *value;
244         tvbuff_t *krb5_tvb;
245
246         item = proto_tree_add_item(tree, hf_spnego_krb5, tvb, offset, 
247                                    -1, FALSE);
248
249         subtree = proto_item_add_subtree(item, ett_spnego_krb5);
250
251         /*
252          * The KRB5 blob conforms to RFC1964:
253          * [APPLICATION 0] {
254          *   OID,
255          *   USHORT (0x0001 == AP-REQ, 0x0002 == AP-REP, 0x0003 == ERROR),
256          *   OCTET STRING } 
257          *
258          * However, for some protocols, the KRB5 blob starts at the SHORT
259          * and has no DER encoded header etc.
260          *
261          * It appears that for some other protocols the KRB5 blob is just
262          * a Kerberos message, with no [APPLICATION 0] header, no OID,
263          * and no USHORT.
264          *
265          * So:
266          *
267          *      If we see an [APPLICATION 0] HEADER, we show the OID and
268          *      the USHORT, and then dissect the rest as a Kerberos message.
269          *
270          *      If we see an [APPLICATION 14] or [APPLICATION 15] header,
271          *      we assume it's an AP-REQ or AP-REP message, and dissect
272          *      it all as a Kerberos message.
273          *
274          *      Otherwise, we show the USHORT, and then dissect the rest
275          *      as a Kerberos message.
276          */
277
278         asn1_open(&hnd, tvb, offset);
279
280         /*
281          * Get the first header ...
282          */
283
284         ret = asn1_header_decode(&hnd, &cls, &con, &tag, &def, &len1);
285
286         if (ret != ASN1_ERR_NOERROR) {
287                 dissect_parse_error(tvb, offset, pinfo, subtree,
288                                     "SPNEGO KRB5 Header", ret);
289                 goto done;
290         }
291
292         if (cls == ASN1_APL && con == ASN1_CON) {
293             /*
294              * [APPLICATION <tag>]
295              */
296             switch (tag) {
297
298             case 0:
299                 /*
300                  * [APPLICATION 0]
301                  */
302
303                 offset = hnd.offset;
304
305                 /* Next, the OID */
306
307                 ret = asn1_oid_decode(&hnd, &oid, &oid_len, &nbytes);
308
309                 if (ret != ASN1_ERR_NOERROR) {
310                     dissect_parse_error(tvb, offset, pinfo, subtree,
311                                         "SPNEGO supportedMech token", ret);
312                     goto done;
313                 }
314
315                 oid_string = format_oid(oid, oid_len);
316
317                 value = gssapi_lookup_oid(oid, oid_len);
318
319                 if (value) 
320                     proto_tree_add_text(subtree, tvb, offset, nbytes, 
321                                         "OID: %s (%s)",
322                                         oid_string, value->comment);
323                 else
324                     proto_tree_add_text(subtree, tvb, offset, nbytes,
325                                         "OID: %s",
326                                         oid_string);
327           
328                 g_free(oid_string);
329
330                 offset += nbytes;
331
332                 /* Next, the token ID ... */
333
334                 token_id = tvb_get_letohs(tvb, offset);
335                 proto_tree_add_uint(subtree, hf_spnego_krb5_tok_id, tvb, offset, 2,
336                                     token_id);
337
338                 hnd.offset += 2;
339
340                 offset += 2;
341
342                 break;
343
344             case 14:    /* [APPLICATION 14] */
345             case 15:    /* [APPLICATION 15] */
346                 /*
347                  * No token ID - just dissect as a Kerberos message and
348                  * return.
349                  */
350                 krb5_tvb = tvb_new_subset(tvb, offset, -1, -1); 
351                 offset = dissect_kerberos_main(krb5_tvb, pinfo, subtree, FALSE);
352                 return;
353
354             default:
355                 proto_tree_add_text(subtree, tvb, offset, 0,
356                         "Unknown header (cls=%d, con=%d, tag=%d)",
357                         cls, con, tag);
358                 goto done;
359             }
360         } else {
361             /* Next, the token ID ... */
362
363             token_id = tvb_get_letohs(tvb, offset);
364             proto_tree_add_uint(subtree, hf_spnego_krb5_tok_id, tvb, offset, 2,
365                                 token_id);
366
367             hnd.offset += 2;
368
369             offset += 2;
370         }
371
372         switch (token_id) {
373
374         case KRB_TOKEN_AP_REQ:
375         case KRB_TOKEN_AP_REP:
376         case KRB_TOKEN_AP_ERR:
377           krb5_tvb = tvb_new_subset(tvb, offset, -1, -1); 
378           offset = dissect_kerberos_main(krb5_tvb, pinfo, subtree, FALSE);
379           break;
380
381         case KRB_TOKEN_GETMIC:
382           offset = dissect_spnego_krb5_getmic_base(tvb, offset, pinfo, subtree); 
383           break;
384
385         case KRB_TOKEN_WRAP:
386           offset = dissect_spnego_krb5_wrap_base(tvb, offset, pinfo, subtree);
387           break;
388
389         case KRB_TOKEN_DELETE_SEC_CONTEXT:
390
391           break;
392
393         default:
394
395           break;
396         }
397
398  done:
399         return;
400 }
401
402 /*
403  * XXX - This is for GSSAPI Wrap tokens ...
404  */
405 static int
406 dissect_spnego_krb5_wrap_base(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
407 {
408         guint16 sgn_alg;
409
410         /*
411          * The KRB5 blob conforms to RFC1964:
412          *   USHORT (0x0102 == GSS_Wrap)
413          *   and so on } 
414          */
415
416         /* Now, the sign and seal algorithms ... */
417
418         sgn_alg = tvb_get_letohs(tvb, offset);
419         proto_tree_add_uint(tree, hf_spnego_krb5_sgn_alg, tvb, offset, 2,
420                             sgn_alg);
421
422         offset += 2;
423
424         proto_tree_add_item(tree, hf_spnego_krb5_seal_alg, tvb, offset, 2,
425                             TRUE);
426
427         offset += 2;
428
429         /* Skip the filler */
430
431         offset += 2;
432
433         /* Encrypted sequence number */
434
435         proto_tree_add_item(tree, hf_spnego_krb5_snd_seq, tvb, offset, 8,
436                             TRUE);
437
438         offset += 8;
439
440         /* Checksum of plaintext padded data */
441
442         proto_tree_add_item(tree, hf_spnego_krb5_sgn_cksum, tvb, offset, 8,
443                             TRUE);
444
445         offset += 8;
446
447         /*
448          * At least according to draft-brezak-win2k-krb-rc4-hmac-04,
449          * if the signing algorithm is KRB_SGN_ALG_HMAC, there's an
450          * extra 8 bytes of "Random confounder" after the checksum.
451          * It certainly confounds code expecting all Kerberos 5
452          * GSS_Wrap() tokens to look the same....
453          */
454         if (sgn_alg == KRB_SGN_ALG_HMAC) {
455           proto_tree_add_item(tree, hf_spnego_krb5_confounder, tvb, offset, 8,
456                               TRUE);
457
458           offset += 8;
459         }
460
461         /*
462          * Return the offset past the checksum, so that we know where
463          * the data we're wrapped around starts.  Also, set the length
464          * of our top-level item to that offset, so it doesn't cover
465          * the data we're wrapped around.
466          */
467         return offset;
468 }
469
470 /*
471  * XXX - This is for GSSAPI GetMIC tokens ...
472  */
473 static int
474 dissect_spnego_krb5_getmic_base(tvbuff_t *tvb, int offset, packet_info *pinfo _U_, proto_tree *tree)
475 {
476         guint16 sgn_alg;
477
478         /*
479          * The KRB5 blob conforms to RFC1964:
480          *   USHORT (0x0101 == GSS_GetMIC)
481          *   and so on } 
482          */
483
484         /* Now, the sign algorithm ... */
485
486         sgn_alg = tvb_get_letohs(tvb, offset);
487         proto_tree_add_uint(tree, hf_spnego_krb5_sgn_alg, tvb, offset, 2,
488                             sgn_alg);
489
490         offset += 2;
491
492         /* Skip the filler */
493
494         offset += 4;
495
496         /* Encrypted sequence number */
497
498         proto_tree_add_item(tree, hf_spnego_krb5_snd_seq, tvb, offset, 8,
499                             TRUE);
500
501         offset += 8;
502
503         /* Checksum of plaintext padded data */
504
505         proto_tree_add_item(tree, hf_spnego_krb5_sgn_cksum, tvb, offset, 8,
506                             TRUE);
507
508         offset += 8;
509
510         /*
511          * At least according to draft-brezak-win2k-krb-rc4-hmac-04,
512          * if the signing algorithm is KRB_SGN_ALG_HMAC, there's an
513          * extra 8 bytes of "Random confounder" after the checksum.
514          * It certainly confounds code expecting all Kerberos 5
515          * GSS_Wrap() tokens to look the same....
516          */
517         if (sgn_alg == KRB_SGN_ALG_HMAC) {
518           proto_tree_add_item(tree, hf_spnego_krb5_confounder, tvb, offset, 8,
519                               TRUE);
520
521           offset += 8;
522         }
523
524         /*
525          * Return the offset past the checksum, so that we know where
526          * the data we're wrapped around starts.  Also, set the length
527          * of our top-level item to that offset, so it doesn't cover
528          * the data we're wrapped around.
529          */
530
531         return offset;
532 }
533
534 /*
535  * XXX - is this for SPNEGO or just GSS-API?
536  * RFC 1964 is "The Kerberos Version 5 GSS-API Mechanism"; presumably one
537  * can directly designate Kerberos V5 as a mechanism in GSS-API, rather
538  * than designating SPNEGO as the mechanism, offering Kerberos V5, and
539  * getting it accepted.
540  */
541 static int
542 dissect_spnego_krb5_wrap(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
543 {
544         proto_item *item;
545         proto_tree *subtree;
546         int offset = 0;
547
548         item = proto_tree_add_item(tree, hf_spnego_krb5, tvb, 0, -1, FALSE);
549
550         subtree = proto_item_add_subtree(item, ett_spnego_krb5);
551
552         /*
553          * The KRB5 blob conforms to RFC1964:
554          *   USHORT (0x0102 == GSS_Wrap)
555          *   and so on } 
556          */
557
558         /* First, the token ID ... */
559
560         proto_tree_add_item(subtree, hf_spnego_krb5_tok_id, tvb, offset, 2,
561                             TRUE);
562
563         offset += 2;
564
565         offset = dissect_spnego_krb5_wrap_base(tvb, offset, pinfo, subtree);
566
567         /*
568          * Return the offset past the checksum, so that we know where
569          * the data we're wrapped around starts.  Also, set the length
570          * of our top-level item to that offset, so it doesn't cover
571          * the data we're wrapped around.
572          */
573         proto_item_set_len(item, offset);
574         return offset;
575 }
576
577 /* Spnego stuff from here */
578
579 static int
580 dissect_spnego_mechTypes(tvbuff_t *tvb, int offset, packet_info *pinfo,
581                          proto_tree *tree, ASN1_SCK *hnd,
582                          gssapi_oid_value **next_level_value_p)
583 {
584         proto_item *item = NULL;
585         proto_tree *subtree = NULL;
586         gboolean def;
587         guint len1, len, cls, con, tag, nbytes;
588         subid_t *oid;
589         gchar *oid_string;
590         int ret;
591         gboolean saw_mechanism = FALSE;
592
593         /*
594          * MechTypeList ::= SEQUENCE OF MechType
595          */
596
597         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
598
599         if (ret != ASN1_ERR_NOERROR) {
600           dissect_parse_error(tvb, offset, pinfo, subtree,
601                               "SPNEGO last sequence header", ret);
602           goto done;
603         }
604
605         if (!(cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ)) {
606           proto_tree_add_text(
607                               subtree, tvb, offset, 0,
608                               "Unknown header (cls=%d, con=%d, tag=%d)",
609                               cls, con, tag);
610           goto done;
611         }
612
613         offset = hnd->offset;
614
615         item = proto_tree_add_item(tree, hf_spnego_mechtype, tvb, offset, 
616                                    len1, FALSE);
617         subtree = proto_item_add_subtree(item, ett_spnego_mechtype);
618
619         /*
620          * Now, the object IDs ... We should translate them: FIXME
621          */
622
623         while (len1) {
624           gssapi_oid_value *value;
625
626           ret = asn1_oid_decode(hnd, &oid, &len, &nbytes);
627
628           if (ret != ASN1_ERR_NOERROR) {
629             dissect_parse_error(tvb, offset, pinfo, subtree,
630                                 "SPNEGO mechTypes token", ret);
631             goto done;
632           }
633
634           oid_string = format_oid(oid, len);
635           value = gssapi_lookup_oid(oid, len);
636           if (value)
637             proto_tree_add_text(subtree, tvb, offset, nbytes, "OID: %s (%s)",
638                                 oid_string, value->comment);
639           else
640             proto_tree_add_text(subtree, tvb, offset, nbytes, "OID: %s",
641                                 oid_string);
642
643           g_free(oid_string);
644
645           /*
646            * Tell our caller the first mechanism we see, so that if
647            * this is a negTokenInit with a mechToken, it can interpret
648            * the mechToken according to the first mechType.  (There
649            * might not have been any indication of the mechType
650            * in prior frames, so we can't necessarily use the
651            * mechanism from the conversation; i.e., a negTokenInit
652            * can contain the initial security token for the desired
653            * mechanism of the initiator - that's the first mechanism
654            * in the list.)
655            */
656           if (!saw_mechanism) {
657             if (value)
658               *next_level_value_p = value;
659             saw_mechanism = TRUE;
660           }
661
662           offset += nbytes;
663           len1 -= nbytes;
664
665         }
666
667  done:
668
669         return offset;
670
671 }
672
673 static int
674 dissect_spnego_reqFlags(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
675                         proto_tree *tree, ASN1_SCK *hnd)
676 {
677         gboolean def;
678         guint len1, cls, con, tag, flags;
679         int ret;
680         proto_item *item;
681         proto_tree *subtree;
682
683         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
684
685         if (ret != ASN1_ERR_NOERROR) {
686                 dissect_parse_error(tvb, offset, pinfo, tree,
687                                     "SPNEGO reqFlags header", ret);
688                 goto done;
689         }
690
691         if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_BTS)) {
692                 proto_tree_add_text(
693                         tree, tvb, offset, 0,
694                         "Unknown header (cls=%d, con=%d, tag=%d)",
695                         cls, con, tag);
696                 goto done;
697         }
698
699         /* We must have a Bit String ... insert it */ 
700
701         offset = hnd->offset;
702
703         flags = tvb_get_guint8(tvb, offset);
704
705         item = proto_tree_add_item(tree, hf_spnego_reqflags, tvb, offset, len1,
706             FALSE);
707
708         subtree = proto_item_add_subtree(item, ett_spnego_reqflags);
709
710         /*
711          * Now, the bits. XXX: Assume 8 bits. FIXME.
712          */
713
714         proto_tree_add_boolean(subtree, hf_gssapi_reqflags_deleg, tvb, offset, len1, flags);
715         proto_tree_add_boolean(subtree, hf_gssapi_reqflags_mutual, tvb, offset, len1, flags);
716         proto_tree_add_boolean(subtree, hf_gssapi_reqflags_replay, tvb, offset, len1, flags);
717         proto_tree_add_boolean(subtree, hf_gssapi_reqflags_sequence, tvb, offset, len1, flags);
718         proto_tree_add_boolean(subtree, hf_gssapi_reqflags_anon, tvb, offset, len1, flags);
719         proto_tree_add_boolean(subtree, hf_gssapi_reqflags_conf, tvb, offset, len1, flags);
720         proto_tree_add_boolean(subtree, hf_gssapi_reqflags_integ, tvb, offset, len1, flags);
721
722         hnd->offset += len1;
723
724  done:
725         return offset + len1;
726
727 }
728
729 static int
730 dissect_spnego_mechToken(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
731                          proto_tree *tree, ASN1_SCK *hnd,
732                          dissector_handle_t next_level_dissector)
733 {
734         proto_item *item;
735         proto_tree *subtree;
736         gboolean def;
737         int ret;
738         guint cls, con, tag, nbytes;
739         gint length_remaining, reported_length_remaining;
740         tvbuff_t *token_tvb;
741
742         /*
743          * This appears to be a simple octet string ...
744          */
745
746         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &nbytes);
747
748         if (ret != ASN1_ERR_NOERROR) {
749                 dissect_parse_error(tvb, offset, pinfo, tree,
750                                     "SPNEGO sequence header", ret);
751                 goto done;
752         }
753
754         if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS)) {
755                 proto_tree_add_text(
756                         tree, tvb, offset, 0,
757                         "Unknown header (cls=%d, con=%d, tag=%d)",
758                         cls, con, tag);
759                 goto done;
760         }
761
762         offset = hnd->offset;
763
764
765         /* Dont try to create an item with more bytes than remains in the
766          * frame or we will not even attempt to dissect those bytes we
767          * do have. (since there will be an exception)
768          *
769          * We use "tvb_ensure_length_remaining()" so that we throw
770          * an exception if there's nothing to dissect.
771          */
772         length_remaining = tvb_ensure_length_remaining(tvb,offset);
773         reported_length_remaining = tvb_reported_length_remaining(tvb,offset);
774         if ((guint)length_remaining > nbytes)
775                 length_remaining = nbytes;
776         if ((guint)reported_length_remaining > nbytes)
777                 reported_length_remaining = nbytes;
778         item = proto_tree_add_item(tree, hf_spnego_mechtoken, tvb, offset, 
779                                    length_remaining, FALSE);
780         subtree = proto_item_add_subtree(item, ett_spnego_mechtoken);
781
782         /*
783          * Now, we should be able to dispatch after creating a new TVB.
784          */
785
786         token_tvb = tvb_new_subset(tvb, offset, length_remaining,
787             reported_length_remaining);
788         if (next_level_dissector)
789           call_dissector(next_level_dissector, token_tvb, pinfo, subtree);
790
791         hnd->offset += nbytes; /* Update this ... */
792
793 done:
794         return offset + nbytes;
795 }
796
797 static int
798 dissect_spnego_mechListMIC(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
799                            proto_tree *tree, ASN1_SCK *hnd,
800                            dissector_handle_t next_level_dissector)
801 {
802         guint len1, cls, con, tag;
803         int ret;
804         gboolean def;
805         proto_tree *subtree = NULL;
806
807         /*
808          * Add the mechListMIC [3] Octet String or General String ...
809          */
810         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
811
812         if (ret != ASN1_ERR_NOERROR) {
813                 dissect_parse_error(tvb, offset, pinfo, subtree,
814                                     "SPNEGO sequence header", ret);
815                 goto done;
816         }
817
818         offset = hnd->offset;
819
820         if (cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ) {
821
822           /*
823            * There seems to be two different forms this can take
824            * One as an Octet string, and one as a general string in a 
825            * sequence ... We will have to dissect this later
826            */
827          
828           proto_tree_add_text(tree, tvb, offset + 4, len1 - 4,
829                               "mechListMIC: %s",
830                               tvb_format_text(tvb, offset + 4, len1 - 4));
831
832           /* Naughty ... but we have to adjust for what we never took */
833
834           hnd->offset += len1;
835           offset += len1;
836
837         }
838         else if (cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS) {
839           tvbuff_t *token_tvb;
840           proto_item *item;
841           proto_tree *subtree;
842
843           item = proto_tree_add_item(tree, hf_spnego_mechlistmic, tvb, offset, 
844                               len1, FALSE); 
845           subtree = proto_item_add_subtree(item, ett_spnego_mechlistmic);
846           
847         /*
848          * Now, we should be able to dispatch after creating a new TVB.
849          */
850
851           token_tvb = tvb_new_subset(tvb, offset, len1, -1);
852           if (next_level_dissector)
853             call_dissector(next_level_dissector, token_tvb, pinfo, subtree);
854
855           hnd->offset += len1; /* Update this ... */
856           offset += len1;
857
858         }
859         else {
860
861           proto_tree_add_text(subtree, tvb, offset, 0,
862                               "Unknown header (cls=%d, con=%d, tag=%d)",
863                               cls, con, tag);
864           goto done;
865         }
866
867  done:
868
869         return offset;
870
871 }
872
873 static int
874 dissect_spnego_negTokenInit(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
875                             proto_tree *tree, ASN1_SCK *hnd,
876                             gssapi_oid_value **next_level_value_p)
877 {
878         proto_item *item;
879         proto_tree *subtree;
880         gboolean def;
881         guint len1, len, cls, con, tag;
882         int ret;
883
884         item = proto_tree_add_item( tree, hf_spnego_negtokeninit, tvb, offset,
885                                     -1, FALSE);
886         subtree = proto_item_add_subtree(item, ett_spnego_negtokeninit);
887
888         /*
889          * Here is what we need to get ...
890          * NegTokenInit ::= SEQUENCE {
891          *          mechTypes [0] MechTypeList OPTIONAL,
892          *          reqFlags [1] ContextFlags OPTIONAL,
893          *          mechToken [2] OCTET STRING OPTIONAL,
894          *          mechListMIC [3] OCTET STRING OPTIONAL }
895
896          */
897
898         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
899
900         if (ret != ASN1_ERR_NOERROR) {
901                 dissect_parse_error(tvb, offset, pinfo, subtree,
902                                     "SPNEGO sequence header", ret);
903                 goto done;
904         }
905
906         if (!(cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ)) {
907                 proto_tree_add_text(
908                         subtree, tvb, offset, 0,
909                         "Unknown header (cls=%d, con=%d, tag=%d)",
910                         cls, con, tag);
911                 goto done;
912         }
913
914         offset = hnd->offset;
915
916         while (len1) {
917           int hdr_ofs;
918
919           hdr_ofs = hnd->offset;
920
921           ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len);
922
923           if (ret != ASN1_ERR_NOERROR) {
924             dissect_parse_error(tvb, offset, pinfo, subtree,
925                                 "SPNEGO context header", ret);
926             goto done;
927           }
928
929           if (!(cls == ASN1_CTX && con == ASN1_CON)) {
930             proto_tree_add_text(subtree, tvb, offset, 0,
931                                 "Unknown header (cls=%d, con=%d, tag=%d)",
932                                 cls, con, tag);
933             goto done;
934           }
935
936           /* Adjust for the length of the header */
937
938           len1 -= (hnd->offset - hdr_ofs);
939
940           /* Should be one of the fields */
941
942           switch (tag) {
943
944           case SPNEGO_mechTypes:
945
946             offset = dissect_spnego_mechTypes(tvb, offset, pinfo,
947                                               subtree, hnd,
948                                               next_level_value_p);
949
950             break;
951
952           case SPNEGO_reqFlags:
953
954             offset = dissect_spnego_reqFlags(tvb, offset, pinfo, subtree, hnd);
955
956             break;
957
958           case SPNEGO_mechToken:
959
960             offset = dissect_spnego_mechToken(tvb, offset, pinfo, subtree, 
961                                               hnd, gssapi_dissector_handle(*next_level_value_p));
962             break;
963
964           case SPNEGO_mechListMIC:
965
966             offset = dissect_spnego_mechListMIC(tvb, offset, pinfo, subtree,
967                                                 hnd, gssapi_dissector_handle(*next_level_value_p));
968             break;
969
970           default:
971
972             break;
973           }
974
975           len1 -= len;
976
977         }
978
979  done:
980
981         return offset; /* Not sure this is right */
982 }
983
984 static int
985 dissect_spnego_negResult(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
986                             proto_tree *tree, ASN1_SCK *hnd)
987 {
988         gboolean def;
989         int ret;
990         guint len, cls, con, tag, val;
991
992         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len);
993
994         if (ret != ASN1_ERR_NOERROR) {
995           dissect_parse_error(tvb, offset, pinfo, tree,
996                               "SPNEGO context header", ret);
997           goto done;
998         }
999
1000         if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_ENUM)) {
1001           proto_tree_add_text(
1002                               tree, tvb, offset, 0,
1003                               "Unknown header (cls=%d, con=%d, tag=%d) xxx",
1004                               cls, con, tag);
1005           goto done;
1006         }
1007
1008         offset = hnd->offset;
1009
1010         /* Now, get the value */
1011
1012         ret = asn1_uint32_value_decode(hnd, len, &val);
1013
1014         if (ret != ASN1_ERR_NOERROR) {
1015           dissect_parse_error(tvb, offset, pinfo, tree,
1016                               "SPNEGO negResult value", ret);
1017           goto done;
1018         }
1019         
1020         proto_tree_add_item(tree, hf_spnego_negtokentarg_negresult, tvb, 
1021                             offset, 1, FALSE);
1022
1023         offset = hnd->offset;
1024
1025  done:
1026         return offset;
1027 }
1028
1029 static int
1030 dissect_spnego_supportedMech(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
1031                              proto_tree *tree, ASN1_SCK *hnd,
1032                              gssapi_oid_value **next_level_value_p)
1033 {
1034         int ret;
1035         guint oid_len, nbytes;
1036         subid_t *oid;
1037         gchar *oid_string;
1038         gssapi_oid_value *value;
1039         conversation_t *conversation;
1040
1041         /*
1042          * Now, get the OID, and find the handle, if any
1043          */
1044
1045         offset = hnd->offset;
1046
1047         ret = asn1_oid_decode(hnd, &oid, &oid_len, &nbytes);
1048
1049         if (ret != ASN1_ERR_NOERROR) {
1050                 dissect_parse_error(tvb, offset, pinfo, tree,
1051                                     "SPNEGO supportedMech token", ret);
1052                 goto done;
1053         }
1054
1055         oid_string = format_oid(oid, oid_len);
1056         value = gssapi_lookup_oid(oid, oid_len);
1057
1058         if (value)
1059           proto_tree_add_text(tree, tvb, offset, nbytes, 
1060                               "supportedMech: %s (%s)",
1061                               oid_string, value->comment);
1062         else
1063           proto_tree_add_text(tree, tvb, offset, nbytes, "supportedMech: %s",
1064                               oid_string);
1065
1066         g_free(oid_string);
1067
1068         offset += nbytes;
1069
1070         /* Should check for an unrecognized OID ... */
1071
1072         if (value)
1073           *next_level_value_p = value;
1074
1075         /*
1076          * Now, we need to save this in per proto info in the
1077          * conversation if it exists. We also should create a 
1078          * conversation if one does not exist. FIXME!
1079          * Hmmm, might need to be smarter, because there can be
1080          * multiple mechTypes in a negTokenInit with one being the
1081          * default used in the Token if present. Then the negTokenTarg
1082          * could override that. :-(
1083          */
1084
1085         if ((conversation = find_conversation(&pinfo->src, &pinfo->dst,
1086                                              pinfo->ptype, pinfo->srcport,
1087                                              pinfo->destport, 0))) {
1088
1089
1090           conversation_add_proto_data(conversation, proto_spnego, 
1091                                       *next_level_value_p);
1092         }
1093         else {
1094
1095         }
1096
1097  done:
1098         return offset;
1099 }
1100
1101 static int
1102 dissect_spnego_responseToken(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
1103                              proto_tree *tree, ASN1_SCK *hnd,
1104                              dissector_handle_t next_level_dissector)
1105 {
1106         gboolean def;
1107         int ret;
1108         guint cls, con, tag, nbytes;
1109         tvbuff_t *token_tvb;
1110         proto_item *item;
1111         proto_tree *subtree;
1112
1113         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &nbytes);
1114
1115         if (ret != ASN1_ERR_NOERROR) {
1116                 dissect_parse_error(tvb, offset, pinfo, tree,
1117                                     "SPNEGO sequence header", ret);
1118                 goto done;
1119         }
1120
1121         if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS)) {
1122                 proto_tree_add_text(
1123                         tree, tvb, offset, 0,
1124                         "Unknown header (cls=%d, con=%d, tag=%d)",
1125                         cls, con, tag);
1126                 goto done;
1127         }
1128
1129         offset = hnd->offset;
1130
1131         item = proto_tree_add_item(tree, hf_spnego_responsetoken, tvb, offset -2 , 
1132                                    nbytes + 2, FALSE); 
1133
1134         subtree = proto_item_add_subtree(item, ett_spnego_responsetoken);
1135
1136
1137         /*
1138          * Now, we should be able to dispatch after creating a new TVB.
1139          * However, we should make sure that there is something in the 
1140          * response token ...
1141          */
1142
1143         if (nbytes) {
1144           token_tvb = tvb_new_subset(tvb, offset, nbytes, -1);
1145           if (next_level_dissector)
1146             call_dissector(next_level_dissector, token_tvb, pinfo, subtree);
1147         }
1148         else {
1149           proto_tree_add_text(subtree, tvb, offset-2, 2, "<Empty String>");
1150         }
1151         hnd->offset += nbytes; /* Update this ... */
1152
1153  done:
1154         return offset + nbytes;
1155 }
1156
1157 static int
1158 dissect_spnego_negTokenTarg(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
1159                             proto_tree *tree, ASN1_SCK *hnd,
1160                             gssapi_oid_value **next_level_value_p)
1161
1162 {
1163         proto_item *item;
1164         proto_tree *subtree;
1165         gboolean def;
1166         int ret;
1167         guint len1, len, cls, con, tag;
1168
1169         item = proto_tree_add_item( tree, hf_spnego_negtokentarg, tvb, offset,
1170                                     -1, FALSE);
1171         subtree = proto_item_add_subtree(item, ett_spnego_negtokentarg);
1172
1173         /* 
1174          * Here is what we need to get ...
1175          * NegTokenTarg ::= SEQUENCE {
1176          *          negResult [0] ENUMERATED {
1177          *              accept_completed (0),
1178          *              accept_incomplete (1),
1179          *              reject (2) } OPTIONAL,
1180          *          supportedMech [1] MechType OPTIONAL,
1181          *          responseToken [2] OCTET STRING OPTIONAL,
1182          *          mechListMIC [3] OCTET STRING OPTIONAL }
1183          */
1184
1185         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
1186
1187         if (ret != ASN1_ERR_NOERROR) {
1188                 dissect_parse_error(tvb, offset, pinfo, subtree,
1189                                     "SPNEGO sequence header", ret);
1190                 goto done;
1191         }
1192
1193         if (!(cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ)) {
1194                 proto_tree_add_text(
1195                         subtree, tvb, offset, 0,
1196                         "Unknown header (cls=%d, con=%d, tag=%d)",
1197                         cls, con, tag);
1198                 goto done;
1199         }
1200
1201         offset = hnd->offset;
1202
1203         while (len1) {
1204           int hdr_ofs;
1205
1206           hdr_ofs = hnd->offset; 
1207
1208           ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len);
1209
1210           if (ret != ASN1_ERR_NOERROR) {
1211             dissect_parse_error(tvb, offset, pinfo, subtree,
1212                                 "SPNEGO context header", ret);
1213             goto done;
1214           }
1215
1216           if (!(cls == ASN1_CTX && con == ASN1_CON)) {
1217             proto_tree_add_text(
1218                                 subtree, tvb, offset, 0,
1219                                 "Unknown header (cls=%d, con=%d, tag=%d)",
1220                                 cls, con, tag);
1221             goto done;
1222           }
1223
1224           /* Adjust for the length of the header */
1225
1226           len1 -= (hnd->offset - hdr_ofs);
1227
1228           /* Should be one of the fields */
1229
1230           switch (tag) {
1231
1232           case SPNEGO_negResult:
1233
1234             offset = dissect_spnego_negResult(tvb, offset, pinfo, subtree, 
1235                                               hnd);
1236             break;
1237
1238           case SPNEGO_supportedMech:
1239
1240             offset = dissect_spnego_supportedMech(tvb, offset, pinfo, subtree,
1241                                                   hnd, next_level_value_p);
1242
1243             break;
1244
1245           case SPNEGO_responseToken:
1246
1247             offset = dissect_spnego_responseToken(tvb, offset, pinfo, subtree,
1248                                                   hnd, gssapi_dissector_handle(*next_level_value_p));
1249             break;
1250
1251           case SPNEGO_mechListMIC:
1252
1253             offset = dissect_spnego_mechListMIC(tvb, offset, pinfo, subtree, 
1254                                                 hnd, gssapi_dissector_handle(*next_level_value_p));
1255             break;
1256
1257           default:
1258
1259             break;
1260           }
1261
1262           len1 -= len;
1263
1264         }
1265
1266  done:
1267         return offset;
1268
1269 }
1270
1271 static void
1272 dissect_spnego(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1273 {
1274         proto_item *item;
1275         proto_tree *subtree;
1276         int ret, offset = 0;
1277         ASN1_SCK hnd;
1278         gboolean def;
1279         guint len1, cls, con, tag;
1280         conversation_t *conversation;
1281         gssapi_oid_value *next_level_value;
1282
1283         /*
1284          * We need this later, so lets get it now ...
1285          * It has to be per-frame as there can be more than one GSS-API
1286          * negotiation in a conversation.
1287          */
1288
1289         next_level_value = p_get_proto_data(pinfo->fd, proto_spnego);
1290         if (!next_level_value && !pinfo->fd->flags.visited) {
1291             /*
1292              * No handle attached to this frame, but it's the first
1293              * pass, so it'd be attached to the conversation.
1294              * If we have a conversation, try to get the handle,
1295              * and if we get one, attach it to the frame.
1296              */
1297             conversation = find_conversation(&pinfo->src, &pinfo->dst,
1298                                              pinfo->ptype, pinfo->srcport,
1299                                              pinfo->destport, 0);
1300
1301             if (conversation) {
1302                 next_level_value = conversation_get_proto_data(conversation, 
1303                                                                proto_spnego);
1304                 if (next_level_value)
1305                     p_add_proto_data(pinfo->fd, proto_spnego, next_level_value);
1306             }
1307         }
1308
1309         item = proto_tree_add_item(tree, hf_spnego, tvb, offset, 
1310                                    -1, FALSE);
1311
1312         subtree = proto_item_add_subtree(item, ett_spnego);
1313
1314         /*
1315          * The TVB contains a [0] header and a sequence that consists of an
1316          * object ID and a blob containing the data ...
1317          * Actually, it contains, according to RFC2478:
1318          * NegotiationToken ::= CHOICE {
1319          *          negTokenInit [0] NegTokenInit,
1320          *          negTokenTarg [1] NegTokenTarg }
1321          * NegTokenInit ::= SEQUENCE {
1322          *          mechTypes [0] MechTypeList OPTIONAL,
1323          *          reqFlags [1] ContextFlags OPTIONAL,
1324          *          mechToken [2] OCTET STRING OPTIONAL,
1325          *          mechListMIC [3] OCTET STRING OPTIONAL }
1326          * NegTokenTarg ::= SEQUENCE {
1327          *          negResult [0] ENUMERATED {
1328          *              accept_completed (0),
1329          *              accept_incomplete (1),
1330          *              reject (2) } OPTIONAL,
1331          *          supportedMech [1] MechType OPTIONAL,
1332          *          responseToken [2] OCTET STRING OPTIONAL,
1333          *          mechListMIC [3] OCTET STRING OPTIONAL }
1334          *
1335          * Windows typically includes mechTypes and mechListMic ('NONE'
1336          * in the case of NTLMSSP only).
1337          * It seems to duplicate the responseToken into the mechListMic field
1338          * as well. Naughty, naughty.
1339          *
1340          */
1341
1342         asn1_open(&hnd, tvb, offset);
1343
1344         /*
1345          * Get the first header ...
1346          */
1347
1348         ret = asn1_header_decode(&hnd, &cls, &con, &tag, &def, &len1);
1349
1350         if (ret != ASN1_ERR_NOERROR) {
1351                 dissect_parse_error(tvb, offset, pinfo, subtree,
1352                                     "SPNEGO context header", ret);
1353                 goto done;
1354         }
1355
1356         if (!(cls == ASN1_CTX && con == ASN1_CON)) {
1357                 proto_tree_add_text(
1358                         subtree, tvb, offset, 0,
1359                         "Unknown header (cls=%d, con=%d, tag=%d)",
1360                         cls, con, tag);
1361                 goto done;
1362         }
1363
1364         offset = hnd.offset;
1365
1366         /*
1367          * The Tag is one of negTokenInit or negTokenTarg
1368          */
1369
1370         switch (tag) {
1371
1372         case SPNEGO_negTokenInit:
1373
1374           offset = dissect_spnego_negTokenInit(tvb, offset, pinfo,
1375                                                subtree, &hnd,
1376                                                &next_level_value);
1377
1378           break;
1379
1380         case SPNEGO_negTokenTarg:
1381
1382           offset = dissect_spnego_negTokenTarg(tvb, offset, pinfo,
1383                                                subtree, &hnd,
1384                                                &next_level_value);
1385           break;
1386
1387         default: /* Broken, what to do? */
1388
1389           break;
1390         }
1391
1392
1393  done:
1394         asn1_close(&hnd, &offset);
1395
1396 }
1397
1398 static int
1399 dissect_spnego_wrap(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1400 {
1401         proto_item *item;
1402         proto_tree *subtree;
1403         int ret, offset = 0;
1404         int return_offset;
1405         ASN1_SCK hnd;
1406         gboolean def;
1407         guint len1, cls, con, tag, nbytes;
1408         guint oid_len;
1409         subid_t *oid;
1410         gchar *oid_string;
1411         conversation_t *conversation;
1412         gssapi_oid_value *next_level_value;
1413         tvbuff_t *token_tvb;
1414         int len;
1415
1416         /*
1417          * We need this later, so lets get it now ...
1418          * It has to be per-frame as there can be more than one GSS-API
1419          * negotiation in a conversation.
1420          */
1421
1422         next_level_value = p_get_proto_data(pinfo->fd, proto_spnego);
1423         if (!next_level_value && !pinfo->fd->flags.visited) {
1424             /*
1425              * No handle attached to this frame, but it's the first
1426              * pass, so it'd be attached to the conversation.
1427              * If we have a conversation, try to get the handle,
1428              * and if we get one, attach it to the frame.
1429              */
1430             conversation = find_conversation(&pinfo->src, &pinfo->dst,
1431                                              pinfo->ptype, pinfo->srcport,
1432                                              pinfo->destport, 0);
1433
1434             if (conversation) {
1435                 next_level_value = conversation_get_proto_data(conversation, 
1436                                                                proto_spnego);
1437                 if (next_level_value)
1438                     p_add_proto_data(pinfo->fd, proto_spnego, next_level_value);
1439             }
1440         }
1441
1442         item = proto_tree_add_item(tree, hf_spnego, tvb, offset, 
1443                                    -1, FALSE);
1444
1445         subtree = proto_item_add_subtree(item, ett_spnego);
1446
1447         /*
1448          * The TVB contains a [0] header and a sequence that consists of an
1449          * object ID and a blob containing the data ...
1450          * XXX - is this RFC 2743's "Mechanism-Independent Token Format",
1451          * with the "optional" "use in non-initial tokens" being chosen.
1452          */
1453
1454         asn1_open(&hnd, tvb, offset);
1455
1456         /*
1457          * Get the first header ...
1458          */
1459
1460         ret = asn1_header_decode(&hnd, &cls, &con, &tag, &def, &len1);
1461
1462         if (ret != ASN1_ERR_NOERROR) {
1463                 dissect_parse_error(tvb, offset, pinfo, subtree,
1464                                     "SPNEGO context header", ret);
1465                 return_offset = tvb_length(tvb);
1466                 goto done;
1467         }
1468
1469         if (!(cls == ASN1_APL && con == ASN1_CON && tag == 0)) {
1470                 proto_tree_add_text(
1471                         subtree, tvb, offset, 0,
1472                         "Unknown header (cls=%d, con=%d, tag=%d)",
1473                         cls, con, tag);
1474                 return_offset = tvb_length(tvb);
1475                 goto done;
1476         }
1477
1478         offset = hnd.offset;
1479
1480         /*
1481          * Get the OID, and find the handle, if any
1482          */
1483
1484         ret = asn1_oid_decode(&hnd, &oid, &oid_len, &nbytes);
1485
1486         if (ret != ASN1_ERR_NOERROR) {
1487                 dissect_parse_error(tvb, offset, pinfo, tree,
1488                                     "SPNEGO wrap token", ret);
1489                 return_offset = tvb_length(tvb);
1490                 goto done;
1491         }
1492
1493         oid_string = format_oid(oid, oid_len);
1494         next_level_value = gssapi_lookup_oid(oid, oid_len);
1495
1496         /*
1497          * XXX - what should we do if this doesn't match the value
1498          * attached to the frame or conversation?  (That would be
1499          * bogus, but that's not impossible - some broken implementation
1500          * might negotiate some security mechanism but put the OID
1501          * for some other security mechanism in GSS_Wrap tokens.)
1502          */
1503         if (next_level_value)
1504           proto_tree_add_text(tree, tvb, offset, nbytes, 
1505                               "thisMech: %s (%s)",
1506                               oid_string, next_level_value->comment);
1507         else
1508           proto_tree_add_text(tree, tvb, offset, nbytes, "thisMech: %s",
1509                               oid_string);
1510
1511         g_free(oid_string);
1512
1513         offset += nbytes;
1514
1515         /*
1516          * Now dissect the GSS_Wrap token; it's assumed to be in the
1517          * rest of the tvbuff.
1518          */
1519         item = proto_tree_add_item(tree, hf_spnego_wraptoken, tvb, offset, 
1520                                    -1, FALSE); 
1521
1522         subtree = proto_item_add_subtree(item, ett_spnego_wraptoken);
1523
1524         /*
1525          * Now, we should be able to dispatch after creating a new TVB.
1526          * The subdissector must return the length of the part of the
1527          * token it dissected, so we can return the length of the part
1528          * we (and it) dissected.
1529          */
1530
1531         token_tvb = tvb_new_subset(tvb, offset, -1, -1);
1532         if (next_level_value->wrap_handle) {
1533           len = call_dissector(next_level_value->wrap_handle, token_tvb, pinfo, subtree);
1534           if (len == 0)
1535             return_offset = tvb_length(tvb);
1536           else
1537             return_offset = offset + len;
1538         } else
1539           return_offset = tvb_length(tvb);
1540  done:
1541         asn1_close(&hnd, &offset);
1542
1543         return return_offset;
1544 }
1545
1546 void
1547 proto_register_spnego(void)
1548 {
1549         static hf_register_info hf[] = {
1550                 { &hf_spnego,
1551                   { "SPNEGO", "spnego", FT_NONE, BASE_NONE, NULL, 0x0,
1552                     "SPNEGO", HFILL }},
1553                 { &hf_spnego_negtokeninit,
1554                   { "negTokenInit", "spnego.negtokeninit", FT_NONE, BASE_NONE,
1555                     NULL, 0x0, "SPNEGO negTokenInit", HFILL}},
1556                 { &hf_spnego_negtokentarg,
1557                   { "negTokenTarg", "spnego.negtokentarg", FT_NONE, BASE_NONE,
1558                     NULL, 0x0, "SPNEGO negTokenTarg", HFILL}},
1559                 { &hf_spnego_mechtype,
1560                   { "mechType", "spnego.negtokeninit.mechtype", FT_NONE,
1561                     BASE_NONE, NULL, 0x0, "SPNEGO negTokenInit mechTypes", HFILL}},
1562                 { &hf_spnego_mechtoken,
1563                   { "mechToken", "spnego.negtokeninit.mechtoken", FT_NONE,
1564                     BASE_NONE, NULL, 0x0, "SPNEGO negTokenInit mechToken", HFILL}},
1565                 { &hf_spnego_mechlistmic,
1566                   { "mechListMIC", "spnego.mechlistmic", FT_NONE,
1567                     BASE_NONE, NULL, 0x0, "SPNEGO mechListMIC", HFILL}}, 
1568                 { &hf_spnego_responsetoken,
1569                   { "responseToken", "spnego.negtokentarg.responsetoken",
1570                     FT_NONE, BASE_NONE, NULL, 0x0, "SPNEGO responseToken",
1571                     HFILL}},
1572                 { &hf_spnego_negtokentarg_negresult,
1573                   { "negResult", "spnego.negtokeninit.negresult", FT_UINT16,
1574                     BASE_HEX, VALS(spnego_negResult_vals), 0, "negResult", HFILL}},
1575                 { &hf_spnego_reqflags, 
1576                   { "reqFlags", "spnego.negtokeninit.reqflags", FT_BYTES,
1577                     BASE_HEX, NULL, 0, "reqFlags", HFILL }},
1578                 { &hf_gssapi_reqflags_deleg,
1579                   { "Delegation", "gssapi.reqflags.deleg", FT_BOOLEAN, 8,
1580                     TFS(&tfs_reqflags_deleg), 0x01, "Delegation", HFILL }},
1581                 { &hf_gssapi_reqflags_mutual,
1582                   { "Mutual Authentication", "gssapi.reqflags.mutual", FT_BOOLEAN,
1583                     8, TFS(&tfs_reqflags_mutual), 0x02, "Mutual Authentication", HFILL}},
1584                 { &hf_gssapi_reqflags_replay,
1585                   { "Replay Detection", "gssapi.reqflags.replay", FT_BOOLEAN,
1586                     8, TFS(&tfs_reqflags_replay), 0x04, "Replay Detection", HFILL}},
1587                 { &hf_gssapi_reqflags_sequence,
1588                   { "Out-of-sequence Detection", "gssapi.reqflags.sequence",
1589                     FT_BOOLEAN, 8, TFS(&tfs_reqflags_sequence), 0x08, 
1590                     "Out-of-sequence Detection", HFILL}},
1591                 { &hf_gssapi_reqflags_anon,
1592                   { "Anonymous Authentication", "gssapi.reqflags.anon", 
1593                     FT_BOOLEAN, 8, TFS(&tfs_reqflags_anon), 0x10,
1594                     "Anonymous Authentication", HFILL}},
1595                 { &hf_gssapi_reqflags_conf,
1596                   { "Per-message Confidentiality", "gssapi.reqflags.conf",
1597                     FT_BOOLEAN, 8, TFS(&tfs_reqflags_conf), 0x20, 
1598                     "Per-message Confidentiality", HFILL}},
1599                 { &hf_gssapi_reqflags_integ,
1600                   { "Per-message Integrity", "gssapi.reqflags.integ", 
1601                     FT_BOOLEAN, 8, TFS(&tfs_reqflags_integ), 0x40,
1602                     "Per-message Integrity", HFILL}},
1603                 { &hf_spnego_wraptoken,
1604                   { "wrapToken", "spnego.wraptoken",
1605                     FT_NONE, BASE_NONE, NULL, 0x0, "SPNEGO wrapToken",
1606                     HFILL}},
1607                 { &hf_spnego_krb5,
1608                   { "krb5_blob", "spnego.krb5.blob", FT_BYTES,
1609                     BASE_NONE, NULL, 0, "krb5_blob", HFILL }},
1610                 { &hf_spnego_krb5_tok_id,
1611                   { "krb5_tok_id", "spnego.krb5.tok_id", FT_UINT16, BASE_HEX,
1612                     VALS(spnego_krb5_tok_id_vals), 0, "KRB5 Token Id", HFILL}},
1613                 { &hf_spnego_krb5_sgn_alg,
1614                   { "krb5_sgn_alg", "spnego.krb5.sgn_alg", FT_UINT16, BASE_HEX,
1615                     VALS(spnego_krb5_sgn_alg_vals), 0, "KRB5 Signing Algorithm", HFILL}},
1616                 { &hf_spnego_krb5_seal_alg,
1617                   { "krb5_seal_alg", "spnego.krb5.seal_alg", FT_UINT16, BASE_HEX,
1618                     VALS(spnego_krb5_seal_alg_vals), 0, "KRB5 Sealing Algorithm", HFILL}},
1619                 { &hf_spnego_krb5_snd_seq,
1620                   { "krb5_snd_seq", "spnego.krb5.snd_seq", FT_BYTES, BASE_NONE,
1621                     NULL, 0, "KRB5 Encrypted Sequence Number", HFILL}},
1622                 { &hf_spnego_krb5_sgn_cksum,
1623                   { "krb5_sgn_cksum", "spnego.krb5.sgn_cksum", FT_BYTES, BASE_NONE,
1624                     NULL, 0, "KRB5 Data Checksum", HFILL}},
1625                 { &hf_spnego_krb5_confounder,
1626                   { "krb5_confounder", "spnego.krb5.confounder", FT_BYTES, BASE_NONE,
1627                     NULL, 0, "KRB5 Confounder", HFILL}},
1628         };
1629
1630         static gint *ett[] = {
1631                 &ett_spnego,
1632                 &ett_spnego_negtokeninit,
1633                 &ett_spnego_negtokentarg,
1634                 &ett_spnego_mechtype,
1635                 &ett_spnego_mechtoken,
1636                 &ett_spnego_mechlistmic,
1637                 &ett_spnego_responsetoken,
1638                 &ett_spnego_wraptoken,
1639                 &ett_spnego_krb5,
1640         };
1641
1642         proto_spnego = proto_register_protocol(
1643                 "Spnego", "Spnego", "spnego");
1644         proto_spnego_krb5 = proto_register_protocol("SPNEGO-KRB5",
1645                                                     "SPNEGO-KRB5",
1646                                                     "spnego-krb5");
1647
1648         proto_register_field_array(proto_spnego, hf, array_length(hf));
1649         proto_register_subtree_array(ett, array_length(ett));
1650 }
1651
1652 void
1653 proto_reg_handoff_spnego(void)
1654 {
1655         dissector_handle_t spnego_handle, spnego_wrap_handle;
1656         dissector_handle_t spnego_krb5_handle, spnego_krb5_wrap_handle;
1657
1658         /* Register protocol with GSS-API module */
1659
1660         spnego_handle = create_dissector_handle(dissect_spnego, proto_spnego);
1661         spnego_wrap_handle = new_create_dissector_handle(dissect_spnego_wrap,
1662                                                          proto_spnego);
1663         gssapi_init_oid("1.3.6.1.5.5.2", proto_spnego, ett_spnego,
1664             spnego_handle, spnego_wrap_handle,
1665             "SPNEGO - Simple Protected Negotiation");
1666
1667         /* Register both the one MS created and the real one */
1668         /*
1669          * Thanks to Jean-Baptiste Marchand and Richard B Ward, the
1670          * mystery of the MS KRB5 OID is cleared up. It was due to a library
1671          * that did not handle OID components greater than 16 bits, and was
1672          * fixed in Win2K SP2 as well as WinXP.
1673          * See the archive of <ietf-krb-wg@anl.gov> for the thread topic
1674          * SPNEGO implementation issues. 3-Dec-2002.
1675          */
1676         spnego_krb5_handle = create_dissector_handle(dissect_spnego_krb5,
1677                                                      proto_spnego_krb5);
1678         spnego_krb5_wrap_handle = new_create_dissector_handle(dissect_spnego_krb5_wrap,
1679                                                               proto_spnego_krb5);
1680         gssapi_init_oid("1.2.840.48018.1.2.2", proto_spnego_krb5, ett_spnego_krb5,
1681                         spnego_krb5_handle, spnego_krb5_wrap_handle,
1682                         "MS KRB5 - Microsoft Kerberos 5");
1683         gssapi_init_oid("1.2.840.113554.1.2.2", proto_spnego_krb5, ett_spnego_krb5,
1684                         spnego_krb5_handle, spnego_krb5_wrap_handle,
1685                         "KRB5 - Kerberos 5");
1686         gssapi_init_oid("1.2.840.113554.1.2.2.3", proto_spnego_krb5, ett_spnego_krb5,
1687                         spnego_krb5_handle, spnego_krb5_wrap_handle,
1688                         "KRB5 - Kerberos 5 - User to User");
1689
1690         /*
1691          * Find the data handle for some calls
1692          */
1693         data_handle = find_dissector("data");
1694 }