Rename crypt-xxx to xxx
[obnox/wireshark/wip.git] / epan / dissectors / packet-radius.c
1 /* packet-radius.c
2  *
3  * Routines for RADIUS packet disassembly
4  * Copyright 1999 Johan Feyaerts
5  * Changed 03/12/2003 Rui Carmo (http://the.taoofmac.com - added all 3GPP VSAs, some parsing)
6  * Changed 07/2005 Luis Ontanon <luis@ontanon.org> - use FreeRADIUS' dictionary
7  * Changed 10/2006 Alejandro Vaquero <alejandrovaquero@yahoo.com> - add Conversations support
8  *
9  * $Id$
10  *
11  * Wireshark - Network traffic analyzer
12  * By Gerald Combs <gerald@wireshark.org>
13  * Copyright 1998 Gerald Combs
14  *
15  * This program is free software; you can redistribute it and/or
16  * modify it under the terms of the GNU General Public License
17  * as published by the Free Software Foundation; either version 2
18  * of the License, or (at your option) any later version.
19  *
20  * This program is distributed in the hope that it will be useful,
21  * but WITHOUT ANY WARRANTY; without even the implied warranty of
22  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
23  * GNU General Public License for more details.
24  *
25  * You should have received a copy of the GNU General Public License
26  * along with this program; if not, write to the Free Software
27  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
28  *
29  * References:
30  *
31  * RFC 2865 - Remote Authentication Dial In User Service (RADIUS)
32  * RFC 2866 - RADIUS Accounting
33  * RFC 2867 - RADIUS Accounting Modifications for Tunnel Protocol Support
34  * RFC 2868 - RADIUS Attributes for Tunnel Protocol Support
35  * RFC 2869 - RADIUS Extensions
36  * RFC 3162 - RADIUS and IPv6
37  * RFC 3576 - Dynamic Authorization Extensions to RADIUS
38  *
39  * See also
40  *
41  *      http://www.iana.org/assignments/radius-types
42  */
43
44
45 /*
46   TO (re)DO: (see svn rev 14786)
47     - dissect_3gpp_ipv6_dns_servers()
48  */
49
50 #ifdef HAVE_CONFIG_H
51 #include "config.h"
52 #endif
53
54 #include <stdlib.h>
55 #include <string.h>
56 #include <ctype.h>
57 #include <errno.h>
58
59 #include <glib.h>
60
61 #include <epan/packet.h>
62 #include <epan/prefs.h>
63 #include <epan/report_err.h>
64 #include <epan/crypt/md5.h>
65 #include <epan/sminmpec.h>
66 #include <epan/filesystem.h>
67 #include <epan/conversation.h>
68 #include <epan/tap.h>
69 #include <epan/addr_resolv.h>
70 #include <epan/emem.h>
71 #include <epan/garrayfix.h>
72
73 #include "packet-radius.h"
74
75 void proto_reg_handoff_radius(void);
76
77 typedef struct _e_radiushdr {
78         guint8 rh_code;
79         guint8 rh_ident;
80         guint16 rh_pktlength;
81 } e_radiushdr;
82
83 typedef struct {
84         GArray* hf;
85         GArray* ett;
86         GArray* vend_vs;
87 } hfett_t;
88
89 #define AUTHENTICATOR_LENGTH    16
90 #define RD_HDR_LENGTH           4
91 #define HDR_LENGTH              (RD_HDR_LENGTH + AUTHENTICATOR_LENGTH)
92
93 #define UDP_PORT_RADIUS         1645
94 #define UDP_PORT_RADIUS_NEW     1812
95 #define UDP_PORT_RADACCT        1646
96 #define UDP_PORT_RADACCT_NEW    1813
97 #define UDP_PORT_DAE_OLD        1700 /* DAE: pre RFC */
98 #define UDP_PORT_DAE            3799 /* DAE: rfc3576 */
99
100 static radius_dictionary_t* dict = NULL;
101
102 static int proto_radius = -1;
103
104 static int hf_radius_req = -1;
105 static int hf_radius_rsp = -1;
106 static int hf_radius_req_frame = -1;
107 static int hf_radius_rsp_frame = -1;
108 static int hf_radius_time = -1;
109
110 static int hf_radius_dup = -1;
111 static int hf_radius_req_dup = -1;
112 static int hf_radius_rsp_dup = -1;
113
114 static int hf_radius_id = -1;
115 static int hf_radius_code = -1;
116 static int hf_radius_length = -1;
117 static int hf_radius_authenticator = -1;
118
119 static int hf_radius_framed_ip_address = -1;
120 static int hf_radius_login_ip_host = -1;
121 static int hf_radius_framed_ipx_network = -1;
122
123 static int hf_radius_cosine_vpi = -1;
124 static int hf_radius_cosine_vci = -1;
125
126 static int hf_radius_ascend_data_filter = -1;
127
128 static gint ett_radius = -1;
129 static gint ett_radius_avp = -1;
130 static gint ett_eap = -1;
131
132 /*
133  * Define the tap for radius
134  */
135 static int radius_tap = -1;
136
137 static radius_vendor_info_t no_vendor = {"Unknown Vendor",0,NULL,-1,1,1,FALSE};
138
139 static radius_attr_info_t no_dictionary_entry = {"Unknown-Attribute",0,FALSE,FALSE,radius_octets, NULL, NULL, -1, -1, -1, -1, -1, NULL };
140
141 static dissector_handle_t eap_handle;
142
143 static const gchar* shared_secret = "";
144 static gboolean show_length = FALSE;
145 static guint alt_port_pref = 0;
146 static guint request_ttl = 5;
147
148 static guint8 authenticator[AUTHENTICATOR_LENGTH];
149
150 /* http://www.iana.org/assignments/radius-types */
151 static const value_string radius_pkt_type_codes[] =
152 {
153         {RADIUS_PKT_TYPE_ACCESS_REQUEST,                        "Access-Request"},                      /*  1 RFC2865 */
154         {RADIUS_PKT_TYPE_ACCESS_ACCEPT,                         "Access-Accept"},                       /*  2 RFC2865 */
155         {RADIUS_PKT_TYPE_ACCESS_REJECT,                         "Access-Reject"},                       /*  3 RFC2865 */
156         {RADIUS_PKT_TYPE_ACCOUNTING_REQUEST,                    "Accounting-Request"},                  /*  4 RFC2865 */
157         {RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE,                   "Accounting-Response"},                 /*  5 RFC2865 */
158         {RADIUS_PKT_TYPE_ACCOUNTING_STATUS,                     "Accounting-Status"},                   /*  6 RFC3575 */
159         {RADIUS_PKT_TYPE_PASSWORD_REQUEST,                      "Password-Request"},                    /*  7 RFC3575 */
160         {RADIUS_PKT_TYPE_PASSWORD_ACK,                          "Password-Ack"},                        /*  8 RFC3575 */
161         {RADIUS_PKT_TYPE_PASSWORD_REJECT,                       "Password-Reject"},                     /*  9 RFC3575 */
162         {RADIUS_PKT_TYPE_ACCOUNTING_MESSAGE,                    "Accounting-Message"},                  /* 10 RFC3575 */
163         {RADIUS_PKT_TYPE_ACCESS_CHALLENGE,                      "Access-Challenge"},                    /* 11 RFC2865 */
164         {RADIUS_PKT_TYPE_STATUS_SERVER,                         "Status-Server"},                       /* 12 RFC2865 */
165         {RADIUS_PKT_TYPE_STATUS_CLIENT,                         "Status-Client"},                       /* 13 RFC2865 */
166
167         {RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST,                 "Resource-Free-Request"},               /* 21 RFC3575 */
168         {RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE,                "Resource-Free-Response"},              /* 22 RFC3575 */
169         {RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST,                "Resource-Query-Request"},              /* 23 RFC3575 */
170         {RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE,               "Query_Response"},                      /* 24 RFC3575 */
171         {RADIUS_PKT_TYPE_ALTERNATE_RESOURCE_RECLAIM_REQUEST,    "Alternate-Resource-Reclaim-Request"},  /* 25 RFC3575 */
172         {RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST,                    "NAS-Reboot-Request"},                  /* 26 RFC3575 */
173         {RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE,                   "NAS-Reboot-Response"},                 /* 27 RFC3575 */
174
175         {RADIUS_PKT_TYPE_NEXT_PASSCODE,                         "Next-Passcode"},                       /* 29 RFC3575 */
176         {RADIUS_PKT_TYPE_NEW_PIN,                               "New-Pin"},                             /* 30 RFC3575 */
177         {RADIUS_PKT_TYPE_TERMINATE_SESSION,                     "Terminate-Session"},                   /* 31 RFC3575 */
178         {RADIUS_PKT_TYPE_PASSWORD_EXPIRED,                      "Password-Expired"},                    /* 32 RFC3575 */
179         {RADIUS_PKT_TYPE_EVENT_REQUEST,                         "Event-Request"},                       /* 33 RFC3575 */
180         {RADIUS_PKT_TYPE_EVENT_RESPONSE,                        "Event-Response"},                      /* 34 RFC3575|RFC5176 */
181
182         {RADIUS_PKT_TYPE_DISCONNECT_REQUEST,                    "Disconnect-Request"},                  /* 40 RFC3575|RFC5176 */
183         {RADIUS_PKT_TYPE_DISCONNECT_ACK,                        "Disconnect-ACK"},                      /* 41 RFC3575|RFC5176 */
184         {RADIUS_PKT_TYPE_DISCONNECT_NAK,                        "Disconnect-NAK"},                      /* 42 RFC3575|RFC5176 */
185         {RADIUS_PKT_TYPE_COA_REQUEST,                           "CoA-Request"},                         /* 43 RFC3575|RFC5176 */
186         {RADIUS_PKT_TYPE_COA_ACK,                               "CoA-ACK"},                             /* 44 RFC3575|RFC5176 */
187         {RADIUS_PKT_TYPE_COA_NAK,                               "CoA-NAK"},                             /* 45 RFC3575|RFC5176 */
188
189         {RADIUS_PKT_TYPE_IP_ADDRESS_ALLOCATE,                   "IP-Address-Allocate"},                 /* 50 RFC3575 */
190         {RADIUS_PKT_TYPE_IP_ADDRESS_RELEASE,                    "IP-Address-Release"},                  /* 51 RFC3575 */
191 /*
192 250-253  Experimental Use             [RFC3575]
193 254-255  Reserved                     [RFC3575]
194 */
195         {0, NULL}
196 };
197 static value_string_ext radius_pkt_type_codes_ext = VALUE_STRING_EXT_INIT(radius_pkt_type_codes);
198
199 /*
200  * Init Hash table stuff for converation
201  */
202
203 typedef struct _radius_call_info_key
204 {
205         guint code;
206         guint ident;
207         conversation_t *conversation;
208         nstime_t req_time;
209 } radius_call_info_key;
210
211 static GHashTable *radius_calls;
212
213 typedef struct _radius_vsa_buffer_key
214 {
215         guint32 vendor_id;
216         guint32 vsa_type;
217 } radius_vsa_buffer_key;
218
219 typedef struct _radius_vsa_buffer
220 {
221         radius_vsa_buffer_key key;
222         guint8* data;
223         guint seg_num;
224         guint len;
225 } radius_vsa_buffer;
226
227 static gint radius_vsa_equal(gconstpointer k1, gconstpointer k2)
228 {
229         const radius_vsa_buffer_key* key1 = (const radius_vsa_buffer_key*) k1;
230         const radius_vsa_buffer_key* key2 = (const radius_vsa_buffer_key*) k2;
231
232         return (((key1->vendor_id == key2->vendor_id) &&
233                 (key1->vsa_type == key2->vsa_type)
234                 ) ? TRUE : FALSE);
235 }
236
237 static guint radius_vsa_hash(gconstpointer k)
238 {
239         const radius_vsa_buffer_key* key = (const radius_vsa_buffer_key*) k;
240
241         return key->vendor_id + key->vsa_type;
242 }
243
244 /* Compare 2 keys */
245 static gboolean radius_call_equal(gconstpointer k1, gconstpointer k2)
246 {
247         const radius_call_info_key* key1 = (const radius_call_info_key*) k1;
248         const radius_call_info_key* key2 = (const radius_call_info_key*) k2;
249
250         if (key1->ident == key2->ident && key1->conversation == key2->conversation) {
251                 nstime_t delta;
252
253                 nstime_delta(&delta, &key1->req_time, &key2->req_time);
254                 if (abs( (int) nstime_to_sec(&delta)) > (double) request_ttl) return 0;
255
256                 if (key1->code == key2->code)
257                         return TRUE;
258                 /* check the request and response are of the same code type */
259                 if ((key1->code == RADIUS_PKT_TYPE_ACCESS_REQUEST) &&
260                     ((key2->code == RADIUS_PKT_TYPE_ACCESS_ACCEPT) || (key2->code == RADIUS_PKT_TYPE_ACCESS_REJECT)))
261                         return TRUE;
262
263                 if ((key1->code == RADIUS_PKT_TYPE_ACCOUNTING_REQUEST) &&
264                     (key2->code == RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE))
265                         return TRUE;
266
267                 if ((key1->code == RADIUS_PKT_TYPE_PASSWORD_REQUEST) &&
268                     ((key2->code == RADIUS_PKT_TYPE_PASSWORD_ACK) || (key2->code == RADIUS_PKT_TYPE_PASSWORD_REJECT)))
269                         return TRUE;
270
271                 if ((key1->code == RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST) &&
272                     (key2->code == RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE))
273                         return TRUE;
274
275                 if ((key1->code == RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST) &&
276                     (key2->code == RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE))
277                         return TRUE;
278
279                 if ((key1->code == RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST) &&
280                     (key2->code == RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE))
281                         return TRUE;
282
283                 if ((key1->code == RADIUS_PKT_TYPE_EVENT_REQUEST) &&
284                     (key2->code == RADIUS_PKT_TYPE_EVENT_RESPONSE))
285                         return TRUE;
286
287                 if ((key1->code == RADIUS_PKT_TYPE_DISCONNECT_REQUEST) &&
288                     ((key2->code == RADIUS_PKT_TYPE_DISCONNECT_ACK) || (key2->code == RADIUS_PKT_TYPE_DISCONNECT_NAK)))
289                         return TRUE;
290
291                 if ((key1->code == RADIUS_PKT_TYPE_COA_REQUEST) &&
292                     ((key2->code == RADIUS_PKT_TYPE_COA_ACK) || (key2->code == RADIUS_PKT_TYPE_COA_NAK)))
293                         return TRUE;
294         }
295         return FALSE;
296 }
297
298 /* Calculate a hash key */
299 static guint radius_call_hash(gconstpointer k)
300 {
301         const radius_call_info_key* key = (const radius_call_info_key*) k;
302
303         return key->ident + /*key->code + */ key->conversation->index;
304 }
305
306
307 static const gchar *dissect_framed_ip_address(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
308         int len;
309         guint32 ip;
310         guint32 ip_h;
311         const gchar *str;
312
313         len = tvb_length(tvb);
314         if (len != 4)
315                 return "[wrong length for IP address]";
316
317         ip=tvb_get_ipv4(tvb,0);
318         ip_h=g_ntohl(ip);
319
320         if (ip_h == 0xFFFFFFFF) {
321                 str = "Negotiated";
322                 proto_tree_add_ipv4_format(tree, hf_radius_framed_ip_address,
323                                            tvb, 0, len, ip, "Framed-IP-Address: %s", str);
324         } else if (ip_h == 0xFFFFFFFE) {
325                 str = "Assigned";
326                 proto_tree_add_ipv4_format(tree, hf_radius_framed_ip_address,
327                                            tvb, 0, len, ip, "Framed-IP-Address: %s", str);
328         } else {
329                 str = ip_to_str((guint8 *)&ip);
330                 proto_tree_add_ipv4_format(tree, hf_radius_framed_ip_address,
331                                            tvb, 0, len, ip, "Framed-IP-Address: %s (%s)",
332                                            get_hostname(ip), str);
333         }
334
335         return str;
336 }
337
338 static const gchar *dissect_login_ip_host(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
339         int len;
340         guint32 ip;
341         guint32 ip_h;
342         const gchar *str;
343
344         len = tvb_length(tvb);
345         if (len != 4)
346                 return "[wrong length for IP address]";
347
348         ip=tvb_get_ipv4(tvb,0);
349         ip_h=g_ntohl(ip);
350
351         if (ip_h == 0xFFFFFFFF) {
352                 str = "User-selected";
353                 proto_tree_add_ipv4_format(tree, hf_radius_login_ip_host,
354                                            tvb, 0, len, ip, "Login-IP-Host: %s", str);
355         } else if (ip_h == 0) {
356                 str = "NAS-selected";
357                 proto_tree_add_ipv4_format(tree, hf_radius_login_ip_host,
358                                            tvb, 0, len, ip, "Login-IP-Host: %s", str);
359         } else {
360                 str = ip_to_str((guint8 *)&ip);
361                 proto_tree_add_ipv4_format(tree, hf_radius_framed_ip_address,
362                                            tvb, 0, len, ip, "Login-IP-Host: %s (%s)",
363                                            get_hostname(ip), str);
364         }
365
366         return str;
367 }
368
369 static const value_string ascenddf_filtertype[] = { {0, "generic"}, {1, "ip"}, {0, NULL} };
370 static const value_string ascenddf_filteror[]   = { {0, "drop"}, {1, "forward"}, {0, NULL} };
371 static const value_string ascenddf_inout[]      = { {0, "out"}, {1, "in"}, {0, NULL} };
372 static const value_string ascenddf_proto[]      = { {1, "icmp"}, {6, "tcp"}, {17, "udp"}, {0, NULL} };
373 static const value_string ascenddf_portq[]      = { {1, "lt"}, {2, "eq"}, {3, "gt"}, {4, "ne"}, {0, NULL} };
374
375 static const gchar *dissect_ascend_data_filter(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
376         const gchar *str;
377         GString *filterstr;
378         int len;
379         guint8 proto, srclen, dstlen;
380         guint32 srcip, dstip;
381         guint16 srcport, dstport;
382         guint8 srcportq, dstportq;
383
384         len=tvb_length(tvb);
385
386         if (len != 24) {
387                 str = ep_strdup_printf("Wrong attribute length %d", len);
388                 return str;
389         }
390
391         filterstr=g_string_sized_new(64);
392
393         proto_tree_add_item(tree, hf_radius_ascend_data_filter, tvb, 0, -1, ENC_NA);
394
395         g_string_printf(filterstr, "%s %s %s",
396                 val_to_str(tvb_get_guint8(tvb, 0), ascenddf_filtertype, "%u"),
397                 val_to_str(tvb_get_guint8(tvb, 2), ascenddf_inout, "%u"),
398                 val_to_str(tvb_get_guint8(tvb, 1), ascenddf_filteror, "%u"));
399
400         proto=tvb_get_guint8(tvb, 14);
401         if (proto) {
402                 str=val_to_str(proto, ascenddf_proto, "%u");
403                 g_string_append_printf(filterstr, " %s", str);
404         }
405
406         srcip=tvb_get_ipv4(tvb, 4);
407         srclen=tvb_get_guint8(tvb, 12);
408         srcport=tvb_get_ntohs(tvb, 16);
409         srcportq=tvb_get_guint8(tvb, 20);
410
411         if (srcip || srclen || srcportq) {
412                 g_string_append_printf(filterstr, " srcip %s/%d", ip_to_str((guint8 *) &srcip), srclen);
413                 if (srcportq)
414                         g_string_append_printf(filterstr, " srcport %s %d",
415                                 val_to_str(srcportq, ascenddf_portq, "%u"), srcport);
416         }
417
418         dstip=tvb_get_ipv4(tvb, 8);
419         dstlen=tvb_get_guint8(tvb, 13);
420         dstport=tvb_get_ntohs(tvb, 18);
421         dstportq=tvb_get_guint8(tvb, 21);
422
423         if (dstip || dstlen || dstportq) {
424                 g_string_append_printf(filterstr, " dstip %s/%d", ip_to_str((guint8 *) &dstip), dstlen);
425                 if (dstportq)
426                         g_string_append_printf(filterstr, " dstport %s %d",
427                                 val_to_str(dstportq, ascenddf_portq, "%u"), dstport);
428         }
429
430         str=ep_strdup(filterstr->str);
431         g_string_free(filterstr, TRUE);
432
433         return str;
434 }
435
436 static const gchar *dissect_framed_ipx_network(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
437         int len;
438         guint32 net;
439         const gchar *str;
440
441         len = tvb_length(tvb);
442         if (len != 4)
443                 return "[wrong length for IPX network]";
444
445         net=tvb_get_ntohl(tvb,0);
446
447         if (net == 0xFFFFFFFE)
448                 str = "NAS-selected";
449         else
450                 str = ep_strdup_printf("0x%08X", net);
451         proto_tree_add_ipxnet_format(tree, hf_radius_framed_ipx_network, tvb, 0,
452                                      len, net, "Framed-IPX-Network: %s", str);
453
454         return str;
455 }
456
457 static const gchar* dissect_cosine_vpvc(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
458         guint vpi, vci;
459
460         if ( tvb_length(tvb) != 4 )
461                 return "[Wrong Length for VP/VC AVP]";
462
463         vpi = tvb_get_ntohs(tvb,0);
464         vci = tvb_get_ntohs(tvb,2);
465
466         proto_tree_add_uint(tree,hf_radius_cosine_vpi,tvb,0,2,vpi);
467         proto_tree_add_uint(tree,hf_radius_cosine_vci,tvb,2,2,vci);
468
469         return ep_strdup_printf("%u/%u",vpi,vci);
470 }
471
472 static void
473 radius_decrypt_avp(gchar *dest,int dest_len,tvbuff_t *tvb,int offset,int length)
474 {
475         md5_state_t md_ctx;
476         md5_byte_t digest[16];
477         int i;
478         gint totlen, returned_length;
479         const guint8 *pd;
480         guchar c;
481
482         DISSECTOR_ASSERT(dest_len > 2);  /* \"\"\0 */
483         dest[0] = '"';
484         dest[1] = '\0';
485         totlen = 1;
486         dest_len -= 1; /* Need to add trailing \" */
487
488         md5_init(&md_ctx);
489         md5_append(&md_ctx,(const guint8*)shared_secret,(int)strlen(shared_secret));
490         md5_append(&md_ctx,authenticator, AUTHENTICATOR_LENGTH);
491         md5_finish(&md_ctx,digest);
492
493         pd = tvb_get_ptr(tvb,offset,length);
494         for( i = 0 ; i < AUTHENTICATOR_LENGTH && i < length ; i++ ) {
495                 c = pd[i] ^ digest[i];
496                 if ( isprint(c) ) {
497                         returned_length = g_snprintf(&dest[totlen], dest_len-totlen,
498                                                      "%c",c);
499                         totlen += MIN(returned_length, dest_len-totlen-1);
500                 } else {
501                         returned_length = g_snprintf(&dest[totlen], dest_len-totlen,
502                                                      "\\%03o",c);
503                         totlen += MIN(returned_length, dest_len-totlen-1);
504                 }
505         }
506         while(i<length) {
507                 if ( isprint(pd[i]) ) {
508                         returned_length = g_snprintf(&dest[totlen], dest_len-totlen,
509                                                      "%c", pd[i]);
510                         totlen += MIN(returned_length, dest_len-totlen-1);
511                 } else {
512                         returned_length = g_snprintf(&dest[totlen], dest_len-totlen,
513                                                      "\\%03o", pd[i]);
514                         totlen += MIN(returned_length, dest_len-totlen-1);
515                 }
516                 i++;
517         }
518         g_snprintf(&dest[totlen], dest_len+1-totlen, "%c", '"');
519 }
520
521
522 void radius_integer(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
523         guint32 uint;
524
525         switch (len) {
526                 case 1:
527                         uint = tvb_get_guint8(tvb,offset);
528                         break;
529                 case 2:
530                         uint = tvb_get_ntohs(tvb,offset);
531                         break;
532                 case 3:
533                         uint = tvb_get_ntoh24(tvb,offset);
534                         break;
535                 case 4:
536                         uint = tvb_get_ntohl(tvb,offset);
537                         break;
538                 case 8: {
539                         guint64 uint64 = tvb_get_ntoh64(tvb,offset);
540                         proto_tree_add_uint64(tree,a->hf64,tvb,offset,len,uint64);
541                         proto_item_append_text(avp_item, "%" G_GINT64_MODIFIER "u", uint64);
542                         return;
543                 }
544                 default:
545                         proto_item_append_text(avp_item, "[unhandled integer length(%u)]", len);
546                         return;
547         }
548         proto_tree_add_item(tree,a->hf,tvb, offset, len, FALSE);
549
550         if (a->vs) {
551                 proto_item_append_text(avp_item, "%s(%u)", val_to_str(uint, a->vs, "Unknown"),uint);
552         } else {
553                 proto_item_append_text(avp_item, "%u", uint);
554         }
555 }
556
557 void radius_signed(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
558         guint32 uint;
559
560         switch (len) {
561                 case 1:
562                         uint = tvb_get_guint8(tvb,offset);
563                         break;
564                 case 2:
565                         uint = tvb_get_ntohs(tvb,offset);
566                         break;
567                 case 3:
568                         uint = tvb_get_ntoh24(tvb,offset);
569                         break;
570                 case 4:
571                         uint = tvb_get_ntohl(tvb,offset);
572                         break;
573                 case 8: {
574                         guint64 uint64 = tvb_get_ntoh64(tvb,offset);
575                         proto_tree_add_int64(tree,a->hf64,tvb,offset,len,uint64);
576                         proto_item_append_text(avp_item, "%" G_GINT64_MODIFIER "u", uint64);
577                         return;
578                 }
579                 default:
580                         proto_item_append_text(avp_item, "[unhandled signed integer length(%u)]", len);
581                         return;
582         }
583
584         proto_tree_add_int(tree,a->hf,tvb,offset,len,uint);
585
586         if (a->vs) {
587                 proto_item_append_text(avp_item, "%s(%d)", val_to_str(uint, a->vs, "Unknown"),uint);
588         } else {
589                 proto_item_append_text(avp_item, "%d", uint);
590         }
591 }
592
593 void radius_string(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
594         if (a->encrypt) {
595                 if (*shared_secret == '\0') {
596                         proto_item_append_text(avp_item, "Encrypted");
597                         proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
598                 } else {
599                         gchar *buffer;
600                         buffer=ep_alloc(1024); /* an AVP value can be at most 253 bytes */
601                         radius_decrypt_avp(buffer,1024,tvb,offset,len);
602                         proto_item_append_text(avp_item, "Decrypted: %s", buffer);
603                         proto_tree_add_string(tree, a->hf, tvb, offset, len, buffer);
604                 }
605         } else {
606                 proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
607                 proto_item_append_text(avp_item, "%s", tvb_format_text(tvb, offset, len));
608         }
609 }
610
611 void radius_octets(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
612         proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
613         proto_item_append_text(avp_item, "%s", tvb_bytes_to_str(tvb, offset, len));
614 }
615
616 void radius_ipaddr(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
617         guint32 ip;
618         gchar buf[MAX_IP_STR_LEN];
619
620         if (len != 4) {
621                 proto_item_append_text(avp_item, "[wrong length for IP address]");
622                 return;
623         }
624
625         ip=tvb_get_ipv4(tvb,offset);
626
627         proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
628
629         ip_to_str_buf((guint8 *)&ip, buf, MAX_IP_STR_LEN);
630         proto_item_append_text(avp_item, "%s", buf);
631 }
632
633 void radius_ipv6addr(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
634         struct e_in6_addr ipv6_buff;
635         gchar txtbuf[256];
636
637         if (len != 16) {
638                 proto_item_append_text(avp_item, "[wrong length for IPv6 address]");
639                 return;
640         }
641
642         proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
643
644         tvb_get_ipv6(tvb, offset, &ipv6_buff);
645         ip6_to_str_buf(&ipv6_buff, txtbuf);
646         proto_item_append_text(avp_item, "%s", txtbuf);
647 }
648
649 void radius_ipv6prefix(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
650         struct e_in6_addr ipv6_buff;
651         gchar txtbuf[256];
652         guint8 n;
653
654         if ((len < 2) || (len > 18) ) {
655                 proto_item_append_text(avp_item, "[wrong length for IPv6 prefix]");
656                 return;
657         }
658
659         /* first byte is reserved == 0x00 */
660         if (tvb_get_guint8(tvb, offset)) {
661                 proto_item_append_text(avp_item, "[invalid reserved byte for IPv6 prefix]");
662                 return;
663         }
664
665         /* this is the prefix length */
666         n = tvb_get_guint8(tvb, offset + 1);
667         if (n > 128) {
668                 proto_item_append_text(avp_item, "[invalid IPv6 prefix length]");
669                 return;
670         }
671
672         proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
673
674         /* cannot use tvb_get_ipv6() here, since the prefix most likely is truncated */
675         memset(&ipv6_buff, 0, sizeof ipv6_buff);
676         tvb_memcpy(tvb, &ipv6_buff, offset + 2,  len - 2);
677         ip6_to_str_buf(&ipv6_buff, txtbuf);
678         proto_item_append_text(avp_item, "%s/%u", txtbuf, n);
679 }
680
681
682 void radius_combo_ip(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
683         guint32 ip;
684         struct e_in6_addr ipv6_buff;
685         gchar buf[256];
686
687         if (len == 4){
688                 ip=tvb_get_ipv4(tvb,offset);
689
690                 proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
691
692                 ip_to_str_buf((guint8 *)&ip, buf, MAX_IP_STR_LEN);
693                 proto_item_append_text(avp_item, "%s", buf);
694         } else if (len == 16) {
695                 proto_tree_add_item(tree, a->hf64, tvb, offset, len, FALSE);
696
697                 tvb_get_ipv6(tvb, offset, &ipv6_buff);
698                 ip6_to_str_buf(&ipv6_buff, buf);
699                 proto_item_append_text(avp_item, "%s", buf);
700         } else {
701                 proto_item_append_text(avp_item, "[wrong length for both of IPv4 and IPv6 address]");
702                 return;
703         }
704 }
705
706 void radius_ipxnet(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
707         guint32 net;
708
709         if (len != 4) {
710                 proto_item_append_text(avp_item, "[wrong length for IPX network]");
711                 return;
712         }
713
714         net=tvb_get_ntohl(tvb,offset);
715
716         proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
717
718         proto_item_append_text(avp_item, "0x%08X", net);
719 }
720
721 void radius_date(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
722         nstime_t time_ptr;
723
724         if (len != 4) {
725                 proto_item_append_text(avp_item, "[wrong length for timestamp]");
726                 return;
727         }
728         time_ptr.secs = tvb_get_ntohl(tvb,offset);
729         time_ptr.nsecs = 0;
730
731         proto_tree_add_time(tree, a->hf, tvb, offset, len, &time_ptr);
732         proto_item_append_text(avp_item, "%s", abs_time_to_str(&time_ptr, ABSOLUTE_TIME_LOCAL, TRUE));
733 }
734
735 /*
736  * "abinary" is Ascend's binary format for filters.  See dissect_ascend_data_filter().
737  */
738 void radius_abinary(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
739         proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
740         proto_item_append_text(avp_item, "%s", tvb_bytes_to_str(tvb, offset, len));
741 }
742
743 void radius_ether(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
744         if (len != 6) {
745                 proto_item_append_text(avp_item, "[wrong length for ethernet address]");
746                 return;
747         }
748
749         proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
750         proto_item_append_text(avp_item, "%s", tvb_ether_to_str(tvb, offset));
751 }
752
753 void radius_ifid(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
754         proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
755         proto_item_append_text(avp_item, "%s", tvb_bytes_to_str(tvb, offset, len));
756 }
757
758 static void add_tlv_to_tree(proto_tree* tlv_tree, proto_item* tlv_item, packet_info* pinfo, tvbuff_t* tvb, radius_attr_info_t* dictionary_entry, guint32 tlv_length, guint32 offset) {
759         proto_item_append_text(tlv_item, ": ");
760         dictionary_entry->type(dictionary_entry,tlv_tree,pinfo,tvb,offset,tlv_length,tlv_item);
761 }
762
763 void radius_tlv(radius_attr_info_t* a, proto_tree* tree, packet_info *pinfo _U_, tvbuff_t* tvb, int offset, int len, proto_item* avp_item) {
764         proto_item* item;
765         gint tlv_num = 0;
766
767         while (len > 0) {
768                 radius_attr_info_t* dictionary_entry = NULL;
769                 guint32 tlv_type;
770                 guint32 tlv_length;
771
772                 proto_item* tlv_item;
773                 proto_item* tlv_len_item;
774                 proto_tree* tlv_tree;
775
776                 if (len < 2) {
777                         item = proto_tree_add_text(tree, tvb, offset, 0,
778                                                    "Not enough room in packet for TLV header");
779                         PROTO_ITEM_SET_GENERATED(item);
780                         return;
781                 }
782                 tlv_type = tvb_get_guint8(tvb,offset);
783                 tlv_length = tvb_get_guint8(tvb,offset+1);
784
785                 if (tlv_length < 2) {
786                         item = proto_tree_add_text(tree, tvb, offset, 0,
787                                                    "TLV too short: length %u < 2", tlv_length);
788                         PROTO_ITEM_SET_GENERATED(item);
789                         return;
790                 }
791
792                 if (len < (gint)tlv_length) {
793                         item = proto_tree_add_text(tree, tvb, offset, 0,
794                                                    "Not enough room in packet for TLV");
795                         PROTO_ITEM_SET_GENERATED(item);
796                         return;
797                 }
798
799                 len -= tlv_length;
800
801                 dictionary_entry = g_hash_table_lookup(a->tlvs_by_id,GUINT_TO_POINTER(tlv_type));
802
803                 if (! dictionary_entry ) {
804                         dictionary_entry = &no_dictionary_entry;
805                 }
806
807                 tlv_item = proto_tree_add_text(tree, tvb, offset, tlv_length,
808                                                "TLV: l=%u  t=%s(%u)", tlv_length,
809                                                dictionary_entry->name, tlv_type);
810
811                 tlv_length -= 2;
812                 offset += 2;
813
814                 tlv_tree = proto_item_add_subtree(tlv_item,dictionary_entry->ett);
815
816                 if (show_length) {
817                         tlv_len_item = proto_tree_add_uint(tlv_tree,
818                                                            dictionary_entry->hf_len,
819                                                            tvb,0,0,tlv_length);
820                         PROTO_ITEM_SET_GENERATED(tlv_len_item);
821                 }
822
823                 add_tlv_to_tree(tlv_tree, tlv_item, pinfo, tvb, dictionary_entry,
824                                 tlv_length, offset);
825                 offset += tlv_length;
826                 tlv_num++;
827         }
828
829         proto_item_append_text(avp_item, "%d TLV(s) inside", tlv_num);
830 }
831
832 static void add_avp_to_tree(proto_tree* avp_tree, proto_item* avp_item, packet_info* pinfo, tvbuff_t* tvb, radius_attr_info_t* dictionary_entry, guint32 avp_length, guint32 offset) {
833         proto_item* pi;
834
835         if (dictionary_entry->tagged) {
836                 guint tag;
837
838                 if (avp_length == 0) {
839                         pi = proto_tree_add_text(avp_tree, tvb, offset,
840                                                  0, "AVP too short for tag");
841                         PROTO_ITEM_SET_GENERATED(pi);
842                         return;
843                 }
844
845                 tag = tvb_get_guint8(tvb, offset);
846
847                 if (tag <=  0x1f) {
848                         proto_tree_add_uint(avp_tree,
849                                             dictionary_entry->hf_tag,
850                                             tvb, offset, 1, tag);
851
852                         proto_item_append_text(avp_item,
853                                                " Tag=0x%.2x", tag);
854
855                         offset++;
856                         avp_length--;
857                 }
858         }
859
860         if ( dictionary_entry->dissector ) {
861                 tvbuff_t* tvb_value;
862                 const gchar* str;
863
864                 tvb_value = tvb_new_subset(tvb, offset, avp_length, (gint) avp_length);
865
866                 str = dictionary_entry->dissector(avp_tree,tvb_value,pinfo);
867
868                 proto_item_append_text(avp_item, ": %s",str);
869         } else {
870                 proto_item_append_text(avp_item, ": ");
871
872                 dictionary_entry->type(dictionary_entry,avp_tree,pinfo,tvb,offset,avp_length,avp_item);
873         }
874 }
875
876 static gboolean vsa_buffer_destroy(gpointer k _U_, gpointer v, gpointer p _U_) {
877         radius_vsa_buffer* vsa_buffer = (radius_vsa_buffer*)v;
878         g_free((gpointer)vsa_buffer->data);
879         g_free(v);
880         return TRUE;
881 }
882
883 static void vsa_buffer_table_destroy(void *table) {
884         if (table) {
885                 g_hash_table_foreach_remove((GHashTable *)table, vsa_buffer_destroy, NULL);
886                 g_hash_table_destroy((GHashTable *)table);
887         }
888 }
889
890
891 void dissect_attribute_value_pairs(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, int offset, guint length) {
892         proto_item* item;
893         gboolean last_eap = FALSE;
894         guint8* eap_buffer = NULL;
895         guint eap_seg_num = 0;
896         guint eap_tot_len_captured = 0;
897         guint eap_tot_len = 0;
898         proto_tree* eap_tree = NULL;
899         tvbuff_t* eap_tvb = NULL;
900
901         GHashTable* vsa_buffer_table = NULL;
902
903         /* Forces load of header fields, if not already done so */
904         DISSECTOR_ASSERT(proto_registrar_get_byname("radius.code"));
905
906         /*
907          * In case we throw an exception, clean up whatever stuff we've
908          * allocated (if any).
909          */
910         CLEANUP_PUSH(g_free, eap_buffer);
911         CLEANUP_PUSH(vsa_buffer_table_destroy, (void *)vsa_buffer_table);
912
913         while (length > 0) {
914                 radius_attr_info_t* dictionary_entry = NULL;
915                 gint tvb_len;
916                 guint32 avp_type;
917                 guint32 avp_length;
918                 guint32 vendor_id;
919
920                 proto_item* avp_item;
921                 proto_item* avp_len_item;
922                 proto_tree* avp_tree;
923
924                 if (length < 2) {
925                         item = proto_tree_add_text(tree, tvb, offset, 0,
926                                                    "Not enough room in packet for AVP header");
927                         PROTO_ITEM_SET_GENERATED(item);
928                         break;  /* exit outer loop, then cleanup & return */
929                 }
930                 avp_type = tvb_get_guint8(tvb,offset);
931                 avp_length = tvb_get_guint8(tvb,offset+1);
932
933                 if (avp_length < 2) {
934                         item = proto_tree_add_text(tree, tvb, offset, 0,
935                                                    "AVP too short: length %u < 2", avp_length);
936                         PROTO_ITEM_SET_GENERATED(item);
937                         break;  /* exit outer loop, then cleanup & return */
938                 }
939
940                 if (length < avp_length) {
941                         item = proto_tree_add_text(tree, tvb, offset, 0,
942                                                    "Not enough room in packet for AVP");
943                         PROTO_ITEM_SET_GENERATED(item);
944                         break;  /* exit outer loop, then cleanup & return */
945                 }
946
947                 length -= avp_length;
948
949                 dictionary_entry = g_hash_table_lookup(dict->attrs_by_id, GUINT_TO_POINTER(avp_type));
950
951                 if (! dictionary_entry ) {
952                         dictionary_entry = &no_dictionary_entry;
953                 }
954
955                 avp_item = proto_tree_add_text(tree, tvb, offset, avp_length,
956                                                "AVP: l=%u  t=%s(%u)", avp_length,
957                                                dictionary_entry->name, avp_type);
958
959                 avp_length -= 2;
960                 offset += 2;
961
962                 if (avp_type == RADIUS_ATTR_TYPE_VENDOR_SPECIFIC) {
963                         radius_vendor_info_t* vendor;
964                         proto_tree* vendor_tree;
965                         gint max_offset = offset + avp_length;
966                         const gchar* vendor_str;
967
968                         /* XXX TODO: handle 2 byte codes for USR */
969
970                         if (avp_length < 4) {
971                                 proto_item_append_text(avp_item, " [AVP too short; no room for vendor ID]");
972                                 offset += avp_length;
973                                 continue; /* while (length > 0) */
974                         }
975                         vendor_id = tvb_get_ntohl(tvb,offset);
976
977                         avp_length -= 4;
978                         offset += 4;
979
980                         vendor = g_hash_table_lookup(dict->vendors_by_id,GUINT_TO_POINTER(vendor_id));
981                         if (vendor) {
982                                 vendor_str = vendor->name;
983                         } else {
984                                 vendor_str = val_to_str_ext_const(vendor_id, &sminmpec_values_ext, "Unknown");
985                                 vendor = &no_vendor;
986                         }
987                         proto_item_append_text(avp_item, " v=%s(%u)", vendor_str,
988                                                vendor_id);
989
990                         vendor_tree = proto_item_add_subtree(avp_item,vendor->ett);
991
992                         while (offset < max_offset) {
993                                 guint32 avp_vsa_type;
994                                 guint32 avp_vsa_len;
995                                 guint8 avp_vsa_flags = 0;
996                                 guint32 avp_vsa_header_len = vendor->type_octets + vendor->length_octets + (vendor->has_flags ? 1 : 0);
997
998                                 switch (vendor->type_octets) {
999                                         case 1:
1000                                                 avp_vsa_type = tvb_get_guint8(tvb,offset++);
1001                                                 break;
1002                                         case 2:
1003                                                 avp_vsa_type = tvb_get_ntohs(tvb,offset);
1004                                                 offset += 2;
1005                                                 break;
1006                                         case 4:
1007                                                 avp_vsa_type = tvb_get_ntohl(tvb,offset);
1008                                                 offset += 4;
1009                                                 break;
1010                                         default:
1011                                                 avp_vsa_type = tvb_get_guint8(tvb,offset++);
1012                                 }
1013
1014                                 switch (vendor->length_octets) {
1015                                         case 1:
1016                                                 avp_vsa_len = tvb_get_guint8(tvb,offset++);
1017                                                 break;
1018                                         case 0:
1019                                                 avp_vsa_len = avp_length;
1020                                                 break;
1021                                         case 2:
1022                                                 avp_vsa_len = tvb_get_ntohs(tvb,offset);
1023                                                 offset += 2;
1024                                                 break;
1025                                         default:
1026                                                 avp_vsa_len = tvb_get_guint8(tvb,offset++);
1027                                 }
1028
1029                                 if (vendor->has_flags) {
1030                                         avp_vsa_flags = tvb_get_guint8(tvb,offset++);
1031                                 }
1032
1033                                 if (avp_vsa_len < avp_vsa_header_len) {
1034                                         proto_tree_add_text(tree, tvb, offset+1, 1,
1035                                                             "[VSA too short]");
1036                                         break; /* exit while (offset < max_offset) loop */
1037                                 }
1038
1039                                 avp_vsa_len -= avp_vsa_header_len;
1040
1041                                 dictionary_entry = g_hash_table_lookup(vendor->attrs_by_id,GUINT_TO_POINTER(avp_vsa_type));
1042
1043                                 if ( !dictionary_entry ) {
1044                                         dictionary_entry = &no_dictionary_entry;
1045                                 }
1046
1047                                 if (vendor->has_flags){
1048                                         avp_item = proto_tree_add_text(vendor_tree,tvb,offset-avp_vsa_header_len,avp_vsa_len+avp_vsa_header_len,
1049                                                                        "VSA: l=%u t=%s(%u) C=0x%02x",
1050                                                                        avp_vsa_len+avp_vsa_header_len, dictionary_entry->name, avp_vsa_type, avp_vsa_flags);
1051                                 } else {
1052                                         avp_item = proto_tree_add_text(vendor_tree,tvb,offset-avp_vsa_header_len,avp_vsa_len+avp_vsa_header_len,
1053                                                                        "VSA: l=%u t=%s(%u)",
1054                                                                        avp_vsa_len+avp_vsa_header_len, dictionary_entry->name, avp_vsa_type);
1055                                 }
1056
1057                                 avp_tree = proto_item_add_subtree(avp_item,dictionary_entry->ett);
1058
1059                                 if (show_length) {
1060                                         avp_len_item = proto_tree_add_uint(avp_tree,
1061                                                                            dictionary_entry->hf_len,
1062                                                                            tvb,0,0,avp_length);
1063                                         PROTO_ITEM_SET_GENERATED(avp_len_item);
1064                                 }
1065
1066                                 if (vendor->has_flags) {
1067                                         radius_vsa_buffer_key key;
1068                                         radius_vsa_buffer* vsa_buffer = NULL;
1069                                         key.vendor_id = vendor_id;
1070                                         key.vsa_type = avp_vsa_type;
1071
1072                                         if (!vsa_buffer_table) {
1073                                                 vsa_buffer_table = g_hash_table_new(radius_vsa_hash, radius_vsa_equal);
1074                                         }
1075
1076                                         vsa_buffer = g_hash_table_lookup(vsa_buffer_table, &key);
1077                                         if (vsa_buffer) {
1078                                                 vsa_buffer->data = g_realloc(vsa_buffer->data, vsa_buffer->len + avp_vsa_len);
1079                                                 tvb_memcpy(tvb, vsa_buffer->data + vsa_buffer->len, offset, avp_vsa_len);
1080                                                 vsa_buffer->len += avp_vsa_len;
1081                                                 vsa_buffer->seg_num++;
1082                                         }
1083
1084                                         if (avp_vsa_flags & 0x80) {
1085                                                 if (!vsa_buffer) {
1086                                                         vsa_buffer = g_malloc(sizeof(radius_vsa_buffer));
1087                                                         vsa_buffer->key.vendor_id = vendor_id;
1088                                                         vsa_buffer->key.vsa_type = avp_vsa_type;
1089                                                         vsa_buffer->len = avp_vsa_len;
1090                                                         vsa_buffer->seg_num = 1;
1091                                                         vsa_buffer->data = g_malloc(avp_vsa_len);
1092                                                         tvb_memcpy(tvb, vsa_buffer->data, offset, avp_vsa_len);
1093                                                         g_hash_table_insert(vsa_buffer_table, &(vsa_buffer->key), vsa_buffer);
1094                                                 }
1095                                                 proto_tree_add_text(avp_tree, tvb, offset, avp_vsa_len, "VSA fragment");
1096                                                 proto_item_append_text(avp_item, ": VSA fragment[%u]", vsa_buffer->seg_num);
1097                                         } else {
1098                                                 if (vsa_buffer) {
1099                                                         tvbuff_t* vsa_tvb = NULL;
1100                                                         proto_tree_add_text(avp_tree, tvb, offset, avp_vsa_len, "VSA fragment");
1101                                                         proto_item_append_text(avp_item, ": Last VSA fragment[%u]", vsa_buffer->seg_num);
1102                                                         vsa_tvb = tvb_new_child_real_data(tvb, vsa_buffer->data, vsa_buffer->len, vsa_buffer->len);
1103                                                         tvb_set_free_cb(vsa_tvb, g_free);
1104                                                         add_new_data_source(pinfo, vsa_tvb, "Reassembled VSA");
1105                                                         add_avp_to_tree(avp_tree, avp_item, pinfo, vsa_tvb, dictionary_entry, vsa_buffer->len, 0);
1106                                                         g_hash_table_remove(vsa_buffer_table, &(vsa_buffer->key));
1107                                                         g_free(vsa_buffer);
1108
1109                                                 } else {
1110                                                         add_avp_to_tree(avp_tree, avp_item, pinfo, tvb, dictionary_entry, avp_vsa_len, offset);
1111                                                 }
1112                                         }
1113                                 } else {
1114                                         add_avp_to_tree(avp_tree, avp_item, pinfo, tvb, dictionary_entry, avp_vsa_len, offset);
1115                                 }
1116
1117                                 offset += avp_vsa_len;
1118                         }; /* while (offset < max_offset) */
1119                         continue;  /* while (length > 0) */
1120                 }
1121
1122                 avp_tree = proto_item_add_subtree(avp_item,dictionary_entry->ett);
1123
1124                 if (show_length) {
1125                         avp_len_item = proto_tree_add_uint(avp_tree,
1126                                                            dictionary_entry->hf_len,
1127                                                            tvb,0,0,avp_length);
1128                         PROTO_ITEM_SET_GENERATED(avp_len_item);
1129                 }
1130
1131                 tvb_len = tvb_length_remaining(tvb, offset);
1132
1133                 if ((gint)avp_length < tvb_len)
1134                         tvb_len = avp_length;
1135
1136                 if (avp_type == RADIUS_ATTR_TYPE_EAP_MESSAGE) {
1137                         eap_seg_num++;
1138
1139                         /* Show this as an EAP fragment. */
1140                         if (tree)
1141                                 proto_tree_add_text(avp_tree, tvb, offset, tvb_len,
1142                                                     "EAP fragment");
1143
1144                         if (eap_tvb != NULL) {
1145                                 /*
1146                                  * Oops, a non-consecutive EAP-Message
1147                                  * attribute.
1148                                  */
1149                                 proto_item_append_text(avp_item, " (non-consecutive)");
1150                         } else {
1151                                 /*
1152                                  * RFC 2869 says, in section 5.13, describing
1153                                  * the EAP-Message attribute:
1154                                  *
1155                                  *    The NAS places EAP messages received
1156                                  *    from the authenticating peer into one
1157                                  *    or more EAP-Message attributes and
1158                                  *    forwards them to the RADIUS Server
1159                                  *    within an Access-Request message.
1160                                  *    If multiple EAP-Messages are
1161                                  *    contained within an Access-Request or
1162                                  *    Access-Challenge packet, they MUST be
1163                                  *    in order and they MUST be consecutive
1164                                  *    attributes in the Access-Request or
1165                                  *    Access-Challenge packet.
1166                                  *
1167                                  *        ...
1168                                  *
1169                                  *    The String field contains EAP packets,
1170                                  *    as defined in [3].  If multiple
1171                                  *    EAP-Message attributes are present
1172                                  *    in a packet their values should be
1173                                  *    concatenated; this allows EAP packets
1174                                  *    longer than 253 octets to be passed
1175                                  *    by RADIUS.
1176                                  *
1177                                  * Do reassembly of EAP-Message attributes.
1178                                  * We just concatenate all the attributes,
1179                                  * and when we see either the end of the
1180                                  * attribute list or a non-EAP-Message
1181                                  * attribute, we know we're done.
1182                                  */
1183
1184                                 if (eap_buffer == NULL)
1185                                         eap_buffer = g_malloc(eap_tot_len_captured + tvb_len);
1186                                 else
1187                                         eap_buffer = g_realloc(eap_buffer,
1188                                                                eap_tot_len_captured + tvb_len);
1189                                 tvb_memcpy(tvb, eap_buffer + eap_tot_len_captured, offset,
1190                                            tvb_len);
1191                                 eap_tot_len_captured += tvb_len;
1192                                 eap_tot_len += avp_length;
1193
1194                                 if ( tvb_bytes_exist(tvb, offset + avp_length + 1, 1) ) {
1195                                         guint8 next_type = tvb_get_guint8(tvb, offset + avp_length);
1196
1197                                         if ( next_type != RADIUS_ATTR_TYPE_EAP_MESSAGE ) {
1198                                                 /* Non-EAP-Message attribute */
1199                                                 last_eap = TRUE;
1200                                         }
1201                                 } else {
1202                                         /*
1203                                          * No more attributes, either because
1204                                          * we're at the end of the packet or
1205                                          * because we're at the end of the
1206                                          * captured packet data.
1207                                          */
1208                                         last_eap = TRUE;
1209                                 }
1210
1211                                 if (last_eap && eap_buffer) {
1212                                         gboolean save_writable;
1213
1214                                         proto_item_append_text(avp_item, " Last Segment[%u]",
1215                                                                eap_seg_num);
1216
1217                                         eap_tree = proto_item_add_subtree(avp_item,ett_eap);
1218
1219                                         eap_tvb = tvb_new_child_real_data(tvb, eap_buffer,
1220                                                                           eap_tot_len_captured,
1221                                                                           eap_tot_len);
1222                                         tvb_set_free_cb(eap_tvb, g_free);
1223                                         add_new_data_source(pinfo, eap_tvb, "Reassembled EAP");
1224
1225                                         /*
1226                                          * Don't free this when we're done -
1227                                          * it's associated with a tvbuff.
1228                                          */
1229                                         eap_buffer = NULL;
1230
1231                                         /*
1232                                          * Set the columns non-writable,
1233                                          * so that the packet list shows
1234                                          * this as an RADIUS packet, not
1235                                          * as an EAP packet.
1236                                          */
1237                                         save_writable = col_get_writable(pinfo->cinfo);
1238                                         col_set_writable(pinfo->cinfo, FALSE);
1239
1240                                         call_dissector(eap_handle, eap_tvb, pinfo, eap_tree);
1241
1242                                         col_set_writable(pinfo->cinfo, save_writable);
1243                                 } else {
1244                                         proto_item_append_text(avp_item, " Segment[%u]",
1245                                                                eap_seg_num);
1246                                 }
1247                         }
1248
1249                         offset += avp_length;
1250                 } else {
1251                         add_avp_to_tree(avp_tree, avp_item, pinfo, tvb, dictionary_entry,
1252                                         avp_length, offset);
1253                         offset += avp_length;
1254                 }
1255
1256         }  /* while (length > 0) */
1257
1258         CLEANUP_CALL_AND_POP; /* vsa_buffer_table_destroy(vsa_buffer_table) */
1259
1260         /*
1261          * Call the cleanup handler to free any reassembled data we haven't
1262          * attached to a tvbuff, and pop the handler.
1263          */
1264         CLEANUP_CALL_AND_POP;
1265 }
1266
1267 /* This function tries to determine whether a packet is radius or not */
1268 static gboolean
1269 is_radius(tvbuff_t *tvb)
1270 {
1271         guint8 code;
1272         guint16 length;
1273
1274         code=tvb_get_guint8(tvb, 0);
1275         if (match_strval_ext(code, &radius_pkt_type_codes_ext) == NULL) {
1276                 return FALSE;
1277         }
1278
1279         /* Check for valid length value:
1280          * Length
1281          *
1282          *  The Length field is two octets.  It indicates the length of the
1283          *  packet including the Code, Identifier, Length, Authenticator and
1284          *  Attribute fields.  Octets outside the range of the Length field
1285          *  MUST be treated as padding and ignored on reception.  If the
1286          *  packet is shorter than the Length field indicates, it MUST be
1287          *  silently discarded.  The minimum length is 20 and maximum length
1288          *  is 4096.
1289          */
1290         length=tvb_get_ntohs(tvb, 2);
1291         if ( (length<20) || (length>4096) ) {
1292                 return FALSE;
1293         }
1294
1295         return TRUE;
1296 }
1297
1298 static void register_radius_fields(const char*);
1299
1300 static int
1301 dissect_radius(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
1302 {
1303         proto_tree *radius_tree = NULL;
1304         proto_tree *avptree = NULL;
1305         proto_item *ti, *hidden_item;
1306         proto_item *avptf;
1307         guint avplength;
1308         e_radiushdr rh;
1309         radius_info_t *rad_info;
1310
1311         conversation_t* conversation;
1312         radius_call_info_key radius_call_key;
1313         radius_call_info_key *new_radius_call_key;
1314         radius_call_t *radius_call = NULL;
1315         static address null_address = { AT_NONE, 0, NULL };
1316
1317         /* does this look like radius ? */
1318         if(!is_radius(tvb)){
1319                 return 0;
1320         }
1321
1322         col_set_str(pinfo->cinfo, COL_PROTOCOL, "RADIUS");
1323         col_clear(pinfo->cinfo, COL_INFO);
1324
1325         rh.rh_code=tvb_get_guint8(tvb,0);
1326         rh.rh_ident=tvb_get_guint8(tvb,1);
1327         rh.rh_pktlength=tvb_get_ntohs(tvb,2);
1328
1329
1330         /* Initialise stat info for passing to tap */
1331         rad_info = ep_alloc(sizeof(radius_info_t));
1332         rad_info->code = 0;
1333         rad_info->ident = 0;
1334         rad_info->req_time.secs = 0;
1335         rad_info->req_time.nsecs = 0;
1336         rad_info->is_duplicate = FALSE;
1337         rad_info->request_available = FALSE;
1338         rad_info->req_num = 0; /* frame number request seen */
1339         rad_info->rspcode = 0;
1340         /* tap stat info */
1341         rad_info->code = rh.rh_code;
1342         rad_info->ident = rh.rh_ident;
1343         tap_queue_packet(radius_tap, pinfo, rad_info);
1344
1345         if (check_col(pinfo->cinfo, COL_INFO))
1346         {
1347                 col_add_fstr(pinfo->cinfo,COL_INFO,"%s(%d) (id=%d, l=%d)",
1348                         val_to_str_ext(rh.rh_code, &radius_pkt_type_codes_ext, "Unknown Packet"),
1349                         rh.rh_code, rh.rh_ident, rh.rh_pktlength);
1350         }
1351
1352         if (tree)
1353         {
1354                 /* Forces load of header fields, if not already done so */
1355                 DISSECTOR_ASSERT(proto_registrar_get_byname("radius.code"));
1356
1357                 ti = proto_tree_add_item(tree,proto_radius, tvb, 0, rh.rh_pktlength, ENC_NA);
1358                 radius_tree = proto_item_add_subtree(ti, ett_radius);
1359                 proto_tree_add_uint(radius_tree,hf_radius_code, tvb, 0, 1, rh.rh_code);
1360                 proto_tree_add_uint_format(radius_tree,hf_radius_id, tvb, 1, 1, rh.rh_ident,
1361                         "Packet identifier: 0x%01x (%d)", rh.rh_ident, rh.rh_ident);
1362         }
1363
1364         /*
1365          * Make sure the length is sane.
1366          */
1367         if (rh.rh_pktlength < HDR_LENGTH)
1368         {
1369                 if (tree)
1370                 {
1371                         proto_tree_add_uint_format(radius_tree, hf_radius_length,
1372                                 tvb, 2, 2, rh.rh_pktlength, "Length: %u (bogus, < %u)",
1373                                 rh.rh_pktlength, HDR_LENGTH);
1374                 }
1375                 return tvb_length(tvb);
1376         }
1377
1378         avplength = rh.rh_pktlength - HDR_LENGTH;
1379         if (tree)
1380         {
1381                 proto_tree_add_uint(radius_tree, hf_radius_length, tvb, 2, 2, rh.rh_pktlength);
1382                 proto_tree_add_item(radius_tree, hf_radius_authenticator, tvb, 4, AUTHENTICATOR_LENGTH,ENC_NA);
1383         }
1384         tvb_memcpy(tvb, authenticator, 4, AUTHENTICATOR_LENGTH);
1385
1386         /* Conversation support REQUEST/RESPONSES */
1387         switch (rh.rh_code)
1388         {
1389                 case RADIUS_PKT_TYPE_ACCESS_REQUEST:
1390                 case RADIUS_PKT_TYPE_ACCOUNTING_REQUEST:
1391                 case RADIUS_PKT_TYPE_PASSWORD_REQUEST:
1392                 case RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST:
1393                 case RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST:
1394                 case RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST:
1395                 case RADIUS_PKT_TYPE_EVENT_REQUEST:
1396                 case RADIUS_PKT_TYPE_DISCONNECT_REQUEST:
1397                 case RADIUS_PKT_TYPE_COA_REQUEST:
1398                         /* Don't bother creating conversations if we're encapsulated within
1399                          * an error packet, such as an ICMP destination unreachable */
1400                         if (pinfo->flags.in_error_pkt)
1401                                 break;
1402
1403                         if (tree)
1404                         {
1405                                 hidden_item = proto_tree_add_boolean(radius_tree, hf_radius_req, tvb, 0, 0, TRUE);
1406                                 PROTO_ITEM_SET_HIDDEN(hidden_item);
1407                         }
1408
1409                         /* Keep track of the address and port whence the call came
1410                          *  so that we can match up requests with replies.
1411                          *
1412                          * Because it is UDP and the reply can come from any IP
1413                          * and port (not necessarly the request dest), we only
1414                          * track the source IP and port of the request to match
1415                          * the reply.
1416                          */
1417
1418                         /*
1419                          * XXX - can we just use NO_ADDR_B?  Unfortunately,
1420                          * you currently still have to pass a non-null
1421                          * pointer for the second address argument even
1422                          * if you do that.
1423                          */
1424                         conversation = find_conversation(pinfo->fd->num, &pinfo->src,
1425                                 &null_address, pinfo->ptype, pinfo->srcport,
1426                                 pinfo->destport, 0);
1427                         if (conversation == NULL)
1428                         {
1429                                 /* It's not part of any conversation - create a new one. */
1430                                 conversation = conversation_new(pinfo->fd->num, &pinfo->src,
1431                                         &null_address, pinfo->ptype, pinfo->srcport,
1432                                         pinfo->destport, 0);
1433                         }
1434
1435                         /* Prepare the key data */
1436                         radius_call_key.code = rh.rh_code;
1437                         radius_call_key.ident = rh.rh_ident;
1438                         radius_call_key.conversation = conversation;
1439                         radius_call_key.req_time = pinfo->fd->abs_ts;
1440
1441                         /* Look up the request */
1442                         radius_call = g_hash_table_lookup(radius_calls, &radius_call_key);
1443                         if (radius_call != NULL)
1444                         {
1445                                 /* We've seen a request with this ID, with the same
1446                                    destination, before - but was it *this* request? */
1447                                 if (pinfo->fd->num != radius_call->req_num)
1448                                 {
1449                                         /* No, so it's a duplicate request. Mark it as such. */
1450                                         rad_info->is_duplicate = TRUE;
1451                                         rad_info->req_num = radius_call->req_num;
1452                                         if (check_col(pinfo->cinfo, COL_INFO))
1453                                         {
1454                                                 col_append_fstr(pinfo->cinfo, COL_INFO,
1455                                                         ", Duplicate Request ID:%u", rh.rh_ident);
1456                                         }
1457                                         if (tree)
1458                                         {
1459                                                 proto_item* item;
1460                                                 hidden_item = proto_tree_add_uint(radius_tree, hf_radius_dup, tvb, 0,0, rh.rh_ident);
1461                                                 PROTO_ITEM_SET_HIDDEN(hidden_item);
1462                                                 item = proto_tree_add_uint(radius_tree, hf_radius_req_dup, tvb, 0,0, rh.rh_ident);
1463                                                 PROTO_ITEM_SET_GENERATED(item);
1464                                         }
1465                                 }
1466                         }
1467                         else
1468                         {
1469                                 /* Prepare the value data.
1470                                    "req_num" and "rsp_num" are frame numbers;
1471                                    frame numbers are 1-origin, so we use 0
1472                                    to mean "we don't yet know in which frame
1473                                    the reply for this call appears". */
1474                                 new_radius_call_key = se_alloc(sizeof(radius_call_info_key));
1475                                 *new_radius_call_key = radius_call_key;
1476                                 radius_call = se_alloc(sizeof(radius_call_t));
1477                                 radius_call->req_num = pinfo->fd->num;
1478                                 radius_call->rsp_num = 0;
1479                                 radius_call->ident = rh.rh_ident;
1480                                 radius_call->code = rh.rh_code;
1481                                 radius_call->responded = FALSE;
1482                                 radius_call->req_time = pinfo->fd->abs_ts;
1483                                 radius_call->rspcode = 0;
1484
1485                                 /* Store it */
1486                                 g_hash_table_insert(radius_calls, new_radius_call_key, radius_call);
1487                         }
1488                         if (tree && radius_call->rsp_num)
1489                         {
1490                                 proto_item* item;
1491                                 item = proto_tree_add_uint_format(radius_tree,
1492                                         hf_radius_rsp_frame, tvb, 0, 0, radius_call->rsp_num,
1493                                         "The response to this request is in frame %u",
1494                                         radius_call->rsp_num);
1495                                 PROTO_ITEM_SET_GENERATED(item);
1496                         }
1497                         break;
1498                 case RADIUS_PKT_TYPE_ACCESS_ACCEPT:
1499                 case RADIUS_PKT_TYPE_ACCESS_REJECT:
1500                 case RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE:
1501                 case RADIUS_PKT_TYPE_PASSWORD_ACK:
1502                 case RADIUS_PKT_TYPE_PASSWORD_REJECT:
1503                 case RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE:
1504                 case RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE:
1505                 case RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE:
1506                 case RADIUS_PKT_TYPE_EVENT_RESPONSE:
1507                 case RADIUS_PKT_TYPE_DISCONNECT_ACK:
1508                 case RADIUS_PKT_TYPE_DISCONNECT_NAK:
1509                 case RADIUS_PKT_TYPE_COA_ACK:
1510                 case RADIUS_PKT_TYPE_COA_NAK:
1511                         /* Don't bother finding conversations if we're encapsulated within
1512                          * an error packet, such as an ICMP destination unreachable */
1513                         if (pinfo->flags.in_error_pkt)
1514                                 break;
1515
1516                         if (tree)
1517                         {
1518                                 hidden_item = proto_tree_add_boolean(radius_tree, hf_radius_rsp, tvb, 0, 0, TRUE);
1519                                 PROTO_ITEM_SET_HIDDEN(hidden_item);
1520                         }
1521
1522                         /* Check for RADIUS response.  A response must match a call that
1523                          * we've seen, and the response must be sent to the same
1524                          * port and address that the call came from.
1525                          *
1526                          * Because it is UDP and the reply can come from any IP
1527                          * and port (not necessarly the request dest), we only
1528                          * track the source IP and port of the request to match
1529                          * the reply.
1530                          */
1531
1532                         /* XXX - can we just use NO_ADDR_B?  Unfortunately,
1533                          * you currently still have to pass a non-null
1534                          * pointer for the second address argument even
1535                          * if you do that.
1536                          */
1537                         conversation = find_conversation(pinfo->fd->num, &null_address,
1538                                 &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
1539                         if (conversation != NULL)
1540                         {
1541                                 /* Look only for matching request, if
1542                                    matching conversation is available. */
1543                                 /* Prepare the key data */
1544                                 radius_call_key.code = rh.rh_code;
1545                                 radius_call_key.ident = rh.rh_ident;
1546                                 radius_call_key.conversation = conversation;
1547                                 radius_call_key.req_time = pinfo->fd->abs_ts;
1548
1549                                 radius_call = g_hash_table_lookup(radius_calls, &radius_call_key);
1550                                 if (radius_call)
1551                                 {
1552                                         /* Indicate the frame to which this is a reply. */
1553                                         if (radius_call->req_num)
1554                                         {
1555                                                 rad_info->request_available = TRUE;
1556                                                 rad_info->req_num = radius_call->req_num;
1557                                                 radius_call->responded = TRUE;
1558
1559                                                 if (tree)
1560                                                 {
1561                                                         nstime_t delta;
1562                                                         proto_item* item;
1563                                                         item = proto_tree_add_uint_format(radius_tree,
1564                                                                 hf_radius_req_frame, tvb, 0, 0,
1565                                                                 radius_call->req_num,
1566                                                                 "This is a response to a request in frame %u",
1567                                                                 radius_call->req_num);
1568                                                         PROTO_ITEM_SET_GENERATED(item);
1569                                                         nstime_delta(&delta, &pinfo->fd->abs_ts, &radius_call->req_time);
1570                                                         item = proto_tree_add_time(radius_tree, hf_radius_time, tvb, 0, 0, &delta);
1571                                                         PROTO_ITEM_SET_GENERATED(item);
1572                                                 }
1573                                         }
1574
1575                                         if (radius_call->rsp_num == 0)
1576                                         {
1577                                                 /* We have not yet seen a response to that call, so
1578                                                    this must be the first response; remember its
1579                                                    frame number. */
1580                                                 radius_call->rsp_num = pinfo->fd->num;
1581                                         }
1582                                         else
1583                                         {
1584                                                 /* We have seen a response to this call - but was it
1585                                                    *this* response? (disregard provisional responses) */
1586                                                 if ( (radius_call->rsp_num != pinfo->fd->num) && (radius_call->rspcode == rh.rh_code) )
1587                                                 {
1588                                                         /* No, so it's a duplicate response. Mark it as such. */
1589                                                         rad_info->is_duplicate = TRUE;
1590                                                         if (check_col(pinfo->cinfo, COL_INFO))
1591                                                         {
1592                                                                 col_append_fstr(pinfo->cinfo, COL_INFO,
1593                                                                         ", Duplicate Response ID:%u", rh.rh_ident);
1594                                                         }
1595                                                         if (tree)
1596                                                         {
1597                                                                 proto_item* item;
1598                                                                 hidden_item = proto_tree_add_uint(radius_tree,
1599                                                                         hf_radius_dup, tvb, 0,0, rh.rh_ident);
1600                                                                 PROTO_ITEM_SET_HIDDEN(hidden_item);
1601                                                                 item = proto_tree_add_uint(radius_tree,
1602                                                                         hf_radius_rsp_dup, tvb, 0, 0, rh.rh_ident);
1603                                                                 PROTO_ITEM_SET_GENERATED(item);
1604                                                         }
1605                                                 }
1606                                         }
1607                                         /* Now store the response code (after comparison above) */
1608                                         radius_call->rspcode = rh.rh_code;
1609                                         rad_info->rspcode = rh.rh_code;
1610                                 }
1611                         }
1612                         break;
1613                 default:
1614                         break;
1615         }
1616
1617         if (radius_call)
1618         {
1619                 rad_info->req_time.secs = radius_call->req_time.secs;
1620                 rad_info->req_time.nsecs = radius_call->req_time.nsecs;
1621         }
1622
1623         if (tree && avplength > 0)
1624         {
1625                 /* list the attribute value pairs */
1626                 avptf = proto_tree_add_text(radius_tree, tvb, HDR_LENGTH,
1627                         avplength, "Attribute Value Pairs");
1628                 avptree = proto_item_add_subtree(avptf, ett_radius_avp);
1629                 dissect_attribute_value_pairs(avptree, pinfo, tvb, HDR_LENGTH,
1630                         avplength);
1631         }
1632
1633         return tvb_length(tvb);
1634 }
1635
1636
1637 static void register_attrs(gpointer k _U_, gpointer v, gpointer p) {
1638         radius_attr_info_t* a = v;
1639         int i;
1640         gint* ett = &(a->ett);
1641         gchar* abbrev = g_strconcat("radius.",a->name,NULL);
1642         hf_register_info hfri[] = {
1643                 { NULL, { NULL,NULL, FT_NONE,  BASE_NONE, NULL, 0x0, NULL, HFILL }},
1644                 { NULL, { NULL,NULL, FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
1645                 { NULL, { NULL,NULL, FT_NONE,  BASE_NONE, NULL, 0x0, NULL, HFILL }},
1646                 { NULL, { NULL,NULL, FT_NONE,  BASE_NONE, NULL, 0x0, NULL, HFILL }}
1647         };
1648         guint len_hf = 2;
1649         hfett_t* ri = p;
1650
1651         for(i=0; abbrev[i]; i++) {
1652                 if(abbrev[i] == '-') abbrev[i] = '_';
1653                 if(abbrev[i] == '/') abbrev[i] = '_';
1654         }
1655
1656         hfri[0].p_id = &(a->hf);
1657         hfri[1].p_id = &(a->hf_len);
1658
1659         hfri[0].hfinfo.name = a->name;
1660         hfri[0].hfinfo.abbrev = abbrev;
1661
1662         hfri[1].hfinfo.name = "Length";
1663         hfri[1].hfinfo.abbrev = g_strconcat(abbrev,".len",NULL);
1664         hfri[1].hfinfo.blurb = g_strconcat(a->name," Length",NULL);
1665
1666         if (a->type == radius_integer) {
1667                 hfri[0].hfinfo.type = FT_UINT32;
1668                 hfri[0].hfinfo.display = BASE_DEC;
1669
1670                 hfri[2].p_id = &(a->hf64);
1671                 hfri[2].hfinfo.name = g_strdup(a->name);
1672                 hfri[2].hfinfo.abbrev = abbrev;
1673                 hfri[2].hfinfo.type = FT_UINT64;
1674                 hfri[2].hfinfo.display = BASE_DEC;
1675
1676                 if (a->vs) {
1677                         hfri[0].hfinfo.strings = VALS(a->vs);
1678                 }
1679
1680                 len_hf++;
1681         }else if (a->type == radius_signed) {
1682                 hfri[0].hfinfo.type = FT_INT32;
1683                 hfri[0].hfinfo.display = BASE_DEC;
1684
1685                 hfri[2].p_id = &(a->hf64);
1686                 hfri[2].hfinfo.name = g_strdup(a->name);
1687                 hfri[2].hfinfo.abbrev = abbrev;
1688                 hfri[2].hfinfo.type = FT_INT64;
1689                 hfri[2].hfinfo.display = BASE_DEC;
1690
1691                 if (a->vs) {
1692                         hfri[0].hfinfo.strings = VALS(a->vs);
1693                 }
1694
1695                 len_hf++;
1696         } else if (a->type == radius_string) {
1697                 hfri[0].hfinfo.type = FT_STRING;
1698                 hfri[0].hfinfo.display = BASE_NONE;
1699         } else if (a->type == radius_octets) {
1700                 hfri[0].hfinfo.type = FT_BYTES;
1701                 hfri[0].hfinfo.display = BASE_NONE;
1702         } else if (a->type == radius_ipaddr) {
1703                 hfri[0].hfinfo.type = FT_IPv4;
1704                 hfri[0].hfinfo.display = BASE_NONE;
1705         } else if (a->type == radius_ipv6addr) {
1706                 hfri[0].hfinfo.type = FT_IPv6;
1707                 hfri[0].hfinfo.display = BASE_NONE;
1708         } else if (a->type == radius_ipv6prefix) {
1709                 hfri[0].hfinfo.type = FT_BYTES;
1710                 hfri[0].hfinfo.display = BASE_NONE;
1711         } else if (a->type == radius_ipxnet) {
1712                 hfri[0].hfinfo.type = FT_IPXNET;
1713                 hfri[0].hfinfo.display = BASE_NONE;
1714         } else if (a->type == radius_date) {
1715                 hfri[0].hfinfo.type = FT_ABSOLUTE_TIME;
1716                 hfri[0].hfinfo.display = ABSOLUTE_TIME_LOCAL;
1717         } else if (a->type == radius_abinary) {
1718                 hfri[0].hfinfo.type = FT_BYTES;
1719                 hfri[0].hfinfo.display = BASE_NONE;
1720         } else if (a->type == radius_ifid) {
1721                 hfri[0].hfinfo.type = FT_BYTES;
1722                 hfri[0].hfinfo.display = BASE_NONE;
1723         } else if (a->type == radius_combo_ip) {
1724                 hfri[0].hfinfo.type = FT_IPv4;
1725                 hfri[0].hfinfo.display = BASE_NONE;
1726
1727                 hfri[2].p_id = &(a->hf64);
1728                 hfri[2].hfinfo.name = g_strdup(a->name);
1729                 hfri[2].hfinfo.abbrev = g_strdup(abbrev);
1730                 hfri[2].hfinfo.type = FT_IPv6;
1731                 hfri[2].hfinfo.display = BASE_NONE;
1732
1733                 len_hf++;
1734         } else if (a->type == radius_tlv) {
1735                 hfri[0].hfinfo.type = FT_BYTES;
1736                 hfri[0].hfinfo.display = BASE_NONE;
1737         } else {
1738                 hfri[0].hfinfo.type = FT_BYTES;
1739                 hfri[0].hfinfo.display = BASE_NONE;
1740         }
1741
1742         if (a->tagged) {
1743                 hfri[len_hf].p_id = &(a->hf_tag);
1744                 hfri[len_hf].hfinfo.name = "Tag";
1745                 hfri[len_hf].hfinfo.abbrev = g_strconcat(abbrev,".tag",NULL);
1746                 hfri[len_hf].hfinfo.blurb = g_strconcat(a->name," Tag",NULL);
1747                 hfri[len_hf].hfinfo.type = FT_UINT8;
1748                 hfri[len_hf].hfinfo.display = BASE_HEX;
1749                 len_hf++;
1750         }
1751
1752         g_array_append_vals(ri->hf,hfri,len_hf);
1753         g_array_append_val(ri->ett,ett);
1754
1755         if (a->tlvs_by_id) {
1756                 g_hash_table_foreach(a->tlvs_by_id,register_attrs,ri);
1757         }
1758 }
1759
1760 static void register_vendors(gpointer k _U_, gpointer v, gpointer p) {
1761         radius_vendor_info_t* vnd = v;
1762         hfett_t* ri = p;
1763         value_string vnd_vs;
1764         gint* ett_p = &(vnd->ett);
1765
1766         vnd_vs.value = vnd->code;
1767         vnd_vs.strptr = vnd->name;
1768
1769         g_array_append_val(ri->vend_vs,vnd_vs);
1770         g_array_append_val(ri->ett,ett_p);
1771
1772         g_hash_table_foreach(vnd->attrs_by_id,register_attrs,ri);
1773
1774 }
1775
1776 extern void radius_register_avp_dissector(guint32 vendor_id, guint32 attribute_id, radius_avp_dissector_t radius_avp_dissector) {
1777         radius_vendor_info_t* vendor;
1778         radius_attr_info_t* dictionary_entry;
1779         GHashTable* by_id;
1780
1781         DISSECTOR_ASSERT(radius_avp_dissector != NULL);
1782
1783         if (vendor_id) {
1784                 vendor = g_hash_table_lookup(dict->vendors_by_id,GUINT_TO_POINTER(vendor_id));
1785
1786                 if ( ! vendor ) {
1787                         vendor = g_malloc(sizeof(radius_vendor_info_t));
1788
1789                         vendor->name = g_strdup_printf("%s-%u",
1790                                                        val_to_str_ext_const(vendor_id, &sminmpec_values_ext, "Unknown"),
1791                                                        vendor_id);
1792                         vendor->code = vendor_id;
1793                         vendor->attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
1794                         vendor->ett = no_vendor.ett;
1795
1796                         /* XXX: Default "standard" values: Should be parameters ?  */
1797                         vendor->type_octets   = 1;
1798                         vendor->length_octets = 1;
1799                         vendor->has_flags     = FALSE;
1800
1801                         g_hash_table_insert(dict->vendors_by_id,GUINT_TO_POINTER(vendor->code),vendor);
1802                         g_hash_table_insert(dict->vendors_by_name,(gpointer)(vendor->name),vendor);
1803                 }
1804
1805                 dictionary_entry = g_hash_table_lookup(vendor->attrs_by_id,GUINT_TO_POINTER(attribute_id));
1806                 by_id = vendor->attrs_by_id;
1807         } else {
1808                 dictionary_entry = g_hash_table_lookup(dict->attrs_by_id,GUINT_TO_POINTER(attribute_id));
1809                 by_id = dict->attrs_by_id;
1810         }
1811
1812         if (!dictionary_entry) {
1813                 dictionary_entry = g_malloc(sizeof(radius_attr_info_t));;
1814
1815                 dictionary_entry->name = g_strdup_printf("Unknown-Attribute-%u",attribute_id);
1816                 dictionary_entry->code = attribute_id;
1817                 dictionary_entry->encrypt = FALSE;
1818                 dictionary_entry->type = NULL;
1819                 dictionary_entry->vs = NULL;
1820                 dictionary_entry->hf = no_dictionary_entry.hf;
1821                 dictionary_entry->tagged = 0;
1822                 dictionary_entry->hf_tag = -1;
1823                 dictionary_entry->hf_len = no_dictionary_entry.hf_len;
1824                 dictionary_entry->ett = no_dictionary_entry.ett;
1825                 dictionary_entry->tlvs_by_id = NULL;
1826
1827                 g_hash_table_insert(by_id,GUINT_TO_POINTER(dictionary_entry->code),dictionary_entry);
1828         }
1829
1830         dictionary_entry->dissector = radius_avp_dissector;
1831
1832 }
1833
1834 /* Discard and init any state we've saved */
1835 static void
1836 radius_init_protocol(void)
1837 {
1838         if (radius_calls != NULL)
1839         {
1840                 g_hash_table_destroy(radius_calls);
1841                 radius_calls = NULL;
1842         }
1843
1844         radius_calls = g_hash_table_new(radius_call_hash, radius_call_equal);
1845 }
1846
1847 static void register_radius_fields(const char* unused _U_) {
1848          hf_register_info base_hf[] = {
1849                  { &hf_radius_req,
1850                  { "Request", "radius.req", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1851                          "TRUE if RADIUS request", HFILL }},
1852                  { &hf_radius_rsp,
1853                  { "Response", "radius.rsp", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1854                          "TRUE if RADIUS response", HFILL }},
1855                  { &hf_radius_req_frame,
1856                  { "Request Frame", "radius.reqframe", FT_FRAMENUM, BASE_NONE, NULL, 0,
1857                          NULL, HFILL }},
1858                  { &hf_radius_rsp_frame,
1859                  { "Response Frame", "radius.rspframe", FT_FRAMENUM, BASE_NONE, NULL, 0,
1860                          NULL, HFILL }},
1861                  { &hf_radius_time,
1862                  { "Time from request", "radius.time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0,
1863                          "Timedelta between Request and Response", HFILL }},
1864                  { &hf_radius_code,
1865                  { "Code","radius.code", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &radius_pkt_type_codes_ext, 0x0,
1866                          NULL, HFILL }},
1867                  { &hf_radius_id,
1868                  { "Identifier",        "radius.id", FT_UINT8, BASE_DEC, NULL, 0x0,
1869                          NULL, HFILL }},
1870                  { &hf_radius_authenticator,
1871                  { "Authenticator",     "radius.authenticator", FT_BYTES, BASE_NONE, NULL, 0x0,
1872                          NULL, HFILL }},
1873                  { &hf_radius_length,
1874                  { "Length","radius.length", FT_UINT16, BASE_DEC, NULL, 0x0,
1875                          NULL, HFILL }},
1876                  { &(no_dictionary_entry.hf),
1877                  { "Unknown-Attribute","radius.Unknown_Attribute", FT_BYTES, BASE_NONE, NULL, 0x0,
1878                          NULL, HFILL }},
1879                  { &(no_dictionary_entry.hf_len),
1880                  { "Unknown-Attribute Length","radius.Unknown_Attribute.length", FT_UINT8, BASE_DEC, NULL, 0x0,
1881                          NULL, HFILL }},
1882                  { &hf_radius_framed_ip_address,
1883                  { "Framed-IP-Address","radius.Framed-IP-Address", FT_IPv4, BASE_NONE, NULL, 0x0,
1884                          NULL, HFILL }},
1885                  { &hf_radius_login_ip_host,
1886                  { "Login-IP-Host","radius.Login-IP-Host", FT_IPv4, BASE_NONE, NULL, 0x0,
1887                          NULL, HFILL }},
1888                  { &hf_radius_framed_ipx_network,
1889                  { "Framed-IPX-Network","radius.Framed-IPX-Network", FT_IPXNET, BASE_NONE, NULL, 0x0,
1890                          NULL, HFILL }},
1891                  { &hf_radius_cosine_vpi,
1892                  { "Cosine-VPI","radius.Cosine-Vpi", FT_UINT16, BASE_DEC, NULL, 0x0,
1893                          NULL, HFILL }},
1894                  { &hf_radius_cosine_vci,
1895                  { "Cosine-VCI","radius.Cosine-Vci", FT_UINT16, BASE_DEC, NULL, 0x0,
1896                          NULL, HFILL }},
1897                  { &hf_radius_dup,
1898                  { "Duplicate Message", "radius.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
1899                          NULL, HFILL }},
1900                  { &hf_radius_req_dup,
1901                  { "Duplicate Request", "radius.req.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
1902                          NULL, HFILL }},
1903                  { &hf_radius_rsp_dup,
1904                  { "Duplicate Response", "radius.rsp.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
1905                          NULL, HFILL }},
1906                  { &hf_radius_ascend_data_filter,
1907                  { "Ascend Data Filter", "radius.ascenddatafilter", FT_BYTES, BASE_NONE, NULL, 0x0,
1908                          NULL, HFILL }}
1909          };
1910
1911          gint *base_ett[] = {
1912                  &ett_radius,
1913                  &ett_radius_avp,
1914                  &ett_eap,
1915                  &(no_dictionary_entry.ett),
1916                  &(no_vendor.ett),
1917          };
1918
1919          hfett_t ri;
1920          char* dir = NULL;
1921          gchar* dict_err_str = NULL;
1922
1923          ri.hf = g_array_new(FALSE,TRUE,sizeof(hf_register_info));
1924          ri.ett = g_array_new(FALSE,TRUE,sizeof(gint *));
1925          ri.vend_vs = g_array_new(TRUE,TRUE,sizeof(value_string));
1926
1927          g_array_append_vals(ri.hf, base_hf, array_length(base_hf));
1928          g_array_append_vals(ri.ett, base_ett, array_length(base_ett));
1929
1930          dir = get_persconffile_path("radius", FALSE, FALSE);
1931
1932          if (test_for_directory(dir) != EISDIR) {
1933                  /* Although dir isn't a directory it may still use memory */
1934                  g_free(dir);
1935
1936                  dir = get_datafile_path("radius");
1937
1938                  if (test_for_directory(dir) != EISDIR) {
1939                          g_free(dir);
1940                          dir = NULL;
1941                  }
1942          }
1943
1944         if (dir) {
1945                  radius_load_dictionary(dict,dir,"dictionary",&dict_err_str);
1946
1947                  if (dict_err_str) {
1948                         report_failure("radius: %s",dict_err_str);
1949                         g_free(dict_err_str);
1950                  }
1951
1952                  g_hash_table_foreach(dict->attrs_by_id,register_attrs,&ri);
1953                  g_hash_table_foreach(dict->vendors_by_id,register_vendors,&ri);
1954         }
1955
1956         g_free(dir);
1957
1958         proto_register_field_array(proto_radius,(hf_register_info*)g_array_data(ri.hf),ri.hf->len);
1959         proto_register_subtree_array((gint**)g_array_data(ri.ett), ri.ett->len);
1960
1961         g_array_free(ri.hf,FALSE);
1962         g_array_free(ri.ett,FALSE);
1963         g_array_free(ri.vend_vs,FALSE);
1964
1965         no_vendor.attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
1966
1967         /*
1968          * Handle attributes that have a special format.
1969          */
1970         radius_register_avp_dissector(0,8,dissect_framed_ip_address);
1971         radius_register_avp_dissector(0,14,dissect_login_ip_host);
1972         radius_register_avp_dissector(0,23,dissect_framed_ipx_network);
1973         radius_register_avp_dissector(VENDOR_COSINE,5,dissect_cosine_vpvc);
1974         /*
1975          * XXX - should we just call dissect_ascend_data_filter()
1976          * in radius_abinary()?
1977          *
1978          * Note that there is no attribute 242 in dictionary.redback.
1979          */
1980         radius_register_avp_dissector(VENDOR_ASCEND,242,dissect_ascend_data_filter);
1981         radius_register_avp_dissector(VENDOR_REDBACK,242,dissect_ascend_data_filter);
1982         radius_register_avp_dissector(0,242,dissect_ascend_data_filter);
1983
1984         /*
1985          * XXX - we should special-case Cisco attribute 252; see the comment in
1986          * dictionary.cisco.
1987          */
1988 }
1989
1990
1991 void
1992 proto_register_radius(void)
1993 {
1994         module_t *radius_module;
1995
1996         proto_radius = proto_register_protocol("Radius Protocol", "RADIUS", "radius");
1997         new_register_dissector("radius", dissect_radius, proto_radius);
1998         register_init_routine(&radius_init_protocol);
1999         radius_module = prefs_register_protocol(proto_radius, proto_reg_handoff_radius);
2000         prefs_register_string_preference(radius_module,"shared_secret","Shared Secret",
2001                                          "Shared secret used to decode User Passwords",
2002                                          &shared_secret);
2003         prefs_register_bool_preference(radius_module,"show_length","Show AVP Lengths",
2004                                        "Whether to add or not to the tree the AVP's payload length",
2005                                        &show_length);
2006         prefs_register_uint_preference(radius_module, "alternate_port","Alternate Port",
2007                                        "An alternate UDP port to decode as RADIUS", 10, &alt_port_pref);
2008         prefs_register_uint_preference(radius_module, "request_ttl", "Request TimeToLive",
2009                                        "Time to live for a radius request used for matching it with a response", 10, &request_ttl);
2010         radius_tap = register_tap("radius");
2011         proto_register_prefix("radius",register_radius_fields);
2012
2013         dict = g_malloc(sizeof(radius_dictionary_t));
2014         dict->attrs_by_id     = g_hash_table_new(g_direct_hash,g_direct_equal);
2015         dict->attrs_by_name   = g_hash_table_new(g_str_hash,g_str_equal);
2016         dict->vendors_by_id   = g_hash_table_new(g_direct_hash,g_direct_equal);
2017         dict->vendors_by_name = g_hash_table_new(g_str_hash,g_str_equal);
2018         dict->tlvs_by_name    = g_hash_table_new(g_str_hash,g_str_equal);
2019 }
2020
2021 void
2022 proto_reg_handoff_radius(void)
2023 {
2024         static gboolean initialized = FALSE;
2025         static dissector_handle_t radius_handle;
2026         static guint alt_port;
2027
2028         if (!initialized) {
2029                 radius_handle = find_dissector("radius");
2030                 dissector_add_uint("udp.port", UDP_PORT_RADIUS, radius_handle);
2031                 dissector_add_uint("udp.port", UDP_PORT_RADIUS_NEW, radius_handle);
2032                 dissector_add_uint("udp.port", UDP_PORT_RADACCT, radius_handle);
2033                 dissector_add_uint("udp.port", UDP_PORT_RADACCT_NEW, radius_handle);
2034                 dissector_add_uint("udp.port", UDP_PORT_DAE_OLD, radius_handle);
2035                 dissector_add_uint("udp.port", UDP_PORT_DAE, radius_handle);
2036
2037                 eap_handle = find_dissector("eap");
2038
2039                 initialized = TRUE;
2040         } else {
2041                 if (alt_port != 0)
2042                         dissector_delete_uint("udp.port", alt_port, radius_handle);
2043         }
2044
2045         if (alt_port_pref != 0)
2046                 dissector_add_uint("udp.port", alt_port_pref, radius_handle);
2047
2048         alt_port = alt_port_pref;
2049 }