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
9 * Wireshark - Network traffic analyzer
10 * By Gerald Combs <gerald@wireshark.org>
11 * Copyright 1998 Gerald Combs
13 * This program is free software; you can redistribute it and/or
14 * modify it under the terms of the GNU General Public License
15 * as published by the Free Software Foundation; either version 2
16 * of the License, or (at your option) any later version.
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program; if not, write to the Free Software
25 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
29 * RFC 2865 - Remote Authentication Dial In User Service (RADIUS)
30 * RFC 2866 - RADIUS Accounting
31 * RFC 2867 - RADIUS Accounting Modifications for Tunnel Protocol Support
32 * RFC 2868 - RADIUS Attributes for Tunnel Protocol Support
33 * RFC 2869 - RADIUS Extensions
34 * RFC 3162 - RADIUS and IPv6
35 * RFC 3576 - Dynamic Authorization Extensions to RADIUS
39 * http://www.iana.org/assignments/radius-types
43 * http://freeradius.org/radiusd/man/dictionary.html
45 * for the dictionary file syntax.
50 TO (re)DO: (see svn rev 14786)
51 - dissect_3gpp_ipv6_dns_servers()
63 #include <wsutil/report_err.h>
64 #include <wsutil/md5.h>
66 #include <epan/packet.h>
67 #include <epan/exceptions.h>
68 #include <epan/prefs.h>
69 #include <epan/sminmpec.h>
70 #include <wsutil/filesystem.h>
71 #include <epan/conversation.h>
73 #include <epan/addr_resolv.h>
74 #include <epan/garrayfix.h>
75 #include <epan/wmem/wmem.h>
77 #include "packet-radius.h"
79 void proto_register_radius(void);
80 void proto_reg_handoff_radius(void);
82 typedef struct _e_radiushdr {
91 wmem_array_t* vend_vs;
94 #define AUTHENTICATOR_LENGTH 16
95 #define RD_HDR_LENGTH 4
96 #define HDR_LENGTH (RD_HDR_LENGTH + AUTHENTICATOR_LENGTH)
98 #define UDP_PORT_RADIUS 1645
99 #define UDP_PORT_RADIUS_NEW 1812
100 #define UDP_PORT_RADACCT 1646
101 #define UDP_PORT_RADACCT_NEW 1813
102 #define UDP_PORT_DAE_OLD 1700 /* DAE: pre RFC */
103 #define UDP_PORT_DAE 3799 /* DAE: rfc3576 */
105 static radius_dictionary_t* dict = NULL;
107 static int proto_radius = -1;
109 static int hf_radius_req = -1;
110 static int hf_radius_rsp = -1;
111 static int hf_radius_req_frame = -1;
112 static int hf_radius_rsp_frame = -1;
113 static int hf_radius_time = -1;
115 static int hf_radius_dup = -1;
116 static int hf_radius_req_dup = -1;
117 static int hf_radius_rsp_dup = -1;
119 static int hf_radius_id = -1;
120 static int hf_radius_code = -1;
121 static int hf_radius_length = -1;
122 static int hf_radius_authenticator = -1;
124 static int hf_radius_chap_password = -1;
125 static int hf_radius_chap_ident = -1;
126 static int hf_radius_chap_string = -1;
127 static int hf_radius_framed_ip_address = -1;
129 static int hf_radius_login_ip_host = -1;
130 static int hf_radius_framed_ipx_network = -1;
132 static int hf_radius_cosine_vpi = -1;
133 static int hf_radius_cosine_vci = -1;
135 static int hf_radius_ascend_data_filter = -1;
137 static gint ett_radius = -1;
138 static gint ett_radius_avp = -1;
139 static gint ett_eap = -1;
140 static gint ett_chap = -1;
142 * Define the tap for radius
144 static int radius_tap = -1;
146 static radius_vendor_info_t no_vendor = {"Unknown Vendor",0,NULL,-1,1,1,FALSE};
148 static radius_attr_info_t no_dictionary_entry = {"Unknown-Attribute",0,FALSE,FALSE,radius_octets, NULL, NULL, -1, -1, -1, -1, -1, NULL };
150 static dissector_handle_t eap_handle;
152 static const gchar* shared_secret = "";
153 static gboolean show_length = FALSE;
154 static guint alt_port_pref = 0;
155 static guint request_ttl = 5;
157 static guint8 authenticator[AUTHENTICATOR_LENGTH];
159 /* http://www.iana.org/assignments/radius-types */
160 static const value_string radius_pkt_type_codes[] =
162 {RADIUS_PKT_TYPE_ACCESS_REQUEST, "Access-Request"}, /* 1 RFC2865 */
163 {RADIUS_PKT_TYPE_ACCESS_ACCEPT, "Access-Accept"}, /* 2 RFC2865 */
164 {RADIUS_PKT_TYPE_ACCESS_REJECT, "Access-Reject"}, /* 3 RFC2865 */
165 {RADIUS_PKT_TYPE_ACCOUNTING_REQUEST, "Accounting-Request"}, /* 4 RFC2865 */
166 {RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE, "Accounting-Response"}, /* 5 RFC2865 */
167 {RADIUS_PKT_TYPE_ACCOUNTING_STATUS, "Accounting-Status"}, /* 6 RFC3575 */
168 {RADIUS_PKT_TYPE_PASSWORD_REQUEST, "Password-Request"}, /* 7 RFC3575 */
169 {RADIUS_PKT_TYPE_PASSWORD_ACK, "Password-Ack"}, /* 8 RFC3575 */
170 {RADIUS_PKT_TYPE_PASSWORD_REJECT, "Password-Reject"}, /* 9 RFC3575 */
171 {RADIUS_PKT_TYPE_ACCOUNTING_MESSAGE, "Accounting-Message"}, /* 10 RFC3575 */
172 {RADIUS_PKT_TYPE_ACCESS_CHALLENGE, "Access-Challenge"}, /* 11 RFC2865 */
173 {RADIUS_PKT_TYPE_STATUS_SERVER, "Status-Server"}, /* 12 RFC2865 */
174 {RADIUS_PKT_TYPE_STATUS_CLIENT, "Status-Client"}, /* 13 RFC2865 */
176 {RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST, "Resource-Free-Request"}, /* 21 RFC3575 */
177 {RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE, "Resource-Free-Response"}, /* 22 RFC3575 */
178 {RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST, "Resource-Query-Request"}, /* 23 RFC3575 */
179 {RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE, "Query_Response"}, /* 24 RFC3575 */
180 {RADIUS_PKT_TYPE_ALTERNATE_RESOURCE_RECLAIM_REQUEST, "Alternate-Resource-Reclaim-Request"}, /* 25 RFC3575 */
181 {RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST, "NAS-Reboot-Request"}, /* 26 RFC3575 */
182 {RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE, "NAS-Reboot-Response"}, /* 27 RFC3575 */
184 {RADIUS_PKT_TYPE_NEXT_PASSCODE, "Next-Passcode"}, /* 29 RFC3575 */
185 {RADIUS_PKT_TYPE_NEW_PIN, "New-Pin"}, /* 30 RFC3575 */
186 {RADIUS_PKT_TYPE_TERMINATE_SESSION, "Terminate-Session"}, /* 31 RFC3575 */
187 {RADIUS_PKT_TYPE_PASSWORD_EXPIRED, "Password-Expired"}, /* 32 RFC3575 */
188 {RADIUS_PKT_TYPE_EVENT_REQUEST, "Event-Request"}, /* 33 RFC3575 */
189 {RADIUS_PKT_TYPE_EVENT_RESPONSE, "Event-Response"}, /* 34 RFC3575|RFC5176 */
191 {RADIUS_PKT_TYPE_DISCONNECT_REQUEST, "Disconnect-Request"}, /* 40 RFC3575|RFC5176 */
192 {RADIUS_PKT_TYPE_DISCONNECT_ACK, "Disconnect-ACK"}, /* 41 RFC3575|RFC5176 */
193 {RADIUS_PKT_TYPE_DISCONNECT_NAK, "Disconnect-NAK"}, /* 42 RFC3575|RFC5176 */
194 {RADIUS_PKT_TYPE_COA_REQUEST, "CoA-Request"}, /* 43 RFC3575|RFC5176 */
195 {RADIUS_PKT_TYPE_COA_ACK, "CoA-ACK"}, /* 44 RFC3575|RFC5176 */
196 {RADIUS_PKT_TYPE_COA_NAK, "CoA-NAK"}, /* 45 RFC3575|RFC5176 */
198 {RADIUS_PKT_TYPE_IP_ADDRESS_ALLOCATE, "IP-Address-Allocate"}, /* 50 RFC3575 */
199 {RADIUS_PKT_TYPE_IP_ADDRESS_RELEASE, "IP-Address-Release"}, /* 51 RFC3575 */
201 {RADIUS_PKT_TYPE_ALU_STATE_REQUEST, "ALU-State-Request"}, /* 129 ALU AAA */
202 {RADIUS_PKT_TYPE_ALU_STATE_ACCEPT, "ALU-State-Accept"}, /* 130 ALU AAA */
203 {RADIUS_PKT_TYPE_ALU_STATE_REJECT, "ALU-State-Reject"}, /* 131 ALU AAA */
204 {RADIUS_PKT_TYPE_ALU_STATE_ERROR, "ALU-State-Error"}, /* 132 ALU AAA */
206 250-253 Experimental Use [RFC3575]
207 254-255 Reserved [RFC3575]
211 static value_string_ext radius_pkt_type_codes_ext = VALUE_STRING_EXT_INIT(radius_pkt_type_codes);
214 * Init Hash table stuff for conversation
217 typedef struct _radius_call_info_key
221 conversation_t *conversation;
223 } radius_call_info_key;
225 static GHashTable *radius_calls;
227 typedef struct _radius_vsa_buffer_key
231 } radius_vsa_buffer_key;
233 typedef struct _radius_vsa_buffer
235 radius_vsa_buffer_key key;
241 static gint radius_vsa_equal(gconstpointer k1, gconstpointer k2)
243 const radius_vsa_buffer_key* key1 = (const radius_vsa_buffer_key*) k1;
244 const radius_vsa_buffer_key* key2 = (const radius_vsa_buffer_key*) k2;
246 return (((key1->vendor_id == key2->vendor_id) &&
247 (key1->vsa_type == key2->vsa_type)
251 static guint radius_vsa_hash(gconstpointer k)
253 const radius_vsa_buffer_key* key = (const radius_vsa_buffer_key*) k;
255 return key->vendor_id + key->vsa_type;
259 static gboolean radius_call_equal(gconstpointer k1, gconstpointer k2)
261 const radius_call_info_key* key1 = (const radius_call_info_key*) k1;
262 const radius_call_info_key* key2 = (const radius_call_info_key*) k2;
264 if (key1->ident == key2->ident && key1->conversation == key2->conversation) {
267 nstime_delta(&delta, &key1->req_time, &key2->req_time);
268 if (abs( (int) nstime_to_sec(&delta)) > (double) request_ttl) return 0;
270 if (key1->code == key2->code)
272 /* check the request and response are of the same code type */
273 if ((key1->code == RADIUS_PKT_TYPE_ACCESS_REQUEST) &&
274 ((key2->code == RADIUS_PKT_TYPE_ACCESS_ACCEPT) ||
275 (key2->code == RADIUS_PKT_TYPE_ACCESS_REJECT) ||
276 (key2->code == RADIUS_PKT_TYPE_ACCESS_CHALLENGE)))
279 if ((key1->code == RADIUS_PKT_TYPE_ACCOUNTING_REQUEST) &&
280 (key2->code == RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE))
283 if ((key1->code == RADIUS_PKT_TYPE_PASSWORD_REQUEST) &&
284 ((key2->code == RADIUS_PKT_TYPE_PASSWORD_ACK) || (key2->code == RADIUS_PKT_TYPE_PASSWORD_REJECT)))
287 if ((key1->code == RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST) &&
288 (key2->code == RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE))
291 if ((key1->code == RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST) &&
292 (key2->code == RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE))
295 if ((key1->code == RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST) &&
296 (key2->code == RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE))
299 if ((key1->code == RADIUS_PKT_TYPE_EVENT_REQUEST) &&
300 (key2->code == RADIUS_PKT_TYPE_EVENT_RESPONSE))
303 if ((key1->code == RADIUS_PKT_TYPE_DISCONNECT_REQUEST) &&
304 ((key2->code == RADIUS_PKT_TYPE_DISCONNECT_ACK) || (key2->code == RADIUS_PKT_TYPE_DISCONNECT_NAK)))
307 if ((key1->code == RADIUS_PKT_TYPE_COA_REQUEST) &&
308 ((key2->code == RADIUS_PKT_TYPE_COA_ACK) || (key2->code == RADIUS_PKT_TYPE_COA_NAK)))
311 if ((key1->code == RADIUS_PKT_TYPE_ALU_STATE_REQUEST) &&
312 ((key2->code == RADIUS_PKT_TYPE_ALU_STATE_ACCEPT) ||
313 (key2->code == RADIUS_PKT_TYPE_ALU_STATE_REJECT) ||
314 (key2->code == RADIUS_PKT_TYPE_ALU_STATE_ERROR)))
320 /* Calculate a hash key */
321 static guint radius_call_hash(gconstpointer k)
323 const radius_call_info_key* key = (const radius_call_info_key*) k;
325 return key->ident + /*key->code + */ key->conversation->index;
329 static const gchar *dissect_chap_password(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
332 proto_tree *chap_tree;
334 len = tvb_length(tvb);
336 return "[wrong length for CHAP-Password]";
338 ti = proto_tree_add_item(tree, hf_radius_chap_password, tvb, 0, len, ENC_NA);
339 chap_tree = proto_item_add_subtree(ti, ett_chap);
340 proto_tree_add_item(chap_tree, hf_radius_chap_ident, tvb, 0, 1, ENC_NA);
341 proto_tree_add_item(chap_tree, hf_radius_chap_string, tvb, 1, 16, ENC_NA);
342 return (tvb_bytes_to_ep_str(tvb, 0, len));
345 static const gchar *dissect_framed_ip_address(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
351 len = tvb_length(tvb);
353 return "[wrong length for IP address]";
355 ip=tvb_get_ipv4(tvb,0);
358 if (ip_h == 0xFFFFFFFF) {
360 proto_tree_add_ipv4_format_value(tree, hf_radius_framed_ip_address,
361 tvb, 0, len, ip, "%s", str);
362 } else if (ip_h == 0xFFFFFFFE) {
364 proto_tree_add_ipv4_format_value(tree, hf_radius_framed_ip_address,
365 tvb, 0, len, ip, "%s", str);
367 str = ip_to_str((guint8 *)&ip);
368 proto_tree_add_ipv4_format_value(tree, hf_radius_framed_ip_address,
369 tvb, 0, len, ip, "%s (%s)",
370 get_hostname(ip), str);
376 static const gchar *dissect_login_ip_host(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
382 len = tvb_length(tvb);
384 return "[wrong length for IP address]";
386 ip=tvb_get_ipv4(tvb,0);
389 if (ip_h == 0xFFFFFFFF) {
390 str = "User-selected";
391 proto_tree_add_ipv4_format_value(tree, hf_radius_login_ip_host,
392 tvb, 0, len, ip, "%s", str);
393 } else if (ip_h == 0) {
394 str = "NAS-selected";
395 proto_tree_add_ipv4_format_value(tree, hf_radius_login_ip_host,
396 tvb, 0, len, ip, "%s", str);
398 str = ip_to_str((guint8 *)&ip);
399 proto_tree_add_ipv4_format_value(tree, hf_radius_login_ip_host,
400 tvb, 0, len, ip, "%s (%s)",
401 get_hostname(ip), str);
407 static const value_string ascenddf_filtertype[] = { {0, "generic"}, {1, "ip"}, {0, NULL} };
408 static const value_string ascenddf_filteror[] = { {0, "drop"}, {1, "forward"}, {0, NULL} };
409 static const value_string ascenddf_inout[] = { {0, "out"}, {1, "in"}, {0, NULL} };
410 static const value_string ascenddf_proto[] = { {1, "icmp"}, {6, "tcp"}, {17, "udp"}, {0, NULL} };
411 static const value_string ascenddf_portq[] = { {1, "lt"}, {2, "eq"}, {3, "gt"}, {4, "ne"}, {0, NULL} };
413 static const gchar *dissect_ascend_data_filter(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
414 wmem_strbuf_t *filterstr;
416 guint8 proto, srclen, dstlen;
417 guint32 srcip, dstip;
418 guint16 srcport, dstport;
419 guint8 srcportq, dstportq;
424 return wmem_strdup_printf(wmem_packet_scope(), "Wrong attribute length %d", len);
427 filterstr=wmem_strbuf_sized_new(wmem_packet_scope(), 64, 64);
429 proto_tree_add_item(tree, hf_radius_ascend_data_filter, tvb, 0, -1, ENC_NA);
431 wmem_strbuf_append_printf(filterstr, "%s %s %s",
432 val_to_str(tvb_get_guint8(tvb, 0), ascenddf_filtertype, "%u"),
433 val_to_str(tvb_get_guint8(tvb, 2), ascenddf_inout, "%u"),
434 val_to_str(tvb_get_guint8(tvb, 1), ascenddf_filteror, "%u"));
436 proto=tvb_get_guint8(tvb, 14);
438 wmem_strbuf_append_printf(filterstr, " %s",
439 val_to_str(proto, ascenddf_proto, "%u"));
442 srcip=tvb_get_ipv4(tvb, 4);
443 srclen=tvb_get_guint8(tvb, 12);
444 srcport=tvb_get_ntohs(tvb, 16);
445 srcportq=tvb_get_guint8(tvb, 20);
447 if (srcip || srclen || srcportq) {
448 wmem_strbuf_append_printf(filterstr, " srcip %s/%d", ip_to_str((guint8 *) &srcip), srclen);
450 wmem_strbuf_append_printf(filterstr, " srcport %s %d",
451 val_to_str(srcportq, ascenddf_portq, "%u"), srcport);
454 dstip=tvb_get_ipv4(tvb, 8);
455 dstlen=tvb_get_guint8(tvb, 13);
456 dstport=tvb_get_ntohs(tvb, 18);
457 dstportq=tvb_get_guint8(tvb, 21);
459 if (dstip || dstlen || dstportq) {
460 wmem_strbuf_append_printf(filterstr, " dstip %s/%d", ip_to_str((guint8 *) &dstip), dstlen);
462 wmem_strbuf_append_printf(filterstr, " dstport %s %d",
463 val_to_str(dstportq, ascenddf_portq, "%u"), dstport);
466 return wmem_strbuf_get_str(filterstr);
469 static const gchar *dissect_framed_ipx_network(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
474 len = tvb_length(tvb);
476 return "[wrong length for IPX network]";
478 net=tvb_get_ntohl(tvb,0);
480 if (net == 0xFFFFFFFE)
481 str = "NAS-selected";
483 str = wmem_strdup_printf(wmem_packet_scope(), "0x%08X", net);
484 proto_tree_add_ipxnet_format_value(tree, hf_radius_framed_ipx_network, tvb, 0,
485 len, net, "Framed-IPX-Network: %s", str);
490 static const gchar* dissect_cosine_vpvc(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
493 if ( tvb_length(tvb) != 4 )
494 return "[Wrong Length for VP/VC AVP]";
496 vpi = tvb_get_ntohs(tvb,0);
497 vci = tvb_get_ntohs(tvb,2);
499 proto_tree_add_uint(tree,hf_radius_cosine_vpi,tvb,0,2,vpi);
500 proto_tree_add_uint(tree,hf_radius_cosine_vci,tvb,2,2,vci);
502 return wmem_strdup_printf(wmem_packet_scope(), "%u/%u",vpi,vci);
505 static const value_string daylight_saving_time_vals[] = {
506 {0, "No adjustment"},
507 {1, "+1 hour adjustment for Daylight Saving Time"},
508 {2, "+2 hours adjustment for Daylight Saving Time"},
514 dissect_radius_3gpp_ms_tmime_zone(proto_tree* tree, tvbuff_t* tvb, packet_info* pinfo _U_) {
517 guint8 oct, daylight_saving_time;
520 /* 3GPP TS 23.040 version 6.6.0 Release 6
521 * 9.2.3.11 TP-Service-Centre-Time-Stamp (TP-SCTS)
523 * The Time Zone indicates the difference, expressed in quarters of an hour,
524 * between the local time and GMT. In the first of the two semi-octets,
525 * the first bit (bit 3 of the seventh octet of the TP-Service-Centre-Time-Stamp field)
526 * represents the algebraic sign of this difference (0: positive, 1: negative).
529 oct = tvb_get_guint8(tvb, offset);
530 sign = (oct & 0x08) ? '-' : '+';
531 oct = (oct >> 4) + (oct & 0x07) * 10;
533 proto_tree_add_text(tree, tvb, offset, 1, "Timezone: GMT %c%d hours %d minutes", sign, oct / 4, oct % 4 * 15);
536 daylight_saving_time = tvb_get_guint8(tvb, offset) & 0x3;
537 proto_tree_add_text(tree, tvb, offset, 1, "%s", val_to_str_const(daylight_saving_time, daylight_saving_time_vals, "Unknown"));
539 return wmem_strdup_printf(wmem_packet_scope(), "Timezone: GMT %c%d hours %d minutes %s ",
540 sign, oct / 4, oct % 4 * 15, val_to_str_const(daylight_saving_time, daylight_saving_time_vals, "Unknown"));
545 radius_decrypt_avp(gchar *dest,int dest_len,tvbuff_t *tvb,int offset,int length)
547 md5_state_t md_ctx, old_md_ctx;
548 md5_byte_t digest[AUTHENTICATOR_LENGTH];
550 gint totlen = 0, returned_length, padded_length;
554 DISSECTOR_ASSERT(dest_len > 0);
559 /* The max avp length is 253 (255 - 2 for type & length), but only the
560 * User-Password is marked with encrypt=1 in dictionary.rfc2865, and the
561 * User-Password max length is only 128 (130 - 2 for type & length) per
562 * tools.ietf.org/html/rfc2865#section-5.2, so enforce that limit here.
568 md5_append(&md_ctx, (const guint8*)shared_secret, (int)strlen(shared_secret));
570 md5_append(&md_ctx, authenticator, AUTHENTICATOR_LENGTH);
571 md5_finish(&md_ctx, digest);
573 padded_length = length + ((length % AUTHENTICATOR_LENGTH) ?
574 (AUTHENTICATOR_LENGTH - (length % AUTHENTICATOR_LENGTH)) : 0);
575 pd = (guint8 *)wmem_alloc0(wmem_packet_scope(), padded_length);
576 tvb_memcpy(tvb, pd, offset, length);
578 for ( i = 0; i < padded_length; i += AUTHENTICATOR_LENGTH ) {
579 for ( j = 0; j < AUTHENTICATOR_LENGTH; j++ ) {
580 c = pd[i + j] ^ digest[j];
581 if ( g_ascii_isprint(c) ) {
582 returned_length = g_snprintf(&dest[totlen], dest_len - totlen,
584 totlen += MIN(returned_length, dest_len - totlen - 1);
587 returned_length = g_snprintf(&dest[totlen], dest_len - totlen,
589 totlen += MIN(returned_length, dest_len - totlen - 1);
594 md5_append(&md_ctx, &pd[i], AUTHENTICATOR_LENGTH);
595 md5_finish(&md_ctx, digest);
600 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) {
605 uintv = tvb_get_guint8(tvb,offset);
608 uintv = tvb_get_ntohs(tvb,offset);
611 uintv = tvb_get_ntoh24(tvb,offset);
614 uintv = tvb_get_ntohl(tvb,offset);
617 guint64 uintv64 = tvb_get_ntoh64(tvb,offset);
618 proto_tree_add_uint64(tree,a->hf_alt,tvb,offset,len,uintv64);
619 proto_item_append_text(avp_item, "%" G_GINT64_MODIFIER "u", uintv64);
623 proto_item_append_text(avp_item, "[unhandled integer length(%u)]", len);
626 proto_tree_add_item(tree,a->hf,tvb, offset, len, ENC_BIG_ENDIAN);
629 proto_item_append_text(avp_item, "%s(%u)", val_to_str_const(uintv, a->vs, "Unknown"),uintv);
631 proto_item_append_text(avp_item, "%u", uintv);
635 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) {
640 uintv = tvb_get_guint8(tvb,offset);
643 uintv = tvb_get_ntohs(tvb,offset);
646 uintv = tvb_get_ntoh24(tvb,offset);
649 uintv = tvb_get_ntohl(tvb,offset);
652 guint64 uintv64 = tvb_get_ntoh64(tvb,offset);
653 proto_tree_add_int64(tree,a->hf_alt,tvb,offset,len,uintv64);
654 proto_item_append_text(avp_item, "%" G_GINT64_MODIFIER "u", uintv64);
658 proto_item_append_text(avp_item, "[unhandled signed integer length(%u)]", len);
662 proto_tree_add_int(tree,a->hf,tvb,offset,len,uintv);
665 proto_item_append_text(avp_item, "%s(%d)", val_to_str_const(uintv, a->vs, "Unknown"),uintv);
667 proto_item_append_text(avp_item, "%d", uintv);
671 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) {
672 switch (a->encrypt) {
674 case 0: /* not encrypted */
675 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_UTF_8|ENC_NA);
676 proto_item_append_text(avp_item, "%s", tvb_format_text(tvb, offset, len));
679 case 1: /* encrypted like User-Password as defined in RFC 2865 */
680 if (*shared_secret == '\0') {
681 proto_item_append_text(avp_item, "Encrypted");
682 proto_tree_add_item(tree, a->hf_alt, tvb, offset, len, ENC_NA);
685 buffer=(gchar *)wmem_alloc(wmem_packet_scope(), 1024); /* an AVP value can be at most 253 bytes */
686 radius_decrypt_avp(buffer,1024,tvb,offset,len);
687 proto_item_append_text(avp_item, "Decrypted: %s", buffer);
688 proto_tree_add_string(tree, a->hf, tvb, offset, len, buffer);
692 case 2: /* encrypted like Tunnel-Password as defined in RFC 2868 */
693 proto_item_append_text(avp_item, "Encrypted");
694 proto_tree_add_item(tree, a->hf_alt, tvb, offset, len, ENC_NA);
697 case 3: /* encrypted like Ascend-Send-Secret as defined by Ascend^WLucent^WAlcatel-Lucent */
698 proto_item_append_text(avp_item, "Encrypted");
699 proto_tree_add_item(tree, a->hf_alt, tvb, offset, len, ENC_NA);
704 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) {
705 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
706 proto_item_append_text(avp_item, "%s", tvb_bytes_to_ep_str(tvb, offset, len));
709 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) {
711 gchar buf[MAX_IP_STR_LEN];
714 proto_item_append_text(avp_item, "[wrong length for IP address]");
718 ip=tvb_get_ipv4(tvb,offset);
720 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_BIG_ENDIAN);
722 ip_to_str_buf((guint8 *)&ip, buf, MAX_IP_STR_LEN);
723 proto_item_append_text(avp_item, "%s", buf);
726 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) {
727 struct e_in6_addr ipv6_buff;
731 proto_item_append_text(avp_item, "[wrong length for IPv6 address]");
735 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
737 tvb_get_ipv6(tvb, offset, &ipv6_buff);
738 ip6_to_str_buf(&ipv6_buff, txtbuf);
739 proto_item_append_text(avp_item, "%s", txtbuf);
742 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) {
743 struct e_in6_addr ipv6_buff;
747 if ((len < 2) || (len > 18) ) {
748 proto_item_append_text(avp_item, "[wrong length for IPv6 prefix]");
752 /* first byte is reserved == 0x00 */
753 if (tvb_get_guint8(tvb, offset)) {
754 proto_item_append_text(avp_item, "[invalid reserved byte for IPv6 prefix]");
758 /* this is the prefix length */
759 n = tvb_get_guint8(tvb, offset + 1);
761 proto_item_append_text(avp_item, "[invalid IPv6 prefix length]");
765 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
767 /* cannot use tvb_get_ipv6() here, since the prefix most likely is truncated */
768 memset(&ipv6_buff, 0, sizeof ipv6_buff);
769 tvb_memcpy(tvb, &ipv6_buff, offset + 2, len - 2);
770 ip6_to_str_buf(&ipv6_buff, txtbuf);
771 proto_item_append_text(avp_item, "%s/%u", txtbuf, n);
775 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) {
777 struct e_in6_addr ipv6_buff;
781 ip=tvb_get_ipv4(tvb,offset);
783 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_BIG_ENDIAN);
785 ip_to_str_buf((guint8 *)&ip, buf, MAX_IP_STR_LEN);
786 proto_item_append_text(avp_item, "%s", buf);
787 } else if (len == 16) {
788 proto_tree_add_item(tree, a->hf_alt, tvb, offset, len, ENC_NA);
790 tvb_get_ipv6(tvb, offset, &ipv6_buff);
791 ip6_to_str_buf(&ipv6_buff, buf);
792 proto_item_append_text(avp_item, "%s", buf);
794 proto_item_append_text(avp_item, "[wrong length for both of IPv4 and IPv6 address]");
799 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) {
803 proto_item_append_text(avp_item, "[wrong length for IPX network]");
807 net=tvb_get_ntohl(tvb,offset);
809 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
811 proto_item_append_text(avp_item, "0x%08X", net);
814 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) {
818 proto_item_append_text(avp_item, "[wrong length for timestamp]");
821 time_ptr.secs = tvb_get_ntohl(tvb,offset);
824 proto_tree_add_time(tree, a->hf, tvb, offset, len, &time_ptr);
825 proto_item_append_text(avp_item, "%s", abs_time_to_str(wmem_packet_scope(), &time_ptr, ABSOLUTE_TIME_LOCAL, TRUE));
829 * "abinary" is Ascend's binary format for filters. See dissect_ascend_data_filter().
831 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) {
832 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
833 proto_item_append_text(avp_item, "%s", tvb_bytes_to_ep_str(tvb, offset, len));
836 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) {
838 proto_item_append_text(avp_item, "[wrong length for ethernet address]");
842 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
843 proto_item_append_text(avp_item, "%s", tvb_ether_to_str(tvb, offset));
846 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) {
847 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
848 proto_item_append_text(avp_item, "%s", tvb_bytes_to_ep_str(tvb, offset, len));
851 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) {
852 proto_item_append_text(tlv_item, ": ");
853 dictionary_entry->type(dictionary_entry,tlv_tree,pinfo,tvb,offset,tlv_length,tlv_item);
856 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) {
861 radius_attr_info_t* dictionary_entry = NULL;
865 proto_item* tlv_item;
866 proto_item* tlv_len_item;
867 proto_tree* tlv_tree;
870 item = proto_tree_add_text(tree, tvb, offset, 0,
871 "Not enough room in packet for TLV header");
872 PROTO_ITEM_SET_GENERATED(item);
875 tlv_type = tvb_get_guint8(tvb,offset);
876 tlv_length = tvb_get_guint8(tvb,offset+1);
878 if (tlv_length < 2) {
879 item = proto_tree_add_text(tree, tvb, offset, 0,
880 "TLV too short: length %u < 2", tlv_length);
881 PROTO_ITEM_SET_GENERATED(item);
885 if (len < (gint)tlv_length) {
886 item = proto_tree_add_text(tree, tvb, offset, 0,
887 "Not enough room in packet for TLV");
888 PROTO_ITEM_SET_GENERATED(item);
894 dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(a->tlvs_by_id,GUINT_TO_POINTER(tlv_type));
896 if (! dictionary_entry ) {
897 dictionary_entry = &no_dictionary_entry;
900 tlv_item = proto_tree_add_text(tree, tvb, offset, tlv_length,
901 "TLV: l=%u t=%s(%u)", tlv_length,
902 dictionary_entry->name, tlv_type);
907 tlv_tree = proto_item_add_subtree(tlv_item,dictionary_entry->ett);
910 tlv_len_item = proto_tree_add_uint(tlv_tree,
911 dictionary_entry->hf_len,
913 PROTO_ITEM_SET_GENERATED(tlv_len_item);
916 add_tlv_to_tree(tlv_tree, tlv_item, pinfo, tvb, dictionary_entry,
918 offset += tlv_length;
922 proto_item_append_text(avp_item, "%d TLV(s) inside", tlv_num);
925 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) {
928 if (dictionary_entry->tagged) {
931 if (avp_length == 0) {
932 pi = proto_tree_add_text(avp_tree, tvb, offset,
933 0, "AVP too short for tag");
934 PROTO_ITEM_SET_GENERATED(pi);
938 tag = tvb_get_guint8(tvb, offset);
941 proto_tree_add_uint(avp_tree,
942 dictionary_entry->hf_tag,
943 tvb, offset, 1, tag);
945 proto_item_append_text(avp_item,
953 if ( dictionary_entry->dissector ) {
957 tvb_value = tvb_new_subset_length(tvb, offset, avp_length);
959 str = dictionary_entry->dissector(avp_tree,tvb_value,pinfo);
961 proto_item_append_text(avp_item, ": %s",str);
963 proto_item_append_text(avp_item, ": ");
965 dictionary_entry->type(dictionary_entry,avp_tree,pinfo,tvb,offset,avp_length,avp_item);
969 static gboolean vsa_buffer_destroy(gpointer k _U_, gpointer v, gpointer p _U_) {
970 radius_vsa_buffer* vsa_buffer = (radius_vsa_buffer*)v;
971 g_free((gpointer)vsa_buffer->data);
976 static void vsa_buffer_table_destroy(void *table) {
978 g_hash_table_foreach_remove((GHashTable *)table, vsa_buffer_destroy, NULL);
979 g_hash_table_destroy((GHashTable *)table);
984 void dissect_attribute_value_pairs(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, int offset, guint length) {
986 gboolean last_eap = FALSE;
987 guint8* eap_buffer = NULL;
988 guint eap_seg_num = 0;
989 guint eap_tot_len_captured = 0;
990 guint eap_tot_len = 0;
991 proto_tree* eap_tree = NULL;
992 tvbuff_t* eap_tvb = NULL;
994 GHashTable* vsa_buffer_table = NULL;
996 /* Forces load of header fields, if not already done so */
997 DISSECTOR_ASSERT(proto_registrar_get_byname("radius.code"));
1000 * In case we throw an exception, clean up whatever stuff we've
1001 * allocated (if any).
1003 CLEANUP_PUSH_PFX(la, g_free, eap_buffer);
1004 CLEANUP_PUSH_PFX(lb, vsa_buffer_table_destroy, (void *)vsa_buffer_table);
1006 while (length > 0) {
1007 radius_attr_info_t* dictionary_entry = NULL;
1013 proto_item* avp_item;
1014 proto_item* avp_len_item;
1015 proto_tree* avp_tree;
1018 item = proto_tree_add_text(tree, tvb, offset, 0,
1019 "Not enough room in packet for AVP header");
1020 PROTO_ITEM_SET_GENERATED(item);
1021 break; /* exit outer loop, then cleanup & return */
1023 avp_type = tvb_get_guint8(tvb,offset);
1024 avp_length = tvb_get_guint8(tvb,offset+1);
1026 if (avp_length < 2) {
1027 item = proto_tree_add_text(tree, tvb, offset, 0,
1028 "AVP too short: length %u < 2", avp_length);
1029 PROTO_ITEM_SET_GENERATED(item);
1030 break; /* exit outer loop, then cleanup & return */
1033 if (length < avp_length) {
1034 item = proto_tree_add_text(tree, tvb, offset, 0,
1035 "Not enough room in packet for AVP");
1036 PROTO_ITEM_SET_GENERATED(item);
1037 break; /* exit outer loop, then cleanup & return */
1040 length -= avp_length;
1042 dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(dict->attrs_by_id, GUINT_TO_POINTER(avp_type));
1044 if (! dictionary_entry ) {
1045 dictionary_entry = &no_dictionary_entry;
1048 avp_item = proto_tree_add_text(tree, tvb, offset, avp_length,
1049 "AVP: l=%u t=%s(%u)", avp_length,
1050 dictionary_entry->name, avp_type);
1055 if (avp_type == RADIUS_ATTR_TYPE_VENDOR_SPECIFIC) {
1056 radius_vendor_info_t* vendor;
1057 proto_tree* vendor_tree;
1058 gint max_offset = offset + avp_length;
1059 const gchar* vendor_str;
1061 /* XXX TODO: handle 2 byte codes for USR */
1063 if (avp_length < 4) {
1064 proto_item_append_text(avp_item, " [AVP too short; no room for vendor ID]");
1065 offset += avp_length;
1066 continue; /* while (length > 0) */
1068 vendor_id = tvb_get_ntohl(tvb,offset);
1073 vendor = (radius_vendor_info_t *)g_hash_table_lookup(dict->vendors_by_id,GUINT_TO_POINTER(vendor_id));
1074 vendor_str = val_to_str_ext_const(vendor_id, &sminmpec_values_ext, "Unknown");
1076 vendor = &no_vendor;
1078 proto_item_append_text(avp_item, " v=%s(%u)", vendor_str,
1081 vendor_tree = proto_item_add_subtree(avp_item,vendor->ett);
1083 while (offset < max_offset) {
1084 guint32 avp_vsa_type;
1085 guint32 avp_vsa_len;
1086 guint8 avp_vsa_flags = 0;
1087 guint32 avp_vsa_header_len = vendor->type_octets + vendor->length_octets + (vendor->has_flags ? 1 : 0);
1089 switch (vendor->type_octets) {
1091 avp_vsa_type = tvb_get_guint8(tvb,offset++);
1094 avp_vsa_type = tvb_get_ntohs(tvb,offset);
1098 avp_vsa_type = tvb_get_ntohl(tvb,offset);
1102 avp_vsa_type = tvb_get_guint8(tvb,offset++);
1105 switch (vendor->length_octets) {
1107 avp_vsa_len = tvb_get_guint8(tvb,offset++);
1110 avp_vsa_len = avp_length;
1113 avp_vsa_len = tvb_get_ntohs(tvb,offset);
1117 avp_vsa_len = tvb_get_guint8(tvb,offset++);
1120 if (vendor->has_flags) {
1121 avp_vsa_flags = tvb_get_guint8(tvb,offset++);
1124 if (avp_vsa_len < avp_vsa_header_len) {
1125 proto_tree_add_text(tree, tvb, offset+1, 1,
1127 break; /* exit while (offset < max_offset) loop */
1130 avp_vsa_len -= avp_vsa_header_len;
1132 dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(vendor->attrs_by_id,GUINT_TO_POINTER(avp_vsa_type));
1134 if ( !dictionary_entry ) {
1135 dictionary_entry = &no_dictionary_entry;
1138 if (vendor->has_flags){
1139 avp_item = proto_tree_add_text(vendor_tree,tvb,offset-avp_vsa_header_len,avp_vsa_len+avp_vsa_header_len,
1140 "VSA: l=%u t=%s(%u) C=0x%02x",
1141 avp_vsa_len+avp_vsa_header_len, dictionary_entry->name, avp_vsa_type, avp_vsa_flags);
1143 avp_item = proto_tree_add_text(vendor_tree,tvb,offset-avp_vsa_header_len,avp_vsa_len+avp_vsa_header_len,
1144 "VSA: l=%u t=%s(%u)",
1145 avp_vsa_len+avp_vsa_header_len, dictionary_entry->name, avp_vsa_type);
1148 avp_tree = proto_item_add_subtree(avp_item,dictionary_entry->ett);
1151 avp_len_item = proto_tree_add_uint(avp_tree,
1152 dictionary_entry->hf_len,
1153 tvb,0,0,avp_length);
1154 PROTO_ITEM_SET_GENERATED(avp_len_item);
1157 if (vendor->has_flags) {
1158 radius_vsa_buffer_key key;
1159 radius_vsa_buffer* vsa_buffer = NULL;
1160 key.vendor_id = vendor_id;
1161 key.vsa_type = avp_vsa_type;
1163 if (!vsa_buffer_table) {
1164 vsa_buffer_table = g_hash_table_new(radius_vsa_hash, radius_vsa_equal);
1167 vsa_buffer = (radius_vsa_buffer *)g_hash_table_lookup(vsa_buffer_table, &key);
1169 vsa_buffer->data = (guint8 *)g_realloc(vsa_buffer->data, vsa_buffer->len + avp_vsa_len);
1170 tvb_memcpy(tvb, vsa_buffer->data + vsa_buffer->len, offset, avp_vsa_len);
1171 vsa_buffer->len += avp_vsa_len;
1172 vsa_buffer->seg_num++;
1175 if (avp_vsa_flags & 0x80) {
1177 vsa_buffer = (radius_vsa_buffer *)g_malloc(sizeof(radius_vsa_buffer));
1178 vsa_buffer->key.vendor_id = vendor_id;
1179 vsa_buffer->key.vsa_type = avp_vsa_type;
1180 vsa_buffer->len = avp_vsa_len;
1181 vsa_buffer->seg_num = 1;
1182 vsa_buffer->data = (guint8 *)g_malloc(avp_vsa_len);
1183 tvb_memcpy(tvb, vsa_buffer->data, offset, avp_vsa_len);
1184 g_hash_table_insert(vsa_buffer_table, &(vsa_buffer->key), vsa_buffer);
1186 proto_tree_add_text(avp_tree, tvb, offset, avp_vsa_len, "VSA fragment");
1187 proto_item_append_text(avp_item, ": VSA fragment[%u]", vsa_buffer->seg_num);
1190 tvbuff_t* vsa_tvb = NULL;
1191 proto_tree_add_text(avp_tree, tvb, offset, avp_vsa_len, "VSA fragment");
1192 proto_item_append_text(avp_item, ": Last VSA fragment[%u]", vsa_buffer->seg_num);
1193 vsa_tvb = tvb_new_child_real_data(tvb, vsa_buffer->data, vsa_buffer->len, vsa_buffer->len);
1194 tvb_set_free_cb(vsa_tvb, g_free);
1195 add_new_data_source(pinfo, vsa_tvb, "Reassembled VSA");
1196 add_avp_to_tree(avp_tree, avp_item, pinfo, vsa_tvb, dictionary_entry, vsa_buffer->len, 0);
1197 g_hash_table_remove(vsa_buffer_table, &(vsa_buffer->key));
1201 add_avp_to_tree(avp_tree, avp_item, pinfo, tvb, dictionary_entry, avp_vsa_len, offset);
1205 add_avp_to_tree(avp_tree, avp_item, pinfo, tvb, dictionary_entry, avp_vsa_len, offset);
1208 offset += avp_vsa_len;
1209 } /* while (offset < max_offset) */
1210 continue; /* while (length > 0) */
1213 avp_tree = proto_item_add_subtree(avp_item,dictionary_entry->ett);
1216 avp_len_item = proto_tree_add_uint(avp_tree,
1217 dictionary_entry->hf_len,
1218 tvb,0,0,avp_length);
1219 PROTO_ITEM_SET_GENERATED(avp_len_item);
1222 tvb_len = tvb_length_remaining(tvb, offset);
1224 if ((gint)avp_length < tvb_len)
1225 tvb_len = avp_length;
1227 if (avp_type == RADIUS_ATTR_TYPE_EAP_MESSAGE) {
1230 /* Show this as an EAP fragment. */
1232 proto_tree_add_text(avp_tree, tvb, offset, tvb_len,
1235 if (eap_tvb != NULL) {
1237 * Oops, a non-consecutive EAP-Message
1240 proto_item_append_text(avp_item, " (non-consecutive)");
1243 * RFC 2869 says, in section 5.13, describing
1244 * the EAP-Message attribute:
1246 * The NAS places EAP messages received
1247 * from the authenticating peer into one
1248 * or more EAP-Message attributes and
1249 * forwards them to the RADIUS Server
1250 * within an Access-Request message.
1251 * If multiple EAP-Messages are
1252 * contained within an Access-Request or
1253 * Access-Challenge packet, they MUST be
1254 * in order and they MUST be consecutive
1255 * attributes in the Access-Request or
1256 * Access-Challenge packet.
1260 * The String field contains EAP packets,
1261 * as defined in [3]. If multiple
1262 * EAP-Message attributes are present
1263 * in a packet their values should be
1264 * concatenated; this allows EAP packets
1265 * longer than 253 octets to be passed
1268 * Do reassembly of EAP-Message attributes.
1269 * We just concatenate all the attributes,
1270 * and when we see either the end of the
1271 * attribute list or a non-EAP-Message
1272 * attribute, we know we're done.
1275 if (eap_buffer == NULL)
1276 eap_buffer = (guint8 *)g_malloc(eap_tot_len_captured + tvb_len);
1278 eap_buffer = (guint8 *)g_realloc(eap_buffer,
1279 eap_tot_len_captured + tvb_len);
1280 tvb_memcpy(tvb, eap_buffer + eap_tot_len_captured, offset,
1282 eap_tot_len_captured += tvb_len;
1283 eap_tot_len += avp_length;
1285 if ( tvb_bytes_exist(tvb, offset + avp_length + 1, 1) ) {
1286 guint8 next_type = tvb_get_guint8(tvb, offset + avp_length);
1288 if ( next_type != RADIUS_ATTR_TYPE_EAP_MESSAGE ) {
1289 /* Non-EAP-Message attribute */
1294 * No more attributes, either because
1295 * we're at the end of the packet or
1296 * because we're at the end of the
1297 * captured packet data.
1302 if (last_eap && eap_buffer) {
1303 gboolean save_writable;
1305 proto_item_append_text(avp_item, " Last Segment[%u]",
1308 eap_tree = proto_item_add_subtree(avp_item,ett_eap);
1310 eap_tvb = tvb_new_child_real_data(tvb, eap_buffer,
1311 eap_tot_len_captured,
1313 tvb_set_free_cb(eap_tvb, g_free);
1314 add_new_data_source(pinfo, eap_tvb, "Reassembled EAP");
1317 * Don't free this when we're done -
1318 * it's associated with a tvbuff.
1323 * Set the columns non-writable,
1324 * so that the packet list shows
1325 * this as an RADIUS packet, not
1328 save_writable = col_get_writable(pinfo->cinfo);
1329 col_set_writable(pinfo->cinfo, FALSE);
1331 call_dissector(eap_handle, eap_tvb, pinfo, eap_tree);
1333 col_set_writable(pinfo->cinfo, save_writable);
1335 proto_item_append_text(avp_item, " Segment[%u]",
1340 offset += avp_length;
1342 add_avp_to_tree(avp_tree, avp_item, pinfo, tvb, dictionary_entry,
1343 avp_length, offset);
1344 offset += avp_length;
1347 } /* while (length > 0) */
1349 CLEANUP_CALL_AND_POP_PFX(lb); /* vsa_buffer_table_destroy(vsa_buffer_table) */
1352 * Call the cleanup handler to free any reassembled data we haven't
1353 * attached to a tvbuff, and pop the handler.
1355 CLEANUP_CALL_AND_POP_PFX(la);
1358 /* This function tries to determine whether a packet is radius or not */
1360 is_radius(tvbuff_t *tvb)
1365 code=tvb_get_guint8(tvb, 0);
1366 if (try_val_to_str_ext(code, &radius_pkt_type_codes_ext) == NULL) {
1370 /* Check for valid length value:
1373 * The Length field is two octets. It indicates the length of the
1374 * packet including the Code, Identifier, Length, Authenticator and
1375 * Attribute fields. Octets outside the range of the Length field
1376 * MUST be treated as padding and ignored on reception. If the
1377 * packet is shorter than the Length field indicates, it MUST be
1378 * silently discarded. The minimum length is 20 and maximum length
1381 length=tvb_get_ntohs(tvb, 2);
1382 if ( (length<20) || (length>4096) ) {
1389 static void register_radius_fields(const char*);
1392 dissect_radius(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
1394 proto_tree *radius_tree = NULL;
1395 proto_tree *avptree = NULL;
1396 proto_item *ti, *hidden_item;
1400 radius_info_t *rad_info;
1402 conversation_t* conversation;
1403 radius_call_info_key radius_call_key;
1404 radius_call_info_key *new_radius_call_key;
1405 radius_call_t *radius_call = NULL;
1406 static address null_address = { AT_NONE, -1, 0, NULL };
1408 /* does this look like radius ? */
1409 if(!is_radius(tvb)){
1413 col_set_str(pinfo->cinfo, COL_PROTOCOL, "RADIUS");
1414 col_clear(pinfo->cinfo, COL_INFO);
1416 rh.rh_code=tvb_get_guint8(tvb,0);
1417 rh.rh_ident=tvb_get_guint8(tvb,1);
1418 rh.rh_pktlength=tvb_get_ntohs(tvb,2);
1421 /* Initialise stat info for passing to tap */
1422 rad_info = wmem_new(wmem_packet_scope(), radius_info_t);
1424 rad_info->ident = 0;
1425 rad_info->req_time.secs = 0;
1426 rad_info->req_time.nsecs = 0;
1427 rad_info->is_duplicate = FALSE;
1428 rad_info->request_available = FALSE;
1429 rad_info->req_num = 0; /* frame number request seen */
1430 rad_info->rspcode = 0;
1432 rad_info->code = rh.rh_code;
1433 rad_info->ident = rh.rh_ident;
1434 tap_queue_packet(radius_tap, pinfo, rad_info);
1436 col_add_fstr(pinfo->cinfo,COL_INFO,"%s(%d) (id=%d, l=%d)",
1437 val_to_str_ext_const(rh.rh_code, &radius_pkt_type_codes_ext, "Unknown Packet"),
1438 rh.rh_code, rh.rh_ident, rh.rh_pktlength);
1442 /* Forces load of header fields, if not already done so */
1443 DISSECTOR_ASSERT(proto_registrar_get_byname("radius.code"));
1445 ti = proto_tree_add_item(tree,proto_radius, tvb, 0, rh.rh_pktlength, ENC_NA);
1446 radius_tree = proto_item_add_subtree(ti, ett_radius);
1447 proto_tree_add_uint(radius_tree,hf_radius_code, tvb, 0, 1, rh.rh_code);
1448 proto_tree_add_uint_format(radius_tree,hf_radius_id, tvb, 1, 1, rh.rh_ident,
1449 "Packet identifier: 0x%01x (%d)", rh.rh_ident, rh.rh_ident);
1453 * Make sure the length is sane.
1455 if (rh.rh_pktlength < HDR_LENGTH)
1459 proto_tree_add_uint_format_value(radius_tree, hf_radius_length,
1460 tvb, 2, 2, rh.rh_pktlength, "%u (bogus, < %u)",
1461 rh.rh_pktlength, HDR_LENGTH);
1463 return tvb_length(tvb);
1466 avplength = rh.rh_pktlength - HDR_LENGTH;
1469 proto_tree_add_uint(radius_tree, hf_radius_length, tvb, 2, 2, rh.rh_pktlength);
1470 proto_tree_add_item(radius_tree, hf_radius_authenticator, tvb, 4, AUTHENTICATOR_LENGTH,ENC_NA);
1472 tvb_memcpy(tvb, authenticator, 4, AUTHENTICATOR_LENGTH);
1474 /* Conversation support REQUEST/RESPONSES */
1477 case RADIUS_PKT_TYPE_ACCESS_REQUEST:
1478 case RADIUS_PKT_TYPE_ACCOUNTING_REQUEST:
1479 case RADIUS_PKT_TYPE_PASSWORD_REQUEST:
1480 case RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST:
1481 case RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST:
1482 case RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST:
1483 case RADIUS_PKT_TYPE_EVENT_REQUEST:
1484 case RADIUS_PKT_TYPE_DISCONNECT_REQUEST:
1485 case RADIUS_PKT_TYPE_COA_REQUEST:
1486 case RADIUS_PKT_TYPE_ALU_STATE_REQUEST:
1487 /* Don't bother creating conversations if we're encapsulated within
1488 * an error packet, such as an ICMP destination unreachable */
1489 if (pinfo->flags.in_error_pkt)
1494 hidden_item = proto_tree_add_boolean(radius_tree, hf_radius_req, tvb, 0, 0, TRUE);
1495 PROTO_ITEM_SET_HIDDEN(hidden_item);
1498 /* Keep track of the address and port whence the call came
1499 * so that we can match up requests with replies.
1501 * Because it is UDP and the reply can come from any IP
1502 * and port (not necessarily the request dest), we only
1503 * track the source IP and port of the request to match
1508 * XXX - can we just use NO_ADDR_B? Unfortunately,
1509 * you currently still have to pass a non-null
1510 * pointer for the second address argument even
1513 conversation = find_conversation(pinfo->fd->num, &pinfo->src,
1514 &null_address, pinfo->ptype, pinfo->srcport,
1515 pinfo->destport, 0);
1516 if (conversation == NULL)
1518 /* It's not part of any conversation - create a new one. */
1519 conversation = conversation_new(pinfo->fd->num, &pinfo->src,
1520 &null_address, pinfo->ptype, pinfo->srcport,
1521 pinfo->destport, 0);
1524 /* Prepare the key data */
1525 radius_call_key.code = rh.rh_code;
1526 radius_call_key.ident = rh.rh_ident;
1527 radius_call_key.conversation = conversation;
1528 radius_call_key.req_time = pinfo->fd->abs_ts;
1530 /* Look up the request */
1531 radius_call = (radius_call_t *)g_hash_table_lookup(radius_calls, &radius_call_key);
1532 if (radius_call != NULL)
1534 /* We've seen a request with this ID, with the same
1535 destination, before - but was it *this* request? */
1536 if (pinfo->fd->num != radius_call->req_num)
1538 /* No, so it's a duplicate request. Mark it as such.
1539 FIXME: This is way too simple, as the request number
1540 is only an 8-bit value. See bug#4096 */
1541 rad_info->is_duplicate = TRUE;
1542 rad_info->req_num = radius_call->req_num;
1543 col_append_fstr(pinfo->cinfo, COL_INFO,
1544 ", Duplicate Request ID:%u", rh.rh_ident);
1549 hidden_item = proto_tree_add_uint(radius_tree, hf_radius_dup, tvb, 0,0, rh.rh_ident);
1550 PROTO_ITEM_SET_HIDDEN(hidden_item);
1551 item = proto_tree_add_uint(radius_tree, hf_radius_req_dup, tvb, 0,0, rh.rh_ident);
1552 PROTO_ITEM_SET_GENERATED(item);
1558 /* Prepare the value data.
1559 "req_num" and "rsp_num" are frame numbers;
1560 frame numbers are 1-origin, so we use 0
1561 to mean "we don't yet know in which frame
1562 the reply for this call appears". */
1563 new_radius_call_key = wmem_new(wmem_file_scope(), radius_call_info_key);
1564 *new_radius_call_key = radius_call_key;
1565 radius_call = wmem_new(wmem_file_scope(), radius_call_t);
1566 radius_call->req_num = pinfo->fd->num;
1567 radius_call->rsp_num = 0;
1568 radius_call->ident = rh.rh_ident;
1569 radius_call->code = rh.rh_code;
1570 radius_call->responded = FALSE;
1571 radius_call->req_time = pinfo->fd->abs_ts;
1572 radius_call->rspcode = 0;
1575 g_hash_table_insert(radius_calls, new_radius_call_key, radius_call);
1577 if (tree && radius_call->rsp_num)
1580 item = proto_tree_add_uint_format(radius_tree,
1581 hf_radius_rsp_frame, tvb, 0, 0, radius_call->rsp_num,
1582 "The response to this request is in frame %u",
1583 radius_call->rsp_num);
1584 PROTO_ITEM_SET_GENERATED(item);
1587 case RADIUS_PKT_TYPE_ACCESS_ACCEPT:
1588 case RADIUS_PKT_TYPE_ACCESS_REJECT:
1589 case RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE:
1590 case RADIUS_PKT_TYPE_PASSWORD_ACK:
1591 case RADIUS_PKT_TYPE_PASSWORD_REJECT:
1592 case RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE:
1593 case RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE:
1594 case RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE:
1595 case RADIUS_PKT_TYPE_EVENT_RESPONSE:
1596 case RADIUS_PKT_TYPE_DISCONNECT_ACK:
1597 case RADIUS_PKT_TYPE_DISCONNECT_NAK:
1598 case RADIUS_PKT_TYPE_COA_ACK:
1599 case RADIUS_PKT_TYPE_COA_NAK:
1600 case RADIUS_PKT_TYPE_ACCESS_CHALLENGE:
1601 case RADIUS_PKT_TYPE_ALU_STATE_ACCEPT:
1602 case RADIUS_PKT_TYPE_ALU_STATE_REJECT:
1603 case RADIUS_PKT_TYPE_ALU_STATE_ERROR:
1604 /* Don't bother finding conversations if we're encapsulated within
1605 * an error packet, such as an ICMP destination unreachable */
1606 if (pinfo->flags.in_error_pkt)
1611 hidden_item = proto_tree_add_boolean(radius_tree, hf_radius_rsp, tvb, 0, 0, TRUE);
1612 PROTO_ITEM_SET_HIDDEN(hidden_item);
1615 /* Check for RADIUS response. A response must match a call that
1616 * we've seen, and the response must be sent to the same
1617 * port and address that the call came from.
1619 * Because it is UDP and the reply can come from any IP
1620 * and port (not necessarily the request dest), we only
1621 * track the source IP and port of the request to match
1625 /* XXX - can we just use NO_ADDR_B? Unfortunately,
1626 * you currently still have to pass a non-null
1627 * pointer for the second address argument even
1630 conversation = find_conversation(pinfo->fd->num, &null_address,
1631 &pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
1632 if (conversation != NULL)
1634 /* Look only for matching request, if
1635 matching conversation is available. */
1636 /* Prepare the key data */
1637 radius_call_key.code = rh.rh_code;
1638 radius_call_key.ident = rh.rh_ident;
1639 radius_call_key.conversation = conversation;
1640 radius_call_key.req_time = pinfo->fd->abs_ts;
1642 radius_call = (radius_call_t *)g_hash_table_lookup(radius_calls, &radius_call_key);
1645 /* Indicate the frame to which this is a reply. */
1646 if (radius_call->req_num)
1648 rad_info->request_available = TRUE;
1649 rad_info->req_num = radius_call->req_num;
1650 radius_call->responded = TRUE;
1656 item = proto_tree_add_uint_format(radius_tree,
1657 hf_radius_req_frame, tvb, 0, 0,
1658 radius_call->req_num,
1659 "This is a response to a request in frame %u",
1660 radius_call->req_num);
1661 PROTO_ITEM_SET_GENERATED(item);
1662 nstime_delta(&delta, &pinfo->fd->abs_ts, &radius_call->req_time);
1663 item = proto_tree_add_time(radius_tree, hf_radius_time, tvb, 0, 0, &delta);
1664 PROTO_ITEM_SET_GENERATED(item);
1668 if (radius_call->rsp_num == 0)
1670 /* We have not yet seen a response to that call, so
1671 this must be the first response; remember its
1673 radius_call->rsp_num = pinfo->fd->num;
1677 /* We have seen a response to this call - but was it
1678 *this* response? (disregard provisional responses) */
1679 if ( (radius_call->rsp_num != pinfo->fd->num) && (radius_call->rspcode == rh.rh_code) )
1681 /* No, so it's a duplicate response. Mark it as such. */
1682 rad_info->is_duplicate = TRUE;
1683 col_append_fstr(pinfo->cinfo, COL_INFO,
1684 ", Duplicate Response ID:%u", rh.rh_ident);
1689 hidden_item = proto_tree_add_uint(radius_tree,
1690 hf_radius_dup, tvb, 0,0, rh.rh_ident);
1691 PROTO_ITEM_SET_HIDDEN(hidden_item);
1692 item = proto_tree_add_uint(radius_tree,
1693 hf_radius_rsp_dup, tvb, 0, 0, rh.rh_ident);
1694 PROTO_ITEM_SET_GENERATED(item);
1698 /* Now store the response code (after comparison above) */
1699 radius_call->rspcode = rh.rh_code;
1700 rad_info->rspcode = rh.rh_code;
1710 rad_info->req_time.secs = radius_call->req_time.secs;
1711 rad_info->req_time.nsecs = radius_call->req_time.nsecs;
1716 /* list the attribute value pairs */
1717 avptf = proto_tree_add_text(radius_tree, tvb, HDR_LENGTH,
1718 avplength, "Attribute Value Pairs");
1719 avptree = proto_item_add_subtree(avptf, ett_radius_avp);
1720 dissect_attribute_value_pairs(avptree, pinfo, tvb, HDR_LENGTH,
1724 return tvb_length(tvb);
1728 static void register_attrs(gpointer k _U_, gpointer v, gpointer p) {
1729 radius_attr_info_t* a = (radius_attr_info_t *)v;
1731 gint* ett = &(a->ett);
1732 gchar* abbrev = wmem_strdup_printf(wmem_epan_scope(), "radius.%s", a->name);
1733 hf_register_info hfri[] = {
1734 { NULL, { NULL,NULL, FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1735 { NULL, { NULL,NULL, FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
1736 { NULL, { NULL,NULL, FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
1737 { NULL, { NULL,NULL, FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}
1740 hfett_t* ri = (hfett_t *)p;
1742 for(i=0; abbrev[i]; i++) {
1743 if(abbrev[i] == '-') abbrev[i] = '_';
1744 if(abbrev[i] == '/') abbrev[i] = '_';
1747 hfri[0].p_id = &(a->hf);
1748 hfri[1].p_id = &(a->hf_len);
1750 hfri[0].hfinfo.name = a->name;
1751 hfri[0].hfinfo.abbrev = abbrev;
1753 hfri[1].hfinfo.name = "Length";
1754 hfri[1].hfinfo.abbrev = wmem_strdup_printf(wmem_epan_scope(), "%s.len", abbrev);
1755 hfri[1].hfinfo.blurb = wmem_strdup_printf(wmem_epan_scope(), "%s Length", a->name);
1757 if (a->type == radius_integer) {
1758 hfri[0].hfinfo.type = FT_UINT32;
1759 hfri[0].hfinfo.display = BASE_DEC;
1761 hfri[2].p_id = &(a->hf_alt);
1762 hfri[2].hfinfo.name = wmem_strdup(wmem_epan_scope(), a->name);
1763 hfri[2].hfinfo.abbrev = abbrev;
1764 hfri[2].hfinfo.type = FT_UINT64;
1765 hfri[2].hfinfo.display = BASE_DEC;
1768 hfri[0].hfinfo.strings = VALS(a->vs);
1772 }else if (a->type == radius_signed) {
1773 hfri[0].hfinfo.type = FT_INT32;
1774 hfri[0].hfinfo.display = BASE_DEC;
1776 hfri[2].p_id = &(a->hf_alt);
1777 hfri[2].hfinfo.name = wmem_strdup(wmem_epan_scope(), a->name);
1778 hfri[2].hfinfo.abbrev = abbrev;
1779 hfri[2].hfinfo.type = FT_INT64;
1780 hfri[2].hfinfo.display = BASE_DEC;
1783 hfri[0].hfinfo.strings = VALS(a->vs);
1787 } else if (a->type == radius_string) {
1788 hfri[0].hfinfo.type = FT_STRING;
1789 hfri[0].hfinfo.display = BASE_NONE;
1791 if (a->encrypt != 0) {
1793 * This attribute is encrypted, so create an
1794 * alternative field for the encrypted value.
1796 hfri[2].p_id = &(a->hf_alt);
1797 hfri[2].hfinfo.name = wmem_strdup_printf(wmem_epan_scope(), "%s (encrypted)", a->name);
1798 hfri[2].hfinfo.abbrev = wmem_strdup_printf(wmem_epan_scope(), "%s_encrypted", abbrev);
1799 hfri[2].hfinfo.type = FT_BYTES;
1800 hfri[2].hfinfo.display = BASE_NONE;
1804 } else if (a->type == radius_octets) {
1805 hfri[0].hfinfo.type = FT_BYTES;
1806 hfri[0].hfinfo.display = BASE_NONE;
1807 } else if (a->type == radius_ipaddr) {
1808 hfri[0].hfinfo.type = FT_IPv4;
1809 hfri[0].hfinfo.display = BASE_NONE;
1810 } else if (a->type == radius_ipv6addr) {
1811 hfri[0].hfinfo.type = FT_IPv6;
1812 hfri[0].hfinfo.display = BASE_NONE;
1813 } else if (a->type == radius_ipv6prefix) {
1814 hfri[0].hfinfo.type = FT_BYTES;
1815 hfri[0].hfinfo.display = BASE_NONE;
1816 } else if (a->type == radius_ipxnet) {
1817 hfri[0].hfinfo.type = FT_IPXNET;
1818 hfri[0].hfinfo.display = BASE_NONE;
1819 } else if (a->type == radius_date) {
1820 hfri[0].hfinfo.type = FT_ABSOLUTE_TIME;
1821 hfri[0].hfinfo.display = ABSOLUTE_TIME_LOCAL;
1822 } else if (a->type == radius_abinary) {
1823 hfri[0].hfinfo.type = FT_BYTES;
1824 hfri[0].hfinfo.display = BASE_NONE;
1825 } else if (a->type == radius_ifid) {
1826 hfri[0].hfinfo.type = FT_BYTES;
1827 hfri[0].hfinfo.display = BASE_NONE;
1828 } else if (a->type == radius_combo_ip) {
1829 hfri[0].hfinfo.type = FT_IPv4;
1830 hfri[0].hfinfo.display = BASE_NONE;
1832 hfri[2].p_id = &(a->hf_alt);
1833 hfri[2].hfinfo.name = wmem_strdup(wmem_epan_scope(), a->name);
1834 hfri[2].hfinfo.abbrev = wmem_strdup(wmem_epan_scope(), abbrev);
1835 hfri[2].hfinfo.type = FT_IPv6;
1836 hfri[2].hfinfo.display = BASE_NONE;
1839 } else if (a->type == radius_tlv) {
1840 hfri[0].hfinfo.type = FT_BYTES;
1841 hfri[0].hfinfo.display = BASE_NONE;
1843 hfri[0].hfinfo.type = FT_BYTES;
1844 hfri[0].hfinfo.display = BASE_NONE;
1848 hfri[len_hf].p_id = &(a->hf_tag);
1849 hfri[len_hf].hfinfo.name = "Tag";
1850 hfri[len_hf].hfinfo.abbrev = wmem_strdup_printf(wmem_epan_scope(), "%s.tag", abbrev);
1851 hfri[len_hf].hfinfo.blurb = wmem_strdup_printf(wmem_epan_scope(), "%s Tag", a->name);
1852 hfri[len_hf].hfinfo.type = FT_UINT8;
1853 hfri[len_hf].hfinfo.display = BASE_HEX;
1857 wmem_array_append(ri->hf,hfri,len_hf);
1858 wmem_array_append_one(ri->ett,ett);
1860 if (a->tlvs_by_id) {
1861 g_hash_table_foreach(a->tlvs_by_id,register_attrs,ri);
1865 static void register_vendors(gpointer k _U_, gpointer v, gpointer p) {
1866 radius_vendor_info_t* vnd = (radius_vendor_info_t *)v;
1867 hfett_t* ri = (hfett_t *)p;
1868 value_string vnd_vs;
1869 gint* ett_p = &(vnd->ett);
1871 vnd_vs.value = vnd->code;
1872 vnd_vs.strptr = vnd->name;
1874 wmem_array_append_one(ri->vend_vs,vnd_vs);
1875 wmem_array_append_one(ri->ett,ett_p);
1877 g_hash_table_foreach(vnd->attrs_by_id,register_attrs,ri);
1881 extern void radius_register_avp_dissector(guint32 vendor_id, guint32 attribute_id, radius_avp_dissector_t radius_avp_dissector) {
1882 radius_vendor_info_t* vendor;
1883 radius_attr_info_t* dictionary_entry;
1886 DISSECTOR_ASSERT(radius_avp_dissector != NULL);
1889 vendor = (radius_vendor_info_t *)g_hash_table_lookup(dict->vendors_by_id,GUINT_TO_POINTER(vendor_id));
1892 vendor = (radius_vendor_info_t *)g_malloc(sizeof(radius_vendor_info_t));
1894 vendor->name = g_strdup_printf("%s-%u",
1895 val_to_str_ext_const(vendor_id, &sminmpec_values_ext, "Unknown"),
1897 vendor->code = vendor_id;
1898 vendor->attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
1899 vendor->ett = no_vendor.ett;
1901 /* XXX: Default "standard" values: Should be parameters ? */
1902 vendor->type_octets = 1;
1903 vendor->length_octets = 1;
1904 vendor->has_flags = FALSE;
1906 g_hash_table_insert(dict->vendors_by_id,GUINT_TO_POINTER(vendor->code),vendor);
1907 g_hash_table_insert(dict->vendors_by_name,(gpointer)(vendor->name),vendor);
1910 dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(vendor->attrs_by_id,GUINT_TO_POINTER(attribute_id));
1911 by_id = vendor->attrs_by_id;
1913 dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(dict->attrs_by_id,GUINT_TO_POINTER(attribute_id));
1914 by_id = dict->attrs_by_id;
1917 if (!dictionary_entry) {
1918 dictionary_entry = (radius_attr_info_t *)g_malloc(sizeof(radius_attr_info_t));
1920 dictionary_entry->name = g_strdup_printf("Unknown-Attribute-%u",attribute_id);
1921 dictionary_entry->code = attribute_id;
1922 dictionary_entry->encrypt = 0;
1923 dictionary_entry->type = NULL;
1924 dictionary_entry->vs = NULL;
1925 dictionary_entry->hf = no_dictionary_entry.hf;
1926 dictionary_entry->tagged = 0;
1927 dictionary_entry->hf_tag = -1;
1928 dictionary_entry->hf_len = no_dictionary_entry.hf_len;
1929 dictionary_entry->ett = no_dictionary_entry.ett;
1930 dictionary_entry->tlvs_by_id = NULL;
1932 g_hash_table_insert(by_id,GUINT_TO_POINTER(dictionary_entry->code),dictionary_entry);
1935 dictionary_entry->dissector = radius_avp_dissector;
1939 /* Discard and init any state we've saved */
1941 radius_init_protocol(void)
1943 if (radius_calls != NULL)
1945 g_hash_table_destroy(radius_calls);
1946 radius_calls = NULL;
1949 radius_calls = g_hash_table_new(radius_call_hash, radius_call_equal);
1952 static void register_radius_fields(const char* unused _U_) {
1953 hf_register_info base_hf[] = {
1955 { "Request", "radius.req", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1956 "TRUE if RADIUS request", HFILL }},
1958 { "Response", "radius.rsp", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
1959 "TRUE if RADIUS response", HFILL }},
1960 { &hf_radius_req_frame,
1961 { "Request Frame", "radius.reqframe", FT_FRAMENUM, BASE_NONE, NULL, 0,
1963 { &hf_radius_rsp_frame,
1964 { "Response Frame", "radius.rspframe", FT_FRAMENUM, BASE_NONE, NULL, 0,
1967 { "Time from request", "radius.time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0,
1968 "Timedelta between Request and Response", HFILL }},
1970 { "Code","radius.code", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &radius_pkt_type_codes_ext, 0x0,
1973 { "Identifier", "radius.id", FT_UINT8, BASE_DEC, NULL, 0x0,
1975 { &hf_radius_authenticator,
1976 { "Authenticator", "radius.authenticator", FT_BYTES, BASE_NONE, NULL, 0x0,
1978 { &hf_radius_length,
1979 { "Length","radius.length", FT_UINT16, BASE_DEC, NULL, 0x0,
1981 { &(no_dictionary_entry.hf),
1982 { "Unknown-Attribute","radius.Unknown_Attribute", FT_BYTES, BASE_NONE, NULL, 0x0,
1984 { &(no_dictionary_entry.hf_len),
1985 { "Unknown-Attribute Length","radius.Unknown_Attribute.length", FT_UINT8, BASE_DEC, NULL, 0x0,
1987 { &hf_radius_chap_password,
1988 { "CHAP-Password","radius.CHAP_Password", FT_BYTES, BASE_NONE, NULL, 0x0,
1990 { &hf_radius_chap_ident,
1991 { "CHAP Ident","radius.CHAP_Ident", FT_UINT8, BASE_HEX, NULL, 0x0,
1993 { &hf_radius_chap_string,
1994 { "CHAP String","radius.CHAP_String", FT_BYTES, BASE_NONE, NULL, 0x0,
1996 { &hf_radius_framed_ip_address,
1997 { "Framed-IP-Address","radius.Framed-IP-Address", FT_IPv4, BASE_NONE, NULL, 0x0,
1999 { &hf_radius_login_ip_host,
2000 { "Login-IP-Host","radius.Login-IP-Host", FT_IPv4, BASE_NONE, NULL, 0x0,
2002 { &hf_radius_framed_ipx_network,
2003 { "Framed-IPX-Network","radius.Framed-IPX-Network", FT_IPXNET, BASE_NONE, NULL, 0x0,
2005 { &hf_radius_cosine_vpi,
2006 { "Cosine-VPI","radius.Cosine-Vpi", FT_UINT16, BASE_DEC, NULL, 0x0,
2008 { &hf_radius_cosine_vci,
2009 { "Cosine-VCI","radius.Cosine-Vci", FT_UINT16, BASE_DEC, NULL, 0x0,
2012 { "Duplicate Message", "radius.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
2014 { &hf_radius_req_dup,
2015 { "Duplicate Request", "radius.req.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
2017 { &hf_radius_rsp_dup,
2018 { "Duplicate Response", "radius.rsp.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
2020 { &hf_radius_ascend_data_filter,
2021 { "Ascend Data Filter", "radius.ascenddatafilter", FT_BYTES, BASE_NONE, NULL, 0x0,
2025 gint *base_ett[] = {
2030 &(no_dictionary_entry.ett),
2036 gchar* dict_err_str = NULL;
2038 ri.hf = wmem_array_new(wmem_epan_scope(), sizeof(hf_register_info));
2039 ri.ett = wmem_array_new(wmem_epan_scope(), sizeof(gint *));
2040 ri.vend_vs = wmem_array_new(wmem_epan_scope(), sizeof(value_string));
2042 wmem_array_append(ri.hf, base_hf, array_length(base_hf));
2043 wmem_array_append(ri.ett, base_ett, array_length(base_ett));
2045 dir = get_persconffile_path("radius", FALSE);
2047 if (test_for_directory(dir) != EISDIR) {
2048 /* Although dir isn't a directory it may still use memory */
2051 dir = get_datafile_path("radius");
2053 if (test_for_directory(dir) != EISDIR) {
2060 radius_load_dictionary(dict,dir,"dictionary",&dict_err_str);
2063 report_failure("radius: %s",dict_err_str);
2064 g_free(dict_err_str);
2067 g_hash_table_foreach(dict->attrs_by_id,register_attrs,&ri);
2068 g_hash_table_foreach(dict->vendors_by_id,register_vendors,&ri);
2073 proto_register_field_array(proto_radius,(hf_register_info*)wmem_array_get_raw(ri.hf),wmem_array_get_count(ri.hf));
2074 proto_register_subtree_array((gint**)wmem_array_get_raw(ri.ett), wmem_array_get_count(ri.ett));
2076 no_vendor.attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
2079 * Handle attributes that have a special format.
2081 radius_register_avp_dissector(0,3,dissect_chap_password);
2082 radius_register_avp_dissector(0,8,dissect_framed_ip_address);
2083 radius_register_avp_dissector(0,14,dissect_login_ip_host);
2084 radius_register_avp_dissector(0,23,dissect_framed_ipx_network);
2085 radius_register_avp_dissector(VENDOR_COSINE,5,dissect_cosine_vpvc);
2087 * XXX - should we just call dissect_ascend_data_filter()
2088 * in radius_abinary()?
2090 * Note that there is no attribute 242 in dictionary.redback.
2092 radius_register_avp_dissector(VENDOR_ASCEND,242,dissect_ascend_data_filter);
2093 radius_register_avp_dissector(VENDOR_REDBACK,242,dissect_ascend_data_filter);
2094 radius_register_avp_dissector(0,242,dissect_ascend_data_filter);
2097 * XXX - we should special-case Cisco attribute 252; see the comment in
2100 radius_register_avp_dissector(VENDOR_THE3GPP,23,dissect_radius_3gpp_ms_tmime_zone);
2105 proto_register_radius(void)
2107 module_t *radius_module;
2109 proto_radius = proto_register_protocol("Radius Protocol", "RADIUS", "radius");
2110 new_register_dissector("radius", dissect_radius, proto_radius);
2111 register_init_routine(&radius_init_protocol);
2112 radius_module = prefs_register_protocol(proto_radius, proto_reg_handoff_radius);
2113 prefs_register_string_preference(radius_module,"shared_secret","Shared Secret",
2114 "Shared secret used to decode User Passwords",
2116 prefs_register_bool_preference(radius_module,"show_length","Show AVP Lengths",
2117 "Whether to add or not to the tree the AVP's payload length",
2119 prefs_register_uint_preference(radius_module, "alternate_port","Alternate Port",
2120 "An alternate UDP port to decode as RADIUS", 10, &alt_port_pref);
2121 prefs_register_uint_preference(radius_module, "request_ttl", "Request TimeToLive",
2122 "Time to live for a radius request used for matching it with a response", 10, &request_ttl);
2123 radius_tap = register_tap("radius");
2124 proto_register_prefix("radius",register_radius_fields);
2126 dict = (radius_dictionary_t *)g_malloc(sizeof(radius_dictionary_t));
2127 dict->attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
2128 dict->attrs_by_name = g_hash_table_new(g_str_hash,g_str_equal);
2129 dict->vendors_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
2130 dict->vendors_by_name = g_hash_table_new(g_str_hash,g_str_equal);
2131 dict->tlvs_by_name = g_hash_table_new(g_str_hash,g_str_equal);
2135 proto_reg_handoff_radius(void)
2137 static gboolean initialized = FALSE;
2138 static dissector_handle_t radius_handle;
2139 static guint alt_port;
2142 radius_handle = find_dissector("radius");
2143 dissector_add_uint("udp.port", UDP_PORT_RADIUS, radius_handle);
2144 dissector_add_uint("udp.port", UDP_PORT_RADIUS_NEW, radius_handle);
2145 dissector_add_uint("udp.port", UDP_PORT_RADACCT, radius_handle);
2146 dissector_add_uint("udp.port", UDP_PORT_RADACCT_NEW, radius_handle);
2147 dissector_add_uint("udp.port", UDP_PORT_DAE_OLD, radius_handle);
2148 dissector_add_uint("udp.port", UDP_PORT_DAE, radius_handle);
2150 eap_handle = find_dissector("eap");
2155 dissector_delete_uint("udp.port", alt_port, radius_handle);
2158 if (alt_port_pref != 0)
2159 dissector_add_uint("udp.port", alt_port_pref, radius_handle);
2161 alt_port = alt_port_pref;
2165 * Editor modelines - http://www.wireshark.org/tools/modelines.html
2170 * indent-tabs-mode: t
2173 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
2174 * :indentSize=8:tabSize=8:noTabs=false: