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