add details for doxygen
[obnox/wireshark/wip.git] / packet-gssapi.c
1 /* packet-gssapi.c
2  * Dissector for GSS-API tokens as described in rfc2078, section 3.1
3  * Copyright 2002, Tim Potter <tpot@samba.org>
4  * Copyright 2002, Richard Sharpe <rsharpe@samba.org> Added a few 
5  *                 bits and pieces ...
6  *
7  * $Id: packet-gssapi.c,v 1.29 2004/01/19 20:10:36 jmayer 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 <string.h>
37
38 #include <glib.h>
39 #include <epan/packet.h>
40
41 #include "asn1.h"
42 #include "format-oid.h"
43 #include "packet-dcerpc.h"
44 #include "packet-gssapi.h"
45 #include "packet-frame.h"
46 #include "epan/conversation.h"
47
48 static int proto_gssapi = -1;
49
50 static int hf_gssapi = -1;
51
52 static gint ett_gssapi = -1;
53
54 /*
55  * Subdissectors
56  */
57
58 static GHashTable *gssapi_oids;
59
60 static gint gssapi_oid_equal(gconstpointer k1, gconstpointer k2)
61 {
62         const char *key1 = (const char *)k1;
63         const char *key2 = (const char *)k2;
64
65         return strcmp(key1, key2) == 0;
66 }
67
68 static guint
69 gssapi_oid_hash(gconstpointer k)
70 {
71         const char *key = (const char *)k;
72         guint hash = 0, i;
73
74         for (i = 0; i < strlen(key); i++)
75                 hash += key[i];
76
77         return hash;
78 }
79
80 void
81 gssapi_init_oid(char *oid, int proto, int ett, dissector_handle_t handle,
82                 dissector_handle_t wrap_handle, gchar *comment)
83 {
84         char *key = g_strdup(oid);
85         gssapi_oid_value *value = g_malloc(sizeof(*value));
86
87         value->proto = find_protocol_by_id(proto);
88         value->ett = ett;
89         value->handle = handle;
90         value->wrap_handle = wrap_handle;
91         value->comment = comment;
92
93         g_hash_table_insert(gssapi_oids, key, value);
94 }
95
96 /*
97  * This takes an OID in binary form, not an OID as a text string, as
98  * an argument.
99  */
100 gssapi_oid_value *
101 gssapi_lookup_oid(subid_t *oid, guint oid_len)
102 {
103         gchar *oid_key;
104         gchar *p;
105         unsigned int i;
106         int len;
107         gssapi_oid_value *value;
108
109         /*
110          * Convert the OID to a string, as text strings are used as
111          * keys in the OID hash table.
112          */
113         oid_key = g_malloc(oid_len * 22 + 1);
114         p = oid_key;
115         len = sprintf(p, "%lu", (unsigned long)oid[0]);
116         p += len;
117         for (i = 1; i < oid_len;i++) {
118                 len = sprintf(p, ".%lu", (unsigned long)oid[i]);
119                 p += len;
120         }
121
122         value = g_hash_table_lookup(gssapi_oids, oid_key);
123         g_free(oid_key);
124         return value;
125 }
126
127 /* Display an ASN1 parse error.  Taken from packet-snmp.c */
128
129 static dissector_handle_t data_handle;
130
131 static void
132 dissect_parse_error(tvbuff_t *tvb, int offset, packet_info *pinfo,
133                     proto_tree *tree, const char *field_name, int ret)
134 {
135         char *errstr;
136
137         errstr = asn1_err_to_str(ret);
138
139         if (tree != NULL) {
140                 proto_tree_add_text(tree, tvb, offset, 0,
141                     "ERROR: Couldn't parse %s: %s", field_name, errstr);
142                 call_dissector(data_handle,
143                     tvb_new_subset(tvb, offset, -1, -1), pinfo, tree);
144         }
145 }
146
147 static int
148 dissect_gssapi_work(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree,
149     gboolean is_verifier)
150 {
151         proto_item *item;
152         proto_tree *subtree;
153         ASN1_SCK hnd;
154         int ret, offset = 0;
155         volatile int return_offset = 0;
156         gboolean def;
157         guint len1, oid_len, cls, con, tag, nbytes;
158         subid_t *oid;
159         gchar *oid_string;
160         gssapi_oid_value *value;
161         volatile dissector_handle_t handle;
162         conversation_t *volatile conversation;
163         tvbuff_t *oid_tvb;
164         int len;
165
166         /*
167          * We need this later, so lets get it now ...
168          */
169
170         conversation = find_conversation(&pinfo->src, &pinfo->dst,
171                                          pinfo->ptype, pinfo->srcport,
172                                          pinfo->destport, 0);
173
174         item = proto_tree_add_item(
175                 tree, hf_gssapi, tvb, offset, -1, FALSE);
176
177         subtree = proto_item_add_subtree(item, ett_gssapi);
178
179         /*
180          * Catch the ReportedBoundsError exception; the stuff we've been
181          * handed doesn't necessarily run to the end of the packet, it's
182          * an item inside a packet, so if it happens to be malformed (or
183          * we, or a dissector we call, has a bug), so that an exception
184          * is thrown, we want to report the error, but return and let
185          * our caller dissect the rest of the packet.
186          *
187          * If it gets a BoundsError, we can stop, as there's nothing more
188          * in the packet after our blob to see, so we just re-throw the
189          * exception.
190          */
191         TRY {
192                 /* Read header */
193
194                 asn1_open(&hnd, tvb, offset);
195
196                 ret = asn1_header_decode(&hnd, &cls, &con, &tag, &def, &len1);
197
198                 if (ret != ASN1_ERR_NOERROR) {
199                         dissect_parse_error(tvb, offset, pinfo, subtree,
200                                     "GSS-API header", ret);
201                         return_offset = tvb_length(tvb);
202                         goto done;
203                 }
204
205                 if (!(cls == ASN1_APL && con == ASN1_CON && tag == 0)) {
206                   /* 
207                    * If we do not recognise an Application class,
208                    * then we are probably dealing with an inner context
209                    * token or a wrap token, and we should retrieve the
210                    * gssapi_oid_value pointer from the per-frame data or,
211                    * if there is no per-frame data (as would be the case
212                    * the first time we dissect this frame), from the
213                    * conversation that exists or that we created from
214                    * pinfo (and then make it per-frame data).
215                    * We need to make it per-frame data as there can be
216                    * more than one GSS-API negotiation in a conversation.
217                    *
218                    * Note! We "cheat". Since we only need the pointer,
219                    * we store that as the data.  (That's not really
220                    * "cheating" - the per-frame data and per-conversation
221                    * data code doesn't care what you supply as a data
222                    * pointer; it just treats it as an opaque pointer, it
223                    * doesn't dereference it or free what it points to.)
224                    */
225                   value = p_get_proto_data(pinfo->fd, proto_gssapi);
226                   if (!value && !pinfo->fd->flags.visited)
227                   {
228                     /* No handle attached to this frame, but it's the first */
229                     /* pass, so it'd be attached to the conversation. */
230                     /* If we have a conversation, try to get the handle, */
231                     /* and if we get one, attach it to the frame. */
232                     if (conversation)
233                     {
234                       value = conversation_get_proto_data(conversation, 
235                                                            proto_gssapi);
236                       if (value)
237                         p_add_proto_data(pinfo->fd, proto_gssapi, value);
238                     }
239                   }
240                   if (!value)
241                   {
242                     proto_tree_add_text(subtree, tvb, offset, 0,
243                         "Unknown header (cls=%d, con=%d, tag=%d)",
244                         cls, con, tag);
245                     return_offset = tvb_length(tvb);
246                     goto done;
247                   }
248                   else 
249                   {
250                     tvbuff_t *oid_tvb;
251
252                     /* Naughty ... no way to reset the offset */
253                     /* Account for the fact we have consumed part of the */
254                     /* ASN.1 and we want to get it back */
255
256                     hnd.offset = offset;
257                     oid_tvb = tvb_new_subset(tvb, offset, -1, -1);
258                     if (is_verifier)
259                         handle = value->wrap_handle;
260                     else
261                         handle = value->handle;
262                     len = call_dissector(handle, oid_tvb, pinfo, subtree);
263                     if (len == 0)
264                         return_offset = tvb_length(tvb);
265                     else
266                         return_offset = offset + len;
267                     goto done; /* We are finished here */
268                   }
269                 }
270
271                 offset = hnd.offset;
272
273                 /* Read oid */
274
275                 ret = asn1_oid_decode(&hnd, &oid, &oid_len, &nbytes);
276
277                 if (ret != ASN1_ERR_NOERROR) {
278                         dissect_parse_error(tvb, offset, pinfo, subtree,
279                                             "GSS-API token", ret);
280                         return_offset = tvb_length(tvb);
281                         goto done;
282                 }
283
284                 oid_string = format_oid(oid, oid_len);
285
286                 /*
287                  * Hand off to subdissector.
288                  */
289
290                 if (((value = gssapi_lookup_oid(oid, oid_len)) == NULL) ||
291                     !proto_is_protocol_enabled(value->proto)) {
292
293                         proto_tree_add_text(subtree, tvb, offset, nbytes, 
294                                             "OID: %s",
295                                             oid_string);
296
297                         offset += nbytes;
298
299                         g_free(oid_string);
300
301                         /* No dissector for this oid */
302
303                         proto_tree_add_text(subtree, tvb, offset, -1,
304                                             "Token object");
305
306                         return_offset = tvb_length(tvb);
307                         goto done;
308                 }
309
310                 if (value)
311                   proto_tree_add_text(subtree, tvb, offset, nbytes, 
312                                       "OID: %s (%s)",
313                                       oid_string, value->comment);
314                 else
315                   proto_tree_add_text(subtree, tvb, offset, nbytes, "OID: %s",
316                                       oid_string);
317
318                 offset += nbytes;
319
320                 g_free(oid_string);
321
322                 /*
323                  * This is not needed, as the sub-dissector adds a tree
324                 sub_item = proto_tree_add_item(subtree, value->proto, tvb,
325                                                offset, -1, FALSE);
326
327                 oid_subtree = proto_item_add_subtree(sub_item, value->ett);
328                 */
329
330                 /* 
331                  * Here we should create a conversation if needed and 
332                  * save a pointer to the data for that OID for the
333                  * GSSAPI protocol.
334                  */
335
336                 if (!conversation) { /* Create one */
337                   conversation = conversation_new(&pinfo->src,
338                                                   &pinfo->dst, 
339                                                   pinfo->ptype, 
340                                                   pinfo->srcport, 
341                                                   pinfo->destport, 0);
342                 }
343
344                 /*
345                  * Now add the proto data ... 
346                  * but only if it is not already there.
347                  */
348
349                 if (!conversation_get_proto_data(conversation,
350                                                  proto_gssapi)) {
351                   conversation_add_proto_data(conversation,
352                                               proto_gssapi, value);
353                 }
354
355                 if (is_verifier) {
356                         handle = value->wrap_handle;
357                         if (handle != NULL) {
358                                 oid_tvb = tvb_new_subset(tvb, offset, -1, -1);
359                                 len = call_dissector(handle, oid_tvb, pinfo,
360                                     subtree);
361                                 if (len == 0)
362                                         return_offset = tvb_length(tvb);
363                                 else
364                                         return_offset = offset + len;
365                         } else {
366                                 proto_tree_add_text(subtree, tvb, offset, -1,
367                                     "Authentication verifier");
368                                 return_offset = tvb_length(tvb);
369                         }
370                 } else {
371                         handle = value->handle;
372                         if (handle != NULL) {
373                                 oid_tvb = tvb_new_subset(tvb, offset, -1, -1);
374                                 len = call_dissector(handle, oid_tvb, pinfo,
375                                     subtree);
376                                 if (len == 0)
377                                         return_offset = tvb_length(tvb);
378                                 else
379                                         return_offset = offset + len;
380                         } else {
381                                 proto_tree_add_text(subtree, tvb, offset, -1,
382                                     "Authentication credentials");
383                                 return_offset = tvb_length(tvb);
384                         }
385                 }
386
387          done:
388                 asn1_close(&hnd, &offset);
389         } CATCH(BoundsError) {
390                 RETHROW;
391         } CATCH(ReportedBoundsError) {
392                 show_reported_bounds_error(tvb, pinfo, tree);
393         } ENDTRY;
394
395         proto_item_set_len(item, return_offset);
396         return return_offset;
397 }
398
399 static void
400 dissect_gssapi(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
401 {
402         dissect_gssapi_work(tvb, pinfo, tree, FALSE);
403 }
404
405 static int
406 dissect_gssapi_verf(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
407 {
408         return dissect_gssapi_work(tvb, pinfo, tree, TRUE);
409 }
410
411 void
412 proto_register_gssapi(void)
413 {
414         static hf_register_info hf[] = {
415                 { &hf_gssapi,
416                   { "GSS-API", "gss-api", FT_NONE, BASE_NONE, NULL, 0x0,
417                     "GSS-API", HFILL }},
418         };
419
420         static gint *ett[] = {
421                 &ett_gssapi,
422         };
423
424         proto_gssapi = proto_register_protocol(
425                 "Generic Security Service Application Program Interface",
426                 "GSS-API", "gss-api");
427
428         proto_register_field_array(proto_gssapi, hf, array_length(hf));
429         proto_register_subtree_array(ett, array_length(ett));
430
431         register_dissector("gssapi", dissect_gssapi, proto_gssapi);
432         new_register_dissector("gssapi_verf", dissect_gssapi_verf, proto_gssapi);
433
434         gssapi_oids = g_hash_table_new(gssapi_oid_hash, gssapi_oid_equal);
435 }
436
437 static int wrap_dissect_gssapi(tvbuff_t *tvb, int offset, 
438                                packet_info *pinfo, 
439                                proto_tree *tree, guint8 *drep _U_)
440 {
441         tvbuff_t *auth_tvb;
442
443         auth_tvb = tvb_new_subset(
444                 tvb, offset, tvb_length_remaining(tvb, offset),
445                 tvb_length_remaining(tvb, offset));
446
447         dissect_gssapi(auth_tvb, pinfo, tree);
448
449         return tvb_length_remaining(tvb, offset);
450 }
451
452 static int wrap_dissect_gssapi_verf(tvbuff_t *tvb, int offset, 
453                                     packet_info *pinfo, 
454                                     proto_tree *tree, guint8 *drep _U_)
455 {
456         tvbuff_t *auth_tvb;
457
458         auth_tvb = tvb_new_subset(
459                 tvb, offset, tvb_length_remaining(tvb, offset),
460                 tvb_length_remaining(tvb, offset));
461
462         return dissect_gssapi_verf(auth_tvb, pinfo, tree);
463 }
464
465 static dcerpc_auth_subdissector_fns gssapi_auth_fns = {
466         wrap_dissect_gssapi,                    /* Bind */
467         wrap_dissect_gssapi,                    /* Bind ACK */
468         wrap_dissect_gssapi,                    /* AUTH3 */
469         wrap_dissect_gssapi_verf,               /* Request verifier */
470         wrap_dissect_gssapi_verf,               /* Response verifier */
471         NULL,                                   /* Request data */
472         NULL                                    /* Response data */
473 };
474
475 void
476 proto_reg_handoff_gssapi(void)
477 {
478         data_handle = find_dissector("data");
479
480         register_dcerpc_auth_subdissector(DCE_C_AUTHN_LEVEL_PKT_PRIVACY,
481                                           DCE_C_RPC_AUTHN_PROTOCOL_SPNEGO,
482                                           &gssapi_auth_fns);
483 }