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