New tap for tethereal: io statistics that provides frames/bytes counts for frames...
[obnox/wireshark/wip.git] / packet-spnego.c
1 /* packet-spnego.c
2  * Routines for the simple and protected GSS-API negotiation mechanism
3  * as described in RFC 2478.
4  * Copyright 2002, Tim Potter <tpot@samba.org>
5  * Copyright 2002, Richard Sharpe <rsharpe@ns.aus.com>
6  *
7  * $Id: packet-spnego.c,v 1.37 2002/10/25 04:22:26 guy Exp $
8  *
9  * Ethereal - Network traffic analyzer
10  * By Gerald Combs <gerald@ethereal.com>
11  * Copyright 1998 Gerald Combs
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 # include "config.h"
30 #endif
31
32 #ifdef HAVE_SYS_TYPES_H
33 # include <sys/types.h>
34 #endif
35
36 #include <glib.h>
37 #include <epan/packet.h>
38
39 #include "asn1.h"
40 #include "format-oid.h"
41 #include "packet-gssapi.h"
42 #include "packet-kerberos.h"
43 #include <epan/conversation.h>
44
45 #define SPNEGO_negTokenInit 0
46 #define SPNEGO_negTokenTarg 1
47 #define SPNEGO_mechTypes 0
48 #define SPNEGO_reqFlags 1
49 #define SPNEGO_mechToken 2
50 #define SPNEGO_mechListMIC 3
51 #define SPNEGO_negResult 0
52 #define SPNEGO_supportedMech 1
53 #define SPNEGO_responseToken 2
54 #define SPNEGO_negResult_accept_completed 0
55 #define SPNEGO_negResult_accept_incomplete 1
56 #define SPNEGO_negResult_accept_reject 2
57
58 static int proto_spnego = -1;
59 static int proto_spnego_krb5 = -1;
60
61 static int hf_spnego = -1;
62 static int hf_spnego_negtokeninit = -1;
63 static int hf_spnego_negtokentarg = -1;
64 static int hf_spnego_mechtype = -1;
65 static int hf_spnego_mechtoken = -1;
66 static int hf_spnego_negtokentarg_negresult = -1;
67 static int hf_spnego_mechlistmic = -1;
68 static int hf_spnego_responsetoken = -1;
69 static int hf_spnego_reqflags = -1;
70 static int hf_spnego_krb5 = -1;
71 static int hf_spnego_krb5_tok_id = -1;
72
73 static gint ett_spnego = -1;
74 static gint ett_spnego_negtokeninit = -1;
75 static gint ett_spnego_negtokentarg = -1;
76 static gint ett_spnego_mechtype = -1;
77 static gint ett_spnego_mechtoken = -1;
78 static gint ett_spnego_mechlistmic = -1;
79 static gint ett_spnego_responsetoken = -1;
80 static gint ett_spnego_krb5 = -1;
81
82 static const value_string spnego_negResult_vals[] = {
83   { SPNEGO_negResult_accept_completed,   "Accept Completed" },
84   { SPNEGO_negResult_accept_incomplete,  "Accept Incomplete" },
85   { SPNEGO_negResult_accept_reject,      "Accept Reject"},
86   { 0, NULL}
87 };
88
89 /* Display an ASN1 parse error.  Taken from packet-snmp.c */
90
91 static dissector_handle_t data_handle;
92
93 static void
94 dissect_parse_error(tvbuff_t *tvb, int offset, packet_info *pinfo,
95                     proto_tree *tree, const char *field_name, int ret)
96 {
97         char *errstr;
98
99         errstr = asn1_err_to_str(ret);
100
101         if (tree != NULL) {
102                 proto_tree_add_text(tree, tvb, offset, 0,
103                     "ERROR: Couldn't parse %s: %s", field_name, errstr);
104                 call_dissector(data_handle,
105                     tvb_new_subset(tvb, offset, -1, -1), pinfo, tree);
106         }
107 }
108
109 /*
110  * This is the SPNEGO KRB5 dissector. It is not true KRB5, but some ASN.1
111  * wrapped blob with an OID, Boolean, and a Ticket, that is also ASN.1 wrapped
112  * by the looks of it.
113  */ 
114
115 #define KRB_TOKEN_AP_REQ 0x0001
116 #define KRB_TOKEN_AP_REP 0x0002
117 #define KRB_TOKEN_AP_ERR 0x0003
118
119 static const value_string spnego_krb5_tok_id_vals[] = {
120   { KRB_TOKEN_AP_REQ, "KRB5_AP_REQ"},
121   { KRB_TOKEN_AP_REP, "KRB5_AP_REP"},
122   { KRB_TOKEN_AP_ERR, "KRB5_ERROR"},
123   { 0, NULL}
124 };
125
126 static void
127 dissect_spnego_krb5(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
128 {
129         proto_item *item;
130         proto_tree *subtree;
131         int ret, offset = 0;
132         ASN1_SCK hnd;
133         gboolean def;
134         guint len1, cls, con, tag, oid_len, nbytes;
135         subid_t *oid;
136         gchar *oid_string;
137         gssapi_oid_value *value;
138         tvbuff_t *krb5_tvb;
139
140         item = proto_tree_add_item(tree, hf_spnego_krb5, tvb, offset, 
141                                    -1, FALSE);
142
143         subtree = proto_item_add_subtree(item, ett_spnego_krb5);
144
145         /*
146          * The KRB5 blob conforms to RFC1964:
147          * [APPLICATION 0] {
148          *   OID,
149          *   USHORT (0x0001 == AP-REQ, 0x0002 == AP-REP, 0x0003 == ERROR,
150          *   OCTET STRING } 
151          *
152          * However, for some protocols, the KRB5 blob stars at the SHORT
153          * and has no DER encoded header etc.
154          *
155          * It appears that for some other protocols the KRB5 blob is just
156          * a Kerberos message, with no [APPLICATION 0] header, no OID,
157          * and no USHORT.
158          *
159          * So:
160          *
161          *      If we see an [APPLICATION 0] HEADER, we show the OID and
162          *      the USHORT, and then dissect the rest as a Kerberos message.
163          *
164          *      If we see an [APPLICATION 14] or [APPLICATION 15] header,
165          *      we assume it's an AP-REQ or AP-REP message, and dissect
166          *      it all as a Kerberos message.
167          *
168          *      Otherwise, we show the USHORT, and then dissect the rest
169          *      as a Kerberos message.
170          */
171
172         asn1_open(&hnd, tvb, offset);
173
174         /*
175          * Get the first header ...
176          */
177
178         ret = asn1_header_decode(&hnd, &cls, &con, &tag, &def, &len1);
179
180         if (ret != ASN1_ERR_NOERROR) {
181                 dissect_parse_error(tvb, offset, pinfo, subtree,
182                                     "SPNEGO KRB5 Header", ret);
183                 goto done;
184         }
185
186         if (cls == ASN1_APL && con == ASN1_CON) {
187             /*
188              * [APPLICATION <tag>]
189              */
190             switch (tag) {
191
192             case 0:
193                 /*
194                  * [APPLICATION 0]
195                  */
196
197                 offset = hnd.offset;
198
199                 /* Next, the OID */
200
201                 ret = asn1_oid_decode(&hnd, &oid, &oid_len, &nbytes);
202
203                 if (ret != ASN1_ERR_NOERROR) {
204                     dissect_parse_error(tvb, offset, pinfo, subtree,
205                                         "SPNEGO supportedMech token", ret);
206                     goto done;
207                 }
208
209                 oid_string = format_oid(oid, oid_len);
210
211                 value = gssapi_lookup_oid(oid, oid_len);
212
213                 if (value) 
214                     proto_tree_add_text(subtree, tvb, offset, nbytes, 
215                                         "OID: %s (%s)",
216                                         oid_string, value->comment);
217                 else
218                     proto_tree_add_text(subtree, tvb, offset, nbytes,
219                                         "OID: %s",
220                                         oid_string);
221           
222                 g_free(oid_string);
223
224                 offset += nbytes;
225
226                 /* Next, the token ID ... */
227
228                 proto_tree_add_item(subtree, hf_spnego_krb5_tok_id, tvb, offset, 2,
229                                     TRUE);
230
231                 hnd.offset += 2;
232
233                 offset += 2;
234
235                 break;
236
237             case 14:    /* [APPLICATION 14] */
238             case 15:    /* [APPLICATION 15] */
239                 break;
240
241             default:
242                 proto_tree_add_text(subtree, tvb, offset, 0,
243                         "Unknown header (cls=%d, con=%d, tag=%d)",
244                         cls, con, tag);
245                 goto done;
246             }
247         } else {
248             /* Next, the token ID ... */
249
250             proto_tree_add_item(subtree, hf_spnego_krb5_tok_id, tvb, offset, 2,
251                                 TRUE);
252
253             hnd.offset += 2;
254
255             offset += 2;
256         }
257
258         krb5_tvb = tvb_new_subset(tvb, offset, -1, -1); 
259
260         offset = dissect_kerberos_main(krb5_tvb, pinfo, subtree, FALSE);
261
262  done:
263         return;
264 }
265
266 /* Spnego stuff from here */
267
268 static int
269 dissect_spnego_mechTypes(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
270                          proto_tree *tree, ASN1_SCK *hnd,
271                          dissector_handle_t *next_level_dissector_p)
272 {
273         proto_item *item = NULL;
274         proto_tree *subtree = NULL;
275         gboolean def;
276         guint len1, len, cls, con, tag, nbytes;
277         subid_t *oid;
278         gchar *oid_string;
279         int ret;
280         gboolean saw_mechanism = FALSE;
281
282         /*
283          * MechTypeList ::= SEQUENCE OF MechType
284          */
285
286         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
287
288         if (ret != ASN1_ERR_NOERROR) {
289           dissect_parse_error(tvb, offset, pinfo, subtree,
290                               "SPNEGO last sequence header", ret);
291           goto done;
292         }
293
294         if (!(cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ)) {
295           proto_tree_add_text(
296                               subtree, tvb, offset, 0,
297                               "Unknown header (cls=%d, con=%d, tag=%d)",
298                               cls, con, tag);
299           goto done;
300         }
301
302         offset = hnd->offset;
303
304         item = proto_tree_add_item(tree, hf_spnego_mechtype, tvb, offset, 
305                                    len1, FALSE);
306         subtree = proto_item_add_subtree(item, ett_spnego_mechtype);
307
308         /*
309          * Now, the object IDs ... We should translate them: FIXME
310          */
311
312         while (len1) {
313           gssapi_oid_value *value;
314
315           ret = asn1_oid_decode(hnd, &oid, &len, &nbytes);
316
317           if (ret != ASN1_ERR_NOERROR) {
318             dissect_parse_error(tvb, offset, pinfo, subtree,
319                                 "SPNEGO mechTypes token", ret);
320             goto done;
321           }
322
323           oid_string = format_oid(oid, len);
324           value = gssapi_lookup_oid(oid, len);
325           if (value)
326             proto_tree_add_text(subtree, tvb, offset, nbytes, "OID: %s (%s)",
327                                 oid_string, value->comment);
328           else
329             proto_tree_add_text(subtree, tvb, offset, nbytes, "OID: %s",
330                                 oid_string);
331
332           g_free(oid_string);
333
334           /*
335            * Tell our caller the first mechanism we see, so that if
336            * this is a negTokenInit with a mechToken, it can interpret
337            * the mechToken according to the first mechType.  (There
338            * might not have been any indication of the mechType
339            * in prior frames, so we can't necessarily use the
340            * mechanism from the conversation; i.e., a negTokenInit
341            * can contain the initial security token for the desired
342            * mechanism of the initiator - that's the first mechanism
343            * in the list.)
344            */
345           if (!saw_mechanism) {
346             if (value)
347               *next_level_dissector_p = value->handle;
348             saw_mechanism = TRUE;
349           }
350
351           offset += nbytes;
352           len1 -= nbytes;
353
354         }
355
356  done:
357
358         return offset;
359
360 }
361
362 static int
363 dissect_spnego_reqFlags(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
364                         proto_tree *tree, ASN1_SCK *hnd)
365 {
366         gboolean def;
367         guint len1, cls, con, tag;
368         int ret;
369
370         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
371
372         if (ret != ASN1_ERR_NOERROR) {
373                 dissect_parse_error(tvb, offset, pinfo, tree,
374                                     "SPNEGO reqFlags header", ret);
375                 goto done;
376         }
377
378         if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_BTS)) {
379                 proto_tree_add_text(
380                         tree, tvb, offset, 0,
381                         "Unknown header (cls=%d, con=%d, tag=%d)",
382                         cls, con, tag);
383                 goto done;
384         }
385
386         /* We must have a Bit String ... insert it */ 
387
388         offset = hnd->offset;
389
390         proto_tree_add_item(tree, hf_spnego_reqflags, tvb, offset, len1,
391                             FALSE);
392
393         hnd->offset += len1;
394
395  done:
396         return offset + len1;
397
398 }
399
400 static int
401 dissect_spnego_mechToken(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
402                          proto_tree *tree, ASN1_SCK *hnd,
403                          dissector_handle_t next_level_dissector)
404 {
405         proto_item *item;
406         proto_tree *subtree;
407         gboolean def;
408         int ret;
409         guint cls, con, tag, nbytes;
410         tvbuff_t *token_tvb;
411
412         /*
413          * This appears to be a simple octet string ...
414          */
415
416         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &nbytes);
417
418         if (ret != ASN1_ERR_NOERROR) {
419                 dissect_parse_error(tvb, offset, pinfo, tree,
420                                     "SPNEGO sequence header", ret);
421                 goto done;
422         }
423
424         if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS)) {
425                 proto_tree_add_text(
426                         tree, tvb, offset, 0,
427                         "Unknown header (cls=%d, con=%d, tag=%d)",
428                         cls, con, tag);
429                 goto done;
430         }
431
432         offset = hnd->offset;
433
434         item = proto_tree_add_item(tree, hf_spnego_mechtoken, tvb, offset, 
435                                    nbytes, FALSE);
436         subtree = proto_item_add_subtree(item, ett_spnego_mechtoken);
437
438         /*
439          * Now, we should be able to dispatch after creating a new TVB.
440          */
441
442         token_tvb = tvb_new_subset(tvb, offset, nbytes, -1);
443         if (next_level_dissector)
444           call_dissector(next_level_dissector, token_tvb, pinfo, subtree);
445
446         hnd->offset += nbytes; /* Update this ... */
447
448  done:
449
450   return offset + nbytes;
451
452 }
453
454 static int
455 dissect_spnego_mechListMIC(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
456                            proto_tree *tree, ASN1_SCK *hnd,
457                            dissector_handle_t next_level_dissector)
458 {
459         guint len1, cls, con, tag;
460         int ret;
461         gboolean def;
462         proto_tree *subtree = NULL;
463
464         /*
465          * Add the mechListMIC [3] Octet String or General String ...
466          */
467         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
468
469         if (ret != ASN1_ERR_NOERROR) {
470                 dissect_parse_error(tvb, offset, pinfo, subtree,
471                                     "SPNEGO sequence header", ret);
472                 goto done;
473         }
474
475         offset = hnd->offset;
476
477         if (cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ) {
478
479           /*
480            * There seems to be two different forms this can take
481            * One as an Octet string, and one as a general string in a 
482            * sequence ... We will have to dissect this later
483            */
484          
485           proto_tree_add_text(tree, tvb, offset + 4, len1 - 4,
486                               "mechListMIC: %s",
487                               tvb_format_text(tvb, offset + 4, len1 - 4));
488
489           /* Naughty ... but we have to adjust for what we never took */
490
491           hnd->offset += len1;
492           offset += len1;
493
494         }
495         else if (cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS) {
496           tvbuff_t *token_tvb;
497           proto_item *item;
498           proto_tree *subtree;
499
500           item = proto_tree_add_item(tree, hf_spnego_mechlistmic, tvb, offset, 
501                               len1, FALSE); 
502           subtree = proto_item_add_subtree(item, ett_spnego_mechlistmic);
503           
504         /*
505          * Now, we should be able to dispatch after creating a new TVB.
506          */
507
508           token_tvb = tvb_new_subset(tvb, offset, len1, -1);
509           if (next_level_dissector)
510             call_dissector(next_level_dissector, token_tvb, pinfo, subtree);
511
512           hnd->offset += len1; /* Update this ... */
513           offset += len1;
514
515         }
516         else {
517
518           proto_tree_add_text(subtree, tvb, offset, 0,
519                               "Unknown header (cls=%d, con=%d, tag=%d)",
520                               cls, con, tag);
521           goto done;
522         }
523
524  done:
525
526         return offset;
527
528 }
529
530 static int
531 dissect_spnego_negTokenInit(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
532                             proto_tree *tree, ASN1_SCK *hnd,
533                             dissector_handle_t *next_level_dissector_p)
534 {
535         proto_item *item;
536         proto_tree *subtree;
537         gboolean def;
538         guint len1, len, cls, con, tag;
539         int ret;
540
541         item = proto_tree_add_item( tree, hf_spnego_negtokeninit, tvb, offset,
542                                     -1, FALSE);
543         subtree = proto_item_add_subtree(item, ett_spnego_negtokeninit);
544
545         /*
546          * Here is what we need to get ...
547          * NegTokenInit ::= SEQUENCE {
548          *          mechTypes [0] MechTypeList OPTIONAL,
549          *          reqFlags [1] ContextFlags OPTIONAL,
550          *          mechToken [2] OCTET STRING OPTIONAL,
551          *          mechListMIC [3] OCTET STRING OPTIONAL }
552
553          */
554
555         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
556
557         if (ret != ASN1_ERR_NOERROR) {
558                 dissect_parse_error(tvb, offset, pinfo, subtree,
559                                     "SPNEGO sequence header", ret);
560                 goto done;
561         }
562
563         if (!(cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ)) {
564                 proto_tree_add_text(
565                         subtree, tvb, offset, 0,
566                         "Unknown header (cls=%d, con=%d, tag=%d)",
567                         cls, con, tag);
568                 goto done;
569         }
570
571         offset = hnd->offset;
572
573         while (len1) {
574           int hdr_ofs;
575
576           hdr_ofs = hnd->offset;
577
578           ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len);
579
580           if (ret != ASN1_ERR_NOERROR) {
581             dissect_parse_error(tvb, offset, pinfo, subtree,
582                                 "SPNEGO context header", ret);
583             goto done;
584           }
585
586           if (!(cls == ASN1_CTX && con == ASN1_CON)) {
587             proto_tree_add_text(subtree, tvb, offset, 0,
588                                 "Unknown header (cls=%d, con=%d, tag=%d)",
589                                 cls, con, tag);
590             goto done;
591           }
592
593           /* Adjust for the length of the header */
594
595           len1 -= (hnd->offset - hdr_ofs);
596
597           /* Should be one of the fields */
598
599           switch (tag) {
600
601           case SPNEGO_mechTypes:
602
603             offset = dissect_spnego_mechTypes(tvb, offset, pinfo,
604                                               subtree, hnd,
605                                               next_level_dissector_p);
606
607             break;
608
609           case SPNEGO_reqFlags:
610
611             offset = dissect_spnego_reqFlags(tvb, offset, pinfo, subtree, hnd);
612
613             break;
614
615           case SPNEGO_mechToken:
616
617             offset = dissect_spnego_mechToken(tvb, offset, pinfo, subtree, 
618                                               hnd, *next_level_dissector_p);
619             break;
620
621           case SPNEGO_mechListMIC:
622
623             offset = dissect_spnego_mechListMIC(tvb, offset, pinfo, subtree,
624                                                 hnd, *next_level_dissector_p);
625             break;
626
627           default:
628
629             break;
630           }
631
632           len1 -= len;
633
634         }
635
636  done:
637
638         return offset; /* Not sure this is right */
639 }
640
641 static int
642 dissect_spnego_negResult(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
643                             proto_tree *tree, ASN1_SCK *hnd)
644 {
645         gboolean def;
646         int ret;
647         guint len, cls, con, tag, val;
648
649         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len);
650
651         if (ret != ASN1_ERR_NOERROR) {
652           dissect_parse_error(tvb, offset, pinfo, tree,
653                               "SPNEGO context header", ret);
654           goto done;
655         }
656
657         if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_ENUM)) {
658           proto_tree_add_text(
659                               tree, tvb, offset, 0,
660                               "Unknown header (cls=%d, con=%d, tag=%d) xxx",
661                               cls, con, tag);
662           goto done;
663         }
664
665         offset = hnd->offset;
666
667         /* Now, get the value */
668
669         ret = asn1_uint32_value_decode(hnd, len, &val);
670
671         if (ret != ASN1_ERR_NOERROR) {
672           dissect_parse_error(tvb, offset, pinfo, tree,
673                               "SPNEGO negResult value", ret);
674           goto done;
675         }
676         
677         proto_tree_add_item(tree, hf_spnego_negtokentarg_negresult, tvb, 
678                             offset, 1, FALSE);
679
680         offset = hnd->offset;
681
682  done:
683         return offset;
684 }
685
686 static int
687 dissect_spnego_supportedMech(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
688                              proto_tree *tree, ASN1_SCK *hnd,
689                              dissector_handle_t *next_level_dissector_p)
690 {
691         int ret;
692         guint oid_len, nbytes;
693         subid_t *oid;
694         gchar *oid_string;
695         gssapi_oid_value *value;
696         conversation_t *conversation;
697
698         /*
699          * Now, get the OID, and find the handle, if any
700          */
701
702         offset = hnd->offset;
703
704         ret = asn1_oid_decode(hnd, &oid, &oid_len, &nbytes);
705
706         if (ret != ASN1_ERR_NOERROR) {
707                 dissect_parse_error(tvb, offset, pinfo, tree,
708                                     "SPNEGO supportedMech token", ret);
709                 goto done;
710         }
711
712         oid_string = format_oid(oid, oid_len);
713         value = gssapi_lookup_oid(oid, oid_len);
714
715         if (value)
716           proto_tree_add_text(tree, tvb, offset, nbytes, 
717                               "supportedMech: %s (%s)",
718                               oid_string, value->comment);
719         else
720           proto_tree_add_text(tree, tvb, offset, nbytes, "supportedMech: %s",
721                               oid_string);
722
723         g_free(oid_string);
724
725         offset += nbytes;
726
727         /* Should check for an unrecognized OID ... */
728
729         if (value)
730           *next_level_dissector_p = value->handle;
731
732         /*
733          * Now, we need to save this in per proto info in the
734          * conversation if it exists. We also should create a 
735          * conversation if one does not exist. FIXME!
736          * Hmmm, might need to be smarter, because there can be
737          * multiple mechTypes in a negTokenInit with one being the
738          * default used in the Token if present. Then the negTokenTarg
739          * could override that. :-(
740          */
741
742         if ((conversation = find_conversation(&pinfo->src, &pinfo->dst,
743                                              pinfo->ptype, pinfo->srcport,
744                                              pinfo->destport, 0))) {
745
746
747           conversation_add_proto_data(conversation, proto_spnego, 
748                                       *next_level_dissector_p);
749         }
750         else {
751
752         }
753
754  done:
755         return offset;
756 }
757
758 static int
759 dissect_spnego_responseToken(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
760                              proto_tree *tree, ASN1_SCK *hnd,
761                              dissector_handle_t next_level_dissector)
762 {
763         gboolean def;
764         int ret;
765         guint cls, con, tag, nbytes;
766         tvbuff_t *token_tvb;
767         proto_item *item;
768         proto_tree *subtree;
769
770         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &nbytes);
771
772         if (ret != ASN1_ERR_NOERROR) {
773                 dissect_parse_error(tvb, offset, pinfo, tree,
774                                     "SPNEGO sequence header", ret);
775                 goto done;
776         }
777
778         if (!(cls == ASN1_UNI && con == ASN1_PRI && tag == ASN1_OTS)) {
779                 proto_tree_add_text(
780                         tree, tvb, offset, 0,
781                         "Unknown header (cls=%d, con=%d, tag=%d)",
782                         cls, con, tag);
783                 goto done;
784         }
785
786         offset = hnd->offset;
787
788         item = proto_tree_add_item(tree, hf_spnego_responsetoken, tvb, offset, 
789                                    nbytes, FALSE); 
790
791         subtree = proto_item_add_subtree(item, ett_spnego_responsetoken);
792
793         /*
794          * Now, we should be able to dispatch after creating a new TVB.
795          */
796
797         token_tvb = tvb_new_subset(tvb, offset, nbytes, -1);
798         if (next_level_dissector)
799           call_dissector(next_level_dissector, token_tvb, pinfo, subtree);
800
801         hnd->offset += nbytes; /* Update this ... */
802
803  done:
804         return offset + nbytes;
805 }
806
807 static int
808 dissect_spnego_negTokenTarg(tvbuff_t *tvb, int offset, packet_info *pinfo _U_,
809                             proto_tree *tree, ASN1_SCK *hnd,
810                             dissector_handle_t *next_level_dissector_p)
811
812 {
813         proto_item *item;
814         proto_tree *subtree;
815         gboolean def;
816         int ret;
817         guint len1, len, cls, con, tag;
818
819         item = proto_tree_add_item( tree, hf_spnego_negtokentarg, tvb, offset,
820                                     -1, FALSE);
821         subtree = proto_item_add_subtree(item, ett_spnego_negtokentarg);
822
823         /* 
824          * Here is what we need to get ...
825          * NegTokenTarg ::= SEQUENCE {
826          *          negResult [0] ENUMERATED {
827          *              accept_completed (0),
828          *              accept_incomplete (1),
829          *              reject (2) } OPTIONAL,
830          *          supportedMech [1] MechType OPTIONAL,
831          *          responseToken [2] OCTET STRING OPTIONAL,
832          *          mechListMIC [3] OCTET STRING OPTIONAL }
833          */
834
835         ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len1);
836
837         if (ret != ASN1_ERR_NOERROR) {
838                 dissect_parse_error(tvb, offset, pinfo, subtree,
839                                     "SPNEGO sequence header", ret);
840                 goto done;
841         }
842
843         if (!(cls == ASN1_UNI && con == ASN1_CON && tag == ASN1_SEQ)) {
844                 proto_tree_add_text(
845                         subtree, tvb, offset, 0,
846                         "Unknown header (cls=%d, con=%d, tag=%d)",
847                         cls, con, tag);
848                 goto done;
849         }
850
851         offset = hnd->offset;
852
853         while (len1) {
854           int hdr_ofs;
855
856           hdr_ofs = hnd->offset; 
857
858           ret = asn1_header_decode(hnd, &cls, &con, &tag, &def, &len);
859
860           if (ret != ASN1_ERR_NOERROR) {
861             dissect_parse_error(tvb, offset, pinfo, subtree,
862                                 "SPNEGO context header", ret);
863             goto done;
864           }
865
866           if (!(cls == ASN1_CTX && con == ASN1_CON)) {
867             proto_tree_add_text(
868                                 subtree, tvb, offset, 0,
869                                 "Unknown header (cls=%d, con=%d, tag=%d)",
870                                 cls, con, tag);
871             goto done;
872           }
873
874           /* Adjust for the length of the header */
875
876           len1 -= (hnd->offset - hdr_ofs);
877
878           /* Should be one of the fields */
879
880           switch (tag) {
881
882           case SPNEGO_negResult:
883
884             offset = dissect_spnego_negResult(tvb, offset, pinfo, subtree, 
885                                               hnd);
886             break;
887
888           case SPNEGO_supportedMech:
889
890             offset = dissect_spnego_supportedMech(tvb, offset, pinfo, subtree,
891                                                   hnd, next_level_dissector_p);
892
893             break;
894
895           case SPNEGO_responseToken:
896
897             offset = dissect_spnego_responseToken(tvb, offset, pinfo, subtree,
898                                                   hnd, *next_level_dissector_p);
899             break;
900
901           case SPNEGO_mechListMIC:
902
903             offset = dissect_spnego_mechListMIC(tvb, offset, pinfo, subtree, 
904                                                 hnd, *next_level_dissector_p);
905             break;
906
907           default:
908
909             break;
910           }
911
912           len1 -= len;
913
914         }
915
916  done:
917         return offset;
918
919 }
920
921 static void
922 dissect_spnego(tvbuff_t *tvb, packet_info *pinfo _U_, proto_tree *tree)
923 {
924         proto_item *item;
925         proto_tree *subtree;
926         int ret, offset = 0;
927         ASN1_SCK hnd;
928         gboolean def;
929         guint len1, cls, con, tag;
930         conversation_t *conversation;
931         dissector_handle_t next_level_dissector = NULL;
932
933         /*
934          * We need this later, so lets get it now ...
935          */
936
937         conversation = find_conversation(&pinfo->src, &pinfo->dst,
938                                          pinfo->ptype, pinfo->srcport,
939                                          pinfo->destport, 0);
940
941         if (conversation)
942             next_level_dissector = conversation_get_proto_data(conversation, 
943                                                                proto_spnego);
944
945         item = proto_tree_add_item(tree, hf_spnego, tvb, offset, 
946                                    -1, FALSE);
947
948         subtree = proto_item_add_subtree(item, ett_spnego);
949
950         /*
951          * The TVB contains a [0] header and a sequence that consists of an
952          * object ID and a blob containing the data ...
953          * Actually, it contains, according to RFC2478:
954          * NegotiationToken ::= CHOICE {
955          *          negTokenInit [0] NegTokenInit,
956          *          negTokenTarg [1] NegTokenTarg }
957          * NegTokenInit ::= SEQUENCE {
958          *          mechTypes [0] MechTypeList OPTIONAL,
959          *          reqFlags [1] ContextFlags OPTIONAL,
960          *          mechToken [2] OCTET STRING OPTIONAL,
961          *          mechListMIC [3] OCTET STRING OPTIONAL }
962          * NegTokenTarg ::= SEQUENCE {
963          *          negResult [0] ENUMERATED {
964          *              accept_completed (0),
965          *              accept_incomplete (1),
966          *              reject (2) } OPTIONAL,
967          *          supportedMech [1] MechType OPTIONAL,
968          *          responseToken [2] OCTET STRING OPTIONAL,
969          *          mechListMIC [3] OCTET STRING OPTIONAL }
970          *
971          * Windows typically includes mechTypes and mechListMic ('NONE'
972          * in the case of NTLMSSP only).
973          * It seems to duplicate the responseToken into the mechListMic field
974          * as well. Naughty, naughty.
975          *
976          */
977
978         asn1_open(&hnd, tvb, offset);
979
980         /*
981          * Get the first header ...
982          */
983
984         ret = asn1_header_decode(&hnd, &cls, &con, &tag, &def, &len1);
985
986         if (ret != ASN1_ERR_NOERROR) {
987                 dissect_parse_error(tvb, offset, pinfo, subtree,
988                                     "SPNEGO context header", ret);
989                 goto done;
990         }
991
992         if (!(cls == ASN1_CTX && con == ASN1_CON)) {
993                 proto_tree_add_text(
994                         subtree, tvb, offset, 0,
995                         "Unknown header (cls=%d, con=%d, tag=%d)",
996                         cls, con, tag);
997                 goto done;
998         }
999
1000         offset = hnd.offset;
1001
1002         /*
1003          * The Tag is one of negTokenInit or negTokenTarg
1004          */
1005
1006         switch (tag) {
1007
1008         case SPNEGO_negTokenInit:
1009
1010           offset = dissect_spnego_negTokenInit(tvb, offset, pinfo,
1011                                                subtree, &hnd,
1012                                                &next_level_dissector);
1013
1014           break;
1015
1016         case SPNEGO_negTokenTarg:
1017
1018           offset = dissect_spnego_negTokenTarg(tvb, offset, pinfo,
1019                                                subtree, &hnd,
1020                                                &next_level_dissector);
1021           break;
1022
1023         default: /* Broken, what to do? */
1024
1025           break;
1026         }
1027
1028
1029  done:
1030         asn1_close(&hnd, &offset);
1031
1032 }
1033
1034 void
1035 proto_register_spnego(void)
1036 {
1037         static hf_register_info hf[] = {
1038                 { &hf_spnego,
1039                   { "SPNEGO", "spnego", FT_NONE, BASE_NONE, NULL, 0x0,
1040                     "SPNEGO", HFILL }},
1041                 { &hf_spnego_negtokeninit,
1042                   { "negTokenInit", "spnego.negtokeninit", FT_NONE, BASE_NONE,
1043                     NULL, 0x0, "SPNEGO negTokenInit", HFILL}},
1044                 { &hf_spnego_negtokentarg,
1045                   { "negTokenTarg", "spnego.negtokentarg", FT_NONE, BASE_NONE,
1046                     NULL, 0x0, "SPNEGO negTokenTarg", HFILL}},
1047                 { &hf_spnego_mechtype,
1048                   { "mechType", "spnego.negtokeninit.mechtype", FT_NONE,
1049                     BASE_NONE, NULL, 0x0, "SPNEGO negTokenInit mechTypes", HFILL}},
1050                 { &hf_spnego_mechtoken,
1051                   { "mechToken", "spnego.negtokeninit.mechtoken", FT_NONE,
1052                     BASE_NONE, NULL, 0x0, "SPNEGO negTokenInit mechToken", HFILL}},
1053                 { &hf_spnego_mechlistmic,
1054                   { "mechListMIC", "spnego.mechlistmic", FT_NONE,
1055                     BASE_NONE, NULL, 0x0, "SPNEGO mechListMIC", HFILL}}, 
1056                 { &hf_spnego_responsetoken,
1057                   { "responseToken", "spnego.negtokentarg.responsetoken",
1058                     FT_NONE, BASE_NONE, NULL, 0x0, "SPNEGO responseToken",
1059                     HFILL}},
1060                 { &hf_spnego_negtokentarg_negresult,
1061                   { "negResult", "spnego.negtokeninit.negresult", FT_UINT16,
1062                     BASE_HEX, VALS(spnego_negResult_vals), 0, "negResult", HFILL}},
1063                 { &hf_spnego_reqflags, 
1064                   { "reqFlags", "spnego.negtokeninit.reqflags", FT_BYTES,
1065                     BASE_HEX, NULL, 0, "reqFlags", HFILL }},
1066                 { &hf_spnego_krb5,
1067                   { "krb5_blob", "spnego.krb5.blob", FT_BYTES,
1068                     BASE_HEX, NULL, 0, "krb5_blob", HFILL }},
1069                 { &hf_spnego_krb5_tok_id,
1070                   { "krb5_tok_id", "spnego.krb5.tok_id", FT_UINT16, BASE_HEX,
1071                     VALS(spnego_krb5_tok_id_vals), 0, "KRB5 Token Ids", HFILL}},
1072         };
1073
1074         static gint *ett[] = {
1075                 &ett_spnego,
1076                 &ett_spnego_negtokeninit,
1077                 &ett_spnego_negtokentarg,
1078                 &ett_spnego_mechtype,
1079                 &ett_spnego_mechtoken,
1080                 &ett_spnego_mechlistmic,
1081                 &ett_spnego_responsetoken,
1082                 &ett_spnego_krb5,
1083         };
1084
1085         proto_spnego = proto_register_protocol(
1086                 "Spnego", "Spnego", "spnego");
1087         proto_spnego_krb5 = proto_register_protocol("SPNEGO-KRB5",
1088                                                     "SPNEGO-KRB5",
1089                                                     "spnego-krb5");
1090
1091         proto_register_field_array(proto_spnego, hf, array_length(hf));
1092         proto_register_subtree_array(ett, array_length(ett));
1093 }
1094
1095 void
1096 proto_reg_handoff_spnego(void)
1097 {
1098         dissector_handle_t spnego_handle, spnego_krb5_handle;
1099
1100         /* Register protocol with GSS-API module */
1101
1102         spnego_handle = create_dissector_handle(dissect_spnego, proto_spnego);
1103         spnego_krb5_handle = create_dissector_handle(dissect_spnego_krb5,
1104                                                      proto_spnego_krb5);
1105         gssapi_init_oid("1.3.6.1.5.5.2", proto_spnego, ett_spnego,
1106             spnego_handle, "SPNEGO - Simple Protected Negotiation");
1107
1108         /* Register both the one MS created and the real one */
1109         gssapi_init_oid("1.2.840.48018.1.2.2", proto_spnego_krb5, ett_spnego_krb5,
1110                         spnego_krb5_handle, "MS KRB5 - Microsoft Kerberos 5");
1111         gssapi_init_oid("1.2.840.113554.1.2.2", proto_spnego_krb5, ett_spnego_krb5,
1112                         spnego_krb5_handle, "KRB5 - Kerberos 5");
1113 }