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
11 * Wireshark - Network traffic analyzer
12 * By Gerald Combs <gerald@wireshark.org>
13 * Copyright 1998 Gerald Combs
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.
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.
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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
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
41 * http://www.iana.org/assignments/radius-types
45 * http://freeradius.org/radiusd/man/dictionary.html
47 * for the dictionary file syntax.
52 TO (re)DO: (see svn rev 14786)
53 - dissect_3gpp_ipv6_dns_servers()
65 #include <wsutil/report_err.h>
66 #include <wsutil/md5.h>
68 #include <epan/packet.h>
69 #include <epan/exceptions.h>
70 #include <epan/prefs.h>
71 #include <epan/sminmpec.h>
72 #include <epan/filesystem.h>
73 #include <epan/conversation.h>
75 #include <epan/addr_resolv.h>
76 #include <epan/garrayfix.h>
77 #include <epan/wmem/wmem.h>
79 #include "packet-radius.h"
81 void proto_reg_handoff_radius(void);
83 typedef struct _e_radiushdr
{
92 wmem_array_t
* vend_vs
;
95 #define AUTHENTICATOR_LENGTH 16
96 #define RD_HDR_LENGTH 4
97 #define HDR_LENGTH (RD_HDR_LENGTH + AUTHENTICATOR_LENGTH)
99 #define UDP_PORT_RADIUS 1645
100 #define UDP_PORT_RADIUS_NEW 1812
101 #define UDP_PORT_RADACCT 1646
102 #define UDP_PORT_RADACCT_NEW 1813
103 #define UDP_PORT_DAE_OLD 1700 /* DAE: pre RFC */
104 #define UDP_PORT_DAE 3799 /* DAE: rfc3576 */
106 static radius_dictionary_t
* dict
= NULL
;
108 static int proto_radius
= -1;
110 static int hf_radius_req
= -1;
111 static int hf_radius_rsp
= -1;
112 static int hf_radius_req_frame
= -1;
113 static int hf_radius_rsp_frame
= -1;
114 static int hf_radius_time
= -1;
116 static int hf_radius_dup
= -1;
117 static int hf_radius_req_dup
= -1;
118 static int hf_radius_rsp_dup
= -1;
120 static int hf_radius_id
= -1;
121 static int hf_radius_code
= -1;
122 static int hf_radius_length
= -1;
123 static int hf_radius_authenticator
= -1;
125 static int hf_radius_chap_password
= -1;
126 static int hf_radius_chap_ident
= -1;
127 static int hf_radius_chap_string
= -1;
128 static int hf_radius_framed_ip_address
= -1;
130 static int hf_radius_login_ip_host
= -1;
131 static int hf_radius_framed_ipx_network
= -1;
133 static int hf_radius_cosine_vpi
= -1;
134 static int hf_radius_cosine_vci
= -1;
136 static int hf_radius_ascend_data_filter
= -1;
138 static gint ett_radius
= -1;
139 static gint ett_radius_avp
= -1;
140 static gint ett_eap
= -1;
141 static gint ett_chap
= -1;
143 * Define the tap for radius
145 static int radius_tap
= -1;
147 static radius_vendor_info_t no_vendor
= {"Unknown Vendor",0,NULL
,-1,1,1,FALSE
};
149 static radius_attr_info_t no_dictionary_entry
= {"Unknown-Attribute",0,FALSE
,FALSE
,radius_octets
, NULL
, NULL
, -1, -1, -1, -1, -1, NULL
};
151 static dissector_handle_t eap_handle
;
153 static const gchar
* shared_secret
= "";
154 static gboolean show_length
= FALSE
;
155 static guint alt_port_pref
= 0;
156 static guint request_ttl
= 5;
158 static guint8 authenticator
[AUTHENTICATOR_LENGTH
];
160 /* http://www.iana.org/assignments/radius-types */
161 static const value_string radius_pkt_type_codes
[] =
163 {RADIUS_PKT_TYPE_ACCESS_REQUEST
, "Access-Request"}, /* 1 RFC2865 */
164 {RADIUS_PKT_TYPE_ACCESS_ACCEPT
, "Access-Accept"}, /* 2 RFC2865 */
165 {RADIUS_PKT_TYPE_ACCESS_REJECT
, "Access-Reject"}, /* 3 RFC2865 */
166 {RADIUS_PKT_TYPE_ACCOUNTING_REQUEST
, "Accounting-Request"}, /* 4 RFC2865 */
167 {RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE
, "Accounting-Response"}, /* 5 RFC2865 */
168 {RADIUS_PKT_TYPE_ACCOUNTING_STATUS
, "Accounting-Status"}, /* 6 RFC3575 */
169 {RADIUS_PKT_TYPE_PASSWORD_REQUEST
, "Password-Request"}, /* 7 RFC3575 */
170 {RADIUS_PKT_TYPE_PASSWORD_ACK
, "Password-Ack"}, /* 8 RFC3575 */
171 {RADIUS_PKT_TYPE_PASSWORD_REJECT
, "Password-Reject"}, /* 9 RFC3575 */
172 {RADIUS_PKT_TYPE_ACCOUNTING_MESSAGE
, "Accounting-Message"}, /* 10 RFC3575 */
173 {RADIUS_PKT_TYPE_ACCESS_CHALLENGE
, "Access-Challenge"}, /* 11 RFC2865 */
174 {RADIUS_PKT_TYPE_STATUS_SERVER
, "Status-Server"}, /* 12 RFC2865 */
175 {RADIUS_PKT_TYPE_STATUS_CLIENT
, "Status-Client"}, /* 13 RFC2865 */
177 {RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST
, "Resource-Free-Request"}, /* 21 RFC3575 */
178 {RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE
, "Resource-Free-Response"}, /* 22 RFC3575 */
179 {RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST
, "Resource-Query-Request"}, /* 23 RFC3575 */
180 {RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE
, "Query_Response"}, /* 24 RFC3575 */
181 {RADIUS_PKT_TYPE_ALTERNATE_RESOURCE_RECLAIM_REQUEST
, "Alternate-Resource-Reclaim-Request"}, /* 25 RFC3575 */
182 {RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST
, "NAS-Reboot-Request"}, /* 26 RFC3575 */
183 {RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE
, "NAS-Reboot-Response"}, /* 27 RFC3575 */
185 {RADIUS_PKT_TYPE_NEXT_PASSCODE
, "Next-Passcode"}, /* 29 RFC3575 */
186 {RADIUS_PKT_TYPE_NEW_PIN
, "New-Pin"}, /* 30 RFC3575 */
187 {RADIUS_PKT_TYPE_TERMINATE_SESSION
, "Terminate-Session"}, /* 31 RFC3575 */
188 {RADIUS_PKT_TYPE_PASSWORD_EXPIRED
, "Password-Expired"}, /* 32 RFC3575 */
189 {RADIUS_PKT_TYPE_EVENT_REQUEST
, "Event-Request"}, /* 33 RFC3575 */
190 {RADIUS_PKT_TYPE_EVENT_RESPONSE
, "Event-Response"}, /* 34 RFC3575|RFC5176 */
192 {RADIUS_PKT_TYPE_DISCONNECT_REQUEST
, "Disconnect-Request"}, /* 40 RFC3575|RFC5176 */
193 {RADIUS_PKT_TYPE_DISCONNECT_ACK
, "Disconnect-ACK"}, /* 41 RFC3575|RFC5176 */
194 {RADIUS_PKT_TYPE_DISCONNECT_NAK
, "Disconnect-NAK"}, /* 42 RFC3575|RFC5176 */
195 {RADIUS_PKT_TYPE_COA_REQUEST
, "CoA-Request"}, /* 43 RFC3575|RFC5176 */
196 {RADIUS_PKT_TYPE_COA_ACK
, "CoA-ACK"}, /* 44 RFC3575|RFC5176 */
197 {RADIUS_PKT_TYPE_COA_NAK
, "CoA-NAK"}, /* 45 RFC3575|RFC5176 */
199 {RADIUS_PKT_TYPE_IP_ADDRESS_ALLOCATE
, "IP-Address-Allocate"}, /* 50 RFC3575 */
200 {RADIUS_PKT_TYPE_IP_ADDRESS_RELEASE
, "IP-Address-Release"}, /* 51 RFC3575 */
202 {RADIUS_PKT_TYPE_ALU_STATE_REQUEST
, "ALU-State-Request"}, /* 129 ALU AAA */
203 {RADIUS_PKT_TYPE_ALU_STATE_ACCEPT
, "ALU-State-Accept"}, /* 130 ALU AAA */
204 {RADIUS_PKT_TYPE_ALU_STATE_REJECT
, "ALU-State-Reject"}, /* 131 ALU AAA */
205 {RADIUS_PKT_TYPE_ALU_STATE_ERROR
, "ALU-State-Error"}, /* 132 ALU AAA */
207 250-253 Experimental Use [RFC3575]
208 254-255 Reserved [RFC3575]
212 static value_string_ext radius_pkt_type_codes_ext
= VALUE_STRING_EXT_INIT(radius_pkt_type_codes
);
215 * Init Hash table stuff for conversation
218 typedef struct _radius_call_info_key
222 conversation_t
*conversation
;
224 } radius_call_info_key
;
226 static GHashTable
*radius_calls
;
228 typedef struct _radius_vsa_buffer_key
232 } radius_vsa_buffer_key
;
234 typedef struct _radius_vsa_buffer
236 radius_vsa_buffer_key key
;
242 static gint
radius_vsa_equal(gconstpointer k1
, gconstpointer k2
)
244 const radius_vsa_buffer_key
* key1
= (const radius_vsa_buffer_key
*) k1
;
245 const radius_vsa_buffer_key
* key2
= (const radius_vsa_buffer_key
*) k2
;
247 return (((key1
->vendor_id
== key2
->vendor_id
) &&
248 (key1
->vsa_type
== key2
->vsa_type
)
252 static guint
radius_vsa_hash(gconstpointer k
)
254 const radius_vsa_buffer_key
* key
= (const radius_vsa_buffer_key
*) k
;
256 return key
->vendor_id
+ key
->vsa_type
;
260 static gboolean
radius_call_equal(gconstpointer k1
, gconstpointer k2
)
262 const radius_call_info_key
* key1
= (const radius_call_info_key
*) k1
;
263 const radius_call_info_key
* key2
= (const radius_call_info_key
*) k2
;
265 if (key1
->ident
== key2
->ident
&& key1
->conversation
== key2
->conversation
) {
268 nstime_delta(&delta
, &key1
->req_time
, &key2
->req_time
);
269 if (abs( (int) nstime_to_sec(&delta
)) > (double) request_ttl
) return 0;
271 if (key1
->code
== key2
->code
)
273 /* check the request and response are of the same code type */
274 if ((key1
->code
== RADIUS_PKT_TYPE_ACCESS_REQUEST
) &&
275 ((key2
->code
== RADIUS_PKT_TYPE_ACCESS_ACCEPT
) ||
276 (key2
->code
== RADIUS_PKT_TYPE_ACCESS_REJECT
) ||
277 (key2
->code
== RADIUS_PKT_TYPE_ACCESS_CHALLENGE
)))
280 if ((key1
->code
== RADIUS_PKT_TYPE_ACCOUNTING_REQUEST
) &&
281 (key2
->code
== RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE
))
284 if ((key1
->code
== RADIUS_PKT_TYPE_PASSWORD_REQUEST
) &&
285 ((key2
->code
== RADIUS_PKT_TYPE_PASSWORD_ACK
) || (key2
->code
== RADIUS_PKT_TYPE_PASSWORD_REJECT
)))
288 if ((key1
->code
== RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST
) &&
289 (key2
->code
== RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE
))
292 if ((key1
->code
== RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST
) &&
293 (key2
->code
== RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE
))
296 if ((key1
->code
== RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST
) &&
297 (key2
->code
== RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE
))
300 if ((key1
->code
== RADIUS_PKT_TYPE_EVENT_REQUEST
) &&
301 (key2
->code
== RADIUS_PKT_TYPE_EVENT_RESPONSE
))
304 if ((key1
->code
== RADIUS_PKT_TYPE_DISCONNECT_REQUEST
) &&
305 ((key2
->code
== RADIUS_PKT_TYPE_DISCONNECT_ACK
) || (key2
->code
== RADIUS_PKT_TYPE_DISCONNECT_NAK
)))
308 if ((key1
->code
== RADIUS_PKT_TYPE_COA_REQUEST
) &&
309 ((key2
->code
== RADIUS_PKT_TYPE_COA_ACK
) || (key2
->code
== RADIUS_PKT_TYPE_COA_NAK
)))
312 if ((key1
->code
== RADIUS_PKT_TYPE_ALU_STATE_REQUEST
) &&
313 ((key2
->code
== RADIUS_PKT_TYPE_ALU_STATE_ACCEPT
) ||
314 (key2
->code
== RADIUS_PKT_TYPE_ALU_STATE_REJECT
) ||
315 (key2
->code
== RADIUS_PKT_TYPE_ALU_STATE_ERROR
)))
321 /* Calculate a hash key */
322 static guint
radius_call_hash(gconstpointer k
)
324 const radius_call_info_key
* key
= (const radius_call_info_key
*) k
;
326 return key
->ident
+ /*key->code + */ key
->conversation
->index
;
330 static const gchar
*dissect_chap_password(proto_tree
* tree
, tvbuff_t
* tvb
, packet_info
* pinfo _U_
) {
333 proto_tree
*chap_tree
;
335 len
= tvb_length(tvb
);
337 return "[wrong length for CHAP-Password]";
339 ti
= proto_tree_add_item(tree
, hf_radius_chap_password
, tvb
, 0, len
, ENC_NA
);
340 chap_tree
= proto_item_add_subtree(ti
, ett_chap
);
341 proto_tree_add_item(chap_tree
, hf_radius_chap_ident
, tvb
, 0, 1, ENC_NA
);
342 proto_tree_add_item(chap_tree
, hf_radius_chap_string
, tvb
, 1, 16, ENC_NA
);
343 return (tvb_bytes_to_str(tvb
, 0, len
));
346 static const gchar
*dissect_framed_ip_address(proto_tree
* tree
, tvbuff_t
* tvb
, packet_info
* pinfo _U_
) {
352 len
= tvb_length(tvb
);
354 return "[wrong length for IP address]";
356 ip
=tvb_get_ipv4(tvb
,0);
359 if (ip_h
== 0xFFFFFFFF) {
361 proto_tree_add_ipv4_format_value(tree
, hf_radius_framed_ip_address
,
362 tvb
, 0, len
, ip
, "%s", str
);
363 } else if (ip_h
== 0xFFFFFFFE) {
365 proto_tree_add_ipv4_format_value(tree
, hf_radius_framed_ip_address
,
366 tvb
, 0, len
, ip
, "%s", str
);
368 str
= ip_to_str((guint8
*)&ip
);
369 proto_tree_add_ipv4_format_value(tree
, hf_radius_framed_ip_address
,
370 tvb
, 0, len
, ip
, "%s (%s)",
371 get_hostname(ip
), str
);
377 static const gchar
*dissect_login_ip_host(proto_tree
* tree
, tvbuff_t
* tvb
, packet_info
* pinfo _U_
) {
383 len
= tvb_length(tvb
);
385 return "[wrong length for IP address]";
387 ip
=tvb_get_ipv4(tvb
,0);
390 if (ip_h
== 0xFFFFFFFF) {
391 str
= "User-selected";
392 proto_tree_add_ipv4_format_value(tree
, hf_radius_login_ip_host
,
393 tvb
, 0, len
, ip
, "%s", str
);
394 } else if (ip_h
== 0) {
395 str
= "NAS-selected";
396 proto_tree_add_ipv4_format_value(tree
, hf_radius_login_ip_host
,
397 tvb
, 0, len
, ip
, "%s", str
);
399 str
= ip_to_str((guint8
*)&ip
);
400 proto_tree_add_ipv4_format_value(tree
, hf_radius_login_ip_host
,
401 tvb
, 0, len
, ip
, "%s (%s)",
402 get_hostname(ip
), str
);
408 static const value_string ascenddf_filtertype
[] = { {0, "generic"}, {1, "ip"}, {0, NULL
} };
409 static const value_string ascenddf_filteror
[] = { {0, "drop"}, {1, "forward"}, {0, NULL
} };
410 static const value_string ascenddf_inout
[] = { {0, "out"}, {1, "in"}, {0, NULL
} };
411 static const value_string ascenddf_proto
[] = { {1, "icmp"}, {6, "tcp"}, {17, "udp"}, {0, NULL
} };
412 static const value_string ascenddf_portq
[] = { {1, "lt"}, {2, "eq"}, {3, "gt"}, {4, "ne"}, {0, NULL
} };
414 static const gchar
*dissect_ascend_data_filter(proto_tree
* tree
, tvbuff_t
* tvb
, packet_info
* pinfo _U_
) {
415 wmem_strbuf_t
*filterstr
;
417 guint8 proto
, srclen
, dstlen
;
418 guint32 srcip
, dstip
;
419 guint16 srcport
, dstport
;
420 guint8 srcportq
, dstportq
;
425 return wmem_strdup_printf(wmem_packet_scope(), "Wrong attribute length %d", len
);
428 filterstr
=wmem_strbuf_sized_new(wmem_packet_scope(), 64, 64);
430 proto_tree_add_item(tree
, hf_radius_ascend_data_filter
, tvb
, 0, -1, ENC_NA
);
432 wmem_strbuf_append_printf(filterstr
, "%s %s %s",
433 val_to_str(tvb_get_guint8(tvb
, 0), ascenddf_filtertype
, "%u"),
434 val_to_str(tvb_get_guint8(tvb
, 2), ascenddf_inout
, "%u"),
435 val_to_str(tvb_get_guint8(tvb
, 1), ascenddf_filteror
, "%u"));
437 proto
=tvb_get_guint8(tvb
, 14);
439 wmem_strbuf_append_printf(filterstr
, " %s",
440 val_to_str(proto
, ascenddf_proto
, "%u"));
443 srcip
=tvb_get_ipv4(tvb
, 4);
444 srclen
=tvb_get_guint8(tvb
, 12);
445 srcport
=tvb_get_ntohs(tvb
, 16);
446 srcportq
=tvb_get_guint8(tvb
, 20);
448 if (srcip
|| srclen
|| srcportq
) {
449 wmem_strbuf_append_printf(filterstr
, " srcip %s/%d", ip_to_str((guint8
*) &srcip
), srclen
);
451 wmem_strbuf_append_printf(filterstr
, " srcport %s %d",
452 val_to_str(srcportq
, ascenddf_portq
, "%u"), srcport
);
455 dstip
=tvb_get_ipv4(tvb
, 8);
456 dstlen
=tvb_get_guint8(tvb
, 13);
457 dstport
=tvb_get_ntohs(tvb
, 18);
458 dstportq
=tvb_get_guint8(tvb
, 21);
460 if (dstip
|| dstlen
|| dstportq
) {
461 wmem_strbuf_append_printf(filterstr
, " dstip %s/%d", ip_to_str((guint8
*) &dstip
), dstlen
);
463 wmem_strbuf_append_printf(filterstr
, " dstport %s %d",
464 val_to_str(dstportq
, ascenddf_portq
, "%u"), dstport
);
467 return wmem_strbuf_get_str(filterstr
);
470 static const gchar
*dissect_framed_ipx_network(proto_tree
* tree
, tvbuff_t
* tvb
, packet_info
* pinfo _U_
) {
475 len
= tvb_length(tvb
);
477 return "[wrong length for IPX network]";
479 net
=tvb_get_ntohl(tvb
,0);
481 if (net
== 0xFFFFFFFE)
482 str
= "NAS-selected";
484 str
= wmem_strdup_printf(wmem_packet_scope(), "0x%08X", net
);
485 proto_tree_add_ipxnet_format_value(tree
, hf_radius_framed_ipx_network
, tvb
, 0,
486 len
, net
, "Framed-IPX-Network: %s", str
);
491 static const gchar
* dissect_cosine_vpvc(proto_tree
* tree
, tvbuff_t
* tvb
, packet_info
* pinfo _U_
) {
494 if ( tvb_length(tvb
) != 4 )
495 return "[Wrong Length for VP/VC AVP]";
497 vpi
= tvb_get_ntohs(tvb
,0);
498 vci
= tvb_get_ntohs(tvb
,2);
500 proto_tree_add_uint(tree
,hf_radius_cosine_vpi
,tvb
,0,2,vpi
);
501 proto_tree_add_uint(tree
,hf_radius_cosine_vci
,tvb
,2,2,vci
);
503 return wmem_strdup_printf(wmem_packet_scope(), "%u/%u",vpi
,vci
);
506 static const value_string daylight_saving_time_vals
[] = {
507 {0, "No adjustment"},
508 {1, "+1 hour adjustment for Daylight Saving Time"},
509 {2, "+2 hours adjustment for Daylight Saving Time"},
515 dissect_radius_3gpp_ms_tmime_zone(proto_tree
* tree
, tvbuff_t
* tvb
, packet_info
* pinfo _U_
) {
518 guint8 oct
, daylight_saving_time
;
521 /* 3GPP TS 23.040 version 6.6.0 Release 6
522 * 9.2.3.11 TP-Service-Centre-Time-Stamp (TP-SCTS)
524 * The Time Zone indicates the difference, expressed in quarters of an hour,
525 * between the local time and GMT. In the first of the two semi-octets,
526 * the first bit (bit 3 of the seventh octet of the TP-Service-Centre-Time-Stamp field)
527 * represents the algebraic sign of this difference (0: positive, 1: negative).
530 oct
= tvb_get_guint8(tvb
, offset
);
531 sign
= (oct
& 0x08) ? '-' : '+';
532 oct
= (oct
>> 4) + (oct
& 0x07) * 10;
534 proto_tree_add_text(tree
, tvb
, offset
, 1, "Timezone: GMT %c%d hours %d minutes", sign
, oct
/ 4, oct
% 4 * 15);
537 daylight_saving_time
= tvb_get_guint8(tvb
, offset
) & 0x3;
538 proto_tree_add_text(tree
, tvb
, offset
, 1, "%s", val_to_str_const(daylight_saving_time
, daylight_saving_time_vals
, "Unknown"));
540 return wmem_strdup_printf(wmem_packet_scope(), "Timezone: GMT %c%d hours %d minutes %s ",
541 sign
, oct
/ 4, oct
% 4 * 15, val_to_str_const(daylight_saving_time
, daylight_saving_time_vals
, "Unknown"));
546 radius_decrypt_avp(gchar
*dest
,int dest_len
,tvbuff_t
*tvb
,int offset
,int length
)
548 md5_state_t md_ctx
, old_md_ctx
;
549 md5_byte_t digest
[AUTHENTICATOR_LENGTH
];
551 gint totlen
= 0, returned_length
, padded_length
;
555 DISSECTOR_ASSERT(dest_len
> 0);
560 /* The max avp length is 253 (255 - 2 for type & length), but only the
561 * User-Password is marked with encrypt=1 in dictionary.rfc2865, and the
562 * User-Password max length is only 128 (130 - 2 for type & length) per
563 * tools.ietf.org/html/rfc2865#section-5.2, so enforce that limit here.
569 md5_append(&md_ctx
, (const guint8
*)shared_secret
, (int)strlen(shared_secret
));
571 md5_append(&md_ctx
, authenticator
, AUTHENTICATOR_LENGTH
);
572 md5_finish(&md_ctx
, digest
);
574 padded_length
= length
+ ((length
% AUTHENTICATOR_LENGTH
) ?
575 (AUTHENTICATOR_LENGTH
- (length
% AUTHENTICATOR_LENGTH
)) : 0);
576 pd
= (guint8
*)wmem_alloc0(wmem_packet_scope(), padded_length
);
577 tvb_memcpy(tvb
, pd
, offset
, length
);
579 for ( i
= 0; i
< padded_length
; i
+= AUTHENTICATOR_LENGTH
) {
580 for ( j
= 0; j
< AUTHENTICATOR_LENGTH
; j
++ ) {
581 c
= pd
[i
+ j
] ^ digest
[j
];
583 returned_length
= g_snprintf(&dest
[totlen
], dest_len
- totlen
,
585 totlen
+= MIN(returned_length
, dest_len
- totlen
- 1);
588 returned_length
= g_snprintf(&dest
[totlen
], dest_len
- totlen
,
590 totlen
+= MIN(returned_length
, dest_len
- totlen
- 1);
595 md5_append(&md_ctx
, &pd
[i
], AUTHENTICATOR_LENGTH
);
596 md5_finish(&md_ctx
, digest
);
601 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
) {
606 uintv
= tvb_get_guint8(tvb
,offset
);
609 uintv
= tvb_get_ntohs(tvb
,offset
);
612 uintv
= tvb_get_ntoh24(tvb
,offset
);
615 uintv
= tvb_get_ntohl(tvb
,offset
);
618 guint64 uintv64
= tvb_get_ntoh64(tvb
,offset
);
619 proto_tree_add_uint64(tree
,a
->hf_alt
,tvb
,offset
,len
,uintv64
);
620 proto_item_append_text(avp_item
, "%" G_GINT64_MODIFIER
"u", uintv64
);
624 proto_item_append_text(avp_item
, "[unhandled integer length(%u)]", len
);
627 proto_tree_add_item(tree
,a
->hf
,tvb
, offset
, len
, ENC_BIG_ENDIAN
);
630 proto_item_append_text(avp_item
, "%s(%u)", val_to_str_const(uintv
, a
->vs
, "Unknown"),uintv
);
632 proto_item_append_text(avp_item
, "%u", uintv
);
636 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
) {
641 uintv
= tvb_get_guint8(tvb
,offset
);
644 uintv
= tvb_get_ntohs(tvb
,offset
);
647 uintv
= tvb_get_ntoh24(tvb
,offset
);
650 uintv
= tvb_get_ntohl(tvb
,offset
);
653 guint64 uintv64
= tvb_get_ntoh64(tvb
,offset
);
654 proto_tree_add_int64(tree
,a
->hf_alt
,tvb
,offset
,len
,uintv64
);
655 proto_item_append_text(avp_item
, "%" G_GINT64_MODIFIER
"u", uintv64
);
659 proto_item_append_text(avp_item
, "[unhandled signed integer length(%u)]", len
);
663 proto_tree_add_int(tree
,a
->hf
,tvb
,offset
,len
,uintv
);
666 proto_item_append_text(avp_item
, "%s(%d)", val_to_str_const(uintv
, a
->vs
, "Unknown"),uintv
);
668 proto_item_append_text(avp_item
, "%d", uintv
);
672 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
) {
673 switch (a
->encrypt
) {
675 case 0: /* not encrypted */
676 proto_tree_add_item(tree
, a
->hf
, tvb
, offset
, len
, ENC_UTF_8
|ENC_NA
);
677 proto_item_append_text(avp_item
, "%s", tvb_format_text(tvb
, offset
, len
));
680 case 1: /* encrypted like User-Password as defined in RFC 2865 */
681 if (*shared_secret
== '\0') {
682 proto_item_append_text(avp_item
, "Encrypted");
683 proto_tree_add_item(tree
, a
->hf_alt
, tvb
, offset
, len
, ENC_NA
);
686 buffer
=(gchar
*)wmem_alloc(wmem_packet_scope(), 1024); /* an AVP value can be at most 253 bytes */
687 radius_decrypt_avp(buffer
,1024,tvb
,offset
,len
);
688 proto_item_append_text(avp_item
, "Decrypted: %s", buffer
);
689 proto_tree_add_string(tree
, a
->hf
, tvb
, offset
, len
, buffer
);
693 case 2: /* encrypted like Tunnel-Password as defined in RFC 2868 */
694 proto_item_append_text(avp_item
, "Encrypted");
695 proto_tree_add_item(tree
, a
->hf_alt
, tvb
, offset
, len
, ENC_NA
);
698 case 3: /* encrypted like Ascend-Send-Secret as defined by Ascend^WLucent^WAlcatel-Lucent */
699 proto_item_append_text(avp_item
, "Encrypted");
700 proto_tree_add_item(tree
, a
->hf_alt
, tvb
, offset
, len
, ENC_NA
);
705 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
) {
706 proto_tree_add_item(tree
, a
->hf
, tvb
, offset
, len
, ENC_NA
);
707 proto_item_append_text(avp_item
, "%s", tvb_bytes_to_str(tvb
, offset
, len
));
710 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
) {
712 gchar buf
[MAX_IP_STR_LEN
];
715 proto_item_append_text(avp_item
, "[wrong length for IP address]");
719 ip
=tvb_get_ipv4(tvb
,offset
);
721 proto_tree_add_item(tree
, a
->hf
, tvb
, offset
, len
, ENC_BIG_ENDIAN
);
723 ip_to_str_buf((guint8
*)&ip
, buf
, MAX_IP_STR_LEN
);
724 proto_item_append_text(avp_item
, "%s", buf
);
727 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
) {
728 struct e_in6_addr ipv6_buff
;
732 proto_item_append_text(avp_item
, "[wrong length for IPv6 address]");
736 proto_tree_add_item(tree
, a
->hf
, tvb
, offset
, len
, ENC_NA
);
738 tvb_get_ipv6(tvb
, offset
, &ipv6_buff
);
739 ip6_to_str_buf(&ipv6_buff
, txtbuf
);
740 proto_item_append_text(avp_item
, "%s", txtbuf
);
743 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
) {
744 struct e_in6_addr ipv6_buff
;
748 if ((len
< 2) || (len
> 18) ) {
749 proto_item_append_text(avp_item
, "[wrong length for IPv6 prefix]");
753 /* first byte is reserved == 0x00 */
754 if (tvb_get_guint8(tvb
, offset
)) {
755 proto_item_append_text(avp_item
, "[invalid reserved byte for IPv6 prefix]");
759 /* this is the prefix length */
760 n
= tvb_get_guint8(tvb
, offset
+ 1);
762 proto_item_append_text(avp_item
, "[invalid IPv6 prefix length]");
766 proto_tree_add_item(tree
, a
->hf
, tvb
, offset
, len
, ENC_NA
);
768 /* cannot use tvb_get_ipv6() here, since the prefix most likely is truncated */
769 memset(&ipv6_buff
, 0, sizeof ipv6_buff
);
770 tvb_memcpy(tvb
, &ipv6_buff
, offset
+ 2, len
- 2);
771 ip6_to_str_buf(&ipv6_buff
, txtbuf
);
772 proto_item_append_text(avp_item
, "%s/%u", txtbuf
, n
);
776 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
) {
778 struct e_in6_addr ipv6_buff
;
782 ip
=tvb_get_ipv4(tvb
,offset
);
784 proto_tree_add_item(tree
, a
->hf
, tvb
, offset
, len
, ENC_BIG_ENDIAN
);
786 ip_to_str_buf((guint8
*)&ip
, buf
, MAX_IP_STR_LEN
);
787 proto_item_append_text(avp_item
, "%s", buf
);
788 } else if (len
== 16) {
789 proto_tree_add_item(tree
, a
->hf_alt
, tvb
, offset
, len
, ENC_NA
);
791 tvb_get_ipv6(tvb
, offset
, &ipv6_buff
);
792 ip6_to_str_buf(&ipv6_buff
, buf
);
793 proto_item_append_text(avp_item
, "%s", buf
);
795 proto_item_append_text(avp_item
, "[wrong length for both of IPv4 and IPv6 address]");
800 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
) {
804 proto_item_append_text(avp_item
, "[wrong length for IPX network]");
808 net
=tvb_get_ntohl(tvb
,offset
);
810 proto_tree_add_item(tree
, a
->hf
, tvb
, offset
, len
, ENC_NA
);
812 proto_item_append_text(avp_item
, "0x%08X", net
);
815 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
) {
819 proto_item_append_text(avp_item
, "[wrong length for timestamp]");
822 time_ptr
.secs
= tvb_get_ntohl(tvb
,offset
);
825 proto_tree_add_time(tree
, a
->hf
, tvb
, offset
, len
, &time_ptr
);
826 proto_item_append_text(avp_item
, "%s", abs_time_to_str(&time_ptr
, ABSOLUTE_TIME_LOCAL
, TRUE
));
830 * "abinary" is Ascend's binary format for filters. See dissect_ascend_data_filter().
832 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
) {
833 proto_tree_add_item(tree
, a
->hf
, tvb
, offset
, len
, ENC_NA
);
834 proto_item_append_text(avp_item
, "%s", tvb_bytes_to_str(tvb
, offset
, len
));
837 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
) {
839 proto_item_append_text(avp_item
, "[wrong length for ethernet address]");
843 proto_tree_add_item(tree
, a
->hf
, tvb
, offset
, len
, ENC_NA
);
844 proto_item_append_text(avp_item
, "%s", tvb_ether_to_str(tvb
, offset
));
847 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
) {
848 proto_tree_add_item(tree
, a
->hf
, tvb
, offset
, len
, ENC_NA
);
849 proto_item_append_text(avp_item
, "%s", tvb_bytes_to_str(tvb
, offset
, len
));
852 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
) {
853 proto_item_append_text(tlv_item
, ": ");
854 dictionary_entry
->type(dictionary_entry
,tlv_tree
,pinfo
,tvb
,offset
,tlv_length
,tlv_item
);
857 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
) {
862 radius_attr_info_t
* dictionary_entry
= NULL
;
866 proto_item
* tlv_item
;
867 proto_item
* tlv_len_item
;
868 proto_tree
* tlv_tree
;
871 item
= proto_tree_add_text(tree
, tvb
, offset
, 0,
872 "Not enough room in packet for TLV header");
873 PROTO_ITEM_SET_GENERATED(item
);
876 tlv_type
= tvb_get_guint8(tvb
,offset
);
877 tlv_length
= tvb_get_guint8(tvb
,offset
+1);
879 if (tlv_length
< 2) {
880 item
= proto_tree_add_text(tree
, tvb
, offset
, 0,
881 "TLV too short: length %u < 2", tlv_length
);
882 PROTO_ITEM_SET_GENERATED(item
);
886 if (len
< (gint
)tlv_length
) {
887 item
= proto_tree_add_text(tree
, tvb
, offset
, 0,
888 "Not enough room in packet for TLV");
889 PROTO_ITEM_SET_GENERATED(item
);
895 dictionary_entry
= (radius_attr_info_t
*)g_hash_table_lookup(a
->tlvs_by_id
,GUINT_TO_POINTER(tlv_type
));
897 if (! dictionary_entry
) {
898 dictionary_entry
= &no_dictionary_entry
;
901 tlv_item
= proto_tree_add_text(tree
, tvb
, offset
, tlv_length
,
902 "TLV: l=%u t=%s(%u)", tlv_length
,
903 dictionary_entry
->name
, tlv_type
);
908 tlv_tree
= proto_item_add_subtree(tlv_item
,dictionary_entry
->ett
);
911 tlv_len_item
= proto_tree_add_uint(tlv_tree
,
912 dictionary_entry
->hf_len
,
914 PROTO_ITEM_SET_GENERATED(tlv_len_item
);
917 add_tlv_to_tree(tlv_tree
, tlv_item
, pinfo
, tvb
, dictionary_entry
,
919 offset
+= tlv_length
;
923 proto_item_append_text(avp_item
, "%d TLV(s) inside", tlv_num
);
926 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
) {
929 if (dictionary_entry
->tagged
) {
932 if (avp_length
== 0) {
933 pi
= proto_tree_add_text(avp_tree
, tvb
, offset
,
934 0, "AVP too short for tag");
935 PROTO_ITEM_SET_GENERATED(pi
);
939 tag
= tvb_get_guint8(tvb
, offset
);
942 proto_tree_add_uint(avp_tree
,
943 dictionary_entry
->hf_tag
,
944 tvb
, offset
, 1, tag
);
946 proto_item_append_text(avp_item
,
954 if ( dictionary_entry
->dissector
) {
958 tvb_value
= tvb_new_subset(tvb
, offset
, avp_length
, (gint
) avp_length
);
960 str
= dictionary_entry
->dissector(avp_tree
,tvb_value
,pinfo
);
962 proto_item_append_text(avp_item
, ": %s",str
);
964 proto_item_append_text(avp_item
, ": ");
966 dictionary_entry
->type(dictionary_entry
,avp_tree
,pinfo
,tvb
,offset
,avp_length
,avp_item
);
970 static gboolean
vsa_buffer_destroy(gpointer k _U_
, gpointer v
, gpointer p _U_
) {
971 radius_vsa_buffer
* vsa_buffer
= (radius_vsa_buffer
*)v
;
972 g_free((gpointer
)vsa_buffer
->data
);
977 static void vsa_buffer_table_destroy(void *table
) {
979 g_hash_table_foreach_remove((GHashTable
*)table
, vsa_buffer_destroy
, NULL
);
980 g_hash_table_destroy((GHashTable
*)table
);
985 void dissect_attribute_value_pairs(proto_tree
*tree
, packet_info
*pinfo
, tvbuff_t
*tvb
, int offset
, guint length
) {
987 gboolean last_eap
= FALSE
;
988 guint8
* eap_buffer
= NULL
;
989 guint eap_seg_num
= 0;
990 guint eap_tot_len_captured
= 0;
991 guint eap_tot_len
= 0;
992 proto_tree
* eap_tree
= NULL
;
993 tvbuff_t
* eap_tvb
= NULL
;
995 GHashTable
* vsa_buffer_table
= NULL
;
997 /* Forces load of header fields, if not already done so */
998 DISSECTOR_ASSERT(proto_registrar_get_byname("radius.code"));
1001 * In case we throw an exception, clean up whatever stuff we've
1002 * allocated (if any).
1004 CLEANUP_PUSH_PFX(la
, g_free
, eap_buffer
);
1005 CLEANUP_PUSH_PFX(lb
, vsa_buffer_table_destroy
, (void *)vsa_buffer_table
);
1007 while (length
> 0) {
1008 radius_attr_info_t
* dictionary_entry
= NULL
;
1014 proto_item
* avp_item
;
1015 proto_item
* avp_len_item
;
1016 proto_tree
* avp_tree
;
1019 item
= proto_tree_add_text(tree
, tvb
, offset
, 0,
1020 "Not enough room in packet for AVP header");
1021 PROTO_ITEM_SET_GENERATED(item
);
1022 break; /* exit outer loop, then cleanup & return */
1024 avp_type
= tvb_get_guint8(tvb
,offset
);
1025 avp_length
= tvb_get_guint8(tvb
,offset
+1);
1027 if (avp_length
< 2) {
1028 item
= proto_tree_add_text(tree
, tvb
, offset
, 0,
1029 "AVP too short: length %u < 2", avp_length
);
1030 PROTO_ITEM_SET_GENERATED(item
);
1031 break; /* exit outer loop, then cleanup & return */
1034 if (length
< avp_length
) {
1035 item
= proto_tree_add_text(tree
, tvb
, offset
, 0,
1036 "Not enough room in packet for AVP");
1037 PROTO_ITEM_SET_GENERATED(item
);
1038 break; /* exit outer loop, then cleanup & return */
1041 length
-= avp_length
;
1043 dictionary_entry
= (radius_attr_info_t
*)g_hash_table_lookup(dict
->attrs_by_id
, GUINT_TO_POINTER(avp_type
));
1045 if (! dictionary_entry
) {
1046 dictionary_entry
= &no_dictionary_entry
;
1049 avp_item
= proto_tree_add_text(tree
, tvb
, offset
, avp_length
,
1050 "AVP: l=%u t=%s(%u)", avp_length
,
1051 dictionary_entry
->name
, avp_type
);
1056 if (avp_type
== RADIUS_ATTR_TYPE_VENDOR_SPECIFIC
) {
1057 radius_vendor_info_t
* vendor
;
1058 proto_tree
* vendor_tree
;
1059 gint max_offset
= offset
+ avp_length
;
1060 const gchar
* vendor_str
;
1062 /* XXX TODO: handle 2 byte codes for USR */
1064 if (avp_length
< 4) {
1065 proto_item_append_text(avp_item
, " [AVP too short; no room for vendor ID]");
1066 offset
+= avp_length
;
1067 continue; /* while (length > 0) */
1069 vendor_id
= tvb_get_ntohl(tvb
,offset
);
1074 vendor
= (radius_vendor_info_t
*)g_hash_table_lookup(dict
->vendors_by_id
,GUINT_TO_POINTER(vendor_id
));
1075 vendor_str
= val_to_str_ext_const(vendor_id
, &sminmpec_values_ext
, "Unknown");
1077 vendor
= &no_vendor
;
1079 proto_item_append_text(avp_item
, " v=%s(%u)", vendor_str
,
1082 vendor_tree
= proto_item_add_subtree(avp_item
,vendor
->ett
);
1084 while (offset
< max_offset
) {
1085 guint32 avp_vsa_type
;
1086 guint32 avp_vsa_len
;
1087 guint8 avp_vsa_flags
= 0;
1088 guint32 avp_vsa_header_len
= vendor
->type_octets
+ vendor
->length_octets
+ (vendor
->has_flags
? 1 : 0);
1090 switch (vendor
->type_octets
) {
1092 avp_vsa_type
= tvb_get_guint8(tvb
,offset
++);
1095 avp_vsa_type
= tvb_get_ntohs(tvb
,offset
);
1099 avp_vsa_type
= tvb_get_ntohl(tvb
,offset
);
1103 avp_vsa_type
= tvb_get_guint8(tvb
,offset
++);
1106 switch (vendor
->length_octets
) {
1108 avp_vsa_len
= tvb_get_guint8(tvb
,offset
++);
1111 avp_vsa_len
= avp_length
;
1114 avp_vsa_len
= tvb_get_ntohs(tvb
,offset
);
1118 avp_vsa_len
= tvb_get_guint8(tvb
,offset
++);
1121 if (vendor
->has_flags
) {
1122 avp_vsa_flags
= tvb_get_guint8(tvb
,offset
++);
1125 if (avp_vsa_len
< avp_vsa_header_len
) {
1126 proto_tree_add_text(tree
, tvb
, offset
+1, 1,
1128 break; /* exit while (offset < max_offset) loop */
1131 avp_vsa_len
-= avp_vsa_header_len
;
1133 dictionary_entry
= (radius_attr_info_t
*)g_hash_table_lookup(vendor
->attrs_by_id
,GUINT_TO_POINTER(avp_vsa_type
));
1135 if ( !dictionary_entry
) {
1136 dictionary_entry
= &no_dictionary_entry
;
1139 if (vendor
->has_flags
){
1140 avp_item
= proto_tree_add_text(vendor_tree
,tvb
,offset
-avp_vsa_header_len
,avp_vsa_len
+avp_vsa_header_len
,
1141 "VSA: l=%u t=%s(%u) C=0x%02x",
1142 avp_vsa_len
+avp_vsa_header_len
, dictionary_entry
->name
, avp_vsa_type
, avp_vsa_flags
);
1144 avp_item
= proto_tree_add_text(vendor_tree
,tvb
,offset
-avp_vsa_header_len
,avp_vsa_len
+avp_vsa_header_len
,
1145 "VSA: l=%u t=%s(%u)",
1146 avp_vsa_len
+avp_vsa_header_len
, dictionary_entry
->name
, avp_vsa_type
);
1149 avp_tree
= proto_item_add_subtree(avp_item
,dictionary_entry
->ett
);
1152 avp_len_item
= proto_tree_add_uint(avp_tree
,
1153 dictionary_entry
->hf_len
,
1154 tvb
,0,0,avp_length
);
1155 PROTO_ITEM_SET_GENERATED(avp_len_item
);
1158 if (vendor
->has_flags
) {
1159 radius_vsa_buffer_key key
;
1160 radius_vsa_buffer
* vsa_buffer
= NULL
;
1161 key
.vendor_id
= vendor_id
;
1162 key
.vsa_type
= avp_vsa_type
;
1164 if (!vsa_buffer_table
) {
1165 vsa_buffer_table
= g_hash_table_new(radius_vsa_hash
, radius_vsa_equal
);
1168 vsa_buffer
= (radius_vsa_buffer
*)g_hash_table_lookup(vsa_buffer_table
, &key
);
1170 vsa_buffer
->data
= (guint8
*)g_realloc(vsa_buffer
->data
, vsa_buffer
->len
+ avp_vsa_len
);
1171 tvb_memcpy(tvb
, vsa_buffer
->data
+ vsa_buffer
->len
, offset
, avp_vsa_len
);
1172 vsa_buffer
->len
+= avp_vsa_len
;
1173 vsa_buffer
->seg_num
++;
1176 if (avp_vsa_flags
& 0x80) {
1178 vsa_buffer
= (radius_vsa_buffer
*)g_malloc(sizeof(radius_vsa_buffer
));
1179 vsa_buffer
->key
.vendor_id
= vendor_id
;
1180 vsa_buffer
->key
.vsa_type
= avp_vsa_type
;
1181 vsa_buffer
->len
= avp_vsa_len
;
1182 vsa_buffer
->seg_num
= 1;
1183 vsa_buffer
->data
= (guint8
*)g_malloc(avp_vsa_len
);
1184 tvb_memcpy(tvb
, vsa_buffer
->data
, offset
, avp_vsa_len
);
1185 g_hash_table_insert(vsa_buffer_table
, &(vsa_buffer
->key
), vsa_buffer
);
1187 proto_tree_add_text(avp_tree
, tvb
, offset
, avp_vsa_len
, "VSA fragment");
1188 proto_item_append_text(avp_item
, ": VSA fragment[%u]", vsa_buffer
->seg_num
);
1191 tvbuff_t
* vsa_tvb
= NULL
;
1192 proto_tree_add_text(avp_tree
, tvb
, offset
, avp_vsa_len
, "VSA fragment");
1193 proto_item_append_text(avp_item
, ": Last VSA fragment[%u]", vsa_buffer
->seg_num
);
1194 vsa_tvb
= tvb_new_child_real_data(tvb
, vsa_buffer
->data
, vsa_buffer
->len
, vsa_buffer
->len
);
1195 tvb_set_free_cb(vsa_tvb
, g_free
);
1196 add_new_data_source(pinfo
, vsa_tvb
, "Reassembled VSA");
1197 add_avp_to_tree(avp_tree
, avp_item
, pinfo
, vsa_tvb
, dictionary_entry
, vsa_buffer
->len
, 0);
1198 g_hash_table_remove(vsa_buffer_table
, &(vsa_buffer
->key
));
1202 add_avp_to_tree(avp_tree
, avp_item
, pinfo
, tvb
, dictionary_entry
, avp_vsa_len
, offset
);
1206 add_avp_to_tree(avp_tree
, avp_item
, pinfo
, tvb
, dictionary_entry
, avp_vsa_len
, offset
);
1209 offset
+= avp_vsa_len
;
1210 } /* while (offset < max_offset) */
1211 continue; /* while (length > 0) */
1214 avp_tree
= proto_item_add_subtree(avp_item
,dictionary_entry
->ett
);
1217 avp_len_item
= proto_tree_add_uint(avp_tree
,
1218 dictionary_entry
->hf_len
,
1219 tvb
,0,0,avp_length
);
1220 PROTO_ITEM_SET_GENERATED(avp_len_item
);
1223 tvb_len
= tvb_length_remaining(tvb
, offset
);
1225 if ((gint
)avp_length
< tvb_len
)
1226 tvb_len
= avp_length
;
1228 if (avp_type
== RADIUS_ATTR_TYPE_EAP_MESSAGE
) {
1231 /* Show this as an EAP fragment. */
1233 proto_tree_add_text(avp_tree
, tvb
, offset
, tvb_len
,
1236 if (eap_tvb
!= NULL
) {
1238 * Oops, a non-consecutive EAP-Message
1241 proto_item_append_text(avp_item
, " (non-consecutive)");
1244 * RFC 2869 says, in section 5.13, describing
1245 * the EAP-Message attribute:
1247 * The NAS places EAP messages received
1248 * from the authenticating peer into one
1249 * or more EAP-Message attributes and
1250 * forwards them to the RADIUS Server
1251 * within an Access-Request message.
1252 * If multiple EAP-Messages are
1253 * contained within an Access-Request or
1254 * Access-Challenge packet, they MUST be
1255 * in order and they MUST be consecutive
1256 * attributes in the Access-Request or
1257 * Access-Challenge packet.
1261 * The String field contains EAP packets,
1262 * as defined in [3]. If multiple
1263 * EAP-Message attributes are present
1264 * in a packet their values should be
1265 * concatenated; this allows EAP packets
1266 * longer than 253 octets to be passed
1269 * Do reassembly of EAP-Message attributes.
1270 * We just concatenate all the attributes,
1271 * and when we see either the end of the
1272 * attribute list or a non-EAP-Message
1273 * attribute, we know we're done.
1276 if (eap_buffer
== NULL
)
1277 eap_buffer
= (guint8
*)g_malloc(eap_tot_len_captured
+ tvb_len
);
1279 eap_buffer
= (guint8
*)g_realloc(eap_buffer
,
1280 eap_tot_len_captured
+ tvb_len
);
1281 tvb_memcpy(tvb
, eap_buffer
+ eap_tot_len_captured
, offset
,
1283 eap_tot_len_captured
+= tvb_len
;
1284 eap_tot_len
+= avp_length
;
1286 if ( tvb_bytes_exist(tvb
, offset
+ avp_length
+ 1, 1) ) {
1287 guint8 next_type
= tvb_get_guint8(tvb
, offset
+ avp_length
);
1289 if ( next_type
!= RADIUS_ATTR_TYPE_EAP_MESSAGE
) {
1290 /* Non-EAP-Message attribute */
1295 * No more attributes, either because
1296 * we're at the end of the packet or
1297 * because we're at the end of the
1298 * captured packet data.
1303 if (last_eap
&& eap_buffer
) {
1304 gboolean save_writable
;
1306 proto_item_append_text(avp_item
, " Last Segment[%u]",
1309 eap_tree
= proto_item_add_subtree(avp_item
,ett_eap
);
1311 eap_tvb
= tvb_new_child_real_data(tvb
, eap_buffer
,
1312 eap_tot_len_captured
,
1314 tvb_set_free_cb(eap_tvb
, g_free
);
1315 add_new_data_source(pinfo
, eap_tvb
, "Reassembled EAP");
1318 * Don't free this when we're done -
1319 * it's associated with a tvbuff.
1324 * Set the columns non-writable,
1325 * so that the packet list shows
1326 * this as an RADIUS packet, not
1329 save_writable
= col_get_writable(pinfo
->cinfo
);
1330 col_set_writable(pinfo
->cinfo
, FALSE
);
1332 call_dissector(eap_handle
, eap_tvb
, pinfo
, eap_tree
);
1334 col_set_writable(pinfo
->cinfo
, save_writable
);
1336 proto_item_append_text(avp_item
, " Segment[%u]",
1341 offset
+= avp_length
;
1343 add_avp_to_tree(avp_tree
, avp_item
, pinfo
, tvb
, dictionary_entry
,
1344 avp_length
, offset
);
1345 offset
+= avp_length
;
1348 } /* while (length > 0) */
1350 CLEANUP_CALL_AND_POP_PFX(lb
); /* vsa_buffer_table_destroy(vsa_buffer_table) */
1353 * Call the cleanup handler to free any reassembled data we haven't
1354 * attached to a tvbuff, and pop the handler.
1356 CLEANUP_CALL_AND_POP_PFX(la
);
1359 /* This function tries to determine whether a packet is radius or not */
1361 is_radius(tvbuff_t
*tvb
)
1366 code
=tvb_get_guint8(tvb
, 0);
1367 if (try_val_to_str_ext(code
, &radius_pkt_type_codes_ext
) == NULL
) {
1371 /* Check for valid length value:
1374 * The Length field is two octets. It indicates the length of the
1375 * packet including the Code, Identifier, Length, Authenticator and
1376 * Attribute fields. Octets outside the range of the Length field
1377 * MUST be treated as padding and ignored on reception. If the
1378 * packet is shorter than the Length field indicates, it MUST be
1379 * silently discarded. The minimum length is 20 and maximum length
1382 length
=tvb_get_ntohs(tvb
, 2);
1383 if ( (length
<20) || (length
>4096) ) {
1390 static void register_radius_fields(const char*);
1393 dissect_radius(tvbuff_t
*tvb
, packet_info
*pinfo
, proto_tree
*tree
, void *data _U_
)
1395 proto_tree
*radius_tree
= NULL
;
1396 proto_tree
*avptree
= NULL
;
1397 proto_item
*ti
, *hidden_item
;
1401 radius_info_t
*rad_info
;
1403 conversation_t
* conversation
;
1404 radius_call_info_key radius_call_key
;
1405 radius_call_info_key
*new_radius_call_key
;
1406 radius_call_t
*radius_call
= NULL
;
1407 static address null_address
= { AT_NONE
, -1, 0, NULL
};
1409 /* does this look like radius ? */
1410 if(!is_radius(tvb
)){
1414 col_set_str(pinfo
->cinfo
, COL_PROTOCOL
, "RADIUS");
1415 col_clear(pinfo
->cinfo
, COL_INFO
);
1417 rh
.rh_code
=tvb_get_guint8(tvb
,0);
1418 rh
.rh_ident
=tvb_get_guint8(tvb
,1);
1419 rh
.rh_pktlength
=tvb_get_ntohs(tvb
,2);
1422 /* Initialise stat info for passing to tap */
1423 rad_info
= wmem_new(wmem_packet_scope(), radius_info_t
);
1425 rad_info
->ident
= 0;
1426 rad_info
->req_time
.secs
= 0;
1427 rad_info
->req_time
.nsecs
= 0;
1428 rad_info
->is_duplicate
= FALSE
;
1429 rad_info
->request_available
= FALSE
;
1430 rad_info
->req_num
= 0; /* frame number request seen */
1431 rad_info
->rspcode
= 0;
1433 rad_info
->code
= rh
.rh_code
;
1434 rad_info
->ident
= rh
.rh_ident
;
1435 tap_queue_packet(radius_tap
, pinfo
, rad_info
);
1437 col_add_fstr(pinfo
->cinfo
,COL_INFO
,"%s(%d) (id=%d, l=%d)",
1438 val_to_str_ext_const(rh
.rh_code
, &radius_pkt_type_codes_ext
, "Unknown Packet"),
1439 rh
.rh_code
, rh
.rh_ident
, rh
.rh_pktlength
);
1443 /* Forces load of header fields, if not already done so */
1444 DISSECTOR_ASSERT(proto_registrar_get_byname("radius.code"));
1446 ti
= proto_tree_add_item(tree
,proto_radius
, tvb
, 0, rh
.rh_pktlength
, ENC_NA
);
1447 radius_tree
= proto_item_add_subtree(ti
, ett_radius
);
1448 proto_tree_add_uint(radius_tree
,hf_radius_code
, tvb
, 0, 1, rh
.rh_code
);
1449 proto_tree_add_uint_format(radius_tree
,hf_radius_id
, tvb
, 1, 1, rh
.rh_ident
,
1450 "Packet identifier: 0x%01x (%d)", rh
.rh_ident
, rh
.rh_ident
);
1454 * Make sure the length is sane.
1456 if (rh
.rh_pktlength
< HDR_LENGTH
)
1460 proto_tree_add_uint_format_value(radius_tree
, hf_radius_length
,
1461 tvb
, 2, 2, rh
.rh_pktlength
, "%u (bogus, < %u)",
1462 rh
.rh_pktlength
, HDR_LENGTH
);
1464 return tvb_length(tvb
);
1467 avplength
= rh
.rh_pktlength
- HDR_LENGTH
;
1470 proto_tree_add_uint(radius_tree
, hf_radius_length
, tvb
, 2, 2, rh
.rh_pktlength
);
1471 proto_tree_add_item(radius_tree
, hf_radius_authenticator
, tvb
, 4, AUTHENTICATOR_LENGTH
,ENC_NA
);
1473 tvb_memcpy(tvb
, authenticator
, 4, AUTHENTICATOR_LENGTH
);
1475 /* Conversation support REQUEST/RESPONSES */
1478 case RADIUS_PKT_TYPE_ACCESS_REQUEST
:
1479 case RADIUS_PKT_TYPE_ACCOUNTING_REQUEST
:
1480 case RADIUS_PKT_TYPE_PASSWORD_REQUEST
:
1481 case RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST
:
1482 case RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST
:
1483 case RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST
:
1484 case RADIUS_PKT_TYPE_EVENT_REQUEST
:
1485 case RADIUS_PKT_TYPE_DISCONNECT_REQUEST
:
1486 case RADIUS_PKT_TYPE_COA_REQUEST
:
1487 case RADIUS_PKT_TYPE_ALU_STATE_REQUEST
:
1488 /* Don't bother creating conversations if we're encapsulated within
1489 * an error packet, such as an ICMP destination unreachable */
1490 if (pinfo
->flags
.in_error_pkt
)
1495 hidden_item
= proto_tree_add_boolean(radius_tree
, hf_radius_req
, tvb
, 0, 0, TRUE
);
1496 PROTO_ITEM_SET_HIDDEN(hidden_item
);
1499 /* Keep track of the address and port whence the call came
1500 * so that we can match up requests with replies.
1502 * Because it is UDP and the reply can come from any IP
1503 * and port (not necessarily the request dest), we only
1504 * track the source IP and port of the request to match
1509 * XXX - can we just use NO_ADDR_B? Unfortunately,
1510 * you currently still have to pass a non-null
1511 * pointer for the second address argument even
1514 conversation
= find_conversation(pinfo
->fd
->num
, &pinfo
->src
,
1515 &null_address
, pinfo
->ptype
, pinfo
->srcport
,
1516 pinfo
->destport
, 0);
1517 if (conversation
== NULL
)
1519 /* It's not part of any conversation - create a new one. */
1520 conversation
= conversation_new(pinfo
->fd
->num
, &pinfo
->src
,
1521 &null_address
, pinfo
->ptype
, pinfo
->srcport
,
1522 pinfo
->destport
, 0);
1525 /* Prepare the key data */
1526 radius_call_key
.code
= rh
.rh_code
;
1527 radius_call_key
.ident
= rh
.rh_ident
;
1528 radius_call_key
.conversation
= conversation
;
1529 radius_call_key
.req_time
= pinfo
->fd
->abs_ts
;
1531 /* Look up the request */
1532 radius_call
= (radius_call_t
*)g_hash_table_lookup(radius_calls
, &radius_call_key
);
1533 if (radius_call
!= NULL
)
1535 /* We've seen a request with this ID, with the same
1536 destination, before - but was it *this* request? */
1537 if (pinfo
->fd
->num
!= radius_call
->req_num
)
1539 /* No, so it's a duplicate request. Mark it as such. */
1540 rad_info
->is_duplicate
= TRUE
;
1541 rad_info
->req_num
= radius_call
->req_num
;
1542 col_append_fstr(pinfo
->cinfo
, COL_INFO
,
1543 ", Duplicate Request ID:%u", rh
.rh_ident
);
1548 hidden_item
= proto_tree_add_uint(radius_tree
, hf_radius_dup
, tvb
, 0,0, rh
.rh_ident
);
1549 PROTO_ITEM_SET_HIDDEN(hidden_item
);
1550 item
= proto_tree_add_uint(radius_tree
, hf_radius_req_dup
, tvb
, 0,0, rh
.rh_ident
);
1551 PROTO_ITEM_SET_GENERATED(item
);
1557 /* Prepare the value data.
1558 "req_num" and "rsp_num" are frame numbers;
1559 frame numbers are 1-origin, so we use 0
1560 to mean "we don't yet know in which frame
1561 the reply for this call appears". */
1562 new_radius_call_key
= wmem_new(wmem_file_scope(), radius_call_info_key
);
1563 *new_radius_call_key
= radius_call_key
;
1564 radius_call
= wmem_new(wmem_file_scope(), radius_call_t
);
1565 radius_call
->req_num
= pinfo
->fd
->num
;
1566 radius_call
->rsp_num
= 0;
1567 radius_call
->ident
= rh
.rh_ident
;
1568 radius_call
->code
= rh
.rh_code
;
1569 radius_call
->responded
= FALSE
;
1570 radius_call
->req_time
= pinfo
->fd
->abs_ts
;
1571 radius_call
->rspcode
= 0;
1574 g_hash_table_insert(radius_calls
, new_radius_call_key
, radius_call
);
1576 if (tree
&& radius_call
->rsp_num
)
1579 item
= proto_tree_add_uint_format(radius_tree
,
1580 hf_radius_rsp_frame
, tvb
, 0, 0, radius_call
->rsp_num
,
1581 "The response to this request is in frame %u",
1582 radius_call
->rsp_num
);
1583 PROTO_ITEM_SET_GENERATED(item
);
1586 case RADIUS_PKT_TYPE_ACCESS_ACCEPT
:
1587 case RADIUS_PKT_TYPE_ACCESS_REJECT
:
1588 case RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE
:
1589 case RADIUS_PKT_TYPE_PASSWORD_ACK
:
1590 case RADIUS_PKT_TYPE_PASSWORD_REJECT
:
1591 case RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE
:
1592 case RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE
:
1593 case RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE
:
1594 case RADIUS_PKT_TYPE_EVENT_RESPONSE
:
1595 case RADIUS_PKT_TYPE_DISCONNECT_ACK
:
1596 case RADIUS_PKT_TYPE_DISCONNECT_NAK
:
1597 case RADIUS_PKT_TYPE_COA_ACK
:
1598 case RADIUS_PKT_TYPE_COA_NAK
:
1599 case RADIUS_PKT_TYPE_ACCESS_CHALLENGE
:
1600 case RADIUS_PKT_TYPE_ALU_STATE_ACCEPT
:
1601 case RADIUS_PKT_TYPE_ALU_STATE_REJECT
:
1602 case RADIUS_PKT_TYPE_ALU_STATE_ERROR
:
1603 /* Don't bother finding conversations if we're encapsulated within
1604 * an error packet, such as an ICMP destination unreachable */
1605 if (pinfo
->flags
.in_error_pkt
)
1610 hidden_item
= proto_tree_add_boolean(radius_tree
, hf_radius_rsp
, tvb
, 0, 0, TRUE
);
1611 PROTO_ITEM_SET_HIDDEN(hidden_item
);
1614 /* Check for RADIUS response. A response must match a call that
1615 * we've seen, and the response must be sent to the same
1616 * port and address that the call came from.
1618 * Because it is UDP and the reply can come from any IP
1619 * and port (not necessarily the request dest), we only
1620 * track the source IP and port of the request to match
1624 /* XXX - can we just use NO_ADDR_B? Unfortunately,
1625 * you currently still have to pass a non-null
1626 * pointer for the second address argument even
1629 conversation
= find_conversation(pinfo
->fd
->num
, &null_address
,
1630 &pinfo
->dst
, pinfo
->ptype
, pinfo
->srcport
, pinfo
->destport
, 0);
1631 if (conversation
!= NULL
)
1633 /* Look only for matching request, if
1634 matching conversation is available. */
1635 /* Prepare the key data */
1636 radius_call_key
.code
= rh
.rh_code
;
1637 radius_call_key
.ident
= rh
.rh_ident
;
1638 radius_call_key
.conversation
= conversation
;
1639 radius_call_key
.req_time
= pinfo
->fd
->abs_ts
;
1641 radius_call
= (radius_call_t
*)g_hash_table_lookup(radius_calls
, &radius_call_key
);
1644 /* Indicate the frame to which this is a reply. */
1645 if (radius_call
->req_num
)
1647 rad_info
->request_available
= TRUE
;
1648 rad_info
->req_num
= radius_call
->req_num
;
1649 radius_call
->responded
= TRUE
;
1655 item
= proto_tree_add_uint_format(radius_tree
,
1656 hf_radius_req_frame
, tvb
, 0, 0,
1657 radius_call
->req_num
,
1658 "This is a response to a request in frame %u",
1659 radius_call
->req_num
);
1660 PROTO_ITEM_SET_GENERATED(item
);
1661 nstime_delta(&delta
, &pinfo
->fd
->abs_ts
, &radius_call
->req_time
);
1662 item
= proto_tree_add_time(radius_tree
, hf_radius_time
, tvb
, 0, 0, &delta
);
1663 PROTO_ITEM_SET_GENERATED(item
);
1667 if (radius_call
->rsp_num
== 0)
1669 /* We have not yet seen a response to that call, so
1670 this must be the first response; remember its
1672 radius_call
->rsp_num
= pinfo
->fd
->num
;
1676 /* We have seen a response to this call - but was it
1677 *this* response? (disregard provisional responses) */
1678 if ( (radius_call
->rsp_num
!= pinfo
->fd
->num
) && (radius_call
->rspcode
== rh
.rh_code
) )
1680 /* No, so it's a duplicate response. Mark it as such. */
1681 rad_info
->is_duplicate
= TRUE
;
1682 col_append_fstr(pinfo
->cinfo
, COL_INFO
,
1683 ", Duplicate Response ID:%u", rh
.rh_ident
);
1688 hidden_item
= proto_tree_add_uint(radius_tree
,
1689 hf_radius_dup
, tvb
, 0,0, rh
.rh_ident
);
1690 PROTO_ITEM_SET_HIDDEN(hidden_item
);
1691 item
= proto_tree_add_uint(radius_tree
,
1692 hf_radius_rsp_dup
, tvb
, 0, 0, rh
.rh_ident
);
1693 PROTO_ITEM_SET_GENERATED(item
);
1697 /* Now store the response code (after comparison above) */
1698 radius_call
->rspcode
= rh
.rh_code
;
1699 rad_info
->rspcode
= rh
.rh_code
;
1709 rad_info
->req_time
.secs
= radius_call
->req_time
.secs
;
1710 rad_info
->req_time
.nsecs
= radius_call
->req_time
.nsecs
;
1715 /* list the attribute value pairs */
1716 avptf
= proto_tree_add_text(radius_tree
, tvb
, HDR_LENGTH
,
1717 avplength
, "Attribute Value Pairs");
1718 avptree
= proto_item_add_subtree(avptf
, ett_radius_avp
);
1719 dissect_attribute_value_pairs(avptree
, pinfo
, tvb
, HDR_LENGTH
,
1723 return tvb_length(tvb
);
1727 static void register_attrs(gpointer k _U_
, gpointer v
, gpointer p
) {
1728 radius_attr_info_t
* a
= (radius_attr_info_t
*)v
;
1730 gint
* ett
= &(a
->ett
);
1731 gchar
* abbrev
= wmem_strdup_printf(wmem_epan_scope(), "radius.%s", a
->name
);
1732 hf_register_info hfri
[] = {
1733 { NULL
, { NULL
,NULL
, FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
1734 { NULL
, { NULL
,NULL
, FT_UINT8
, BASE_DEC
, NULL
, 0x0, NULL
, HFILL
}},
1735 { NULL
, { NULL
,NULL
, FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}},
1736 { NULL
, { NULL
,NULL
, FT_NONE
, BASE_NONE
, NULL
, 0x0, NULL
, HFILL
}}
1739 hfett_t
* ri
= (hfett_t
*)p
;
1741 for(i
=0; abbrev
[i
]; i
++) {
1742 if(abbrev
[i
] == '-') abbrev
[i
] = '_';
1743 if(abbrev
[i
] == '/') abbrev
[i
] = '_';
1746 hfri
[0].p_id
= &(a
->hf
);
1747 hfri
[1].p_id
= &(a
->hf_len
);
1749 hfri
[0].hfinfo
.name
= a
->name
;
1750 hfri
[0].hfinfo
.abbrev
= abbrev
;
1752 hfri
[1].hfinfo
.name
= "Length";
1753 hfri
[1].hfinfo
.abbrev
= wmem_strdup_printf(wmem_epan_scope(), "%s.len", abbrev
);
1754 hfri
[1].hfinfo
.blurb
= wmem_strdup_printf(wmem_epan_scope(), "%s Length", a
->name
);
1756 if (a
->type
== radius_integer
) {
1757 hfri
[0].hfinfo
.type
= FT_UINT32
;
1758 hfri
[0].hfinfo
.display
= BASE_DEC
;
1760 hfri
[2].p_id
= &(a
->hf_alt
);
1761 hfri
[2].hfinfo
.name
= wmem_strdup(wmem_epan_scope(), a
->name
);
1762 hfri
[2].hfinfo
.abbrev
= abbrev
;
1763 hfri
[2].hfinfo
.type
= FT_UINT64
;
1764 hfri
[2].hfinfo
.display
= BASE_DEC
;
1767 hfri
[0].hfinfo
.strings
= VALS(a
->vs
);
1771 }else if (a
->type
== radius_signed
) {
1772 hfri
[0].hfinfo
.type
= FT_INT32
;
1773 hfri
[0].hfinfo
.display
= BASE_DEC
;
1775 hfri
[2].p_id
= &(a
->hf_alt
);
1776 hfri
[2].hfinfo
.name
= wmem_strdup(wmem_epan_scope(), a
->name
);
1777 hfri
[2].hfinfo
.abbrev
= abbrev
;
1778 hfri
[2].hfinfo
.type
= FT_INT64
;
1779 hfri
[2].hfinfo
.display
= BASE_DEC
;
1782 hfri
[0].hfinfo
.strings
= VALS(a
->vs
);
1786 } else if (a
->type
== radius_string
) {
1787 hfri
[0].hfinfo
.type
= FT_STRING
;
1788 hfri
[0].hfinfo
.display
= BASE_NONE
;
1790 if (a
->encrypt
!= 0) {
1792 * This attribute is encrypted, so create an
1793 * alternative field for the encrypted value.
1795 hfri
[2].p_id
= &(a
->hf_alt
);
1796 hfri
[2].hfinfo
.name
= wmem_strdup_printf(wmem_epan_scope(), "%s (encrypted)", a
->name
);
1797 hfri
[2].hfinfo
.abbrev
= wmem_strdup_printf(wmem_epan_scope(), "%s_encrypted", abbrev
);
1798 hfri
[2].hfinfo
.type
= FT_BYTES
;
1799 hfri
[2].hfinfo
.display
= BASE_NONE
;
1803 } else if (a
->type
== radius_octets
) {
1804 hfri
[0].hfinfo
.type
= FT_BYTES
;
1805 hfri
[0].hfinfo
.display
= BASE_NONE
;
1806 } else if (a
->type
== radius_ipaddr
) {
1807 hfri
[0].hfinfo
.type
= FT_IPv4
;
1808 hfri
[0].hfinfo
.display
= BASE_NONE
;
1809 } else if (a
->type
== radius_ipv6addr
) {
1810 hfri
[0].hfinfo
.type
= FT_IPv6
;
1811 hfri
[0].hfinfo
.display
= BASE_NONE
;
1812 } else if (a
->type
== radius_ipv6prefix
) {
1813 hfri
[0].hfinfo
.type
= FT_BYTES
;
1814 hfri
[0].hfinfo
.display
= BASE_NONE
;
1815 } else if (a
->type
== radius_ipxnet
) {
1816 hfri
[0].hfinfo
.type
= FT_IPXNET
;
1817 hfri
[0].hfinfo
.display
= BASE_NONE
;
1818 } else if (a
->type
== radius_date
) {
1819 hfri
[0].hfinfo
.type
= FT_ABSOLUTE_TIME
;
1820 hfri
[0].hfinfo
.display
= ABSOLUTE_TIME_LOCAL
;
1821 } else if (a
->type
== radius_abinary
) {
1822 hfri
[0].hfinfo
.type
= FT_BYTES
;
1823 hfri
[0].hfinfo
.display
= BASE_NONE
;
1824 } else if (a
->type
== radius_ifid
) {
1825 hfri
[0].hfinfo
.type
= FT_BYTES
;
1826 hfri
[0].hfinfo
.display
= BASE_NONE
;
1827 } else if (a
->type
== radius_combo_ip
) {
1828 hfri
[0].hfinfo
.type
= FT_IPv4
;
1829 hfri
[0].hfinfo
.display
= BASE_NONE
;
1831 hfri
[2].p_id
= &(a
->hf_alt
);
1832 hfri
[2].hfinfo
.name
= wmem_strdup(wmem_epan_scope(), a
->name
);
1833 hfri
[2].hfinfo
.abbrev
= wmem_strdup(wmem_epan_scope(), abbrev
);
1834 hfri
[2].hfinfo
.type
= FT_IPv6
;
1835 hfri
[2].hfinfo
.display
= BASE_NONE
;
1838 } else if (a
->type
== radius_tlv
) {
1839 hfri
[0].hfinfo
.type
= FT_BYTES
;
1840 hfri
[0].hfinfo
.display
= BASE_NONE
;
1842 hfri
[0].hfinfo
.type
= FT_BYTES
;
1843 hfri
[0].hfinfo
.display
= BASE_NONE
;
1847 hfri
[len_hf
].p_id
= &(a
->hf_tag
);
1848 hfri
[len_hf
].hfinfo
.name
= "Tag";
1849 hfri
[len_hf
].hfinfo
.abbrev
= wmem_strdup_printf(wmem_epan_scope(), "%s.tag", abbrev
);
1850 hfri
[len_hf
].hfinfo
.blurb
= wmem_strdup_printf(wmem_epan_scope(), "%s Tag", a
->name
);
1851 hfri
[len_hf
].hfinfo
.type
= FT_UINT8
;
1852 hfri
[len_hf
].hfinfo
.display
= BASE_HEX
;
1856 wmem_array_append(ri
->hf
,hfri
,len_hf
);
1857 wmem_array_append_one(ri
->ett
,ett
);
1859 if (a
->tlvs_by_id
) {
1860 g_hash_table_foreach(a
->tlvs_by_id
,register_attrs
,ri
);
1864 static void register_vendors(gpointer k _U_
, gpointer v
, gpointer p
) {
1865 radius_vendor_info_t
* vnd
= (radius_vendor_info_t
*)v
;
1866 hfett_t
* ri
= (hfett_t
*)p
;
1867 value_string vnd_vs
;
1868 gint
* ett_p
= &(vnd
->ett
);
1870 vnd_vs
.value
= vnd
->code
;
1871 vnd_vs
.strptr
= vnd
->name
;
1873 wmem_array_append_one(ri
->vend_vs
,vnd_vs
);
1874 wmem_array_append_one(ri
->ett
,ett_p
);
1876 g_hash_table_foreach(vnd
->attrs_by_id
,register_attrs
,ri
);
1880 extern void radius_register_avp_dissector(guint32 vendor_id
, guint32 attribute_id
, radius_avp_dissector_t radius_avp_dissector
) {
1881 radius_vendor_info_t
* vendor
;
1882 radius_attr_info_t
* dictionary_entry
;
1885 DISSECTOR_ASSERT(radius_avp_dissector
!= NULL
);
1888 vendor
= (radius_vendor_info_t
*)g_hash_table_lookup(dict
->vendors_by_id
,GUINT_TO_POINTER(vendor_id
));
1891 vendor
= (radius_vendor_info_t
*)g_malloc(sizeof(radius_vendor_info_t
));
1893 vendor
->name
= g_strdup_printf("%s-%u",
1894 val_to_str_ext_const(vendor_id
, &sminmpec_values_ext
, "Unknown"),
1896 vendor
->code
= vendor_id
;
1897 vendor
->attrs_by_id
= g_hash_table_new(g_direct_hash
,g_direct_equal
);
1898 vendor
->ett
= no_vendor
.ett
;
1900 /* XXX: Default "standard" values: Should be parameters ? */
1901 vendor
->type_octets
= 1;
1902 vendor
->length_octets
= 1;
1903 vendor
->has_flags
= FALSE
;
1905 g_hash_table_insert(dict
->vendors_by_id
,GUINT_TO_POINTER(vendor
->code
),vendor
);
1906 g_hash_table_insert(dict
->vendors_by_name
,(gpointer
)(vendor
->name
),vendor
);
1909 dictionary_entry
= (radius_attr_info_t
*)g_hash_table_lookup(vendor
->attrs_by_id
,GUINT_TO_POINTER(attribute_id
));
1910 by_id
= vendor
->attrs_by_id
;
1912 dictionary_entry
= (radius_attr_info_t
*)g_hash_table_lookup(dict
->attrs_by_id
,GUINT_TO_POINTER(attribute_id
));
1913 by_id
= dict
->attrs_by_id
;
1916 if (!dictionary_entry
) {
1917 dictionary_entry
= (radius_attr_info_t
*)g_malloc(sizeof(radius_attr_info_t
));
1919 dictionary_entry
->name
= g_strdup_printf("Unknown-Attribute-%u",attribute_id
);
1920 dictionary_entry
->code
= attribute_id
;
1921 dictionary_entry
->encrypt
= 0;
1922 dictionary_entry
->type
= NULL
;
1923 dictionary_entry
->vs
= NULL
;
1924 dictionary_entry
->hf
= no_dictionary_entry
.hf
;
1925 dictionary_entry
->tagged
= 0;
1926 dictionary_entry
->hf_tag
= -1;
1927 dictionary_entry
->hf_len
= no_dictionary_entry
.hf_len
;
1928 dictionary_entry
->ett
= no_dictionary_entry
.ett
;
1929 dictionary_entry
->tlvs_by_id
= NULL
;
1931 g_hash_table_insert(by_id
,GUINT_TO_POINTER(dictionary_entry
->code
),dictionary_entry
);
1934 dictionary_entry
->dissector
= radius_avp_dissector
;
1938 /* Discard and init any state we've saved */
1940 radius_init_protocol(void)
1942 if (radius_calls
!= NULL
)
1944 g_hash_table_destroy(radius_calls
);
1945 radius_calls
= NULL
;
1948 radius_calls
= g_hash_table_new(radius_call_hash
, radius_call_equal
);
1951 static void register_radius_fields(const char* unused _U_
) {
1952 hf_register_info base_hf
[] = {
1954 { "Request", "radius.req", FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
1955 "TRUE if RADIUS request", HFILL
}},
1957 { "Response", "radius.rsp", FT_BOOLEAN
, BASE_NONE
, NULL
, 0x0,
1958 "TRUE if RADIUS response", HFILL
}},
1959 { &hf_radius_req_frame
,
1960 { "Request Frame", "radius.reqframe", FT_FRAMENUM
, BASE_NONE
, NULL
, 0,
1962 { &hf_radius_rsp_frame
,
1963 { "Response Frame", "radius.rspframe", FT_FRAMENUM
, BASE_NONE
, NULL
, 0,
1966 { "Time from request", "radius.time", FT_RELATIVE_TIME
, BASE_NONE
, NULL
, 0,
1967 "Timedelta between Request and Response", HFILL
}},
1969 { "Code","radius.code", FT_UINT8
, BASE_DEC
|BASE_EXT_STRING
, &radius_pkt_type_codes_ext
, 0x0,
1972 { "Identifier", "radius.id", FT_UINT8
, BASE_DEC
, NULL
, 0x0,
1974 { &hf_radius_authenticator
,
1975 { "Authenticator", "radius.authenticator", FT_BYTES
, BASE_NONE
, NULL
, 0x0,
1977 { &hf_radius_length
,
1978 { "Length","radius.length", FT_UINT16
, BASE_DEC
, NULL
, 0x0,
1980 { &(no_dictionary_entry
.hf
),
1981 { "Unknown-Attribute","radius.Unknown_Attribute", FT_BYTES
, BASE_NONE
, NULL
, 0x0,
1983 { &(no_dictionary_entry
.hf_len
),
1984 { "Unknown-Attribute Length","radius.Unknown_Attribute.length", FT_UINT8
, BASE_DEC
, NULL
, 0x0,
1986 { &hf_radius_chap_password
,
1987 { "CHAP-Password","radius.CHAP_Password", FT_BYTES
, BASE_NONE
, NULL
, 0x0,
1989 { &hf_radius_chap_ident
,
1990 { "CHAP Ident","radius.CHAP_Ident", FT_UINT8
, BASE_HEX
, NULL
, 0x0,
1992 { &hf_radius_chap_string
,
1993 { "CHAP String","radius.CHAP_String", FT_BYTES
, BASE_NONE
, NULL
, 0x0,
1995 { &hf_radius_framed_ip_address
,
1996 { "Framed-IP-Address","radius.Framed-IP-Address", FT_IPv4
, BASE_NONE
, NULL
, 0x0,
1998 { &hf_radius_login_ip_host
,
1999 { "Login-IP-Host","radius.Login-IP-Host", FT_IPv4
, BASE_NONE
, NULL
, 0x0,
2001 { &hf_radius_framed_ipx_network
,
2002 { "Framed-IPX-Network","radius.Framed-IPX-Network", FT_IPXNET
, BASE_NONE
, NULL
, 0x0,
2004 { &hf_radius_cosine_vpi
,
2005 { "Cosine-VPI","radius.Cosine-Vpi", FT_UINT16
, BASE_DEC
, NULL
, 0x0,
2007 { &hf_radius_cosine_vci
,
2008 { "Cosine-VCI","radius.Cosine-Vci", FT_UINT16
, BASE_DEC
, NULL
, 0x0,
2011 { "Duplicate Message", "radius.dup", FT_UINT32
, BASE_DEC
, NULL
, 0x0,
2013 { &hf_radius_req_dup
,
2014 { "Duplicate Request", "radius.req.dup", FT_UINT32
, BASE_DEC
, NULL
, 0x0,
2016 { &hf_radius_rsp_dup
,
2017 { "Duplicate Response", "radius.rsp.dup", FT_UINT32
, BASE_DEC
, NULL
, 0x0,
2019 { &hf_radius_ascend_data_filter
,
2020 { "Ascend Data Filter", "radius.ascenddatafilter", FT_BYTES
, BASE_NONE
, NULL
, 0x0,
2024 gint
*base_ett
[] = {
2029 &(no_dictionary_entry
.ett
),
2035 gchar
* dict_err_str
= NULL
;
2037 ri
.hf
= wmem_array_new(wmem_epan_scope(), sizeof(hf_register_info
));
2038 ri
.ett
= wmem_array_new(wmem_epan_scope(), sizeof(gint
*));
2039 ri
.vend_vs
= wmem_array_new(wmem_epan_scope(), sizeof(value_string
));
2041 wmem_array_append(ri
.hf
, base_hf
, array_length(base_hf
));
2042 wmem_array_append(ri
.ett
, base_ett
, array_length(base_ett
));
2044 dir
= get_persconffile_path("radius", FALSE
);
2046 if (test_for_directory(dir
) != EISDIR
) {
2047 /* Although dir isn't a directory it may still use memory */
2050 dir
= get_datafile_path("radius");
2052 if (test_for_directory(dir
) != EISDIR
) {
2059 radius_load_dictionary(dict
,dir
,"dictionary",&dict_err_str
);
2062 report_failure("radius: %s",dict_err_str
);
2063 g_free(dict_err_str
);
2066 g_hash_table_foreach(dict
->attrs_by_id
,register_attrs
,&ri
);
2067 g_hash_table_foreach(dict
->vendors_by_id
,register_vendors
,&ri
);
2072 proto_register_field_array(proto_radius
,(hf_register_info
*)wmem_array_get_raw(ri
.hf
),wmem_array_get_count(ri
.hf
));
2073 proto_register_subtree_array((gint
**)wmem_array_get_raw(ri
.ett
), wmem_array_get_count(ri
.ett
));
2075 no_vendor
.attrs_by_id
= g_hash_table_new(g_direct_hash
,g_direct_equal
);
2078 * Handle attributes that have a special format.
2080 radius_register_avp_dissector(0,3,dissect_chap_password
);
2081 radius_register_avp_dissector(0,8,dissect_framed_ip_address
);
2082 radius_register_avp_dissector(0,14,dissect_login_ip_host
);
2083 radius_register_avp_dissector(0,23,dissect_framed_ipx_network
);
2084 radius_register_avp_dissector(VENDOR_COSINE
,5,dissect_cosine_vpvc
);
2086 * XXX - should we just call dissect_ascend_data_filter()
2087 * in radius_abinary()?
2089 * Note that there is no attribute 242 in dictionary.redback.
2091 radius_register_avp_dissector(VENDOR_ASCEND
,242,dissect_ascend_data_filter
);
2092 radius_register_avp_dissector(VENDOR_REDBACK
,242,dissect_ascend_data_filter
);
2093 radius_register_avp_dissector(0,242,dissect_ascend_data_filter
);
2096 * XXX - we should special-case Cisco attribute 252; see the comment in
2099 radius_register_avp_dissector(VENDOR_THE3GPP
,23,dissect_radius_3gpp_ms_tmime_zone
);
2104 proto_register_radius(void)
2106 module_t
*radius_module
;
2108 proto_radius
= proto_register_protocol("Radius Protocol", "RADIUS", "radius");
2109 new_register_dissector("radius", dissect_radius
, proto_radius
);
2110 register_init_routine(&radius_init_protocol
);
2111 radius_module
= prefs_register_protocol(proto_radius
, proto_reg_handoff_radius
);
2112 prefs_register_string_preference(radius_module
,"shared_secret","Shared Secret",
2113 "Shared secret used to decode User Passwords",
2115 prefs_register_bool_preference(radius_module
,"show_length","Show AVP Lengths",
2116 "Whether to add or not to the tree the AVP's payload length",
2118 prefs_register_uint_preference(radius_module
, "alternate_port","Alternate Port",
2119 "An alternate UDP port to decode as RADIUS", 10, &alt_port_pref
);
2120 prefs_register_uint_preference(radius_module
, "request_ttl", "Request TimeToLive",
2121 "Time to live for a radius request used for matching it with a response", 10, &request_ttl
);
2122 radius_tap
= register_tap("radius");
2123 proto_register_prefix("radius",register_radius_fields
);
2125 dict
= (radius_dictionary_t
*)g_malloc(sizeof(radius_dictionary_t
));
2126 dict
->attrs_by_id
= g_hash_table_new(g_direct_hash
,g_direct_equal
);
2127 dict
->attrs_by_name
= g_hash_table_new(g_str_hash
,g_str_equal
);
2128 dict
->vendors_by_id
= g_hash_table_new(g_direct_hash
,g_direct_equal
);
2129 dict
->vendors_by_name
= g_hash_table_new(g_str_hash
,g_str_equal
);
2130 dict
->tlvs_by_name
= g_hash_table_new(g_str_hash
,g_str_equal
);
2134 proto_reg_handoff_radius(void)
2136 static gboolean initialized
= FALSE
;
2137 static dissector_handle_t radius_handle
;
2138 static guint alt_port
;
2141 radius_handle
= find_dissector("radius");
2142 dissector_add_uint("udp.port", UDP_PORT_RADIUS
, radius_handle
);
2143 dissector_add_uint("udp.port", UDP_PORT_RADIUS_NEW
, radius_handle
);
2144 dissector_add_uint("udp.port", UDP_PORT_RADACCT
, radius_handle
);
2145 dissector_add_uint("udp.port", UDP_PORT_RADACCT_NEW
, radius_handle
);
2146 dissector_add_uint("udp.port", UDP_PORT_DAE_OLD
, radius_handle
);
2147 dissector_add_uint("udp.port", UDP_PORT_DAE
, radius_handle
);
2149 eap_handle
= find_dissector("eap");
2154 dissector_delete_uint("udp.port", alt_port
, radius_handle
);
2157 if (alt_port_pref
!= 0)
2158 dissector_add_uint("udp.port", alt_port_pref
, radius_handle
);
2160 alt_port
= alt_port_pref
;
2164 * Editor modelines - http://www.wireshark.org/tools/modelines.html
2169 * indent-tabs-mode: t
2172 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
2173 * :indentSize=8:tabSize=8:noTabs=false: