http://www.wireshark.org/lists/wireshark-dev/200803/msg00308.html
[metze/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@gmail.com> - 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 <string.h>
55 #include <ctype.h>
56 #include <errno.h>
57 #include <epan/packet.h>
58 #include <epan/prefs.h>
59 #include <epan/report_err.h>
60 #include <epan/crypt/crypt-md5.h>
61 #include <epan/sminmpec.h>
62 #include <epan/filesystem.h>
63 #include <epan/conversation.h>
64 #include <epan/tap.h>
65 #include <epan/addr_resolv.h>
66 #include <epan/emem.h>
67 #include <epan/garrayfix.h>
68
69 #include "packet-radius.h"
70
71 typedef struct _e_radiushdr {
72         guint8 rh_code;
73         guint8 rh_ident;
74         guint16 rh_pktlength;
75 } e_radiushdr;
76
77 typedef struct {
78         GArray* hf;
79         GArray* ett;
80         GArray* vend_vs;
81 } hfett_t;
82
83 #define AUTHENTICATOR_LENGTH    16
84 #define RD_HDR_LENGTH           4
85 #define HDR_LENGTH              (RD_HDR_LENGTH + AUTHENTICATOR_LENGTH)
86
87 #define UDP_PORT_RADIUS         1645
88 #define UDP_PORT_RADIUS_NEW     1812
89 #define UDP_PORT_RADACCT        1646
90 #define UDP_PORT_RADACCT_NEW    1813
91
92 static radius_dictionary_t* dict = NULL;
93
94 static int proto_radius = -1;
95
96 static int hf_radius_req = -1;
97 static int hf_radius_rsp = -1;
98 static int hf_radius_req_frame = -1;
99 static int hf_radius_rsp_frame = -1;
100 static int hf_radius_time = -1;
101
102 static int hf_radius_dup = -1;
103 static int hf_radius_req_dup = -1;
104 static int hf_radius_rsp_dup = -1;
105
106 static int hf_radius_id = -1;
107 static int hf_radius_code = -1;
108 static int hf_radius_length = -1;
109 static int hf_radius_authenticator = -1;
110
111 static int hf_radius_framed_ip_address = -1;
112 static int hf_radius_login_ip_host = -1;
113 static int hf_radius_framed_ipx_network = -1;
114
115 static int hf_radius_cosine_vpi = -1;
116 static int hf_radius_cosine_vci = -1;
117
118 static gint ett_radius = -1;
119 static gint ett_radius_avp = -1;
120 static gint ett_eap = -1;
121
122 /*
123  * Define the tap for radius
124  */
125 static int radius_tap = -1;
126
127 radius_vendor_info_t no_vendor = {"Unknown Vendor",0,NULL,-1};
128
129 radius_attr_info_t no_dictionary_entry = {"Unknown-Attribute",0,FALSE,FALSE,radius_octets, NULL, NULL, -1, -1, -1, -1, -1 };
130
131 static dissector_handle_t eap_handle;
132 static dissector_handle_t radius_handle;
133
134 static const gchar* shared_secret = "";
135 static gboolean show_length = FALSE;
136 static guint alt_port = 0;
137 static guint alt_port_pref = 0;
138
139 static guint8 authenticator[AUTHENTICATOR_LENGTH];
140
141 static const value_string* radius_vendors = NULL;
142
143 /* http://www.iana.org/assignments/radius-types */
144 static const value_string radius_vals[] =
145 {
146         {RADIUS_ACCESS_REQUEST,                                 "Access-Request"},                                      /*  1 RFC2865 */
147         {RADIUS_ACCESS_ACCEPT,                                  "Access-Accept"},                                       /*  2 RFC2865 */
148         {RADIUS_ACCESS_REJECT,                                  "Access-Reject"},                                       /*  3 RFC2865 */
149         {RADIUS_ACCOUNTING_REQUEST,                             "Accounting-Request"},                          /*  4 RFC2865 */
150         {RADIUS_ACCOUNTING_RESPONSE,                    "Accounting-Response"},                         /*  5 RFC2865 */
151         {RADIUS_ACCOUNTING_STATUS,                              "Accounting-Status"},                           /*  6 RFC2865 */
152         {RADIUS_ACCESS_PASSWORD_REQUEST,                "Password-Request"},                            /*  7 RFC3575 */
153         {RADIUS_ACCESS_PASSWORD_ACK,                    "Password-Ack"},                                        /*  8 RFC3575 */
154         {RADIUS_ACCESS_PASSWORD_REJECT,                 "Password-Reject"},                                     /*  9 RFC3575 */
155         {RADIUS_ACCOUNTING_MESSAGE,                             "Accounting-Message"},                          /* 10 RFC3575 */
156         {RADIUS_ACCESS_CHALLENGE,                               "Access-challenge"},                            /* 11 RFC2865 */
157         {RADIUS_STATUS_SERVER,                                  "Status-Server"},                                       /* 12 RFC2865 */
158         {RADIUS_STATUS_CLIENT,                                  "Status-Client"},                                       /* 13 RFC2865 */
159 /*
160 21       Resource-Free-Request        [RFC3575]
161 22       Resource-Free-Response       [RFC3575]
162 23       Resource-Query-Request       [RFC3575]
163 24       Resource-Query-Response      [RFC3575]
164 25       Alternate-Resource-
165          Reclaim-Request              [RFC3575]
166 26       NAS-Reboot-Request           [RFC3575]
167 */
168         {RADIUS_VENDOR_SPECIFIC_CODE,                   "Vendor-Specific"},                                     /* 26 */
169 /*
170 27       NAS-Reboot-Response          [RFC3575]
171 28       Reserved
172 */
173         {RADIUS_ASCEND_ACCESS_NEXT_CODE,                "Next-Passcode"},                                       /* 29 RFC3575 */
174         {RADIUS_ASCEND_ACCESS_NEW_PIN,                  "New-Pin"},                                                     /* 30 RFC3575 */
175         {31,                                                                    "Terminate-Session"},                           /* 31 RFC3575 */
176         {RADIUS_ASCEND_PASSWORD_EXPIRED,                "Password-Expired"},                            /* 32 RFC3575 */
177         {RADIUS_ASCEND_ACCESS_EVENT_REQUEST,    "Event-Request"},                                       /* 33 RFC3575 */
178         {RADIUS_ASCEND_ACCESS_EVENT_RESPONSE,   "Event-Response"},                                      /* 34 RFC3575 */
179         {RADIUS_DISCONNECT_REQUEST,                             "Disconnect-Request"},                          /* 40 RFC3575 */
180         {RADIUS_DISCONNECT_REQUEST_ACK,                 "Disconnect-ACK"},                                      /* 41 RFC3575 */
181         {RADIUS_DISCONNECT_REQUEST_NAK,                 "Disconnect-NAK"}               ,                       /* 42 RFC3575 */
182         {RADIUS_CHANGE_FILTER_REQUEST,                  "CoA-Request"},                                         /* 43 */
183         {RADIUS_CHANGE_FILTER_REQUEST_ACK,              "CoA-ACK"},                                                     /* 44 */ 
184         {RADIUS_CHANGE_FILTER_REQUEST_NAK,              "CoA-NAK"},                                                     /* 45 */ 
185 /*
186 50       IP-Address-Allocate          [RFC3575]
187 51       IP-Address-Release           [RFC3575]
188 250-253  Experimental Use             [RFC3575]
189 254      Reserved                     [RFC3575]
190 */
191         {RADIUS_RESERVED,                                               "Reserved"},
192         {0, NULL}
193 };
194
195 /*
196  * Init Hash table stuff for converation
197  */
198
199 typedef struct _radius_call_info_key
200 {
201         guint code;
202         guint ident;
203         conversation_t *conversation;
204         nstime_t req_time;
205 } radius_call_info_key;
206
207 static GMemChunk *radius_call_info_key_chunk;
208 static GMemChunk *radius_call_info_value_chunk;
209 static GHashTable *radius_calls;
210
211 /* Compare 2 keys */
212 static gint radius_call_equal(gconstpointer k1, gconstpointer k2)
213 {
214         const radius_call_info_key* key1 = (const radius_call_info_key*) k1;
215         const radius_call_info_key* key2 = (const radius_call_info_key*) k2;
216
217         if (key1->ident == key2->ident && key1->conversation == key2->conversation) {
218                 nstime_t delta;
219
220                 nstime_delta(&delta, &key1->req_time, &key2->req_time);
221                 if (abs( (int) nstime_to_sec(&delta)) > (double) 5) return 0;
222
223                 if (key1->code == key2->code)
224                         return 1;
225                 /* check the request and response are of the same code type */
226                 if (key1->code == RADIUS_ACCESS_REQUEST && ( key2->code == RADIUS_ACCESS_ACCEPT || key2->code == RADIUS_ACCESS_REJECT ) )
227                         return 1;
228
229                 if (key1->code == RADIUS_ACCOUNTING_REQUEST && key2->code == RADIUS_ACCOUNTING_RESPONSE )
230                         return 1;
231
232                 if (key1->code == RADIUS_ACCESS_PASSWORD_REQUEST && ( key2->code == RADIUS_ACCESS_PASSWORD_ACK || key2->code == RADIUS_ACCESS_PASSWORD_REJECT ) )
233                         return 1;
234
235                 if (key1->code == RADIUS_ASCEND_ACCESS_EVENT_REQUEST && key2->code == RADIUS_ASCEND_ACCESS_EVENT_RESPONSE )
236                         return 1;
237
238                 if (key1->code == RADIUS_DISCONNECT_REQUEST && ( key2->code == RADIUS_DISCONNECT_REQUEST_ACK || key2->code == RADIUS_DISCONNECT_REQUEST_NAK ) )
239                         return 1;
240
241                 if (key1->code == RADIUS_CHANGE_FILTER_REQUEST && ( key2->code == RADIUS_CHANGE_FILTER_REQUEST_ACK || key2->code == RADIUS_CHANGE_FILTER_REQUEST_NAK ) )
242                         return 1;
243         }
244         return 0;
245 }
246
247 /* Calculate a hash key */
248 static guint radius_call_hash(gconstpointer k)
249 {
250         const radius_call_info_key* key = (const radius_call_info_key*) k;
251
252         return key->ident + /*key->code + */ key->conversation->index;
253 }
254
255
256 static const gchar *dissect_framed_ip_address(proto_tree* tree, tvbuff_t* tvb) {
257         int len;
258         guint32 ip;
259         guint32 ip_h;
260         const gchar *str;
261
262         len = tvb_length(tvb);
263         if (len != 4)
264                 return "[wrong length for IP address]";
265
266         ip=tvb_get_ipv4(tvb,0);
267         ip_h=g_ntohl(ip);
268
269         if (ip_h == 0xFFFFFFFF) {
270                 str = "Negotiated";
271                 proto_tree_add_ipv4_format(tree, hf_radius_framed_ip_address,
272                     tvb, 0, len, ip, "Framed-IP-Address: %s", str);
273         } else if (ip_h == 0xFFFFFFFE) {
274                 str = "Assigned";
275                 proto_tree_add_ipv4_format(tree, hf_radius_framed_ip_address,
276                     tvb, 0, len, ip, "Framed-IP-Address: %s", str);
277         } else {
278                 str = ip_to_str((guint8 *)&ip);
279                 proto_tree_add_ipv4_format(tree, hf_radius_framed_ip_address,
280                     tvb, 0, len, ip, "Framed-IP-Address: %s (%s)",
281                     get_hostname(ip), str);
282         }
283
284         return str;
285 }
286
287 static const gchar *dissect_login_ip_host(proto_tree* tree, tvbuff_t* tvb) {
288         int len;
289         guint32 ip;
290         guint32 ip_h;
291         const gchar *str;
292
293         len = tvb_length(tvb);
294         if (len != 4)
295                 return "[wrong length for IP address]";
296
297         ip=tvb_get_ipv4(tvb,0);
298         ip_h=g_ntohl(ip);
299
300         if (ip_h == 0xFFFFFFFF) {
301                 str = "User-selected";
302                 proto_tree_add_ipv4_format(tree, hf_radius_login_ip_host,
303                     tvb, 0, len, ip, "Login-IP-Host: %s", str);
304         } else if (ip_h == 0) {
305                 str = "NAS-selected";
306                 proto_tree_add_ipv4_format(tree, hf_radius_login_ip_host,
307                     tvb, 0, len, ip, "Login-IP-Host: %s", str);
308         } else {
309                 str = ip_to_str((guint8 *)&ip);
310                 proto_tree_add_ipv4_format(tree, hf_radius_framed_ip_address,
311                     tvb, 0, len, ip, "Login-IP-Host: %s (%s)",
312                     get_hostname(ip), str);
313         }
314
315         return str;
316 }
317
318 static const gchar *dissect_framed_ipx_network(proto_tree* tree, tvbuff_t* tvb) {
319         int len;
320         guint32 net;
321         const gchar *str;
322
323         len = tvb_length(tvb);
324         if (len != 4)
325                 return "[wrong length for IPX network]";
326
327         net=tvb_get_ntohl(tvb,0);
328
329         if (net == 0xFFFFFFFE)
330                 str = "NAS-selected";
331         else
332                 str = ep_strdup_printf("0x%08X", net);
333         proto_tree_add_ipxnet_format(tree, hf_radius_framed_ipx_network, tvb, 0,
334             len, net, "Framed-IPX-Network: %s", str);
335
336         return str;
337 }
338
339 static const gchar* dissect_cosine_vpvc(proto_tree* tree, tvbuff_t* tvb) {
340         guint vpi, vci;
341
342         if ( tvb_length(tvb) != 4 )
343                 return "[Wrong Length for VP/VC AVP]";
344
345         vpi = tvb_get_ntohs(tvb,0);
346         vci = tvb_get_ntohs(tvb,2);
347
348         proto_tree_add_uint(tree,hf_radius_cosine_vpi,tvb,0,2,vpi);
349         proto_tree_add_uint(tree,hf_radius_cosine_vci,tvb,2,2,vci);
350
351         return ep_strdup_printf("%u/%u",vpi,vci);
352 }
353
354 static void
355 radius_decrypt_avp(gchar *dest,int dest_len,tvbuff_t *tvb,int offset,int length)
356 {
357     md5_state_t md_ctx;
358     md5_byte_t digest[16];
359     int i;
360     size_t totlen, returned_length;
361     const guint8 *pd;
362     guchar c;
363
364     DISSECTOR_ASSERT(dest_len > 2);  /* \"\"\0 */
365     dest[0] = '"';
366     dest[1] = '\0';
367     totlen = 1;
368     dest_len -= 1; /* Need to add trailing \" */
369
370     md5_init(&md_ctx);
371     md5_append(&md_ctx,(const guint8*)shared_secret,strlen(shared_secret));
372     md5_append(&md_ctx,authenticator, AUTHENTICATOR_LENGTH);
373     md5_finish(&md_ctx,digest);
374
375     pd = tvb_get_ptr(tvb,offset,length);
376     for( i = 0 ; i < AUTHENTICATOR_LENGTH && i < length ; i++ ) {
377                 c = pd[i] ^ digest[i];
378                 if ( isprint(c) ) {
379                         returned_length = g_snprintf(&dest[totlen], dest_len-totlen,
380                                 "%c",c);
381                         totlen += MIN(returned_length, dest_len-totlen-1);
382                 } else {
383                         returned_length = g_snprintf(&dest[totlen], dest_len-totlen,
384                                 "\\%03o",c);
385                         totlen += MIN(returned_length, dest_len-totlen-1);
386                 }
387     }
388     while(i<length) {
389                 if ( isprint(pd[i]) ) {
390                         returned_length = g_snprintf(&dest[totlen], dest_len-totlen,
391                                 "%c", pd[i]);
392                         totlen += MIN(returned_length, dest_len-totlen-1);
393                 } else {
394                         returned_length = g_snprintf(&dest[totlen], dest_len-totlen,
395                                 "\\%03o", pd[i]);
396                         totlen += MIN(returned_length, dest_len-totlen-1);
397                 }
398                 i++;
399     }
400     g_snprintf(&dest[totlen], dest_len+1-totlen, "%c", '"');
401 }
402
403
404 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) {
405         guint32 uint;
406
407         switch (len) {
408                 case 2:
409                         uint = tvb_get_ntohs(tvb,offset);
410                         break;
411                 case 3:
412                         uint = tvb_get_ntoh24(tvb,offset);
413                         break;
414                 case 4:
415                         uint = tvb_get_ntohl(tvb,offset);
416                         break;
417                 case 8: {
418                         guint64 uint64 = tvb_get_ntoh64(tvb,offset);
419                         proto_tree_add_uint64(tree,a->hf64,tvb,offset,len,uint64);
420                         proto_item_append_text(avp_item, "%" G_GINT64_MODIFIER "u", uint64);
421                         return;
422                 }
423                 default:
424                         proto_item_append_text(avp_item, "[unhandled integer length(%u)]", len);
425                         return;
426         }
427
428         proto_tree_add_uint(tree,a->hf,tvb,offset,len,uint);
429
430         if (a->vs) {
431                 proto_item_append_text(avp_item, "%s(%u)", val_to_str(uint, a->vs, "Unknown"),uint);
432         } else {
433                 proto_item_append_text(avp_item, "%u", uint);
434         }
435 }
436
437 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) {
438         if (a->encrypt) {
439                 if (*shared_secret == '\0') {
440                         proto_item_append_text(avp_item, "Encrypted");
441                         proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
442                 } else {
443                         gchar *buffer;
444                         buffer=ep_alloc(1024); /* an AVP value can be at most 253 bytes */
445                         radius_decrypt_avp(buffer,1024,tvb,offset,len);
446                         proto_item_append_text(avp_item, "Decrypted: %s", buffer);
447                         proto_tree_add_string(tree, a->hf, tvb, offset, len, buffer);
448                 }
449         } else {
450                 proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
451                 proto_item_append_text(avp_item, "%s", tvb_format_text(tvb, offset, len));
452         }
453 }
454
455 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) {
456         proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
457         proto_item_append_text(avp_item, "%s", tvb_bytes_to_str(tvb, offset, len));
458 }
459
460 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) {
461         guint32 ip;
462         gchar buf[MAX_IP_STR_LEN];
463
464         if (len != 4) {
465                 proto_item_append_text(avp_item, "[wrong length for IP address]");
466                 return;
467         }
468
469         ip=tvb_get_ipv4(tvb,offset);
470
471         proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
472
473         ip_to_str_buf((guint8 *)&ip, buf, MAX_IP_STR_LEN);
474         proto_item_append_text(avp_item, "%s", buf);
475 }
476
477 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) {
478         struct e_in6_addr ipv6_buff;
479         gchar txtbuf[256];
480
481         if (len != 16) {
482                 proto_item_append_text(avp_item, "[wrong length for IPv6 address]");
483                 return;
484         }
485
486         proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
487
488         tvb_get_ipv6(tvb, offset, &ipv6_buff);
489         ip6_to_str_buf(&ipv6_buff, txtbuf);
490         proto_item_append_text(avp_item, "%s", txtbuf);
491 }
492
493 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) {
494         guint32 net;
495
496         if (len != 4) {
497                 proto_item_append_text(avp_item, "[wrong length for IPX network]");
498                 return;
499         }
500
501         net=tvb_get_ntohl(tvb,offset);
502
503         proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
504
505         proto_item_append_text(avp_item, "0x%08X", net);
506 }
507
508 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) {
509         nstime_t time_ptr;
510
511         if (len != 4) {
512                 proto_item_append_text(avp_item, "[wrong length for timestamp]");
513                 return;
514         }
515         time_ptr.secs = tvb_get_ntohl(tvb,offset);
516         time_ptr.nsecs = 0;
517
518         proto_tree_add_time(tree, a->hf, tvb, offset, len, &time_ptr);
519         proto_item_append_text(avp_item, "%s", abs_time_to_str(&time_ptr));
520 }
521
522 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) {
523         proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
524         proto_item_append_text(avp_item, "%s", tvb_bytes_to_str(tvb, offset, len));
525 }
526
527 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) {
528         proto_tree_add_item(tree, a->hf, tvb, offset, len, FALSE);
529         proto_item_append_text(avp_item, "%s", tvb_bytes_to_str(tvb, offset, len));
530 }
531
532 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) {
533     proto_item* pi;
534
535     if (dictionary_entry->tagged) {
536         guint tag;
537
538         if (avp_length == 0) {
539             pi = proto_tree_add_text(avp_tree, tvb, offset,
540                                 0, "AVP too short for tag");
541             PROTO_ITEM_SET_GENERATED(pi);
542             return;
543         }
544
545         tag = tvb_get_guint8(tvb, offset);
546
547         if (tag <=  0x1f) {
548             proto_tree_add_uint(avp_tree,
549                                 dictionary_entry->hf_tag,
550                                 tvb, offset, 1, tag);
551
552             proto_item_append_text(avp_item,
553                                    " Tag=0x%.2x", tag);
554
555             offset++;
556             avp_length--;
557         }
558     }
559
560     if ( dictionary_entry->dissector ) {
561         tvbuff_t* tvb_value;
562         const gchar* str;
563
564         tvb_value = tvb_new_subset(tvb, offset, avp_length, (gint) avp_length);
565
566         str = dictionary_entry->dissector(avp_tree,tvb_value);
567
568         proto_item_append_text(avp_item, ": %s",str);
569     } else {
570         proto_item_append_text(avp_item, ": ");
571
572         dictionary_entry->type(dictionary_entry,avp_tree,pinfo,tvb,offset,avp_length,avp_item);
573     }
574 }
575
576 static void dissect_attribute_value_pairs(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, int offset, guint length) {
577     proto_item* item;
578     gboolean last_eap = FALSE;
579     guint8* eap_buffer = NULL;
580     guint eap_seg_num = 0;
581     guint eap_tot_len_captured = 0;
582     guint eap_tot_len = 0;
583     proto_tree* eap_tree = NULL;
584     tvbuff_t* eap_tvb = NULL;
585
586     /*
587      * In case we throw an exception, clean up whatever stuff we've
588      * allocated (if any).
589      */
590     CLEANUP_PUSH(g_free, eap_buffer);
591
592     while (length > 0) {
593         radius_attr_info_t* dictionary_entry = NULL;
594         gint tvb_len;
595         guint32 avp_type;
596         guint32 avp_length;
597         guint32 vendor_id;
598
599         proto_item* avp_item;
600         proto_item* avp_len_item;
601         proto_tree* avp_tree;
602
603         if (length < 2) {
604             item = proto_tree_add_text(tree, tvb, offset, 0,
605                         "Not enough room in packet for AVP header");
606             PROTO_ITEM_SET_GENERATED(item);
607             return;
608         }
609         avp_type = tvb_get_guint8(tvb,offset);
610         avp_length = tvb_get_guint8(tvb,offset+1);
611
612         if (avp_length < 2) {
613             item = proto_tree_add_text(tree, tvb, offset, 0,
614                         "AVP too short: length %u < 2", avp_length);
615             PROTO_ITEM_SET_GENERATED(item);
616             return;
617         }
618
619         if (length < avp_length) {
620             item = proto_tree_add_text(tree, tvb, offset, 0,
621                         "Not enough room in packet for AVP");
622             PROTO_ITEM_SET_GENERATED(item);
623             return;
624         }
625
626         length -= avp_length;
627
628         dictionary_entry = g_hash_table_lookup(dict->attrs_by_id,GUINT_TO_POINTER(avp_type));
629
630         if (! dictionary_entry ) {
631             dictionary_entry = &no_dictionary_entry;
632         }
633
634         avp_item = proto_tree_add_text(tree, tvb, offset, avp_length,
635                                        "AVP: l=%u  t=%s(%u)", avp_length,
636                                        dictionary_entry->name, avp_type);
637
638         avp_length -= 2;
639         offset += 2;
640
641         if (avp_type == RADIUS_VENDOR_SPECIFIC_CODE) {
642             radius_vendor_info_t* vendor;
643             proto_tree* vendor_tree;
644             gint max_offset = offset + avp_length;
645             const gchar* vendor_str;
646
647             /* XXX TODO: handle 2 byte codes for USR */
648
649             if (avp_length < 4) {
650                 proto_item_append_text(avp_item, " [AVP too short; no room for vendor ID]");
651                 offset += avp_length;
652                 continue;
653             }
654             vendor_id = tvb_get_ntohl(tvb,offset);
655
656             avp_length -= 4;
657             offset += 4;
658
659             vendor = g_hash_table_lookup(dict->vendors_by_id,GUINT_TO_POINTER(vendor_id));
660             if (vendor) {
661                 vendor_str = vendor->name;
662             } else {
663                 vendor_str = val_to_str(vendor_id, sminmpec_values, "Unknown");
664                 vendor = &no_vendor;
665             }
666             proto_item_append_text(avp_item, " v=%s(%u)", vendor_str,
667                                    vendor_id);
668
669             vendor_tree = proto_item_add_subtree(avp_item,vendor->ett);
670
671             while (offset < max_offset) {
672                 guint32 avp_vsa_type = tvb_get_guint8(tvb,offset++);
673                 guint32 avp_vsa_len = tvb_get_guint8(tvb,offset++);
674
675
676                 if (avp_vsa_len < 2) {
677                     proto_tree_add_text(tree, tvb, offset+1, 1,
678                                             "[VSA too short]");
679                     return;
680                 }
681
682                 avp_vsa_len -= 2;
683
684                 dictionary_entry = g_hash_table_lookup(vendor->attrs_by_id,GUINT_TO_POINTER(avp_vsa_type));
685
686                 if ( !dictionary_entry ) {
687                     dictionary_entry = &no_dictionary_entry;
688                 }
689
690                 avp_item = proto_tree_add_text(vendor_tree,tvb,offset-2,avp_vsa_len+2,
691                                                "VSA: l=%u t=%s(%u)",
692                                                avp_vsa_len+2, dictionary_entry->name, avp_vsa_type);
693
694                 avp_tree = proto_item_add_subtree(avp_item,dictionary_entry->ett);
695
696                 if (show_length) {
697                     avp_len_item = proto_tree_add_uint(avp_tree,
698                                                        dictionary_entry->hf_len,
699                                                        tvb,0,0,avp_length);
700                     PROTO_ITEM_SET_GENERATED(avp_len_item);
701                 }
702
703                 add_avp_to_tree(avp_tree, avp_item, pinfo, tvb, dictionary_entry, avp_vsa_len, offset);
704
705                 offset += avp_vsa_len;
706             };
707             continue;
708         }
709
710         avp_tree = proto_item_add_subtree(avp_item,dictionary_entry->ett);
711
712         if (show_length) {
713             avp_len_item = proto_tree_add_uint(avp_tree,
714                                                dictionary_entry->hf_len,
715                                                tvb,0,0,avp_length);
716             PROTO_ITEM_SET_GENERATED(avp_len_item);
717         }
718
719         tvb_len = tvb_length_remaining(tvb, offset);
720
721         if ((gint)avp_length < tvb_len)
722             tvb_len = avp_length;
723
724         if (avp_type == RADIUS_EAP_MESSAGE_CODE) {
725             eap_seg_num++;
726
727             /* Show this as an EAP fragment. */
728             if (tree)
729                 proto_tree_add_text(avp_tree, tvb, offset, tvb_len,
730                                     "EAP fragment");
731
732             if (eap_tvb != NULL) {
733                 /*
734                  * Oops, a non-consecutive EAP-Message
735                  * attribute.
736                  */
737                 proto_item_append_text(avp_item, " (non-consecutive)");
738             } else {
739                 /*
740                  * RFC 2869 says, in section 5.13, describing
741                  * the EAP-Message attribute:
742                  *
743                  *    The NAS places EAP messages received
744                  *    from the authenticating peer into one
745                  *    or more EAP-Message attributes and
746                  *    forwards them to the RADIUS Server
747                  *    within an Access-Request message.
748                  *    If multiple EAP-Messages are
749                  *    contained within an Access-Request or
750                  *    Access-Challenge packet, they MUST be
751                  *    in order and they MUST be consecutive
752                  *    attributes in the Access-Request or
753                  *    Access-Challenge packet.
754                  *
755                  *        ...
756                  *
757                  *    The String field contains EAP packets,
758                  *    as defined in [3].  If multiple
759                  *    EAP-Message attributes are present
760                  *    in a packet their values should be
761                  *    concatenated; this allows EAP packets
762                  *    longer than 253 octets to be passed
763                  *    by RADIUS.
764                  *
765                  * Do reassembly of EAP-Message attributes.
766                  * We just concatenate all the attributes,
767                  * and when we see either the end of the
768                  * attribute list or a non-EAP-Message
769                  * attribute, we know we're done.
770                  */
771
772                 if (eap_buffer == NULL)
773                     eap_buffer = g_malloc(eap_tot_len_captured + tvb_len);
774                 else
775                     eap_buffer = g_realloc(eap_buffer,
776                                            eap_tot_len_captured + tvb_len);
777                 tvb_memcpy(tvb, eap_buffer + eap_tot_len_captured, offset,
778                            tvb_len);
779                 eap_tot_len_captured += tvb_len;
780                 eap_tot_len += avp_length;
781
782                 if ( tvb_bytes_exist(tvb, offset + avp_length + 1, 1) ) {
783                     guint8 next_type = tvb_get_guint8(tvb, offset + avp_length);
784
785                     if ( next_type != RADIUS_EAP_MESSAGE_CODE ) {
786                         /* Non-EAP-Message attribute */
787                         last_eap = TRUE;
788                     }
789                 } else {
790                     /*
791                      * No more attributes, either because
792                      * we're at the end of the packet or
793                      * because we're at the end of the
794                      * captured packet data.
795                      */
796                     last_eap = TRUE;
797                 }
798
799                 if (last_eap && eap_buffer) {
800                     gboolean save_writable;
801
802                     proto_item_append_text(avp_item, " Last Segment[%u]",
803                                            eap_seg_num);
804
805                     eap_tree = proto_item_add_subtree(avp_item,ett_eap);
806
807                     eap_tvb = tvb_new_real_data(eap_buffer,
808                                                 eap_tot_len_captured,
809                                                 eap_tot_len);
810                     tvb_set_free_cb(eap_tvb, g_free);
811                     tvb_set_child_real_data_tvbuff(tvb, eap_tvb);
812                     add_new_data_source(pinfo, eap_tvb, "Reassembled EAP");
813
814                     /*
815                      * Don't free this when we're done -
816                      * it's associated with a tvbuff.
817                      */
818                     eap_buffer = NULL;
819
820                     /*
821                      * Set the columns non-writable,
822                      * so that the packet list shows
823                      * this as an RADIUS packet, not
824                      * as an EAP packet.
825                      */
826                     save_writable = col_get_writable(pinfo->cinfo);
827                     col_set_writable(pinfo->cinfo, FALSE);
828
829                     call_dissector(eap_handle, eap_tvb, pinfo, eap_tree);
830
831                     col_set_writable(pinfo->cinfo, save_writable);
832                 } else {
833                     proto_item_append_text(avp_item, " Segment[%u]",
834                                            eap_seg_num);
835                 }
836             }
837
838             offset += avp_length;
839         } else {
840             add_avp_to_tree(avp_tree, avp_item, pinfo, tvb, dictionary_entry,
841                             avp_length, offset);
842             offset += avp_length;
843         }
844
845     }
846
847     /*
848      * Call the cleanup handler to free any reassembled data we haven't
849      * attached to a tvbuff, and pop the handler.
850      */
851     CLEANUP_CALL_AND_POP;
852 }
853
854 /* This function tries to determine whether a packet is radius or not */
855 static gboolean
856 is_radius(tvbuff_t *tvb)
857 {
858         guint8 code;
859         guint16 length;
860
861         code=tvb_get_guint8(tvb, 0);
862         switch(code){
863         case RADIUS_ACCESS_REQUEST:
864         case RADIUS_ACCESS_ACCEPT:
865         case RADIUS_ACCESS_REJECT:
866         case RADIUS_ACCOUNTING_REQUEST:
867         case RADIUS_ACCOUNTING_RESPONSE:
868         case RADIUS_ACCOUNTING_STATUS:
869         case RADIUS_ACCESS_PASSWORD_REQUEST:
870         case RADIUS_ACCESS_PASSWORD_ACK:
871         case RADIUS_ACCESS_PASSWORD_REJECT:
872         case RADIUS_ACCOUNTING_MESSAGE:
873         case RADIUS_ACCESS_CHALLENGE:
874         case RADIUS_STATUS_SERVER:
875         case RADIUS_STATUS_CLIENT:
876         case RADIUS_VENDOR_SPECIFIC_CODE:
877         case RADIUS_ASCEND_ACCESS_NEXT_CODE:
878         case RADIUS_ASCEND_ACCESS_NEW_PIN:
879         case RADIUS_ASCEND_PASSWORD_EXPIRED:
880         case RADIUS_ASCEND_ACCESS_EVENT_REQUEST:
881         case RADIUS_ASCEND_ACCESS_EVENT_RESPONSE:
882         case RADIUS_DISCONNECT_REQUEST:
883         case RADIUS_DISCONNECT_REQUEST_ACK:
884         case RADIUS_DISCONNECT_REQUEST_NAK:
885         case RADIUS_CHANGE_FILTER_REQUEST:
886         case RADIUS_CHANGE_FILTER_REQUEST_ACK:
887         case RADIUS_CHANGE_FILTER_REQUEST_NAK:
888         case RADIUS_EAP_MESSAGE_CODE:
889         case RADIUS_MESSAGE_AUTHENTICATOR:
890                 break;
891         default:
892                 return FALSE;
893         }
894
895         /* Check for valid length value:
896          * Length
897          *
898          *  The Length field is two octets.  It indicates the length of the
899          *  packet including the Code, Identifier, Length, Authenticator and
900          *  Attribute fields.  Octets outside the range of the Length field
901          *  MUST be treated as padding and ignored on reception.  If the
902          *  packet is shorter than the Length field indicates, it MUST be
903          *  silently discarded.  The minimum length is 20 and maximum length
904          *  is 4096.
905          */
906         length=tvb_get_ntohs(tvb, 2);
907         if ( (length<20) || (length>4096) ) {
908                 return FALSE;
909         }
910
911         return TRUE;
912 }
913
914 static void register_radius_fields(const char*);
915
916 static int
917 dissect_radius(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree)
918 {
919         proto_tree *radius_tree = NULL;
920         proto_tree *avptree = NULL;
921         proto_item *ti;
922         proto_item *avptf;
923         guint avplength;
924         e_radiushdr rh;
925         radius_info_t *rad_info;
926         
927
928         conversation_t* conversation;
929         radius_call_info_key radius_call_key;
930         radius_call_info_key *new_radius_call_key = NULL;
931         radius_call_t *radius_call = NULL;
932         nstime_t delta;
933         static address null_address = { AT_NONE, 0, NULL };
934         
935
936         /* does this look like radius ? */
937         if(!is_radius(tvb)){
938                 return 0;
939         }
940         
941
942         if (check_col(pinfo->cinfo, COL_PROTOCOL))
943                 col_set_str(pinfo->cinfo, COL_PROTOCOL, "RADIUS");
944         if (check_col(pinfo->cinfo, COL_INFO))
945                 col_clear(pinfo->cinfo, COL_INFO);
946
947         rh.rh_code=tvb_get_guint8(tvb,0);
948         rh.rh_ident=tvb_get_guint8(tvb,1);
949         rh.rh_pktlength=tvb_get_ntohs(tvb,2);
950
951
952         /* Initialise stat info for passing to tap */
953         rad_info = ep_alloc(sizeof(radius_info_t));
954         rad_info->code = 0;
955         rad_info->ident = 0;
956         rad_info->req_time.secs = 0;
957         rad_info->req_time.nsecs = 0;
958         rad_info->is_duplicate = FALSE;
959         rad_info->request_available = FALSE;
960         rad_info->req_num = 0; /* frame number request seen */
961         rad_info->rspcode = 0;
962         /* tap stat info */
963         rad_info->code = rh.rh_code;
964         rad_info->ident = rh.rh_ident;
965         tap_queue_packet(radius_tap, pinfo, rad_info);
966
967
968         if (check_col(pinfo->cinfo, COL_INFO))
969         {
970                 col_add_fstr(pinfo->cinfo,COL_INFO,"%s(%d) (id=%d, l=%d)",
971                              val_to_str(rh.rh_code,radius_vals,"Unknown Packet"),
972                              rh.rh_code, rh.rh_ident, rh.rh_pktlength);
973         }
974
975
976         if (tree)
977         {
978                 if(!radius_vendors) register_radius_fields(NULL);
979
980                 ti = proto_tree_add_item(tree,proto_radius, tvb, 0, rh.rh_pktlength, FALSE);
981
982                 radius_tree = proto_item_add_subtree(ti, ett_radius);
983
984                 proto_tree_add_uint(radius_tree,hf_radius_code, tvb, 0, 1, rh.rh_code);
985
986                 proto_tree_add_uint_format(radius_tree,hf_radius_id, tvb, 1, 1, rh.rh_ident,
987                                                                    "Packet identifier: 0x%01x (%d)", rh.rh_ident, rh.rh_ident);
988         }
989
990         /*
991          * Make sure the length is sane.
992          */
993         if (rh.rh_pktlength < HDR_LENGTH)
994         {
995                 if (tree)
996                 {
997                         proto_tree_add_uint_format(radius_tree, hf_radius_length,
998                                                    tvb, 2, 2, rh.rh_pktlength,
999                                                    "Length: %u (bogus, < %u)",
1000                                                    rh.rh_pktlength, HDR_LENGTH);
1001                 }
1002                 goto end_of_radius;
1003         }
1004         avplength = rh.rh_pktlength - HDR_LENGTH;
1005         if (tree)
1006         {
1007                 proto_tree_add_uint(radius_tree, hf_radius_length, tvb,
1008                                     2, 2, rh.rh_pktlength);
1009
1010                 proto_tree_add_item(radius_tree, hf_radius_authenticator, tvb, 4,AUTHENTICATOR_LENGTH,FALSE);
1011         }
1012         tvb_memcpy(tvb,authenticator,4,AUTHENTICATOR_LENGTH);
1013
1014         if (tree) {
1015
1016                 /* Conversation support REQUEST/RESPONSES */
1017                 switch (rh.rh_code)
1018                 {
1019                         case RADIUS_ACCESS_REQUEST:
1020                         case RADIUS_ACCOUNTING_REQUEST:
1021                         case RADIUS_ACCESS_PASSWORD_REQUEST:
1022                         case RADIUS_ASCEND_ACCESS_EVENT_REQUEST:
1023                         case RADIUS_DISCONNECT_REQUEST:
1024                         case RADIUS_CHANGE_FILTER_REQUEST:
1025                                 proto_tree_add_boolean_hidden(radius_tree, hf_radius_req, tvb, 0, 0, TRUE);
1026                                 /* Keep track of the address and port whence the call came
1027                                  *  so that we can match up requests with replies.
1028                                  *
1029                                  * Because it is UDP and the reply can come from any IP
1030                                  * and port (not necessarly the request dest), we only
1031                                  * track the source IP and port of the request to match
1032                                  * the reply.
1033                                  */
1034
1035                                 /*
1036                                  * XXX - can we just use NO_ADDR_B?  Unfortunately,
1037                                  * you currently still have to pass a non-null
1038                                  * pointer for the second address argument even
1039                                  * if you do that.
1040                                  */
1041                                 conversation = find_conversation(pinfo->fd->num, &pinfo->src,
1042                                                                          &null_address, pinfo->ptype, pinfo->srcport,
1043                                                                          pinfo->destport, 0);
1044                                 if (conversation == NULL)
1045                                 {
1046                                         /* It's not part of any conversation - create a new one. */
1047                                         conversation = conversation_new(pinfo->fd->num, &pinfo->src,
1048                                                                         &null_address, pinfo->ptype, pinfo->srcport,
1049                                                                         pinfo->destport, 0);
1050                                 }
1051
1052                                 /* Prepare the key data */
1053                                 radius_call_key.code = rh.rh_code;
1054                                 radius_call_key.ident = rh.rh_ident;
1055                                 radius_call_key.conversation = conversation;
1056                                 radius_call_key.req_time = pinfo->fd->abs_ts;
1057
1058                                 /* Look up the request */
1059                                 radius_call = g_hash_table_lookup(radius_calls, &radius_call_key);
1060                                 if (radius_call != NULL)
1061                                 {
1062                                         /* We've seen a request with this ID, with the same
1063                                            destination, before - but was it *this* request? */
1064                                         if (pinfo->fd->num != radius_call->req_num)
1065                                         {
1066                                                 /* No, so it's a duplicate request. Mark it as such. */
1067                                                 rad_info->is_duplicate = TRUE;
1068                                                 rad_info->req_num = radius_call->req_num;
1069                                                 if (check_col(pinfo->cinfo, COL_INFO))
1070                                                 {
1071                                                         col_append_fstr(pinfo->cinfo, COL_INFO,
1072                                                                         ", Duplicate Request ID:%u",
1073                                                                         rh.rh_ident);
1074                                                 }
1075                                                 if (tree)
1076                                                 {
1077                                                         proto_item* item;
1078                                                         proto_tree_add_uint_hidden(radius_tree, hf_radius_dup, tvb, 0,0, rh.rh_ident);
1079                                                         item = proto_tree_add_uint(radius_tree, hf_radius_req_dup, tvb, 0,0, rh.rh_ident);
1080                                                         PROTO_ITEM_SET_GENERATED(item);
1081                                                 }
1082                                         }
1083                                 }
1084                                 else
1085                                 {
1086                                         /* Prepare the value data.
1087                                            "req_num" and "rsp_num" are frame numbers;
1088                                            frame numbers are 1-origin, so we use 0
1089                                            to mean "we don't yet know in which frame
1090                                            the reply for this call appears". */
1091                                         new_radius_call_key = g_mem_chunk_alloc(radius_call_info_key_chunk);
1092                                         *new_radius_call_key = radius_call_key;
1093                                         radius_call = g_mem_chunk_alloc(radius_call_info_value_chunk);
1094                                         radius_call->req_num = pinfo->fd->num;
1095                                         radius_call->rsp_num = 0;
1096                                         radius_call->ident = rh.rh_ident;
1097                                         radius_call->code = rh.rh_code;
1098                                         radius_call->responded = FALSE;
1099                                         radius_call->req_time=pinfo->fd->abs_ts;
1100                                         radius_call->rspcode = 0;
1101
1102                                         /* Store it */
1103                                         g_hash_table_insert(radius_calls, new_radius_call_key, radius_call);
1104                                 }
1105                                 if (radius_call && radius_call->rsp_num)
1106                                 {
1107                                         proto_item* item = proto_tree_add_uint_format(radius_tree, hf_radius_rsp_frame,
1108                                                                                       tvb, 0, 0, radius_call->rsp_num,
1109                                                                                       "The response to this request is in frame %u",
1110                                                                                       radius_call->rsp_num);
1111                                         PROTO_ITEM_SET_GENERATED(item);
1112                                 }
1113                                 break;
1114                         case RADIUS_ACCESS_ACCEPT:
1115                         case RADIUS_ACCESS_REJECT:
1116                         case RADIUS_ACCOUNTING_RESPONSE:
1117                         case RADIUS_ACCESS_PASSWORD_ACK:
1118                         case RADIUS_ACCESS_PASSWORD_REJECT:
1119                         case RADIUS_ASCEND_ACCESS_EVENT_RESPONSE:
1120                         case RADIUS_DISCONNECT_REQUEST_ACK:
1121                         case RADIUS_DISCONNECT_REQUEST_NAK:
1122                         case RADIUS_CHANGE_FILTER_REQUEST_ACK:
1123                         case RADIUS_CHANGE_FILTER_REQUEST_NAK:
1124                                 proto_tree_add_boolean_hidden(radius_tree, hf_radius_rsp, tvb, 0, 0, TRUE);
1125                                 /* Check for RADIUS response.  A response must match a call that
1126                                  * we've seen, and the response must be sent to the same
1127                                  * port and address that the call came from.
1128                                  *
1129                                  * Because it is UDP and the reply can come from any IP
1130                                  * and port (not necessarly the request dest), we only
1131                                  * track the source IP and port of the request to match
1132                                  * the reply.
1133                                  */
1134
1135                                 /* XXX - can we just use NO_ADDR_B?  Unfortunately,
1136                                  * you currently still have to pass a non-null
1137                                  * pointer for the second address argument even
1138                                  * if you do that.
1139                                  */
1140                                 conversation = find_conversation(pinfo->fd->num, &null_address,
1141                                                                          &pinfo->dst, pinfo->ptype, pinfo->srcport,
1142                                                                          pinfo->destport, 0);
1143                                 if (conversation != NULL)
1144                                 {
1145                                         /* Look only for matching request, if
1146                                            matching conversation is available. */
1147                                         /* Prepare the key data */
1148                                         radius_call_key.code = rh.rh_code;
1149                                         radius_call_key.ident = rh.rh_ident;
1150                                         radius_call_key.conversation = conversation;
1151                                         radius_call_key.req_time = pinfo->fd->abs_ts;
1152
1153                                         radius_call = g_hash_table_lookup(radius_calls, &radius_call_key);
1154                                         if (radius_call)
1155                                         {
1156                                                 /* Indicate the frame to which this is a reply. */
1157                                                 if (radius_call->req_num)
1158                                                 {
1159                                                         proto_item* item;
1160                                                         rad_info->request_available = TRUE;
1161                                                         rad_info->req_num = radius_call->req_num;
1162                                                         radius_call->responded = TRUE;
1163
1164                                                         item = proto_tree_add_uint_format(radius_tree, hf_radius_req_frame,
1165                                                                                           tvb, 0, 0, radius_call->req_num,
1166                                                                                           "This is a response to a request in frame %u",
1167                                                                                           radius_call->req_num);
1168                                                         PROTO_ITEM_SET_GENERATED(item);
1169                                                         nstime_delta(&delta, &pinfo->fd->abs_ts, &radius_call->req_time);
1170                                                         item = proto_tree_add_time(radius_tree, hf_radius_time, tvb, 0, 0, &delta);
1171                                                         PROTO_ITEM_SET_GENERATED(item);
1172                                                 }
1173
1174                                                 if (radius_call->rsp_num == 0)
1175                                                 {
1176                                                         /* We have not yet seen a response to that call, so
1177                                                            this must be the first response; remember its
1178                                                            frame number. */
1179                                                         radius_call->rsp_num = pinfo->fd->num;
1180                                                 }
1181                                                 else
1182                                                 {
1183                                                         /* We have seen a response to this call - but was it
1184                                                            *this* response? (disregard provisional responses) */
1185                                                         if ( (radius_call->rsp_num != pinfo->fd->num) && (radius_call->rspcode == rh.rh_code) )
1186                                                         {
1187                                                                 /* No, so it's a duplicate response. Mark it as such. */
1188                                                                 rad_info->is_duplicate = TRUE;
1189                                                                 if (check_col(pinfo->cinfo, COL_INFO))
1190                                                                 {
1191                                                                         col_append_fstr(pinfo->cinfo, COL_INFO,
1192                                                                                         ", Duplicate Response ID:%u",
1193                                                                                         rh.rh_ident);
1194                                                                 }
1195                                                                 if (tree)
1196                                                                 {
1197                                                                         proto_item* item;
1198                                                                         proto_tree_add_uint_hidden(radius_tree, hf_radius_dup, tvb, 0,0, rh.rh_ident);
1199                                                                         item = proto_tree_add_uint(radius_tree, hf_radius_rsp_dup,
1200                                                                                                    tvb, 0, 0, rh.rh_ident);
1201                                                                         PROTO_ITEM_SET_GENERATED(item);
1202                                                                 }
1203                                                         }
1204                                                 }
1205                                                 /* Now store the response code (after comparison above) */
1206                                                 radius_call->rspcode = rh.rh_code;
1207                                                 rad_info->rspcode = rh.rh_code;
1208                                         }
1209                                 }
1210                                 break;
1211                         default:
1212                                 break;
1213                 }
1214
1215                 if (radius_call)
1216                 {
1217                         rad_info->req_time.secs = radius_call->req_time.secs;
1218                         rad_info->req_time.nsecs = radius_call->req_time.nsecs;
1219                 }
1220
1221                 if (avplength > 0) {
1222                         /* list the attribute value pairs */
1223                         avptf = proto_tree_add_text(radius_tree, tvb, HDR_LENGTH,
1224                                                         avplength, "Attribute Value Pairs");
1225                         avptree = proto_item_add_subtree(avptf, ett_radius_avp);
1226
1227                         dissect_attribute_value_pairs(avptree, pinfo, tvb, HDR_LENGTH,
1228                                                           avplength);
1229                 }
1230         }
1231
1232 end_of_radius:
1233         return tvb_length(tvb);
1234 }
1235
1236
1237 static void register_attrs(gpointer k _U_, gpointer v, gpointer p) {
1238         radius_attr_info_t* a = v;
1239         int i;
1240         gint* ett = &(a->ett);
1241         gchar* abbrev = g_strconcat("radius.",a->name,NULL);
1242         hf_register_info hfri[] = {
1243                 { NULL, { NULL,NULL, FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL }},
1244                 { NULL, { NULL,NULL, FT_UINT8, BASE_DEC, NULL, 0x0, "", HFILL }},
1245                 { NULL, { NULL,NULL, FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL }},
1246                 { NULL, { NULL,NULL, FT_NONE, BASE_NONE, NULL, 0x0, "", HFILL }}
1247         };
1248         guint len_hf = 2;
1249         hfett_t* ri = p;
1250
1251         for(i=0; abbrev[i]; i++) {
1252                 if(abbrev[i] == '-') abbrev[i] = '_';
1253         if(abbrev[i] == '/') abbrev[i] = '_';
1254         }
1255
1256         hfri[0].p_id = &(a->hf);
1257         hfri[1].p_id = &(a->hf_len);
1258
1259         hfri[0].hfinfo.name = a->name;
1260         hfri[0].hfinfo.abbrev = abbrev;
1261
1262         hfri[1].hfinfo.name = "Length";
1263         hfri[1].hfinfo.abbrev = g_strconcat(abbrev,".len",NULL);
1264         hfri[1].hfinfo.blurb = g_strconcat(a->name," Length",NULL);
1265
1266         if (a->type == radius_integer) {
1267                 hfri[0].hfinfo.type = FT_UINT32;
1268                 hfri[0].hfinfo.display = BASE_DEC;
1269
1270                 hfri[2].p_id = &(a->hf64);
1271                 hfri[2].hfinfo.name = g_strdup(a->name);
1272                 hfri[2].hfinfo.abbrev = abbrev;
1273                 hfri[2].hfinfo.type = FT_UINT64;
1274                 hfri[2].hfinfo.display = BASE_DEC;
1275
1276                 if (a->vs) {
1277                         hfri[0].hfinfo.strings = VALS(a->vs);
1278                 }
1279
1280                 len_hf++;
1281
1282         } else if (a->type == radius_string) {
1283                 hfri[0].hfinfo.type = FT_STRING;
1284                 hfri[0].hfinfo.display = BASE_NONE;
1285         } else if (a->type == radius_octets) {
1286                 hfri[0].hfinfo.type = FT_BYTES;
1287                 hfri[0].hfinfo.display = BASE_NONE;
1288         } else if (a->type == radius_ipaddr) {
1289                 hfri[0].hfinfo.type = FT_IPv4;
1290                 hfri[0].hfinfo.display = BASE_NONE;
1291         } else if (a->type == radius_ipv6addr) {
1292                 hfri[0].hfinfo.type = FT_IPv6;
1293                 hfri[0].hfinfo.display = BASE_NONE;
1294         } else if (a->type == radius_ipxnet) {
1295                 hfri[0].hfinfo.type = FT_IPXNET;
1296                 hfri[0].hfinfo.display = BASE_NONE;
1297         } else if (a->type == radius_date) {
1298                 hfri[0].hfinfo.type = FT_ABSOLUTE_TIME;
1299                 hfri[0].hfinfo.display = BASE_NONE;
1300         } else if (a->type == radius_abinary) {
1301                 hfri[0].hfinfo.type = FT_BYTES;
1302                 hfri[0].hfinfo.display = BASE_NONE;
1303         } else if (a->type == radius_ifid) {
1304                 hfri[0].hfinfo.type = FT_BYTES;
1305                 hfri[0].hfinfo.display = BASE_NONE;
1306         } else {
1307                 hfri[0].hfinfo.type = FT_BYTES;
1308                 hfri[0].hfinfo.display = BASE_NONE;
1309         }
1310
1311         if (a->tagged) {
1312                 hfri[len_hf].p_id = &(a->hf_tag);
1313                 hfri[len_hf].hfinfo.name = "Tag";
1314                 hfri[len_hf].hfinfo.abbrev = g_strconcat(abbrev,".tag",NULL);
1315                 hfri[len_hf].hfinfo.blurb = g_strconcat(a->name," Tag",NULL);
1316                 hfri[len_hf].hfinfo.type = FT_UINT8;
1317                 hfri[len_hf].hfinfo.display = BASE_HEX;
1318                 len_hf++;
1319         }
1320
1321         g_array_append_vals(ri->hf,hfri,len_hf);
1322         g_array_append_val(ri->ett,ett);
1323
1324 }
1325
1326 static void register_vendors(gpointer k _U_, gpointer v, gpointer p) {
1327         radius_vendor_info_t* vnd = v;
1328         hfett_t* ri = p;
1329         value_string vnd_vs;
1330         gint* ett_p = &(vnd->ett);
1331
1332         vnd_vs.value = vnd->code;
1333         vnd_vs.strptr = vnd->name;
1334
1335         g_array_append_val(ri->vend_vs,vnd_vs);
1336         g_array_append_val(ri->ett,ett_p);
1337
1338         g_hash_table_foreach(vnd->attrs_by_id,register_attrs,ri);
1339
1340 }
1341
1342 extern void radius_register_avp_dissector(guint32 vendor_id, guint32 attribute_id, radius_avp_dissector_t radius_avp_dissector) {
1343         radius_vendor_info_t* vendor;
1344         radius_attr_info_t* dictionary_entry;
1345         GHashTable* by_id;
1346
1347         DISSECTOR_ASSERT(radius_avp_dissector != NULL);
1348
1349         if (vendor_id) {
1350                 vendor = g_hash_table_lookup(dict->vendors_by_id,GUINT_TO_POINTER(vendor_id));
1351
1352                 if ( ! vendor ) {
1353                         vendor = g_malloc(sizeof(radius_vendor_info_t));
1354
1355                         vendor->name = g_strdup_printf("%s-%u",val_to_str(vendor_id, sminmpec_values, "Unknown"),vendor_id);
1356                         vendor->code = vendor_id;
1357                         vendor->attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
1358                         vendor->ett = no_vendor.ett;
1359
1360                         g_hash_table_insert(dict->vendors_by_id,GUINT_TO_POINTER(vendor->code),vendor);
1361                         g_hash_table_insert(dict->vendors_by_name,(gpointer)(vendor->name),vendor);
1362                 }
1363
1364                 dictionary_entry = g_hash_table_lookup(vendor->attrs_by_id,GUINT_TO_POINTER(attribute_id));
1365                 by_id = vendor->attrs_by_id;
1366         } else {
1367                 dictionary_entry = g_hash_table_lookup(dict->attrs_by_id,GUINT_TO_POINTER(attribute_id));
1368                 by_id = dict->attrs_by_id;
1369         }
1370
1371         if (!dictionary_entry) {
1372                 dictionary_entry = g_malloc(sizeof(radius_attr_info_t));;
1373
1374                 dictionary_entry->name = g_strdup_printf("Unknown-Attribute-%u",attribute_id);
1375                 dictionary_entry->code = attribute_id;
1376                 dictionary_entry->encrypt = FALSE;
1377                 dictionary_entry->type = NULL;
1378                 dictionary_entry->vs = NULL;
1379                 dictionary_entry->hf = no_dictionary_entry.hf;
1380                 dictionary_entry->hf_len = no_dictionary_entry.hf_len;
1381                 dictionary_entry->ett = no_dictionary_entry.ett;
1382
1383                 g_hash_table_insert(by_id,GUINT_TO_POINTER(dictionary_entry->code),dictionary_entry);
1384         }
1385
1386         dictionary_entry->dissector = radius_avp_dissector;
1387
1388 }
1389
1390 static void reinit_radius(void) {
1391         if ( alt_port_pref != alt_port ) {
1392
1393                 if (alt_port)
1394                         dissector_delete("udp.port", alt_port, radius_handle);
1395
1396                 if (alt_port_pref)
1397                         dissector_add("udp.port", alt_port_pref, radius_handle);
1398
1399                 alt_port = alt_port_pref;
1400         }
1401 }
1402
1403 /* Discard and init any state we've saved */
1404 static void
1405 radius_init_protocol(void)
1406 {
1407         if (radius_calls != NULL)
1408         {
1409                 g_hash_table_destroy(radius_calls);
1410                 radius_calls = NULL;
1411         }
1412         if (radius_call_info_key_chunk != NULL)
1413         {
1414                 g_mem_chunk_destroy(radius_call_info_key_chunk);
1415                 radius_call_info_key_chunk = NULL;
1416         }
1417         if (radius_call_info_value_chunk != NULL)
1418         {
1419                 g_mem_chunk_destroy(radius_call_info_value_chunk);
1420                 radius_call_info_value_chunk = NULL;
1421         }
1422
1423         radius_calls = g_hash_table_new(radius_call_hash, radius_call_equal);
1424         radius_call_info_key_chunk = g_mem_chunk_new("call_info_key_chunk",
1425                                                    sizeof(radius_call_info_key),
1426                                                    200 * sizeof(radius_call_info_key),
1427                                                    G_ALLOC_ONLY);
1428         radius_call_info_value_chunk = g_mem_chunk_new("call_info_value_chunk",
1429                                                      sizeof(radius_call_t),
1430                                                      200 * sizeof(radius_call_t),
1431                                                      G_ALLOC_ONLY);
1432 }
1433
1434 static void register_radius_fields(const char* unused _U_) {
1435          hf_register_info base_hf[] = {
1436                  { &hf_radius_req,
1437                  { "Request", "radius.req", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1438                          "TRUE if RADIUS request", HFILL }},
1439                  { &hf_radius_rsp,
1440                  { "Response", "radius.rsp", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1441                          "TRUE if RADIUS response", HFILL }},
1442                  { &hf_radius_req_frame,
1443                  { "Request Frame", "radius.reqframe", FT_FRAMENUM, BASE_NONE, NULL, 0,
1444                          "Request Frame", HFILL }},
1445                  { &hf_radius_rsp_frame,
1446                  { "Response Frame", "radius.rspframe", FT_FRAMENUM, BASE_NONE, NULL, 0,
1447                          "Response Frame", HFILL }},
1448                  { &hf_radius_time,
1449                  { "Time from request", "radius.time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0,
1450                          "Timedelta between Request and Response", HFILL }},
1451                  { &hf_radius_code,
1452                  { "Code","radius.code", FT_UINT8, BASE_DEC, VALS(radius_vals), 0x0,
1453                          "", HFILL }},
1454                  { &hf_radius_id,
1455                  { "Identifier",        "radius.id", FT_UINT8, BASE_DEC, NULL, 0x0,
1456                          "", HFILL }},
1457                  { &hf_radius_authenticator,
1458                  { "Authenticator",     "radius.authenticator", FT_BYTES, BASE_HEX, NULL, 0x0,
1459                          "", HFILL }},
1460                  { &hf_radius_length,
1461                  { "Length","radius.length", FT_UINT16, BASE_DEC, NULL, 0x0,
1462                          "", HFILL }},
1463                  { &(no_dictionary_entry.hf),
1464                  { "Unknown-Attribute","radius.Unknown_Attribute", FT_BYTES, BASE_HEX, NULL, 0x0,
1465                          "", HFILL }},
1466                  { &(no_dictionary_entry.hf_len),
1467                  { "Unknown-Attribute Length","radius.Unknown_Attribute.length", FT_UINT8, BASE_DEC, NULL, 0x0,
1468                          "", HFILL }},
1469                  { &hf_radius_framed_ip_address,
1470                  { "Framed-IP-Address","radius.Framed-IP-Address", FT_IPv4, BASE_NONE, NULL, 0x0,
1471                          "", HFILL }},
1472                  { &hf_radius_login_ip_host,
1473                  { "Login-IP-Host","radius.Login-IP-Host", FT_IPv4, BASE_NONE, NULL, 0x0,
1474                          "", HFILL }},
1475                  { &hf_radius_framed_ipx_network,
1476                  { "Framed-IPX-Network","radius.Framed-IPX-Network", FT_IPXNET, BASE_NONE, NULL, 0x0,
1477                          "", HFILL }},
1478                  { &hf_radius_cosine_vpi,
1479                  { "Cosine-VPI","radius.Cosine-Vpi", FT_UINT16, BASE_DEC, NULL, 0x0,
1480                          "", HFILL }},
1481                  { &hf_radius_cosine_vci,
1482                  { "Cosine-VCI","radius.Cosine-Vci", FT_UINT16, BASE_DEC, NULL, 0x0,
1483                          "", HFILL }},
1484                  { &hf_radius_dup,
1485                  { "Duplicate Message", "radius.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
1486                          "Duplicate Message", HFILL }},
1487                  { &hf_radius_req_dup,
1488                  { "Duplicate Request", "radius.req.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
1489                          "Duplicate Request", HFILL }},
1490                  { &hf_radius_rsp_dup,
1491                  { "Duplicate Response", "radius.rsp.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
1492                          "Duplicate Response", HFILL }}
1493          };
1494          
1495          gint *base_ett[] = {
1496                  &ett_radius,
1497                  &ett_radius_avp,
1498                  &ett_eap,
1499                  &(no_dictionary_entry.ett),
1500                  &(no_vendor.ett),
1501          };
1502          
1503          hfett_t ri;
1504          char* dir = NULL;
1505          gchar* dict_err_str = NULL;
1506          
1507          ri.hf = g_array_new(FALSE,TRUE,sizeof(hf_register_info));
1508          ri.ett = g_array_new(FALSE,TRUE,sizeof(gint *));
1509          ri.vend_vs = g_array_new(TRUE,TRUE,sizeof(value_string));
1510          
1511          g_array_append_vals(ri.hf, base_hf, array_length(base_hf));
1512          g_array_append_vals(ri.ett, base_ett, array_length(base_ett));
1513          
1514          dir = get_persconffile_path("radius", FALSE, FALSE);
1515          
1516          if (test_for_directory(dir) != EISDIR) {
1517                  /* Although dir isn't a directory it may still use memory */
1518                  g_free(dir);
1519                  
1520                  dir = get_datafile_path("radius");
1521                  
1522                  if (test_for_directory(dir) != EISDIR) {
1523                          g_free(dir);
1524                          dir = NULL;
1525                  }
1526          }
1527          
1528         if (dir) {
1529                  radius_load_dictionary(dict,dir,"dictionary",&dict_err_str);
1530
1531                  if (dict_err_str) {
1532                                 g_warning("radius: %s",dict_err_str);
1533                                 g_free(dict_err_str);
1534                  }
1535                  
1536                  g_hash_table_foreach(dict->attrs_by_id,register_attrs,&ri);
1537                  g_hash_table_foreach(dict->vendors_by_id,register_vendors,&ri);
1538         }
1539
1540         g_free(dir);
1541
1542         radius_vendors = (value_string*)g_array_data(ri.vend_vs);
1543
1544         proto_register_field_array(proto_radius,(hf_register_info*)g_array_data(ri.hf),ri.hf->len);
1545         proto_register_subtree_array((gint**)g_array_data(ri.ett), ri.ett->len);
1546
1547         g_array_free(ri.hf,FALSE);
1548         g_array_free(ri.ett,FALSE);
1549         g_array_free(ri.vend_vs,FALSE);
1550
1551         no_vendor.attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
1552 }
1553
1554
1555 void
1556 proto_register_radius(void)
1557 {
1558         module_t *radius_module;
1559
1560         proto_radius = proto_register_protocol("Radius Protocol", "RADIUS", "radius");
1561         new_register_dissector("radius", dissect_radius, proto_radius);
1562         register_init_routine(&radius_init_protocol);
1563         radius_module = prefs_register_protocol(proto_radius,reinit_radius);
1564         prefs_register_string_preference(radius_module,"shared_secret","Shared Secret",
1565                                      "Shared secret used to decode User Passwords",
1566                                      &shared_secret);
1567         prefs_register_bool_preference(radius_module,"show_length","Show AVP Lengths",
1568                                      "Whether to add or not to the tree the AVP's payload length",
1569                                      &show_length);
1570     prefs_register_uint_preference(radius_module, "alternate_port","Alternate Port",
1571                                    "An alternate UDP port to decode as RADIUS", 10, &alt_port_pref);
1572         radius_tap = register_tap("radius");
1573         proto_register_prefix("radius",register_radius_fields);
1574         
1575         dict = g_malloc(sizeof(radius_dictionary_t));
1576         dict->attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
1577         dict->attrs_by_name = g_hash_table_new(g_str_hash,g_str_equal);
1578         dict->vendors_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
1579         dict->vendors_by_name = g_hash_table_new(g_str_hash,g_str_equal);
1580 }
1581
1582 void
1583 proto_reg_handoff_radius(void)
1584 {
1585
1586         eap_handle = find_dissector("eap");
1587
1588         radius_handle = new_create_dissector_handle(dissect_radius, proto_radius);
1589
1590         dissector_add("udp.port", UDP_PORT_RADIUS, radius_handle);
1591         dissector_add("udp.port", UDP_PORT_RADIUS_NEW, radius_handle);
1592         dissector_add("udp.port", UDP_PORT_RADACCT, radius_handle);
1593         dissector_add("udp.port", UDP_PORT_RADACCT_NEW, radius_handle);
1594         
1595         radius_register_avp_dissector(0,8,dissect_framed_ip_address);
1596         radius_register_avp_dissector(0,14,dissect_login_ip_host);
1597         radius_register_avp_dissector(0,23,dissect_framed_ipx_network);
1598         radius_register_avp_dissector(VENDOR_COSINE,5,dissect_cosine_vpvc);     
1599
1600 }