Revert "TODO epan/dissectors/asn1/kerberos/packet-kerberos-template.c new GSS flags"
[wireshark-sm.git] / epan / dissectors / packet-radius.c
blob609f5052b966f557453e05c3e82c24a571ced71c
1 /* packet-radius.c
3 * Routines for RADIUS packet disassembly
4 * Copyright 1999 Johan Feyaerts
5 * Changed 03/12/2003 Rui Carmo (http://the.taoofmac.com - added all 3GPP VSAs, some parsing)
6 * Changed 07/2005 Luis Ontanon <luis@ontanon.org> - use FreeRADIUS' dictionary
7 * Changed 10/2006 Alejandro Vaquero <alejandrovaquero@yahoo.com> - add Conversations support
8 * Changed 08/2015 Didier Arenzana <darenzana@yahoo.fr> - add response authenticator validation
10 * Wireshark - Network traffic analyzer
11 * By Gerald Combs <gerald@wireshark.org>
12 * Copyright 1998 Gerald Combs
14 * SPDX-License-Identifier: GPL-2.0-or-later
16 * References:
18 * RFC 2865 - Remote Authentication Dial In User Service (RADIUS)
19 * RFC 2866 - RADIUS Accounting
20 * RFC 2867 - RADIUS Accounting Modifications for Tunnel Protocol Support
21 * RFC 2868 - RADIUS Attributes for Tunnel Protocol Support
22 * RFC 2869 - RADIUS Extensions
23 * RFC 3162 - RADIUS and IPv6
24 * RFC 3576 - Dynamic Authorization Extensions to RADIUS
25 * RFC 6929 - Remote Authentication Dial-In User Service (RADIUS) Protocol Extensions
27 * See also
29 * http://www.iana.org/assignments/radius-types
31 * and see
33 * http://freeradius.org/radiusd/man/dictionary.html
35 * for the dictionary file syntax.
40 TO (re)DO: (see svn rev 14786)
41 - dissect_3gpp_ipv6_dns_servers()
44 #include "config.h"
46 #include <stdlib.h>
47 #include <errno.h>
49 #include <epan/packet.h>
50 #include <epan/exceptions.h>
51 #include <epan/expert.h>
52 #include <epan/prefs.h>
53 #include <epan/sminmpec.h>
54 #include <epan/conversation.h>
55 #include <epan/tap.h>
56 #include <epan/rtd_table.h>
57 #include <epan/addr_resolv.h>
58 #include <wsutil/filesystem.h>
59 #include <wsutil/report_message.h>
60 #include <wsutil/wsgcrypt.h>
63 #include "packet-radius.h"
64 #include "packet-e212.h"
66 void proto_register_radius(void);
67 void proto_reg_handoff_radius(void);
69 typedef struct _e_radiushdr {
70 uint8_t rh_code;
71 uint8_t rh_ident;
72 uint16_t rh_pktlength;
73 } e_radiushdr;
75 typedef struct {
76 wmem_array_t *hf;
77 wmem_array_t *ett;
78 wmem_array_t *vend_vs;
79 } hfett_t;
81 #define AUTHENTICATOR_LENGTH 16
82 #define RD_HDR_LENGTH 4
83 #define HDR_LENGTH (RD_HDR_LENGTH + AUTHENTICATOR_LENGTH)
86 /* Container for tapping relevant data */
87 typedef struct _radius_info_t
89 unsigned code;
90 unsigned ident;
91 nstime_t req_time;
92 bool is_duplicate;
93 bool request_available;
94 uint32_t req_num; /* frame number request seen */
95 uint32_t rspcode;
96 } radius_info_t;
100 * Default RADIUS ports:
101 * 1645 (Authentication, pre RFC 2865)
102 * 1646 (Accounting, pre RFC 2866)
103 * 1812 (Authentication, RFC 2865)
104 * 1813 (Accounting, RFC 2866)
105 * 1700 (Dynamic Authorization Extensions, pre RFC 3576)
106 * 3799 (Dynamic Authorization Extensions, RFC 3576)
108 #define DEFAULT_RADIUS_PORT_RANGE "1645,1646,1700,1812,1813,3799"
110 static radius_dictionary_t *dict;
112 static int proto_radius;
114 static int hf_radius_req;
115 static int hf_radius_rsp;
116 static int hf_radius_req_frame;
117 static int hf_radius_rsp_frame;
118 static int hf_radius_time;
120 static int hf_radius_dup;
121 static int hf_radius_req_dup;
122 static int hf_radius_rsp_dup;
124 static int hf_radius_id;
125 static int hf_radius_code;
126 static int hf_radius_length;
127 static int hf_radius_authenticator;
128 static int hf_radius_authenticator_valid;
129 static int hf_radius_authenticator_invalid;
130 static int hf_radius_message_authenticator_valid;
131 static int hf_radius_message_authenticator_invalid;
133 static int hf_radius_chap_password;
134 static int hf_radius_chap_ident;
135 static int hf_radius_chap_string;
136 static int hf_radius_framed_ip_address;
138 static int hf_radius_login_ip_host;
139 static int hf_radius_framed_ipx_network;
141 static int hf_radius_cosine_vpi;
142 static int hf_radius_cosine_vci;
144 static int hf_radius_ascend_data_filter;
145 static int hf_radius_ascend_data_filter_type;
146 static int hf_radius_ascend_data_filter_filteror;
147 static int hf_radius_ascend_data_filter_inout;
148 static int hf_radius_ascend_data_filter_spare;
149 static int hf_radius_ascend_data_filter_src_ipv4;
150 static int hf_radius_ascend_data_filter_dst_ipv4;
151 static int hf_radius_ascend_data_filter_src_ipv6;
152 static int hf_radius_ascend_data_filter_dst_ipv6;
153 static int hf_radius_ascend_data_filter_src_ip_prefix;
154 static int hf_radius_ascend_data_filter_dst_ip_prefix;
155 static int hf_radius_ascend_data_filter_protocol;
156 static int hf_radius_ascend_data_filter_established;
157 static int hf_radius_ascend_data_filter_src_port;
158 static int hf_radius_ascend_data_filter_dst_port;
159 static int hf_radius_ascend_data_filter_src_port_qualifier;
160 static int hf_radius_ascend_data_filter_dst_port_qualifier;
161 static int hf_radius_ascend_data_filter_reserved;
163 static int hf_radius_vsa_fragment;
164 static int hf_radius_eap_fragment;
165 static int hf_radius_avp;
166 static int hf_radius_avp_length;
167 static int hf_radius_avp_type;
168 static int hf_radius_avp_vendor_id;
169 static int hf_radius_avp_vendor_type;
170 static int hf_radius_avp_vendor_len;
171 static int hf_radius_avp_extended_type;
172 static int hf_radius_avp_extended_more;
173 static int hf_radius_3gpp_ms_tmime_zone;
175 static int hf_radius_egress_vlanid_tag;
176 static int hf_radius_egress_vlanid_pad;
177 static int hf_radius_egress_vlanid;
179 static int hf_radius_egress_vlan_name_tag;
180 static int hf_radius_egress_vlan_name;
183 static int ett_radius;
184 static int ett_radius_avp;
186 static int ett_radius_authenticator;
187 static int ett_radius_ascend;
189 static int ett_eap;
190 static int ett_chap;
192 static expert_field ei_radius_invalid_length;
195 * Define the tap for radius
197 static int radius_tap;
199 static radius_vendor_info_t no_vendor = {"Unknown Vendor", 0, NULL, -1, 1, 1, false};
201 static radius_attr_info_t no_dictionary_entry = {"Unknown-Attribute", { { 0, 0 } }, false, false, false, radius_octets, NULL, NULL, -1, -1, -1, -1, -1, -1, NULL };
203 static dissector_handle_t eap_handle;
204 static dissector_handle_t radius_handle;
207 static const char *shared_secret = "";
208 static bool validate_authenticator;
209 static bool show_length;
210 static bool disable_extended_attributes;
212 static uint8_t authenticator[AUTHENTICATOR_LENGTH];
214 /* http://www.iana.org/assignments/radius-types */
215 static const value_string radius_pkt_type_codes[] =
217 {RADIUS_PKT_TYPE_ACCESS_REQUEST, "Access-Request"}, /* 1 RFC2865 */
218 {RADIUS_PKT_TYPE_ACCESS_ACCEPT, "Access-Accept"}, /* 2 RFC2865 */
219 {RADIUS_PKT_TYPE_ACCESS_REJECT, "Access-Reject"}, /* 3 RFC2865 */
220 {RADIUS_PKT_TYPE_ACCOUNTING_REQUEST, "Accounting-Request"}, /* 4 RFC2865 */
221 {RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE, "Accounting-Response"}, /* 5 RFC2865 */
222 {RADIUS_PKT_TYPE_ACCOUNTING_STATUS, "Accounting-Status"}, /* 6 RFC3575 */
223 {RADIUS_PKT_TYPE_PASSWORD_REQUEST, "Password-Request"}, /* 7 RFC3575 */
224 {RADIUS_PKT_TYPE_PASSWORD_ACK, "Password-Ack"}, /* 8 RFC3575 */
225 {RADIUS_PKT_TYPE_PASSWORD_REJECT, "Password-Reject"}, /* 9 RFC3575 */
226 {RADIUS_PKT_TYPE_ACCOUNTING_MESSAGE, "Accounting-Message"}, /* 10 RFC3575 */
227 {RADIUS_PKT_TYPE_ACCESS_CHALLENGE, "Access-Challenge"}, /* 11 RFC2865 */
228 {RADIUS_PKT_TYPE_STATUS_SERVER, "Status-Server"}, /* 12 RFC2865 */
229 {RADIUS_PKT_TYPE_STATUS_CLIENT, "Status-Client"}, /* 13 RFC2865 */
231 {RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST, "Resource-Free-Request"}, /* 21 RFC3575 */
232 {RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE, "Resource-Free-Response"}, /* 22 RFC3575 */
233 {RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST, "Resource-Query-Request"}, /* 23 RFC3575 */
234 {RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE, "Query_Response"}, /* 24 RFC3575 */
235 {RADIUS_PKT_TYPE_ALTERNATE_RESOURCE_RECLAIM_REQUEST, "Alternate-Resource-Reclaim-Request"}, /* 25 RFC3575 */
236 {RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST, "NAS-Reboot-Request"}, /* 26 RFC3575 */
237 {RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE, "NAS-Reboot-Response"}, /* 27 RFC3575 */
239 {RADIUS_PKT_TYPE_NEXT_PASSCODE, "Next-Passcode"}, /* 29 RFC3575 */
240 {RADIUS_PKT_TYPE_NEW_PIN, "New-Pin"}, /* 30 RFC3575 */
241 {RADIUS_PKT_TYPE_TERMINATE_SESSION, "Terminate-Session"}, /* 31 RFC3575 */
242 {RADIUS_PKT_TYPE_PASSWORD_EXPIRED, "Password-Expired"}, /* 32 RFC3575 */
243 {RADIUS_PKT_TYPE_EVENT_REQUEST, "Event-Request"}, /* 33 RFC3575 */
244 {RADIUS_PKT_TYPE_EVENT_RESPONSE, "Event-Response"}, /* 34 RFC3575|RFC5176 */
246 {RADIUS_PKT_TYPE_DISCONNECT_REQUEST, "Disconnect-Request"}, /* 40 RFC3575|RFC5176 */
247 {RADIUS_PKT_TYPE_DISCONNECT_ACK, "Disconnect-ACK"}, /* 41 RFC3575|RFC5176 */
248 {RADIUS_PKT_TYPE_DISCONNECT_NAK, "Disconnect-NAK"}, /* 42 RFC3575|RFC5176 */
249 {RADIUS_PKT_TYPE_COA_REQUEST, "CoA-Request"}, /* 43 RFC3575|RFC5176 */
250 {RADIUS_PKT_TYPE_COA_ACK, "CoA-ACK"}, /* 44 RFC3575|RFC5176 */
251 {RADIUS_PKT_TYPE_COA_NAK, "CoA-NAK"}, /* 45 RFC3575|RFC5176 */
253 {RADIUS_PKT_TYPE_IP_ADDRESS_ALLOCATE, "IP-Address-Allocate"}, /* 50 RFC3575 */
254 {RADIUS_PKT_TYPE_IP_ADDRESS_RELEASE, "IP-Address-Release"}, /* 51 RFC3575 */
256 {RADIUS_PKT_TYPE_ALU_STATE_REQUEST, "ALU-State-Request"}, /* 129 ALU AAA */
257 {RADIUS_PKT_TYPE_ALU_STATE_ACCEPT, "ALU-State-Accept"}, /* 130 ALU AAA */
258 {RADIUS_PKT_TYPE_ALU_STATE_REJECT, "ALU-State-Reject"}, /* 131 ALU AAA */
259 {RADIUS_PKT_TYPE_ALU_STATE_ERROR, "ALU-State-Error"}, /* 132 ALU AAA */
261 250-253 Experimental Use [RFC3575]
262 254-255 Reserved [RFC3575]
264 {0, NULL}
266 static value_string_ext radius_pkt_type_codes_ext = VALUE_STRING_EXT_INIT(radius_pkt_type_codes);
268 typedef enum _radius_category {
269 RADIUS_CAT_OVERALL = 0,
270 RADIUS_CAT_ACCESS,
271 RADIUS_CAT_ACCOUNTING,
272 RADIUS_CAT_PASSWORD,
273 RADIUS_CAT_RESOURCE_FREE,
274 RADIUS_CAT_RESOURCE_QUERY,
275 RADIUS_CAT_NAS_REBOOT,
276 RADIUS_CAT_EVENT,
277 RADIUS_CAT_DISCONNECT,
278 RADIUS_CAT_COA,
279 RADIUS_CAT_OTHERS,
280 RADIUS_CAT_NUM_TIMESTATS
281 } radius_category;
283 static const value_string radius_message_code[] = {
284 { RADIUS_CAT_OVERALL, "Overall"},
285 { RADIUS_CAT_ACCESS, "Access"},
286 { RADIUS_CAT_ACCOUNTING, "Accounting"},
287 { RADIUS_CAT_PASSWORD, "Password"},
288 { RADIUS_CAT_RESOURCE_FREE, "Resource Free"},
289 { RADIUS_CAT_RESOURCE_QUERY, "Resource Query"},
290 { RADIUS_CAT_NAS_REBOOT, "NAS Reboot"},
291 { RADIUS_CAT_EVENT, "Event"},
292 { RADIUS_CAT_DISCONNECT, "Disconnect"},
293 { RADIUS_CAT_COA, "CoA"},
294 { RADIUS_CAT_OTHERS, "Other"},
295 { 0, NULL}
298 static tap_packet_status
299 radiusstat_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pri, tap_flags_t flags _U_)
301 rtd_data_t *rtd_data = (rtd_data_t *)prs;
302 rtd_stat_table *rs = &rtd_data->stat_table;
303 const radius_info_t *ri = (const radius_info_t *)pri;
304 nstime_t delta;
305 radius_category radius_cat = RADIUS_CAT_OTHERS;
306 tap_packet_status ret = TAP_PACKET_DONT_REDRAW;
308 switch (ri->code) {
309 case RADIUS_PKT_TYPE_ACCESS_REQUEST:
310 case RADIUS_PKT_TYPE_ACCESS_ACCEPT:
311 case RADIUS_PKT_TYPE_ACCESS_REJECT:
312 radius_cat = RADIUS_CAT_ACCESS;
313 break;
314 case RADIUS_PKT_TYPE_ACCOUNTING_REQUEST:
315 case RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE:
316 radius_cat = RADIUS_CAT_ACCOUNTING;
317 break;
318 case RADIUS_PKT_TYPE_PASSWORD_REQUEST:
319 case RADIUS_PKT_TYPE_PASSWORD_ACK:
320 case RADIUS_PKT_TYPE_PASSWORD_REJECT:
321 radius_cat = RADIUS_CAT_PASSWORD;
322 break;
323 case RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST:
324 case RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE:
325 radius_cat = RADIUS_CAT_RESOURCE_FREE;
326 break;
327 case RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST:
328 case RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE:
329 radius_cat = RADIUS_CAT_RESOURCE_QUERY;
330 break;
331 case RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST:
332 case RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE:
333 radius_cat = RADIUS_CAT_NAS_REBOOT;
334 break;
335 case RADIUS_PKT_TYPE_EVENT_REQUEST:
336 case RADIUS_PKT_TYPE_EVENT_RESPONSE:
337 radius_cat = RADIUS_CAT_EVENT;
338 break;
339 case RADIUS_PKT_TYPE_DISCONNECT_REQUEST:
340 case RADIUS_PKT_TYPE_DISCONNECT_ACK:
341 case RADIUS_PKT_TYPE_DISCONNECT_NAK:
342 radius_cat = RADIUS_CAT_DISCONNECT;
343 break;
344 case RADIUS_PKT_TYPE_COA_REQUEST:
345 case RADIUS_PKT_TYPE_COA_ACK:
346 case RADIUS_PKT_TYPE_COA_NAK:
347 radius_cat = RADIUS_CAT_COA;
348 break;
351 switch (ri->code) {
353 case RADIUS_PKT_TYPE_ACCESS_REQUEST:
354 case RADIUS_PKT_TYPE_ACCOUNTING_REQUEST:
355 case RADIUS_PKT_TYPE_PASSWORD_REQUEST:
356 case RADIUS_PKT_TYPE_EVENT_REQUEST:
357 case RADIUS_PKT_TYPE_DISCONNECT_REQUEST:
358 case RADIUS_PKT_TYPE_COA_REQUEST:
359 if (ri->is_duplicate) {
360 /* Duplicate is ignored */
361 rs->time_stats[RADIUS_CAT_OVERALL].req_dup_num++;
362 rs->time_stats[radius_cat].req_dup_num++;
363 } else {
364 rs->time_stats[RADIUS_CAT_OVERALL].open_req_num++;
365 rs->time_stats[radius_cat].open_req_num++;
367 break;
369 case RADIUS_PKT_TYPE_ACCESS_ACCEPT:
370 case RADIUS_PKT_TYPE_ACCESS_REJECT:
371 case RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE:
372 case RADIUS_PKT_TYPE_PASSWORD_ACK:
373 case RADIUS_PKT_TYPE_PASSWORD_REJECT:
374 case RADIUS_PKT_TYPE_EVENT_RESPONSE:
375 case RADIUS_PKT_TYPE_DISCONNECT_ACK:
376 case RADIUS_PKT_TYPE_DISCONNECT_NAK:
377 case RADIUS_PKT_TYPE_COA_ACK:
378 case RADIUS_PKT_TYPE_COA_NAK:
379 if (ri->is_duplicate) {
380 /* Duplicate is ignored */
381 rs->time_stats[RADIUS_CAT_OVERALL].rsp_dup_num++;
382 rs->time_stats[radius_cat].rsp_dup_num++;
383 } else if (!ri->request_available) {
384 /* no request was seen */
385 rs->time_stats[RADIUS_CAT_OVERALL].disc_rsp_num++;
386 rs->time_stats[radius_cat].disc_rsp_num++;
387 } else {
388 rs->time_stats[RADIUS_CAT_OVERALL].open_req_num--;
389 rs->time_stats[radius_cat].open_req_num--;
390 /* calculate time delta between request and response */
391 nstime_delta(&delta, &pinfo->abs_ts, &ri->req_time);
393 time_stat_update(&(rs->time_stats[RADIUS_CAT_OVERALL].rtd[0]),&delta, pinfo);
394 time_stat_update(&(rs->time_stats[radius_cat].rtd[0]),&delta, pinfo);
396 ret = TAP_PACKET_REDRAW;
398 break;
400 default:
401 break;
404 return ret;
410 * Init Hash table stuff for conversation
413 typedef struct _radius_call_info_key
415 unsigned code;
416 unsigned ident;
417 conversation_t *conversation;
418 nstime_t req_time;
419 } radius_call_info_key;
421 static wmem_map_t *radius_calls;
423 typedef struct _radius_vsa_buffer_key
425 uint32_t vendor_id;
426 uint32_t vsa_type;
427 } radius_vsa_buffer_key;
429 typedef struct _radius_vsa_buffer
431 radius_vsa_buffer_key key;
432 uint8_t *data;
433 unsigned seg_num;
434 unsigned len;
435 } radius_vsa_buffer;
437 static int
438 radius_vsa_equal(const void *k1, const void *k2)
440 const radius_vsa_buffer_key *key1 = (const radius_vsa_buffer_key *) k1;
441 const radius_vsa_buffer_key *key2 = (const radius_vsa_buffer_key *) k2;
443 return (((key1->vendor_id == key2->vendor_id) &&
444 (key1->vsa_type == key2->vsa_type)
445 ) ? true : false);
448 static unsigned
449 radius_vsa_hash(const void *k)
451 const radius_vsa_buffer_key *key = (const radius_vsa_buffer_key *) k;
453 return key->vendor_id + key->vsa_type;
456 /* Compare 2 keys */
457 static gboolean
458 radius_call_equal(const void *k1, const void *k2)
460 const radius_call_info_key *key1 = (const radius_call_info_key *) k1;
461 const radius_call_info_key *key2 = (const radius_call_info_key *) k2;
463 if (key1->ident == key2->ident && key1->conversation == key2->conversation) {
464 if (key1->code == key2->code)
465 return true;
467 /* check the request and response are of the same code type */
468 if ((key1->code == RADIUS_PKT_TYPE_ACCESS_REQUEST) &&
469 ((key2->code == RADIUS_PKT_TYPE_ACCESS_ACCEPT) ||
470 (key2->code == RADIUS_PKT_TYPE_ACCESS_REJECT) ||
471 (key2->code == RADIUS_PKT_TYPE_ACCESS_CHALLENGE)))
472 return true;
473 if ((key2->code == RADIUS_PKT_TYPE_ACCESS_REQUEST) &&
474 ((key1->code == RADIUS_PKT_TYPE_ACCESS_ACCEPT) ||
475 (key1->code == RADIUS_PKT_TYPE_ACCESS_REJECT) ||
476 (key1->code == RADIUS_PKT_TYPE_ACCESS_CHALLENGE)))
477 return true;
479 if ((key1->code == RADIUS_PKT_TYPE_ACCOUNTING_REQUEST) &&
480 (key2->code == RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE))
481 return true;
482 if ((key2->code == RADIUS_PKT_TYPE_ACCOUNTING_REQUEST) &&
483 (key1->code == RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE))
484 return true;
486 if ((key1->code == RADIUS_PKT_TYPE_PASSWORD_REQUEST) &&
487 ((key2->code == RADIUS_PKT_TYPE_PASSWORD_ACK) ||
488 (key2->code == RADIUS_PKT_TYPE_PASSWORD_REJECT)))
489 return true;
490 if ((key2->code == RADIUS_PKT_TYPE_PASSWORD_REQUEST) &&
491 ((key1->code == RADIUS_PKT_TYPE_PASSWORD_ACK) ||
492 (key1->code == RADIUS_PKT_TYPE_PASSWORD_REJECT)))
493 return true;
495 if ((key1->code == RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST) &&
496 (key2->code == RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE))
497 return true;
498 if ((key2->code == RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST) &&
499 (key1->code == RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE))
500 return true;
502 if ((key1->code == RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST) &&
503 (key2->code == RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE))
504 return true;
505 if ((key2->code == RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST) &&
506 (key1->code == RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE))
507 return true;
509 if ((key1->code == RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST) &&
510 (key2->code == RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE))
511 return true;
512 if ((key2->code == RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST) &&
513 (key1->code == RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE))
514 return true;
516 if ((key1->code == RADIUS_PKT_TYPE_EVENT_REQUEST) &&
517 (key2->code == RADIUS_PKT_TYPE_EVENT_RESPONSE))
518 return true;
519 if ((key2->code == RADIUS_PKT_TYPE_EVENT_REQUEST) &&
520 (key1->code == RADIUS_PKT_TYPE_EVENT_RESPONSE))
521 return true;
523 if ((key1->code == RADIUS_PKT_TYPE_DISCONNECT_REQUEST) &&
524 ((key2->code == RADIUS_PKT_TYPE_DISCONNECT_ACK) ||
525 (key2->code == RADIUS_PKT_TYPE_DISCONNECT_NAK)))
526 return true;
527 if ((key2->code == RADIUS_PKT_TYPE_DISCONNECT_REQUEST) &&
528 ((key1->code == RADIUS_PKT_TYPE_DISCONNECT_ACK) ||
529 (key1->code == RADIUS_PKT_TYPE_DISCONNECT_NAK)))
530 return true;
532 if ((key1->code == RADIUS_PKT_TYPE_COA_REQUEST) &&
533 ((key2->code == RADIUS_PKT_TYPE_COA_ACK) ||
534 (key2->code == RADIUS_PKT_TYPE_COA_NAK)))
535 return true;
536 if ((key2->code == RADIUS_PKT_TYPE_COA_REQUEST) &&
537 ((key1->code == RADIUS_PKT_TYPE_COA_ACK) ||
538 (key1->code == RADIUS_PKT_TYPE_COA_NAK)))
539 return true;
541 if ((key1->code == RADIUS_PKT_TYPE_ALU_STATE_REQUEST) &&
542 ((key2->code == RADIUS_PKT_TYPE_ALU_STATE_ACCEPT) ||
543 (key2->code == RADIUS_PKT_TYPE_ALU_STATE_REJECT) ||
544 (key2->code == RADIUS_PKT_TYPE_ALU_STATE_ERROR)))
545 return true;
546 if ((key2->code == RADIUS_PKT_TYPE_ALU_STATE_REQUEST) &&
547 ((key1->code == RADIUS_PKT_TYPE_ALU_STATE_ACCEPT) ||
548 (key1->code == RADIUS_PKT_TYPE_ALU_STATE_REJECT) ||
549 (key1->code == RADIUS_PKT_TYPE_ALU_STATE_ERROR)))
550 return true;
552 return false;
555 /* Calculate a hash key */
556 static unsigned
557 radius_call_hash(const void *k)
559 const radius_call_info_key *key = (const radius_call_info_key *) k;
561 return key->ident + key->conversation->conv_index;
565 static const char *
566 dissect_chap_password(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo)
568 int len;
569 proto_item *ti;
570 proto_tree *chap_tree;
572 len = tvb_reported_length(tvb);
573 if (len != 17)
574 return "[wrong length for CHAP-Password]";
576 ti = proto_tree_add_item(tree, hf_radius_chap_password, tvb, 0, len, ENC_NA);
577 chap_tree = proto_item_add_subtree(ti, ett_chap);
578 proto_tree_add_item(chap_tree, hf_radius_chap_ident, tvb, 0, 1, ENC_BIG_ENDIAN);
579 proto_tree_add_item(chap_tree, hf_radius_chap_string, tvb, 1, 16, ENC_NA);
580 return (tvb_bytes_to_str(pinfo->pool, tvb, 0, len));
583 static const char *
584 dissect_framed_ip_address(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_)
586 int len;
587 uint32_t ip;
588 uint32_t ip_h;
589 const char *str;
591 len = tvb_reported_length(tvb);
592 if (len != 4)
593 return "[wrong length for IP address]";
595 ip = tvb_get_ipv4(tvb, 0);
596 ip_h = g_ntohl(ip);
598 if (ip_h == 0xFFFFFFFF) {
599 str = "Negotiated";
600 proto_tree_add_ipv4_format_value(tree, hf_radius_framed_ip_address,
601 tvb, 0, len, ip, "%s", str);
602 } else if (ip_h == 0xFFFFFFFE) {
603 str = "Assigned";
604 proto_tree_add_ipv4_format_value(tree, hf_radius_framed_ip_address,
605 tvb, 0, len, ip, "%s", str);
606 } else {
607 str = tvb_ip_to_str(pinfo->pool, tvb, 0);
608 proto_tree_add_item(tree, hf_radius_framed_ip_address,
609 tvb, 0, len, ENC_BIG_ENDIAN);
612 return str;
615 static const char *
616 dissect_login_ip_host(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_)
618 int len;
619 uint32_t ip;
620 uint32_t ip_h;
621 const char *str;
623 len = tvb_reported_length(tvb);
624 if (len != 4)
625 return "[wrong length for IP address]";
627 ip = tvb_get_ipv4(tvb, 0);
628 ip_h = g_ntohl(ip);
630 if (ip_h == 0xFFFFFFFF) {
631 str = "User-selected";
632 proto_tree_add_ipv4_format_value(tree, hf_radius_login_ip_host,
633 tvb, 0, len, ip, "%s", str);
634 } else if (ip_h == 0) {
635 str = "NAS-selected";
636 proto_tree_add_ipv4_format_value(tree, hf_radius_login_ip_host,
637 tvb, 0, len, ip, "%s", str);
638 } else {
639 str = tvb_ip_to_str(pinfo->pool, tvb, 0);
640 proto_tree_add_item(tree, hf_radius_login_ip_host,
641 tvb, 0, len, ENC_BIG_ENDIAN);
644 return str;
647 static const value_string ascenddf_filtertype[] = { {0, "generic"}, {1, "ipv4"}, {3, "ipv6"}, {0, NULL} };
648 static const value_string ascenddf_filteror[] = { {0, "drop"}, {1, "forward"}, {0, NULL} };
649 static const value_string ascenddf_inout[] = { {0, "out"}, {1, "in"}, {0, NULL} };
650 static const value_string ascenddf_proto[] = { {1, "icmp"}, {6, "tcp"}, {17, "udp"}, {0, NULL} };
651 static const value_string ascenddf_portq[] = { {1, "lt"}, {2, "eq"}, {3, "gt"}, {4, "ne"}, {0, NULL} };
653 static const char *
654 dissect_ascend_data_filter(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo)
656 wmem_strbuf_t *filterstr;
657 proto_item *ti;
658 proto_tree *ascend_tree;
659 int len;
660 uint8_t type, proto, srclen, dstlen;
661 address srcip, dstip;
662 uint16_t srcport, dstport;
663 uint8_t srcportq, dstportq;
664 uint8_t iplen = 4;
665 unsigned offset = 0;
666 len=tvb_reported_length(tvb);
668 if (len != 24 && len != 48) {
669 return wmem_strdup_printf(pinfo->pool, "Wrong attribute length %d", len);
672 filterstr = wmem_strbuf_new_sized(pinfo->pool, 128);
674 ti = proto_tree_add_item(tree, hf_radius_ascend_data_filter, tvb, 0, -1, ENC_NA);
675 ascend_tree = proto_item_add_subtree(ti, ett_radius_ascend);
677 proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_type, tvb, offset, 1, ENC_BIG_ENDIAN);
678 type = tvb_get_uint8(tvb, 0);
679 offset += 1;
680 if (type == 3) { /* IPv6 */
681 iplen = 16;
684 proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_filteror, tvb, offset, 1, ENC_BIG_ENDIAN);
685 offset += 1;
686 proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_inout, tvb, offset, 1, ENC_BIG_ENDIAN);
687 offset += 1;
688 proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_spare, tvb, offset, 1, ENC_BIG_ENDIAN);
689 offset += 1;
691 if (type == 3) { /* IPv6 */
692 proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_src_ipv6, tvb, offset, 16, ENC_NA);
693 offset += 16;
694 proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_dst_ipv6, tvb, offset, 16, ENC_NA);
695 offset += 16;
696 } else { /* IPv4 */
697 proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_src_ipv4, tvb, offset, 4, ENC_BIG_ENDIAN);
698 offset += 4;
699 proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_dst_ipv4, tvb, offset, 4, ENC_BIG_ENDIAN);
700 offset += 4;
702 proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_src_ip_prefix, tvb, offset, 1, ENC_BIG_ENDIAN);
703 offset += 1;
704 proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_dst_ip_prefix, tvb, offset, 1, ENC_BIG_ENDIAN);
705 offset += 1;
707 proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_protocol, tvb, offset, 1, ENC_BIG_ENDIAN);
708 offset += 1;
710 proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_established, tvb, offset, 1, ENC_BIG_ENDIAN);
711 offset += 1;
713 proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_src_port, tvb, offset, 2, ENC_BIG_ENDIAN);
714 offset += 2;
716 proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_dst_port, tvb, offset, 2, ENC_BIG_ENDIAN);
717 offset += 2;
719 proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_src_port_qualifier, tvb, offset, 1, ENC_BIG_ENDIAN);
720 offset += 1;
722 proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_dst_port_qualifier, tvb, offset, 1, ENC_BIG_ENDIAN);
723 offset += 1;
725 proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_reserved, tvb, offset, 2, ENC_BIG_ENDIAN);
727 wmem_strbuf_append_printf(filterstr, "%s %s %s",
728 val_to_str(type, ascenddf_filtertype, "%u"),
729 val_to_str(tvb_get_uint8(tvb, 2), ascenddf_inout, "%u"),
730 val_to_str(tvb_get_uint8(tvb, 1), ascenddf_filteror, "%u"));
733 proto = tvb_get_uint8(tvb, 6+iplen*2);
734 if (proto) {
735 wmem_strbuf_append_printf(filterstr, " %s",
736 val_to_str(proto, ascenddf_proto, "%u"));
739 if (type == 3) { /* IPv6 */
740 set_address_tvb(&srcip, AT_IPv6, 16, tvb, 4);
741 } else {
742 set_address_tvb(&srcip, AT_IPv4, 4, tvb, 4);
744 srclen = tvb_get_uint8(tvb, 4+iplen*2);
745 srcport = tvb_get_ntohs(tvb, 9+iplen*2);
746 srcportq = tvb_get_uint8(tvb, 12+iplen*2);
748 if (srclen || srcportq) {
749 wmem_strbuf_append_printf(filterstr, " srcip %s/%d", address_to_display(pinfo->pool, &srcip), srclen);
750 if (srcportq)
751 wmem_strbuf_append_printf(filterstr, " srcport %s %d",
752 val_to_str(srcportq, ascenddf_portq, "%u"), srcport);
755 if (type == 3) { /* IPv6-*/
756 set_address_tvb(&dstip, AT_IPv6, 16, tvb, 4+iplen);
757 } else {
758 set_address_tvb(&dstip, AT_IPv4, 4, tvb, 4+iplen);
760 dstlen = tvb_get_uint8(tvb, 5+iplen*2);
761 dstport = tvb_get_ntohs(tvb, 10+iplen*2);
762 dstportq = tvb_get_uint8(tvb, 13+iplen*2);
764 if (dstlen || dstportq) {
765 wmem_strbuf_append_printf(filterstr, " dstip %s/%d", address_to_display(pinfo->pool, &dstip), dstlen);
766 if (dstportq)
767 wmem_strbuf_append_printf(filterstr, " dstport %s %d",
768 val_to_str(dstportq, ascenddf_portq, "%u"), dstport);
771 return wmem_strbuf_get_str(filterstr);
774 static const char *
775 dissect_framed_ipx_network(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo)
777 int len;
778 uint32_t net;
779 const char *str;
781 len = tvb_reported_length(tvb);
782 if (len != 4)
783 return "[wrong length for IPX network]";
785 net = tvb_get_ntohl(tvb, 0);
787 if (net == 0xFFFFFFFE)
788 str = "NAS-selected";
789 else
790 str = wmem_strdup_printf(pinfo->pool, "0x%08X", net);
791 proto_tree_add_ipxnet_format_value(tree, hf_radius_framed_ipx_network, tvb, 0,
792 len, net, "Framed-IPX-Network: %s", str);
794 return str;
797 static const char *
798 dissect_cosine_vpvc(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo)
800 unsigned vpi, vci;
802 if (tvb_reported_length(tvb) != 4)
803 return "[Wrong Length for VP/VC AVP]";
805 vpi = tvb_get_ntohs(tvb, 0);
806 vci = tvb_get_ntohs(tvb, 2);
808 proto_tree_add_uint(tree, hf_radius_cosine_vpi, tvb, 0, 2, vpi);
809 proto_tree_add_uint(tree, hf_radius_cosine_vci, tvb, 2, 2, vci);
811 return wmem_strdup_printf(pinfo->pool, "%u/%u", vpi, vci);
814 static const value_string daylight_saving_time_vals[] = {
815 {0, "No adjustment"},
816 {1, "+1 hour adjustment for Daylight Saving Time"},
817 {2, "+2 hours adjustment for Daylight Saving Time"},
818 {3, "Reserved"},
819 {0, NULL}
822 static const char *
823 dissect_radius_3gpp_imsi(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo)
825 return dissect_e212_utf8_imsi(tvb, pinfo, tree, 0, tvb_reported_length(tvb));
828 static const char *
829 dissect_radius_3gpp_ms_tmime_zone(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo)
832 int offset = 0;
833 uint8_t oct, daylight_saving_time;
834 char sign;
836 /* 3GPP TS 23.040 version 6.6.0 Release 6
837 * 9.2.3.11 TP-Service-Centre-Time-Stamp (TP-SCTS)
839 * The Time Zone indicates the difference, expressed in quarters of an hour,
840 * between the local time and GMT. In the first of the two semi-octets,
841 * the first bit (bit 3 of the seventh octet of the TP-Service-Centre-Time-Stamp field)
842 * represents the algebraic sign of this difference (0: positive, 1: negative).
845 oct = tvb_get_uint8(tvb, offset);
846 sign = (oct & 0x08) ? '-' : '+';
847 oct = (oct >> 4) + (oct & 0x07) * 10;
848 daylight_saving_time = tvb_get_uint8(tvb, offset+1) & 0x3;
850 proto_tree_add_bytes_format_value(tree, hf_radius_3gpp_ms_tmime_zone, tvb, offset, 2, NULL,
851 "GMT %c%d hours %d minutes %s", sign, oct / 4, oct % 4 * 15,
852 val_to_str_const(daylight_saving_time, daylight_saving_time_vals, "Unknown"));
854 return wmem_strdup_printf(pinfo->pool, "Timezone: GMT %c%d hours %d minutes %s ",
855 sign, oct / 4, oct % 4 * 15, val_to_str_const(daylight_saving_time, daylight_saving_time_vals, "Unknown"));
859 static const value_string egress_vlan_tag_vals[] = {
860 { 0x31, "Tagged"},
861 { 0x32, "Untagged"},
862 { 0, NULL}
864 static const char *
865 dissect_rfc4675_egress_vlanid(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo)
867 int len;
868 uint32_t vlanid;
870 len = tvb_reported_length(tvb);
871 if (len != 4)
872 return "[wrong length for Egress-VLANID ]";
874 proto_tree_add_item(tree, hf_radius_egress_vlanid_tag, tvb, 0, 4, ENC_BIG_ENDIAN);
875 proto_tree_add_item(tree, hf_radius_egress_vlanid_pad, tvb, 0, 4, ENC_BIG_ENDIAN);
876 proto_tree_add_item(tree, hf_radius_egress_vlanid, tvb, 0, 4, ENC_BIG_ENDIAN);
877 vlanid = tvb_get_ntohl(tvb, 0);
879 return wmem_strdup_printf(pinfo->pool, "%s, Vlan ID: %u",
880 val_to_str_const(((vlanid&0xFF000000)>>24), egress_vlan_tag_vals, "Unknown"), vlanid&0xFFF);
883 static const char *
884 dissect_rfc4675_egress_vlan_name(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo)
886 int len;
887 uint8_t tag;
888 const uint8_t *name;
890 len = tvb_reported_length(tvb);
891 if (len < 2)
892 return "[wrong length for Egress-VLAN-Name ]";
894 proto_tree_add_item(tree, hf_radius_egress_vlan_name_tag, tvb, 0, 1, ENC_BIG_ENDIAN);
895 tag = tvb_get_uint8(tvb, 0);
896 len -= 1;
897 proto_tree_add_item_ret_string(tree, hf_radius_egress_vlan_name, tvb, 1, len, ENC_ASCII|ENC_NA, pinfo->pool, &name);
899 return wmem_strdup_printf(pinfo->pool, "%s, Vlan Name: %s",
900 val_to_str_const(tag, egress_vlan_tag_vals, "Unknown"), name);
903 static void
904 radius_decrypt_avp(uint8_t *dest, packet_info *pinfo, tvbuff_t *tvb, int offset, int length, uint8_t *request_authenticator, uint8_t *salt, int salt_len, int type)
906 gcry_md_hd_t md5_handle;
907 uint8_t digest[HASH_MD5_LENGTH];
908 int i, j;
909 int padded_length;
910 uint8_t *pd;
912 if (gcry_md_open(&md5_handle, GCRY_MD_MD5, 0)) {
913 return;
915 if (type == 3){
916 gcry_md_write(md5_handle, request_authenticator, AUTHENTICATOR_LENGTH);
917 gcry_md_write(md5_handle, (const uint8_t *)shared_secret, (int)strlen(shared_secret));
918 } else {
919 gcry_md_write(md5_handle, (const uint8_t *)shared_secret, (int)strlen(shared_secret));
920 gcry_md_write(md5_handle, request_authenticator, AUTHENTICATOR_LENGTH);
921 gcry_md_write(md5_handle, salt, salt_len);
923 memcpy(digest, gcry_md_read(md5_handle, 0), HASH_MD5_LENGTH);
925 padded_length = length + ((length % AUTHENTICATOR_LENGTH) ?
926 (AUTHENTICATOR_LENGTH - (length % AUTHENTICATOR_LENGTH)) : 0);
927 pd = (uint8_t *)wmem_alloc0(pinfo->pool, padded_length);
928 tvb_memcpy(tvb, pd, offset, length);
930 for (i = 0; i < padded_length; i += AUTHENTICATOR_LENGTH) {
931 for (j = 0; j < AUTHENTICATOR_LENGTH && i + j < length; j++) {
932 dest[i + j] = pd[i + j] ^ digest[j];
934 gcry_md_reset(md5_handle);
935 gcry_md_write(md5_handle, (const uint8_t *)shared_secret, (int)strlen(shared_secret));
936 gcry_md_write(md5_handle, &pd[i], AUTHENTICATOR_LENGTH);
937 memcpy(digest, gcry_md_read(md5_handle, 0), HASH_MD5_LENGTH);
939 gcry_md_close(md5_handle);
942 static void
943 add_avp_to_tree_with_dissector(proto_tree *avp_tree, proto_item *avp_item, packet_info *pinfo, tvbuff_t *tvb, radius_avp_dissector_t *avp_dissector, uint32_t avp_length, uint32_t offset);
946 void
947 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)
949 uint32_t uintv;
951 switch (len) {
952 case 1:
953 uintv = tvb_get_uint8(tvb, offset);
954 break;
955 case 2:
956 uintv = tvb_get_ntohs(tvb, offset);
957 break;
958 case 3:
959 uintv = tvb_get_ntoh24(tvb, offset);
960 break;
961 case 4:
962 uintv = tvb_get_ntohl(tvb, offset);
963 break;
964 case 8: {
965 uint64_t uintv64 = tvb_get_ntoh64(tvb, offset);
966 proto_tree_add_uint64(tree, a->hf_alt, tvb, offset, len, uintv64);
967 proto_item_append_text(avp_item, "%" PRIu64, uintv64);
968 return;
970 default:
971 proto_item_append_text(avp_item, "[unhandled integer length(%u)]", len);
972 return;
974 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_BIG_ENDIAN);
976 if (a->vs) {
977 proto_item_append_text(avp_item, "%s(%u)", val_to_str_const(uintv, a->vs, "Unknown"), uintv);
978 } else {
979 proto_item_append_text(avp_item, "%u", uintv);
983 void
984 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)
986 uint32_t uintv;
988 switch (len) {
989 case 1:
990 uintv = tvb_get_uint8(tvb, offset);
991 break;
992 case 2:
993 uintv = tvb_get_ntohs(tvb, offset);
994 break;
995 case 3:
996 uintv = tvb_get_ntoh24(tvb, offset);
997 break;
998 case 4:
999 uintv = tvb_get_ntohl(tvb, offset);
1000 break;
1001 case 8: {
1002 uint64_t uintv64 = tvb_get_ntoh64(tvb, offset);
1003 proto_tree_add_int64(tree, a->hf_alt, tvb, offset, len, uintv64);
1004 proto_item_append_text(avp_item, "%" PRIu64, uintv64);
1005 return;
1007 default:
1008 proto_item_append_text(avp_item, "[unhandled signed integer length(%u)]", len);
1009 return;
1012 proto_tree_add_int(tree, a->hf, tvb, offset, len, uintv);
1014 if (a->vs) {
1015 proto_item_append_text(avp_item, "%s(%d)", val_to_str_const(uintv, a->vs, "Unknown"), uintv);
1016 } else {
1017 proto_item_append_text(avp_item, "%d", uintv);
1021 void
1022 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)
1024 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_UTF_8|ENC_NA);
1025 proto_item_append_text(avp_item, "%s", tvb_format_text(pinfo->pool, tvb, offset, len));
1028 void
1029 radius_octets(radius_attr_info_t *a, proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, int offset, int len, proto_item *avp_item)
1031 if (len == 0) {
1032 proto_item_append_text(avp_item, "[wrong length]");
1033 return;
1036 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
1037 proto_item_append_text(avp_item, "%s", tvb_bytes_to_str(pinfo->pool, tvb, offset, len));
1040 void
1041 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)
1044 if (len != 4) {
1045 proto_item_append_text(avp_item, "[wrong length for IP address]");
1046 return;
1049 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_BIG_ENDIAN);
1051 proto_item_append_text(avp_item, "%s", tvb_ip_to_str(pinfo->pool, tvb, offset));
1054 void
1055 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)
1058 if (len != 16) {
1059 proto_item_append_text(avp_item, "[wrong length for IPv6 address]");
1060 return;
1063 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
1065 proto_item_append_text(avp_item, "%s", tvb_ip6_to_str(pinfo->pool, tvb, offset));
1068 void
1069 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)
1071 ws_in6_addr ipv6_buff;
1072 char txtbuf[256];
1073 uint8_t n;
1075 if ((len < 2) || (len > 18)) {
1076 proto_item_append_text(avp_item, "[wrong length for IPv6 prefix]");
1077 return;
1080 /* first byte is reserved == 0x00 */
1081 if (tvb_get_uint8(tvb, offset)) {
1082 proto_item_append_text(avp_item, "[invalid reserved byte for IPv6 prefix]");
1083 return;
1086 /* this is the prefix length */
1087 n = tvb_get_uint8(tvb, offset + 1);
1088 if (n > 128) {
1089 proto_item_append_text(avp_item, "[invalid IPv6 prefix length]");
1090 return;
1093 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
1095 /* cannot use tvb_get_ipv6() here, since the prefix most likely is truncated */
1096 memset(&ipv6_buff, 0, sizeof ipv6_buff);
1097 tvb_memcpy(tvb, &ipv6_buff, offset + 2, len - 2);
1098 ip6_to_str_buf(&ipv6_buff, txtbuf, sizeof(txtbuf));
1099 proto_item_append_text(avp_item, "%s/%u", txtbuf, n);
1103 void
1104 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)
1107 if (len == 4) {
1108 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_BIG_ENDIAN);
1109 proto_item_append_text(avp_item, "%s", tvb_ip_to_str(pinfo->pool, tvb, offset));
1110 } else if (len == 16) {
1111 proto_tree_add_item(tree, a->hf_alt, tvb, offset, len, ENC_NA);
1112 proto_item_append_text(avp_item, "%s", tvb_ip6_to_str(pinfo->pool, tvb, offset));
1113 } else {
1114 proto_item_append_text(avp_item, "[wrong length for both of IPv4 and IPv6 address]");
1115 return;
1119 void
1120 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)
1122 uint32_t net;
1124 if (len != 4) {
1125 proto_item_append_text(avp_item, "[wrong length for IPX network]");
1126 return;
1129 net = tvb_get_ntohl(tvb, offset);
1131 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
1133 proto_item_append_text(avp_item, "0x%08X", net);
1136 void
1137 radius_date(radius_attr_info_t *a, proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, int offset, int len, proto_item *avp_item)
1139 nstime_t time_ptr;
1141 if (len != 4) {
1142 proto_item_append_text(avp_item, "[wrong length for timestamp]");
1143 return;
1146 time_ptr.secs = tvb_get_ntohl(tvb, offset);
1147 time_ptr.nsecs = 0;
1149 proto_tree_add_time(tree, a->hf, tvb, offset, len, &time_ptr);
1150 proto_item_append_text(avp_item, "%s", abs_time_to_str(pinfo->pool, &time_ptr, ABSOLUTE_TIME_LOCAL, true));
1154 * "abinary" is Ascend's binary format for filters. See dissect_ascend_data_filter().
1156 void
1157 radius_abinary(radius_attr_info_t *a, proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, int offset, int len, proto_item *avp_item)
1159 if (a->code.u8_code[0] == 242) {
1160 add_avp_to_tree_with_dissector(tree, avp_item, pinfo, tvb, dissect_ascend_data_filter, len, offset);
1161 return;
1163 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
1164 proto_item_append_text(avp_item, "%s", tvb_bytes_to_str(pinfo->pool, tvb, offset, len));
1167 void
1168 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)
1170 if (len != 6) {
1171 proto_item_append_text(avp_item, "[wrong length for ethernet address]");
1172 return;
1175 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
1176 proto_item_append_text(avp_item, "%s", tvb_ether_to_str(pinfo->pool, tvb, offset));
1179 void
1180 radius_ifid(radius_attr_info_t *a, proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, int offset, int len, proto_item *avp_item)
1182 proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA);
1183 proto_item_append_text(avp_item, "%s", tvb_bytes_to_str(pinfo->pool, tvb, offset, len));
1186 static void
1187 add_tlv_to_tree(proto_tree *tlv_tree, proto_item *tlv_item, packet_info *pinfo, tvbuff_t *tvb, radius_attr_info_t *dictionary_entry, uint32_t tlv_length, uint32_t offset)
1189 proto_item_append_text(tlv_item, ": ");
1190 dictionary_entry->type(dictionary_entry, tlv_tree, pinfo, tvb, offset, tlv_length, tlv_item);
1193 void
1194 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)
1196 int tlv_num = 0;
1198 while (len > 0) {
1199 radius_attr_info_t *dictionary_entry = NULL;
1200 uint32_t tlv_type;
1201 uint32_t tlv_length;
1203 proto_item *tlv_item;
1204 proto_item *tlv_len_item;
1205 proto_tree *tlv_tree;
1207 if (len < 2) {
1208 proto_tree_add_expert_format(tree, pinfo, &ei_radius_invalid_length, tvb, offset, 0,
1209 "Not enough room in packet for TLV header");
1210 return;
1212 tlv_type = tvb_get_uint8(tvb, offset);
1213 tlv_length = tvb_get_uint8(tvb, offset+1);
1215 if (tlv_length < 2) {
1216 proto_tree_add_expert_format(tree, pinfo, &ei_radius_invalid_length, tvb, offset, 0,
1217 "TLV too short: length %u < 2", tlv_length);
1218 return;
1221 if (len < (int)tlv_length) {
1222 proto_tree_add_expert_format(tree, pinfo, &ei_radius_invalid_length, tvb, offset, 0,
1223 "Not enough room in packet for TLV");
1224 return;
1227 len -= tlv_length;
1229 if (a->tlvs_by_id)
1230 dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(a->tlvs_by_id, GUINT_TO_POINTER(tlv_type));
1232 if (!dictionary_entry) {
1233 dictionary_entry = &no_dictionary_entry;
1236 tlv_tree = proto_tree_add_subtree_format(tree, tvb, offset, tlv_length,
1237 dictionary_entry->ett, &tlv_item, "TLV: t=%s(%u) l=%u ", dictionary_entry->name, tlv_type,
1238 tlv_length);
1240 tlv_length -= 2;
1241 offset += 2;
1243 if (show_length) {
1244 tlv_len_item = proto_tree_add_uint(tlv_tree,
1245 dictionary_entry->hf_len,
1246 tvb, 0, 0, tlv_length);
1247 proto_item_set_generated(tlv_len_item);
1250 add_tlv_to_tree(tlv_tree, tlv_item, pinfo, tvb, dictionary_entry,
1251 tlv_length, offset);
1252 offset += tlv_length;
1253 tlv_num++;
1256 proto_item_append_text(avp_item, "%d TLV(s) inside", tlv_num);
1259 static void
1260 add_avp_to_tree_with_dissector(proto_tree *avp_tree, proto_item *avp_item, packet_info *pinfo, tvbuff_t *tvb, radius_avp_dissector_t *avp_dissector, uint32_t avp_length, uint32_t offset)
1262 tvbuff_t *tvb_value;
1263 const char *str;
1265 tvb_value = tvb_new_subset_length(tvb, offset, avp_length);
1266 str = avp_dissector(avp_tree, tvb_value, pinfo);
1267 proto_item_append_text(avp_item, "%s", str);
1270 static void
1271 add_avp_to_tree(proto_tree *avp_tree, proto_item *avp_item, packet_info *pinfo, tvbuff_t *tvb, radius_attr_info_t *dictionary_entry, uint32_t avp_length, uint32_t offset, radius_call_t *radius_call)
1274 if (dictionary_entry->tagged) {
1275 unsigned tag;
1277 if (avp_length == 0) {
1278 proto_tree_add_expert_format(avp_tree, pinfo, &ei_radius_invalid_length, tvb, offset,
1279 0, "AVP too short for tag");
1280 return;
1283 tag = tvb_get_uint8(tvb, offset);
1285 if (tag <= 0x1f) {
1286 proto_tree_add_uint(avp_tree,
1287 dictionary_entry->hf_tag,
1288 tvb, offset, 1, tag);
1290 proto_item_append_text(avp_item,
1291 " Tag=0x%.2x", tag);
1293 offset++;
1294 avp_length--;
1298 proto_item_append_text(avp_item, " val=");
1300 if (dictionary_entry->dissector) {
1301 add_avp_to_tree_with_dissector(avp_tree, avp_item, pinfo, tvb, dictionary_entry->dissector, avp_length, offset);
1302 return;
1305 if (dictionary_entry->encrypt > 0) {
1306 if (*shared_secret =='\0' || avp_length == 0 || !radius_call) {
1307 proto_item_append_text(avp_item, "Encrypted");
1308 proto_tree_add_item(avp_tree, dictionary_entry->hf_enc, tvb, offset, avp_length, ENC_NA);
1309 } else {
1310 tvbuff_t *tvb_decrypted;
1311 uint8_t *buffer;
1313 switch (dictionary_entry->encrypt) {
1314 case 1: /* encrypted like User-Password as defined in RFC 2865 */
1315 /* decrypted data is same length as encrypted data */
1316 buffer = (uint8_t *)wmem_alloc(pinfo->pool, avp_length);
1318 radius_decrypt_avp(buffer, pinfo, tvb, offset, avp_length, radius_call->req_authenticator, NULL, 0, 1);
1319 tvb_decrypted = tvb_new_child_real_data(tvb, buffer, avp_length, avp_length);
1320 proto_item_append_text(avp_item, "Decrypted: ");
1321 add_new_data_source(pinfo, tvb_decrypted, "Decrypted Data");
1322 /* strip padding for string type */
1323 if (dictionary_entry->type == radius_string) {
1324 for (uint8_t i=0; i < avp_length; i++){
1325 if (buffer[i] == '\0')
1326 avp_length = i;
1329 dictionary_entry->type(dictionary_entry, avp_tree, pinfo, tvb_decrypted, 0, avp_length, avp_item);
1330 break;
1332 case 2: /* encrypted like Tunnel-Password as defined in RFC 2868 */
1333 /* check if there is at least 1 byte of encrypted data after salt */
1334 if (avp_length < 3) {
1335 proto_item_append_text(avp_item, "Encrypted");
1336 proto_tree_add_item(avp_tree, dictionary_entry->hf_enc, tvb, offset, avp_length, ENC_NA);
1337 break;
1339 /* decrypted data is same length as encrypted data */
1340 buffer = (char *)wmem_alloc(pinfo->pool, avp_length - 2);
1341 uint8_t salt[2];
1343 tvb_memcpy(tvb, salt, offset, 2);
1344 avp_length -= 2;
1345 radius_decrypt_avp(buffer, pinfo, tvb, offset + 2, avp_length, radius_call->req_authenticator, salt, 2, 2);
1346 tvb_decrypted = tvb_new_child_real_data(tvb, buffer, avp_length, avp_length);
1347 proto_item_append_text(avp_item, "Decrypted: ");
1348 /* first byte contains length of decrypted data */
1349 avp_length = (buffer[0] < avp_length) ? buffer[0] : avp_length -1;
1350 add_new_data_source(pinfo, tvb_decrypted, "Decrypted Data");
1351 dictionary_entry->type(dictionary_entry, avp_tree, pinfo, tvb_decrypted, 1, avp_length, avp_item);
1352 break;
1354 case 3: /* encrypted like Ascend-Send-Secret as defined by Ascend^WLucent^WAlcatel-Lucent */
1355 /* maximum length is MD5 hash length */
1356 if (avp_length > HASH_MD5_LENGTH)
1357 avp_length = HASH_MD5_LENGTH;
1358 /* decrypted data is same length as encrypted data */
1359 buffer = (uint8_t *)wmem_alloc(pinfo->pool, avp_length);
1361 radius_decrypt_avp(buffer, pinfo, tvb, offset, avp_length, radius_call->req_authenticator, NULL, 0, 3);
1362 tvb_decrypted = tvb_new_child_real_data(tvb, buffer, avp_length, avp_length);
1363 proto_item_append_text(avp_item, "Decrypted: ");
1364 add_new_data_source(pinfo, tvb_decrypted, "Decrypted Data");
1365 dictionary_entry->type(dictionary_entry, avp_tree, pinfo, tvb_decrypted, 0, avp_length, avp_item);
1366 break;
1369 } else {
1370 dictionary_entry->type(dictionary_entry, avp_tree, pinfo, tvb, offset, avp_length, avp_item);
1374 static gboolean
1375 vsa_buffer_destroy(void *k _U_, void *v, void *p _U_)
1377 radius_vsa_buffer *vsa_buffer = (radius_vsa_buffer *)v;
1378 g_free((void *)vsa_buffer->data);
1379 g_free(v);
1380 return true;
1383 static void
1384 eap_buffer_free_indirect(void *context)
1386 uint8_t *eap_buffer = *(uint8_t **)context;
1387 g_free(eap_buffer);
1390 static void
1391 vsa_buffer_table_destroy_indirect(void *context)
1393 GHashTable *vsa_buffer_table = *(GHashTable **)context;
1394 if (vsa_buffer_table) {
1395 g_hash_table_foreach_remove(vsa_buffer_table, vsa_buffer_destroy, NULL);
1396 g_hash_table_destroy(vsa_buffer_table);
1401 * returns true if the authenticator is valid
1402 * input: tvb of the radius packet, corresponding request authenticator (not used for request),
1403 * uses the shared secret to calculate the (message) authenticator
1404 * and checks with the current.
1405 * see RFC 2865, packet format page 16
1406 * see RFC 2866, Request Authenticator page 7
1407 * see RFC 2869, Message-Authenticator page 33
1409 static int
1410 valid_authenticator (packet_info *pinfo, tvbuff_t *tvb, uint8_t request_authenticator[], bool rfc2869, int offset)
1412 gcry_md_hd_t md5_handle;
1413 uint8_t *digest;
1414 bool result;
1415 unsigned tvb_length;
1416 uint8_t rh_code;
1417 uint8_t *payload;
1418 uint8_t message_authenticator[AUTHENTICATOR_LENGTH];
1420 tvb_length = tvb_captured_length(tvb);
1422 if (tvb_length != tvb_reported_length(tvb) || tvb_length < (unsigned)(offset + AUTHENTICATOR_LENGTH)) {
1423 return -1;
1426 /* copy packet into payload */
1427 payload = (uint8_t *)tvb_memdup(pinfo->pool, tvb, 0, tvb_length);
1429 rh_code = tvb_get_uint8(tvb, 0);
1431 if (rfc2869) {
1432 /* reset (message) authenticator field */
1433 memset(payload+offset, 0, AUTHENTICATOR_LENGTH);
1434 if (rh_code != RADIUS_PKT_TYPE_ACCESS_REQUEST) {
1435 /* replace authenticator in reply with the one in request */
1436 memcpy(payload+4, request_authenticator, AUTHENTICATOR_LENGTH);
1438 /* copy authenticator data */
1439 tvb_memcpy(tvb, message_authenticator, offset, AUTHENTICATOR_LENGTH);
1440 /* calculate HMAC_MD5 hash (payload) */
1441 if (gcry_md_open(&md5_handle, GCRY_MD_MD5, GCRY_MD_FLAG_HMAC)) {
1442 return -1;
1444 gcry_md_setkey(md5_handle, shared_secret, strlen(shared_secret));
1445 gcry_md_write(md5_handle, payload, tvb_length);
1446 digest = gcry_md_read(md5_handle, 0);
1448 result = !memcmp(digest, message_authenticator, AUTHENTICATOR_LENGTH);
1449 gcry_md_close(md5_handle);
1450 } else {
1451 if (rh_code == RADIUS_PKT_TYPE_ACCOUNTING_REQUEST) {
1452 /* reset (message) authenticator field */
1453 memset(payload+4, 0, AUTHENTICATOR_LENGTH);
1454 } else {
1455 /* replace authenticator in reply with the one in request */
1456 memcpy(payload+4, request_authenticator, AUTHENTICATOR_LENGTH);
1458 /* calculate MD5 hash (payload+shared_secret) */
1459 if (gcry_md_open(&md5_handle, GCRY_MD_MD5, 0)) {
1460 return -1;
1462 gcry_md_write(md5_handle, payload, tvb_length);
1463 gcry_md_write(md5_handle, shared_secret, strlen(shared_secret));
1464 digest = gcry_md_read(md5_handle, 0);
1466 result = !memcmp(digest, authenticator, AUTHENTICATOR_LENGTH);
1467 gcry_md_close(md5_handle);
1469 return result;
1472 void
1473 dissect_attribute_value_pairs(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, int offset, unsigned length, radius_call_t *radius_call)
1475 bool last_eap = false;
1476 uint8_t *eap_buffer = NULL;
1477 unsigned eap_seg_num = 0;
1478 unsigned eap_tot_len_captured = 0;
1479 unsigned eap_tot_len = 0;
1480 proto_tree *eap_tree = NULL;
1481 tvbuff_t *eap_tvb = NULL;
1483 GHashTable *vsa_buffer_table = NULL;
1485 if (hf_radius_code <= 0)
1486 proto_registrar_get_byname("radius.code");
1489 * In case we throw an exception, clean up whatever stuff we've
1490 * allocated (if any).
1492 CLEANUP_PUSH_PFX(la, eap_buffer_free_indirect, &eap_buffer);
1493 CLEANUP_PUSH_PFX(lb, vsa_buffer_table_destroy_indirect, &vsa_buffer_table);
1495 while (length > 0) {
1496 radius_attr_info_t *dictionary_entry = NULL;
1497 uint32_t avp_type0 = 0, avp_type1 = 0;
1498 radius_attr_type_t avp_type;
1499 uint32_t avp_length;
1500 uint32_t vendor_id;
1501 bool avp_is_extended = false;
1502 int avp_offset_start = offset;
1504 proto_item *avp_item;
1505 proto_item *avp_len_item;
1506 proto_tree *avp_tree;
1508 if (length < 2) {
1509 proto_tree_add_expert_format(tree, pinfo, &ei_radius_invalid_length, tvb, offset, 0,
1510 "Not enough room in packet for AVP header");
1511 break; /* exit outer loop, then cleanup & return */
1514 avp_type0 = tvb_get_uint8(tvb, offset);
1515 avp_length = tvb_get_uint8(tvb, offset+1);
1516 avp_is_extended = RADIUS_ATTR_TYPE_IS_EXTENDED(avp_type0);
1517 if (avp_is_extended) {
1518 avp_type1 = tvb_get_uint8(tvb, offset+2);
1520 memset(&avp_type, 0, sizeof(avp_type));
1521 avp_type.u8_code[0] = avp_type0;
1522 avp_type.u8_code[1] = avp_type1;
1524 if (disable_extended_attributes) {
1525 avp_is_extended = false;
1526 avp_type.u8_code[1] = 0;
1529 if (avp_length < 2) {
1530 proto_tree_add_expert_format(tree, pinfo, &ei_radius_invalid_length, tvb, offset, 0,
1531 "AVP too short: length %u < 2", avp_length);
1532 break; /* exit outer loop, then cleanup & return */
1535 if (avp_is_extended && avp_length < 3) {
1536 proto_tree_add_expert_format(tree, pinfo, &ei_radius_invalid_length, tvb, offset, 0,
1537 "Extended AVP too short: length %u < 3", avp_length);
1538 break; /* exit outer loop, then cleanup & return */
1541 if (length < avp_length) {
1542 proto_tree_add_expert_format(tree, pinfo, &ei_radius_invalid_length, tvb, offset, 0,
1543 "Not enough room in packet for AVP");
1544 break; /* exit outer loop, then cleanup & return */
1547 length -= avp_length;
1549 dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(dict->attrs_by_id, GUINT_TO_POINTER(avp_type.value));
1551 if (!dictionary_entry) {
1552 dictionary_entry = &no_dictionary_entry;
1555 avp_item = proto_tree_add_bytes_format_value(tree, hf_radius_avp, tvb, offset, avp_length,
1556 NULL, "t=%s", dictionary_entry->name);
1557 if (avp_is_extended)
1558 proto_item_append_text(avp_item, "(%u.%u)", avp_type0, avp_type1);
1559 else
1560 proto_item_append_text(avp_item, "(%u)", avp_type0);
1562 proto_item_append_text(avp_item, " l=%u", avp_length);
1564 avp_length -= 2;
1565 offset += 2;
1566 if (avp_is_extended) {
1567 avp_length -= 1;
1568 offset += 1;
1569 if (RADIUS_ATTR_TYPE_IS_EXTENDED_LONG(avp_type0)) {
1570 avp_length -= 1;
1571 offset += 1;
1575 if (avp_type0 == RADIUS_ATTR_TYPE_VENDOR_SPECIFIC || (avp_is_extended && avp_type1 == RADIUS_ATTR_TYPE_VENDOR_SPECIFIC)) {
1576 radius_vendor_info_t *vendor;
1577 proto_tree *vendor_tree;
1578 int max_offset = offset + avp_length;
1579 const char *vendor_str;
1580 int vendor_offset;
1582 /* XXX TODO: handle 2 byte codes for USR */
1584 if (avp_length < 4) {
1585 expert_add_info_format(pinfo, avp_item, &ei_radius_invalid_length, "AVP too short; no room for vendor ID");
1586 offset += avp_length;
1587 continue; /* while (length > 0) */
1589 vendor_id = tvb_get_ntohl(tvb, offset);
1591 avp_length -= 4;
1592 offset += 4;
1594 vendor = (radius_vendor_info_t *)g_hash_table_lookup(dict->vendors_by_id, GUINT_TO_POINTER(vendor_id));
1595 vendor_str = enterprises_lookup(vendor_id, "Unknown");
1596 if (!vendor) {
1597 vendor = &no_vendor;
1599 proto_item_append_text(avp_item, " vnd=%s(%u)", vendor_str,
1600 vendor_id);
1602 vendor_tree = proto_item_add_subtree(avp_item, vendor->ett);
1604 vendor_offset = avp_offset_start;
1605 proto_tree_add_item(vendor_tree, hf_radius_avp_type, tvb, vendor_offset, 1, ENC_BIG_ENDIAN);
1606 proto_tree_add_item(vendor_tree, hf_radius_avp_length, tvb, vendor_offset+1, 1, ENC_BIG_ENDIAN);
1607 vendor_offset += 2;
1608 if (avp_is_extended) {
1609 proto_tree_add_item(vendor_tree, hf_radius_avp_extended_type, tvb, vendor_offset, 1, ENC_BIG_ENDIAN);
1610 vendor_offset += 1;
1611 if (RADIUS_ATTR_TYPE_IS_EXTENDED_LONG(avp_type0)) {
1612 proto_tree_add_item(vendor_tree, hf_radius_avp_extended_more, tvb, vendor_offset, 1, ENC_BIG_ENDIAN);
1613 vendor_offset += 1;
1616 proto_tree_add_uint_format_value(vendor_tree, hf_radius_avp_vendor_id, tvb, vendor_offset, 4, vendor_id, "%s (%u)", vendor_str, vendor_id);
1617 vendor_offset += 4;
1619 while (offset < max_offset) {
1620 radius_attr_type_t vendor_type;
1621 uint32_t avp_vsa_type;
1622 uint32_t avp_vsa_len;
1623 uint8_t avp_vsa_flags = 0;
1624 uint32_t avp_vsa_header_len;
1625 uint32_t vendor_attribute_len;
1627 switch (vendor->type_octets) {
1628 case 1:
1629 avp_vsa_type = tvb_get_uint8(tvb, offset++);
1630 break;
1631 case 2:
1632 avp_vsa_type = tvb_get_ntohs(tvb, offset);
1633 offset += 2;
1634 break;
1635 case 4:
1636 avp_vsa_type = tvb_get_ntohl(tvb, offset);
1637 offset += 4;
1638 break;
1639 default:
1640 /* vendor->type_octets = 1; */
1641 DISSECTOR_ASSERT_NOT_REACHED();
1642 break;
1645 if (!avp_is_extended) {
1646 switch (vendor->length_octets) {
1647 case 1:
1648 avp_vsa_len = tvb_get_uint8(tvb, offset++);
1649 break;
1650 case 0:
1651 avp_vsa_len = avp_length;
1652 break;
1653 case 2:
1654 avp_vsa_len = tvb_get_ntohs(tvb, offset);
1655 offset += 2;
1656 break;
1657 default:
1658 /* vendor->length_octets = 1; */
1659 DISSECTOR_ASSERT_NOT_REACHED();
1660 break;
1662 avp_vsa_header_len = vendor->type_octets + vendor->length_octets + (vendor->has_flags ? 1 : 0);
1663 } else {
1664 avp_vsa_len = avp_length;
1665 avp_vsa_header_len = vendor->type_octets + (vendor->has_flags ? 1 : 0);
1668 if (vendor->has_flags) {
1669 avp_vsa_flags = tvb_get_uint8(tvb, offset++);
1672 if (avp_vsa_len < avp_vsa_header_len) {
1673 proto_tree_add_expert_format(tree, pinfo, &ei_radius_invalid_length, tvb, offset+1, 1,
1674 "VSA too short");
1675 break; /* exit while (offset < max_offset) loop */
1678 avp_vsa_len -= avp_vsa_header_len;
1680 memset(&vendor_type, 0, sizeof(vendor_type));
1681 if (avp_is_extended) {
1682 vendor_type.u8_code[0] = avp_type.u8_code[0];
1683 vendor_type.u8_code[1] = avp_vsa_type;
1684 } else {
1685 vendor_type.u8_code[0] = avp_vsa_type;
1686 vendor_type.u8_code[1] = 0;
1688 if (vendor->attrs_by_id) {
1689 dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(vendor->attrs_by_id, GUINT_TO_POINTER(vendor_type.value));
1690 } else {
1691 dictionary_entry = NULL;
1694 if (!dictionary_entry) {
1695 dictionary_entry = &no_dictionary_entry;
1698 if (vendor->has_flags) {
1699 avp_tree = proto_tree_add_subtree_format(vendor_tree, tvb, offset-avp_vsa_header_len, avp_vsa_len+avp_vsa_header_len,
1700 dictionary_entry->ett, &avp_item, "VSA: t=%s(%u) l=%u C=0x%02x",
1701 dictionary_entry->name, avp_vsa_type, avp_vsa_len+avp_vsa_header_len, avp_vsa_flags);
1702 } else if (avp_is_extended) {
1703 avp_tree = proto_tree_add_subtree_format(vendor_tree, tvb, offset-avp_vsa_header_len, avp_vsa_len+avp_vsa_header_len,
1704 dictionary_entry->ett, &avp_item, "EVS: t=%s(%u) l=%u",
1705 dictionary_entry->name, avp_vsa_type, avp_vsa_len+avp_vsa_header_len);
1706 } else {
1707 avp_tree = proto_tree_add_subtree_format(vendor_tree, tvb, offset-avp_vsa_header_len, avp_vsa_len+avp_vsa_header_len,
1708 dictionary_entry->ett, &avp_item, "VSA: t=%s(%u) l=%u",
1709 dictionary_entry->name, avp_vsa_type, avp_vsa_len+avp_vsa_header_len);
1712 proto_tree_add_item(avp_tree, hf_radius_avp_vendor_type, tvb, vendor_offset, vendor->type_octets, ENC_BIG_ENDIAN);
1713 vendor_offset += vendor->type_octets;
1714 if (!avp_is_extended && vendor->length_octets) {
1715 proto_tree_add_item_ret_uint(avp_tree, hf_radius_avp_vendor_len, tvb, vendor_offset, vendor->length_octets, ENC_BIG_ENDIAN, &vendor_attribute_len);
1716 vendor_offset += (vendor_attribute_len - vendor->type_octets);
1719 if (show_length) {
1720 avp_len_item = proto_tree_add_uint(avp_tree,
1721 dictionary_entry->hf_len,
1722 tvb, 0, 0, avp_length);
1723 proto_item_set_generated(avp_len_item);
1726 if (vendor->has_flags) {
1728 * WiMAX VSA's have a non-standard format:
1730 * type 1 octet
1731 * length 1 octet
1732 * continuation 1 octet 0bcrrrrrrr
1733 * value 1+ octets
1735 * If the high bit of the "continuation" field is set, then
1736 * the next attribute of the same WiMAX type should have it's
1737 * value concatenated to this one.
1739 * See "dictionary.wimax" from FreeRADIUS for details and references.
1741 radius_vsa_buffer_key key;
1742 radius_vsa_buffer *vsa_buffer = NULL;
1743 key.vendor_id = vendor_id;
1744 key.vsa_type = avp_vsa_type;
1746 if (!vsa_buffer_table) {
1747 vsa_buffer_table = g_hash_table_new(radius_vsa_hash, radius_vsa_equal);
1750 vsa_buffer = (radius_vsa_buffer *)g_hash_table_lookup(vsa_buffer_table, &key);
1751 if (vsa_buffer) {
1752 vsa_buffer->data = (uint8_t *)g_realloc(vsa_buffer->data, vsa_buffer->len + avp_vsa_len);
1753 tvb_memcpy(tvb, vsa_buffer->data + vsa_buffer->len, offset, avp_vsa_len);
1754 vsa_buffer->len += avp_vsa_len;
1755 vsa_buffer->seg_num++;
1758 if (avp_vsa_flags & 0x80) {
1759 if (!vsa_buffer) {
1760 vsa_buffer = g_new(radius_vsa_buffer, 1);
1761 vsa_buffer->key.vendor_id = vendor_id;
1762 vsa_buffer->key.vsa_type = avp_vsa_type;
1763 vsa_buffer->len = avp_vsa_len;
1764 vsa_buffer->seg_num = 1;
1765 vsa_buffer->data = (uint8_t *)g_malloc(avp_vsa_len);
1766 tvb_memcpy(tvb, vsa_buffer->data, offset, avp_vsa_len);
1767 g_hash_table_insert(vsa_buffer_table, &(vsa_buffer->key), vsa_buffer);
1769 proto_tree_add_item(avp_tree, hf_radius_vsa_fragment, tvb, offset, avp_vsa_len, ENC_NA);
1770 proto_item_append_text(avp_item, ": VSA fragment[%u]", vsa_buffer->seg_num);
1771 } else {
1772 if (vsa_buffer) {
1773 tvbuff_t *vsa_tvb = NULL;
1774 proto_tree_add_item(avp_tree, hf_radius_vsa_fragment, tvb, offset, avp_vsa_len, ENC_NA);
1775 proto_item_append_text(avp_item, ": Last VSA fragment[%u]", vsa_buffer->seg_num);
1776 vsa_tvb = tvb_new_child_real_data(tvb, vsa_buffer->data, vsa_buffer->len, vsa_buffer->len);
1777 tvb_set_free_cb(vsa_tvb, g_free);
1778 add_new_data_source(pinfo, vsa_tvb, "Reassembled VSA");
1779 add_avp_to_tree(avp_tree, avp_item, pinfo, vsa_tvb, dictionary_entry, vsa_buffer->len, 0, radius_call);
1780 g_hash_table_remove(vsa_buffer_table, &(vsa_buffer->key));
1781 g_free(vsa_buffer);
1783 } else {
1784 add_avp_to_tree(avp_tree, avp_item, pinfo, tvb, dictionary_entry, avp_vsa_len, offset, radius_call);
1787 } else {
1788 add_avp_to_tree(avp_tree, avp_item, pinfo, tvb, dictionary_entry, avp_vsa_len, offset, radius_call);
1791 offset += avp_vsa_len;
1792 } /* while (offset < max_offset) */
1793 continue; /* while (length > 0) */
1796 avp_tree = proto_item_add_subtree(avp_item, dictionary_entry->ett);
1798 proto_tree_add_item(avp_tree, hf_radius_avp_type, tvb, avp_offset_start, 1, ENC_BIG_ENDIAN);
1799 proto_tree_add_item(avp_tree, hf_radius_avp_length, tvb, avp_offset_start+1, 1, ENC_BIG_ENDIAN);
1801 if (show_length) {
1802 avp_len_item = proto_tree_add_uint(avp_tree,
1803 dictionary_entry->hf_len,
1804 tvb, 0, 0, avp_length);
1805 proto_item_set_generated(avp_len_item);
1808 if (avp_is_extended) {
1809 proto_tree_add_item(avp_tree, hf_radius_avp_extended_type, tvb, avp_offset_start+2, 1, ENC_BIG_ENDIAN);
1810 if (RADIUS_ATTR_TYPE_IS_EXTENDED_LONG(avp_type0)) {
1811 proto_tree_add_item(avp_tree, hf_radius_avp_extended_more, tvb, avp_offset_start+3, 1, ENC_BIG_ENDIAN);
1815 /* XXX - Do similar concatenation if dictionary_entry->concat == true */
1816 if (avp_type0 == RADIUS_ATTR_TYPE_EAP_MESSAGE) {
1817 int tvb_len;
1819 eap_seg_num++;
1821 tvb_len = tvb_captured_length_remaining(tvb, offset);
1823 if ((int)avp_length < tvb_len)
1824 tvb_len = avp_length;
1826 /* Show this as an EAP fragment. */
1827 proto_tree_add_item(avp_tree, hf_radius_eap_fragment, tvb, offset, tvb_len, ENC_NA);
1829 if (eap_tvb != NULL) {
1831 * Oops, a non-consecutive EAP-Message
1832 * attribute.
1834 proto_item_append_text(avp_item, " (non-consecutive)");
1835 } else {
1837 * RFC 2869 says, in section 5.13, describing
1838 * the EAP-Message attribute:
1840 * The NAS places EAP messages received
1841 * from the authenticating peer into one
1842 * or more EAP-Message attributes and
1843 * forwards them to the RADIUS Server
1844 * within an Access-Request message.
1845 * If multiple EAP-Messages are
1846 * contained within an Access-Request or
1847 * Access-Challenge packet, they MUST be
1848 * in order and they MUST be consecutive
1849 * attributes in the Access-Request or
1850 * Access-Challenge packet.
1852 * ...
1854 * The String field contains EAP packets,
1855 * as defined in [3]. If multiple
1856 * EAP-Message attributes are present
1857 * in a packet their values should be
1858 * concatenated; this allows EAP packets
1859 * longer than 253 octets to be passed
1860 * by RADIUS.
1862 * Do reassembly of EAP-Message attributes.
1863 * We just concatenate all the attributes,
1864 * and when we see either the end of the
1865 * attribute list or a non-EAP-Message
1866 * attribute, we know we're done.
1869 if (eap_buffer == NULL)
1870 eap_buffer = (uint8_t *)g_malloc(eap_tot_len_captured + tvb_len);
1871 else
1872 eap_buffer = (uint8_t *)g_realloc(eap_buffer,
1873 eap_tot_len_captured + tvb_len);
1874 tvb_memcpy(tvb, eap_buffer + eap_tot_len_captured, offset,
1875 tvb_len);
1876 eap_tot_len_captured += tvb_len;
1877 eap_tot_len += avp_length;
1879 if (tvb_bytes_exist(tvb, offset + avp_length + 1, 1)) {
1880 uint8_t next_type = tvb_get_uint8(tvb, offset + avp_length);
1882 if (next_type != RADIUS_ATTR_TYPE_EAP_MESSAGE) {
1883 /* Non-EAP-Message attribute */
1884 last_eap = true;
1886 } else {
1888 * No more attributes, either because
1889 * we're at the end of the packet or
1890 * because we're at the end of the
1891 * captured packet data.
1893 last_eap = true;
1896 if (last_eap && eap_buffer) {
1897 bool save_writable;
1899 proto_item_append_text(avp_item, " Last Segment[%u]",
1900 eap_seg_num);
1902 eap_tree = proto_item_add_subtree(avp_item, ett_eap);
1904 eap_tvb = tvb_new_child_real_data(tvb, eap_buffer,
1905 eap_tot_len_captured,
1906 eap_tot_len);
1907 tvb_set_free_cb(eap_tvb, g_free);
1908 add_new_data_source(pinfo, eap_tvb, "Reassembled EAP");
1911 * Don't free this when we're done -
1912 * it's associated with a tvbuff.
1914 eap_buffer = NULL;
1917 * Set the columns non-writable,
1918 * so that the packet list shows
1919 * this as an RADIUS packet, not
1920 * as an EAP packet.
1922 save_writable = col_get_writable(pinfo->cinfo, -1);
1923 col_set_writable(pinfo->cinfo, -1, false);
1925 call_dissector(eap_handle, eap_tvb, pinfo, eap_tree);
1927 col_set_writable(pinfo->cinfo, -1, save_writable);
1928 } else {
1929 proto_item_append_text(avp_item, " Segment[%u]",
1930 eap_seg_num);
1934 offset += avp_length;
1935 continue;
1938 if (avp_type0 == RADIUS_ATTR_TYPE_MESSAGE_AUTHENTICATOR && validate_authenticator && *shared_secret != '\0' && radius_call) {
1939 proto_item *authenticator_tree, *item;
1940 int valid;
1942 valid = valid_authenticator(pinfo, tvb, radius_call->req_authenticator, true, offset);
1943 if (valid >= 0) {
1944 proto_item_append_text(avp_item, " [%s]", valid? "correct" : "incorrect");
1946 authenticator_tree = proto_item_add_subtree(avp_item, ett_radius_authenticator);
1947 item = proto_tree_add_boolean(authenticator_tree, hf_radius_message_authenticator_valid, tvb, offset, AUTHENTICATOR_LENGTH, valid == 1 ? true : false);
1948 proto_item_set_generated(item);
1949 item = proto_tree_add_boolean(authenticator_tree, hf_radius_message_authenticator_invalid, tvb, offset, AUTHENTICATOR_LENGTH, valid == 0 ? true : false);
1950 proto_item_set_generated(item);
1952 if (valid == 0) {
1953 col_append_str(pinfo->cinfo, COL_INFO, " [incorrect message authenticator]");
1957 add_avp_to_tree(avp_tree, avp_item, pinfo, tvb, dictionary_entry,
1958 avp_length, offset, radius_call);
1959 offset += avp_length;
1961 } /* while (length > 0) */
1963 CLEANUP_CALL_AND_POP_PFX(lb); /* vsa_buffer_table_destroy_indirect(&vsa_buffer_table) */
1966 * Call the cleanup handler to free any reassembled data we haven't
1967 * attached to a tvbuff, and pop the handler.
1969 CLEANUP_CALL_AND_POP_PFX(la); /* eap_buffer_free_indirect(&eap_buffer); */
1972 /* This function tries to determine whether a packet is radius or not */
1973 static bool
1974 is_radius(tvbuff_t *tvb)
1976 uint8_t code;
1977 uint16_t length;
1979 code = tvb_get_uint8(tvb, 0);
1980 if (try_val_to_str_ext(code, &radius_pkt_type_codes_ext) == NULL) {
1981 return false;
1984 /* Check for valid length value:
1985 * Length
1987 * The Length field is two octets. It indicates the length of the
1988 * packet including the Code, Identifier, Length, Authenticator and
1989 * Attribute fields. Octets outside the range of the Length field
1990 * MUST be treated as padding and ignored on reception. If the
1991 * packet is shorter than the Length field indicates, it MUST be
1992 * silently discarded. The minimum length is 20 and maximum length
1993 * is 4096.
1995 length = tvb_get_ntohs(tvb, 2);
1996 if ((length < 20) || (length > 4096)) {
1997 return false;
2000 return true;
2003 static int
2004 dissect_radius(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_)
2006 proto_tree *radius_tree = NULL;
2007 proto_tree *avptree = NULL;
2008 proto_item *ti, *hidden_item, *authenticator_item = NULL;
2009 unsigned avplength;
2010 e_radiushdr rh;
2011 radius_info_t *rad_info;
2013 conversation_t *conversation;
2014 radius_call_info_key radius_call_key;
2015 radius_call_info_key *new_radius_call_key;
2016 wmem_tree_t *radius_call_tree;
2017 radius_call_t *radius_call = NULL;
2018 static address null_address = ADDRESS_INIT_NONE;
2020 /* does this look like radius ? */
2021 if (!is_radius(tvb)) {
2022 return 0;
2025 col_set_str(pinfo->cinfo, COL_PROTOCOL, "RADIUS");
2026 col_clear(pinfo->cinfo, COL_INFO);
2028 rh.rh_code = tvb_get_uint8(tvb, 0);
2029 rh.rh_ident = tvb_get_uint8(tvb, 1);
2030 rh.rh_pktlength = tvb_get_ntohs(tvb, 2);
2033 /* Initialise stat info for passing to tap */
2034 rad_info = wmem_new(wmem_packet_scope(), radius_info_t);
2035 rad_info->req_time.secs = 0;
2036 rad_info->req_time.nsecs = 0;
2037 rad_info->is_duplicate = false;
2038 rad_info->request_available = false;
2039 rad_info->req_num = 0; /* frame number request seen */
2040 rad_info->rspcode = 0;
2041 /* tap stat info */
2042 rad_info->code = rh.rh_code;
2043 rad_info->ident = rh.rh_ident;
2044 tap_queue_packet(radius_tap, pinfo, rad_info);
2046 col_add_fstr(pinfo->cinfo, COL_INFO, "%s id=%d",
2047 val_to_str_ext_const(rh.rh_code, &radius_pkt_type_codes_ext, "Unknown Packet"),
2048 rh.rh_ident);
2050 /* Load header fields if not already done */
2051 if (hf_radius_code <= 0)
2052 proto_registrar_get_byname("radius.code");
2054 ti = proto_tree_add_item(tree, proto_radius, tvb, 0, rh.rh_pktlength, ENC_NA);
2055 radius_tree = proto_item_add_subtree(ti, ett_radius);
2056 proto_tree_add_uint(radius_tree, hf_radius_code, tvb, 0, 1, rh.rh_code);
2057 proto_tree_add_uint_format(radius_tree, hf_radius_id, tvb, 1, 1, rh.rh_ident,
2058 "Packet identifier: 0x%01x (%d)", rh.rh_ident, rh.rh_ident);
2061 * Make sure the length is sane.
2063 if (rh.rh_pktlength < HDR_LENGTH) {
2064 proto_tree_add_uint_format_value(radius_tree, hf_radius_length,
2065 tvb, 2, 2, rh.rh_pktlength, "%u (bogus, < %u)",
2066 rh.rh_pktlength, HDR_LENGTH);
2067 return tvb_captured_length(tvb);
2070 avplength = rh.rh_pktlength - HDR_LENGTH;
2071 proto_tree_add_uint(radius_tree, hf_radius_length, tvb, 2, 2, rh.rh_pktlength);
2072 authenticator_item = proto_tree_add_item(radius_tree, hf_radius_authenticator, tvb, 4, AUTHENTICATOR_LENGTH, ENC_NA);
2073 tvb_memcpy(tvb, authenticator, 4, AUTHENTICATOR_LENGTH);
2075 /* Conversation support REQUEST/RESPONSES */
2076 switch (rh.rh_code)
2078 case RADIUS_PKT_TYPE_ACCESS_REQUEST:
2079 case RADIUS_PKT_TYPE_ACCOUNTING_REQUEST:
2080 case RADIUS_PKT_TYPE_PASSWORD_REQUEST:
2081 case RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST:
2082 case RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST:
2083 case RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST:
2084 case RADIUS_PKT_TYPE_EVENT_REQUEST:
2085 case RADIUS_PKT_TYPE_DISCONNECT_REQUEST:
2086 case RADIUS_PKT_TYPE_COA_REQUEST:
2087 case RADIUS_PKT_TYPE_ALU_STATE_REQUEST:
2088 /* Don't bother creating conversations if we're encapsulated within
2089 * an error packet, such as an ICMP destination unreachable */
2090 if (pinfo->flags.in_error_pkt)
2091 break;
2093 hidden_item = proto_tree_add_boolean(radius_tree, hf_radius_req, tvb, 0, 0, true);
2094 proto_item_set_hidden(hidden_item);
2096 /* Keep track of the address and port whence the call came
2097 * so that we can match up requests with replies.
2099 * Because it is UDP and the reply can come from any IP
2100 * and port (not necessarily the request dest), we only
2101 * track the source IP and port of the request to match
2102 * the reply.
2106 * XXX - can we just use NO_ADDR_B? Unfortunately,
2107 * you currently still have to pass a non-null
2108 * pointer for the second address argument even
2109 * if you do that.
2111 conversation = find_conversation(pinfo->num, &pinfo->src,
2112 &null_address, conversation_pt_to_conversation_type(pinfo->ptype), pinfo->srcport,
2113 pinfo->destport, 0);
2114 if (conversation == NULL)
2116 /* It's not part of any conversation - create a new one. */
2117 conversation = conversation_new(pinfo->num, &pinfo->src,
2118 &null_address, conversation_pt_to_conversation_type(pinfo->ptype), pinfo->srcport,
2119 pinfo->destport, 0);
2122 /* Prepare the key data */
2123 radius_call_key.code = rh.rh_code;
2124 radius_call_key.ident = rh.rh_ident;
2125 radius_call_key.conversation = conversation;
2126 radius_call_key.req_time = pinfo->abs_ts;
2128 /* Look up the tree of calls with this ident */
2129 radius_call_tree = (wmem_tree_t *)wmem_map_lookup(radius_calls, &radius_call_key);
2131 if (!radius_call_tree) {
2132 radius_call_tree = wmem_tree_new(wmem_file_scope());
2133 new_radius_call_key = wmem_new(wmem_file_scope(), radius_call_info_key);
2134 *new_radius_call_key = radius_call_key;
2135 wmem_map_insert(radius_calls, new_radius_call_key, radius_call_tree);
2138 /* Find the last call we've seen (for this ident in this conversation) */
2139 radius_call = (radius_call_t *)wmem_tree_lookup32_le(radius_call_tree, pinfo->num);
2140 if (radius_call != NULL) {
2141 /* We found a request with the same ident (in this conversation).
2142 * Is it really a duplicate?
2144 if (pinfo->num != radius_call->req_num &&
2145 !memcmp(radius_call->req_authenticator, authenticator, AUTHENTICATOR_LENGTH)) {
2146 /* Yes, mark it as such */
2147 rad_info->is_duplicate = true;
2148 rad_info->req_num = radius_call->req_num;
2149 col_append_str(pinfo->cinfo, COL_INFO, ", Duplicate Request");
2151 if (tree) {
2152 proto_item *item;
2153 hidden_item = proto_tree_add_uint(radius_tree, hf_radius_dup, tvb, 0, 0, rh.rh_ident);
2154 proto_item_set_hidden(hidden_item);
2155 item = proto_tree_add_uint(radius_tree, hf_radius_req_dup, tvb, 0, 0, radius_call->req_num);
2156 proto_item_set_generated(item);
2160 /* Accounting Request Authenticator Validation */
2161 if (rh.rh_code == RADIUS_PKT_TYPE_ACCOUNTING_REQUEST && validate_authenticator && *shared_secret != '\0') {
2162 proto_item *authenticator_tree, *item;
2163 int valid;
2164 valid = valid_authenticator(pinfo, tvb, radius_call->req_authenticator, false, 4);
2165 if (valid >= 0) {
2166 proto_item_append_text(authenticator_item, " [%s]", valid? "correct" : "incorrect");
2168 authenticator_tree = proto_item_add_subtree(authenticator_item, ett_radius_authenticator);
2169 item = proto_tree_add_boolean(authenticator_tree, hf_radius_authenticator_valid, tvb, 4, AUTHENTICATOR_LENGTH, valid == 1 ? true : false);
2170 proto_item_set_generated(item);
2171 item = proto_tree_add_boolean(authenticator_tree, hf_radius_authenticator_invalid, tvb, 4, AUTHENTICATOR_LENGTH, valid == 0 ? true : false);
2172 proto_item_set_generated(item);
2174 if (valid == 0) {
2175 col_append_str(pinfo->cinfo, COL_INFO, " [incorrect authenticator]");
2180 if (!PINFO_FD_VISITED(pinfo) && (radius_call == NULL || !rad_info->is_duplicate)) {
2181 /* Prepare the value data.
2182 * "req_num" and "rsp_num" are frame numbers;
2183 * frame numbers are 1-origin, so we use 0
2184 * to mean "we don't yet know in which frame
2185 * the reply for this call appears".
2187 radius_call = wmem_new(wmem_file_scope(), radius_call_t);
2188 radius_call->req_num = pinfo->num;
2189 radius_call->rsp_num = 0;
2190 radius_call->ident = rh.rh_ident;
2191 radius_call->code = rh.rh_code;
2192 memcpy(radius_call->req_authenticator, authenticator, AUTHENTICATOR_LENGTH);
2193 radius_call->responded = false;
2194 radius_call->req_time = pinfo->abs_ts;
2195 radius_call->rspcode = 0;
2197 /* Store it */
2198 wmem_tree_insert32(radius_call_tree, pinfo->num, radius_call);
2201 if (radius_call && radius_call->rsp_num) {
2202 proto_item *item;
2203 item = proto_tree_add_uint_format(radius_tree,
2204 hf_radius_rsp_frame, tvb, 0, 0, radius_call->rsp_num,
2205 "The response to this request is in frame %u",
2206 radius_call->rsp_num);
2207 proto_item_set_generated(item);
2209 break;
2210 case RADIUS_PKT_TYPE_ACCESS_ACCEPT:
2211 case RADIUS_PKT_TYPE_ACCESS_REJECT:
2212 case RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE:
2213 case RADIUS_PKT_TYPE_PASSWORD_ACK:
2214 case RADIUS_PKT_TYPE_PASSWORD_REJECT:
2215 case RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE:
2216 case RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE:
2217 case RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE:
2218 case RADIUS_PKT_TYPE_EVENT_RESPONSE:
2219 case RADIUS_PKT_TYPE_DISCONNECT_ACK:
2220 case RADIUS_PKT_TYPE_DISCONNECT_NAK:
2221 case RADIUS_PKT_TYPE_COA_ACK:
2222 case RADIUS_PKT_TYPE_COA_NAK:
2223 case RADIUS_PKT_TYPE_ACCESS_CHALLENGE:
2224 case RADIUS_PKT_TYPE_ALU_STATE_ACCEPT:
2225 case RADIUS_PKT_TYPE_ALU_STATE_REJECT:
2226 case RADIUS_PKT_TYPE_ALU_STATE_ERROR:
2227 /* Don't bother finding conversations if we're encapsulated within
2228 * an error packet, such as an ICMP destination unreachable */
2229 if (pinfo->flags.in_error_pkt)
2230 break;
2232 hidden_item = proto_tree_add_boolean(radius_tree, hf_radius_rsp, tvb, 0, 0, true);
2233 proto_item_set_hidden(hidden_item);
2235 /* Check for RADIUS response. A response must match a call that
2236 * we've seen, and the response must be sent to the same
2237 * port and address that the call came from.
2239 * Because it is UDP and the reply can come from any IP
2240 * and port (not necessarily the request dest), we only
2241 * track the source IP and port of the request to match
2242 * the reply.
2245 /* XXX - can we just use NO_ADDR_B? Unfortunately,
2246 * you currently still have to pass a non-null
2247 * pointer for the second address argument even
2248 * if you do that.
2250 conversation = find_conversation(pinfo->num, &null_address,
2251 &pinfo->dst, conversation_pt_to_conversation_type(pinfo->ptype), pinfo->srcport, pinfo->destport, 0);
2252 if (conversation == NULL) {
2253 /* Nothing more to do here */
2254 break;
2257 /* Prepare the key data */
2258 radius_call_key.code = rh.rh_code;
2259 radius_call_key.ident = rh.rh_ident;
2260 radius_call_key.conversation = conversation;
2261 radius_call_key.req_time = pinfo->abs_ts;
2263 /* Look up the tree of calls with this ident */
2264 radius_call_tree = (wmem_tree_t *)wmem_map_lookup(radius_calls, &radius_call_key);
2265 if (radius_call_tree == NULL) {
2266 /* Nothing more to do here */
2267 break;
2270 /* Find the last call we've seen (for this ident in this conversation) */
2271 radius_call = (radius_call_t *)wmem_tree_lookup32_le(radius_call_tree, pinfo->num);
2272 if (radius_call == NULL) {
2273 /* Nothing more to do here */
2274 break;
2277 /* Indicate the frame to which this is a reply. */
2278 if (radius_call->req_num) {
2279 nstime_t delta;
2280 proto_item *item;
2282 rad_info->request_available = true;
2283 rad_info->req_num = radius_call->req_num;
2284 radius_call->responded = true;
2286 item = proto_tree_add_uint_format(radius_tree,
2287 hf_radius_req_frame, tvb, 0, 0,
2288 radius_call->req_num,
2289 "This is a response to a request in frame %u",
2290 radius_call->req_num);
2291 proto_item_set_generated(item);
2292 nstime_delta(&delta, &pinfo->abs_ts, &radius_call->req_time);
2293 item = proto_tree_add_time(radius_tree, hf_radius_time, tvb, 0, 0, &delta);
2294 proto_item_set_generated(item);
2295 /* Response Authenticator Validation */
2296 if (validate_authenticator && *shared_secret != '\0') {
2297 proto_item *authenticator_tree;
2298 int valid;
2299 valid = valid_authenticator(pinfo, tvb, radius_call->req_authenticator, false, 4);
2300 if (valid >= 0) {
2301 proto_item_append_text(authenticator_item, " [%s]", valid? "correct" : "incorrect");
2303 authenticator_tree = proto_item_add_subtree(authenticator_item, ett_radius_authenticator);
2304 item = proto_tree_add_boolean(authenticator_tree, hf_radius_authenticator_valid, tvb, 4, AUTHENTICATOR_LENGTH, valid == 1 ? true : false);
2305 proto_item_set_generated(item);
2306 item = proto_tree_add_boolean(authenticator_tree, hf_radius_authenticator_invalid, tvb, 4, AUTHENTICATOR_LENGTH, valid == 0 ? true : false);
2307 proto_item_set_generated(item);
2309 if (valid == 0) {
2310 col_append_str(pinfo->cinfo, COL_INFO, " [incorrect authenticator]");
2315 if (radius_call->rsp_num == 0) {
2316 /* We have not yet seen a response to that call, so
2317 this must be the first response; remember its
2318 frame number. */
2319 radius_call->rsp_num = pinfo->num;
2320 } else {
2321 /* We have seen a response to this call - but was it
2322 *this* response? (disregard provisional responses) */
2323 if ((radius_call->rsp_num != pinfo->num) && (radius_call->rspcode == rh.rh_code)) {
2324 /* No, so it's a duplicate response. Mark it as such. */
2325 rad_info->is_duplicate = true;
2326 col_append_str(pinfo->cinfo, COL_INFO, ", Duplicate Response");
2328 if (tree) {
2329 proto_item *item;
2330 hidden_item = proto_tree_add_uint(radius_tree,
2331 hf_radius_dup, tvb, 0, 0, rh.rh_ident);
2332 proto_item_set_hidden(hidden_item);
2333 item = proto_tree_add_uint(radius_tree,
2334 hf_radius_rsp_dup, tvb, 0, 0, radius_call->rsp_num);
2335 proto_item_set_generated(item);
2339 /* Now store the response code (after comparison above) */
2340 radius_call->rspcode = rh.rh_code;
2341 rad_info->rspcode = rh.rh_code;
2342 break;
2343 default:
2344 break;
2347 if (radius_call) {
2348 rad_info->req_time = radius_call->req_time;
2351 if (avplength > 0) {
2352 /* list the attribute value pairs */
2353 avptree = proto_tree_add_subtree(radius_tree, tvb, HDR_LENGTH,
2354 avplength, ett_radius_avp, NULL, "Attribute Value Pairs");
2355 dissect_attribute_value_pairs(avptree, pinfo, tvb, HDR_LENGTH,
2356 avplength, radius_call);
2359 return tvb_captured_length(tvb);
2362 void
2363 free_radius_attr_info(void *data)
2365 radius_attr_info_t* attr = (radius_attr_info_t*)data;
2366 value_string *vs = (value_string *)attr->vs;
2368 g_free(attr->name);
2369 if (attr->tlvs_by_id) {
2370 g_hash_table_destroy(attr->tlvs_by_id);
2372 if (vs) {
2373 for (; vs->strptr; vs++) {
2374 g_free((void *)vs->strptr);
2376 g_free((void *)attr->vs);
2379 g_free(attr);
2382 static void
2383 free_radius_vendor_info(void *data)
2385 radius_vendor_info_t* vendor = (radius_vendor_info_t*)data;
2387 g_free(vendor->name);
2388 if (vendor->attrs_by_id)
2389 g_hash_table_destroy(vendor->attrs_by_id);
2391 g_free(vendor);
2394 static void
2395 register_attrs(void *k _U_, void *v, void *p)
2397 radius_attr_info_t *a = (radius_attr_info_t *)v;
2398 int i;
2399 int *ett = &(a->ett);
2400 char *abbrev = wmem_strdup_printf(wmem_epan_scope(), "radius.%s", a->name);
2401 hf_register_info hfri[] = {
2402 { NULL, { NULL, NULL, FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
2403 { NULL, { NULL, NULL, FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }},
2404 { NULL, { NULL, NULL, FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
2405 { NULL, { NULL, NULL, FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }},
2406 { NULL, { NULL, NULL, FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}
2408 unsigned len_hf = 2;
2409 hfett_t *ri = (hfett_t *)p;
2411 for(i=0; abbrev[i]; i++) {
2412 if (abbrev[i] == '-') abbrev[i] = '_';
2413 if (abbrev[i] == '/') abbrev[i] = '_';
2416 hfri[0].p_id = &(a->hf);
2417 hfri[1].p_id = &(a->hf_len);
2419 hfri[0].hfinfo.name = a->name;
2420 hfri[0].hfinfo.abbrev = abbrev;
2422 hfri[1].hfinfo.name = "Length";
2423 hfri[1].hfinfo.abbrev = wmem_strdup_printf(wmem_epan_scope(), "%s.len", abbrev);
2424 hfri[1].hfinfo.blurb = wmem_strdup_printf(wmem_epan_scope(), "%s Length", a->name);
2426 if (a->type == radius_integer) {
2427 hfri[0].hfinfo.type = FT_UINT32;
2428 hfri[0].hfinfo.display = BASE_DEC;
2430 hfri[2].p_id = &(a->hf_alt);
2431 hfri[2].hfinfo.name = wmem_strdup(wmem_epan_scope(), a->name);
2432 hfri[2].hfinfo.abbrev = abbrev;
2433 hfri[2].hfinfo.type = FT_UINT64;
2434 hfri[2].hfinfo.display = BASE_DEC;
2436 if (a->vs) {
2437 hfri[0].hfinfo.strings = VALS(a->vs);
2440 len_hf++;
2441 } else if (a->type == radius_signed) {
2442 hfri[0].hfinfo.type = FT_INT32;
2443 hfri[0].hfinfo.display = BASE_DEC;
2445 hfri[2].p_id = &(a->hf_alt);
2446 hfri[2].hfinfo.name = wmem_strdup(wmem_epan_scope(), a->name);
2447 hfri[2].hfinfo.abbrev = abbrev;
2448 hfri[2].hfinfo.type = FT_INT64;
2449 hfri[2].hfinfo.display = BASE_DEC;
2451 if (a->vs) {
2452 hfri[0].hfinfo.strings = VALS(a->vs);
2455 len_hf++;
2456 } else if (a->type == radius_string) {
2457 hfri[0].hfinfo.type = FT_STRING;
2458 hfri[0].hfinfo.display = BASE_NONE;
2459 } else if (a->type == radius_octets) {
2460 hfri[0].hfinfo.type = FT_BYTES;
2461 hfri[0].hfinfo.display = BASE_NONE;
2462 } else if (a->type == radius_ipaddr) {
2463 hfri[0].hfinfo.type = FT_IPv4;
2464 hfri[0].hfinfo.display = BASE_NONE;
2465 } else if (a->type == radius_ipv6addr) {
2466 hfri[0].hfinfo.type = FT_IPv6;
2467 hfri[0].hfinfo.display = BASE_NONE;
2468 } else if (a->type == radius_ipv6prefix) {
2469 hfri[0].hfinfo.type = FT_BYTES;
2470 hfri[0].hfinfo.display = BASE_NONE;
2471 } else if (a->type == radius_ipxnet) {
2472 hfri[0].hfinfo.type = FT_IPXNET;
2473 hfri[0].hfinfo.display = BASE_NONE;
2474 } else if (a->type == radius_date) {
2475 hfri[0].hfinfo.type = FT_ABSOLUTE_TIME;
2476 hfri[0].hfinfo.display = ABSOLUTE_TIME_LOCAL;
2477 } else if (a->type == radius_abinary) {
2478 hfri[0].hfinfo.type = FT_BYTES;
2479 hfri[0].hfinfo.display = BASE_NONE;
2480 } else if (a->type == radius_ifid) {
2481 hfri[0].hfinfo.type = FT_BYTES;
2482 hfri[0].hfinfo.display = BASE_NONE;
2483 } else if (a->type == radius_combo_ip) {
2484 hfri[0].hfinfo.type = FT_IPv4;
2485 hfri[0].hfinfo.display = BASE_NONE;
2487 hfri[2].p_id = &(a->hf_alt);
2488 hfri[2].hfinfo.name = wmem_strdup(wmem_epan_scope(), a->name);
2489 hfri[2].hfinfo.abbrev = wmem_strdup(wmem_epan_scope(), abbrev);
2490 hfri[2].hfinfo.type = FT_IPv6;
2491 hfri[2].hfinfo.display = BASE_NONE;
2493 len_hf++;
2494 #if 0 /* Fix -Wduplicated-branches */
2495 } else if (a->type == radius_tlv) {
2496 hfri[0].hfinfo.type = FT_BYTES;
2497 hfri[0].hfinfo.display = BASE_NONE;
2498 #endif
2499 } else {
2500 hfri[0].hfinfo.type = FT_BYTES;
2501 hfri[0].hfinfo.display = BASE_NONE;
2504 if (a->tagged) {
2505 hfri[len_hf].p_id = &(a->hf_tag);
2506 hfri[len_hf].hfinfo.name = "Tag";
2507 hfri[len_hf].hfinfo.abbrev = wmem_strdup_printf(wmem_epan_scope(), "%s.tag", abbrev);
2508 hfri[len_hf].hfinfo.blurb = wmem_strdup_printf(wmem_epan_scope(), "%s Tag", a->name);
2509 hfri[len_hf].hfinfo.type = FT_UINT8;
2510 hfri[len_hf].hfinfo.display = BASE_HEX;
2511 len_hf++;
2514 if (a->encrypt != 0) {
2516 * This attribute is encrypted, so create an
2517 * alternative field for the encrypted value.
2519 hfri[len_hf].p_id = &(a->hf_enc);
2520 hfri[len_hf].hfinfo.name = wmem_strdup_printf(wmem_epan_scope(), "%s (encrypted)", a->name);
2521 hfri[len_hf].hfinfo.abbrev = wmem_strdup_printf(wmem_epan_scope(), "%s_encrypted", abbrev);
2522 hfri[len_hf].hfinfo.type = FT_BYTES;
2523 hfri[len_hf].hfinfo.display = BASE_NONE;
2524 len_hf++;
2527 wmem_array_append(ri->hf, hfri, len_hf);
2528 wmem_array_append_one(ri->ett, ett);
2530 if (a->tlvs_by_id) {
2531 g_hash_table_foreach(a->tlvs_by_id, register_attrs, ri);
2535 static void
2536 register_vendors(void *k _U_, void *v, void *p)
2538 radius_vendor_info_t *vnd = (radius_vendor_info_t *)v;
2539 hfett_t *ri = (hfett_t *)p;
2540 value_string vnd_vs;
2541 int *ett_p = &(vnd->ett);
2543 vnd_vs.value = vnd->code;
2544 vnd_vs.strptr = vnd->name;
2546 wmem_array_append_one(ri->vend_vs, vnd_vs);
2547 wmem_array_append_one(ri->ett, ett_p);
2549 g_hash_table_foreach(vnd->attrs_by_id, register_attrs, ri);
2552 extern void
2553 radius_register_avp_dissector(uint32_t vendor_id, uint32_t _attribute_id, radius_avp_dissector_t radius_avp_dissector)
2555 radius_vendor_info_t *vendor;
2556 radius_attr_info_t *dictionary_entry;
2557 GHashTable *by_id;
2558 radius_attr_type_t attribute_id;
2560 DISSECTOR_ASSERT(radius_avp_dissector != NULL);
2561 memset(&attribute_id, 0, sizeof(attribute_id));
2562 attribute_id.u8_code[0] = _attribute_id;
2564 if (vendor_id) {
2565 vendor = (radius_vendor_info_t *)g_hash_table_lookup(dict->vendors_by_id, GUINT_TO_POINTER(vendor_id));
2567 if (!vendor) {
2568 vendor = g_new(radius_vendor_info_t, 1);
2570 vendor->name = ws_strdup_printf("%s-%u",
2571 enterprises_lookup(vendor_id, "Unknown"),
2572 vendor_id);
2573 vendor->code = vendor_id;
2574 vendor->attrs_by_id = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_radius_attr_info);
2575 vendor->ett = no_vendor.ett;
2577 /* XXX: Default "standard" values: Should be parameters ? */
2578 vendor->type_octets = 1;
2579 vendor->length_octets = 1;
2580 vendor->has_flags = false;
2582 g_hash_table_insert(dict->vendors_by_id, GUINT_TO_POINTER(vendor->code), vendor);
2583 g_hash_table_insert(dict->vendors_by_name, (void *)(vendor->name), vendor);
2586 dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(vendor->attrs_by_id, GUINT_TO_POINTER(attribute_id.value));
2587 by_id = vendor->attrs_by_id;
2588 } else {
2589 dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(dict->attrs_by_id, GUINT_TO_POINTER(attribute_id.value));
2590 by_id = dict->attrs_by_id;
2593 if (!dictionary_entry) {
2594 dictionary_entry = g_new(radius_attr_info_t, 1);
2596 dictionary_entry->name = ws_strdup_printf("Unknown-Attribute-%u", attribute_id.value);
2597 dictionary_entry->code = attribute_id;
2598 dictionary_entry->encrypt = 0;
2599 dictionary_entry->type = NULL;
2600 dictionary_entry->vs = NULL;
2601 dictionary_entry->hf = no_dictionary_entry.hf;
2602 dictionary_entry->tagged = false;
2603 dictionary_entry->concat = false;
2604 dictionary_entry->hf_tag = -1;
2605 dictionary_entry->hf_len = no_dictionary_entry.hf_len;
2606 dictionary_entry->ett = no_dictionary_entry.ett;
2607 dictionary_entry->tlvs_by_id = NULL;
2609 g_hash_table_insert(by_id, GUINT_TO_POINTER(dictionary_entry->code.value), dictionary_entry);
2612 dictionary_entry->dissector = radius_avp_dissector;
2616 /* Discard and init any state we've saved */
2617 static void
2618 radius_init_protocol(void)
2620 module_t *radius_module = prefs_find_module("radius");
2621 pref_t *alternate_port;
2623 if (radius_module) {
2624 /* Find alternate_port preference and mark it obsolete (thus hiding it from a user) */
2625 alternate_port = prefs_find_preference(radius_module, "alternate_port");
2626 if (! prefs_get_preference_obsolete(alternate_port))
2627 prefs_set_preference_obsolete(alternate_port);
2631 static void
2632 radius_shutdown(void)
2634 if (dict != NULL) {
2635 g_hash_table_destroy(dict->attrs_by_id);
2636 g_hash_table_destroy(dict->attrs_by_name);
2637 g_hash_table_destroy(dict->vendors_by_id);
2638 g_hash_table_destroy(dict->vendors_by_name);
2639 g_hash_table_destroy(dict->tlvs_by_name);
2640 g_free(dict);
2644 static void
2645 _radius_load_dictionary(char* dir)
2647 char *dict_err_str = NULL;
2649 if (!dir || test_for_directory(dir) != EISDIR) {
2650 return;
2653 radius_load_dictionary(dict, dir, "dictionary", &dict_err_str);
2655 if (dict_err_str) {
2656 report_failure("radius: %s", dict_err_str);
2657 g_free(dict_err_str);
2661 static void
2662 register_radius_fields(const char *unused _U_)
2664 hf_register_info base_hf[] = {
2665 { &hf_radius_req,
2666 { "Request", "radius.req", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
2667 "true if RADIUS request", HFILL }},
2668 { &hf_radius_rsp,
2669 { "Response", "radius.rsp", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
2670 "true if RADIUS response", HFILL }},
2671 { &hf_radius_req_frame,
2672 { "Request Frame", "radius.reqframe", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0,
2673 NULL, HFILL }},
2674 { &hf_radius_rsp_frame,
2675 { "Response Frame", "radius.rspframe", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0,
2676 NULL, HFILL }},
2677 { &hf_radius_time,
2678 { "Time from request", "radius.time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0,
2679 "Timedelta between Request and Response", HFILL }},
2680 { &hf_radius_code,
2681 { "Code", "radius.code", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &radius_pkt_type_codes_ext, 0x0,
2682 NULL, HFILL }},
2683 { &hf_radius_id,
2684 { "Identifier", "radius.id", FT_UINT8, BASE_DEC, NULL, 0x0,
2685 NULL, HFILL }},
2686 { &hf_radius_authenticator,
2687 { "Authenticator", "radius.authenticator", FT_BYTES, BASE_NONE, NULL, 0x0,
2688 NULL, HFILL }},
2689 { &hf_radius_authenticator_valid,
2690 { "Valid Authenticator", "radius.authenticator.valid", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
2691 "true if Authenticator is valid", HFILL }},
2692 { &hf_radius_authenticator_invalid,
2693 { "Invalid Authenticator", "radius.authenticator.invalid", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
2694 "true if Authenticator is invalid", HFILL }},
2695 { &hf_radius_message_authenticator_valid,
2696 { "Valid Message-Authenticator", "radius.Message_Authenticator.valid", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
2697 "true if Message-Authenticator is valid", HFILL }},
2698 { &hf_radius_message_authenticator_invalid,
2699 { "Invalid Message-Authenticator", "radius.Message_Authenticator.invalid", FT_BOOLEAN, BASE_NONE, NULL, 0x0,
2700 "true if Message-Authenticator is invalid", HFILL }},
2701 { &hf_radius_length,
2702 { "Length", "radius.length", FT_UINT16, BASE_DEC, NULL, 0x0,
2703 NULL, HFILL }},
2704 { &(no_dictionary_entry.hf),
2705 { "Unknown-Attribute", "radius.Unknown_Attribute", FT_BYTES, BASE_NONE, NULL, 0x0,
2706 NULL, HFILL }},
2707 { &(no_dictionary_entry.hf_len),
2708 { "Unknown-Attribute Length", "radius.Unknown_Attribute.length", FT_UINT8, BASE_DEC, NULL, 0x0,
2709 NULL, HFILL }},
2710 { &hf_radius_chap_password,
2711 { "CHAP-Password", "radius.CHAP_Password", FT_BYTES, BASE_NONE, NULL, 0x0,
2712 NULL, HFILL }},
2713 { &hf_radius_chap_ident,
2714 { "CHAP Ident", "radius.CHAP_Ident", FT_UINT8, BASE_HEX, NULL, 0x0,
2715 NULL, HFILL }},
2716 { &hf_radius_chap_string,
2717 { "CHAP String", "radius.CHAP_String", FT_BYTES, BASE_NONE, NULL, 0x0,
2718 NULL, HFILL }},
2719 { &hf_radius_framed_ip_address,
2720 { "Framed-IP-Address", "radius.Framed-IP-Address", FT_IPv4, BASE_NONE, NULL, 0x0,
2721 NULL, HFILL }},
2722 { &hf_radius_login_ip_host,
2723 { "Login-IP-Host", "radius.Login-IP-Host", FT_IPv4, BASE_NONE, NULL, 0x0,
2724 NULL, HFILL }},
2725 { &hf_radius_framed_ipx_network,
2726 { "Framed-IPX-Network", "radius.Framed-IPX-Network", FT_IPXNET, BASE_NONE, NULL, 0x0,
2727 NULL, HFILL }},
2728 { &hf_radius_cosine_vpi,
2729 { "Cosine-VPI", "radius.Cosine-Vpi", FT_UINT16, BASE_DEC, NULL, 0x0,
2730 NULL, HFILL }},
2731 { &hf_radius_cosine_vci,
2732 { "Cosine-VCI", "radius.Cosine-Vci", FT_UINT16, BASE_DEC, NULL, 0x0,
2733 NULL, HFILL }},
2734 { &hf_radius_dup,
2735 { "Duplicate Message ID", "radius.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
2736 NULL, HFILL }},
2737 { &hf_radius_req_dup,
2738 { "Duplicate Request Frame Number", "radius.req.dup", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
2739 NULL, HFILL }},
2740 { &hf_radius_rsp_dup,
2741 { "Duplicate Response Frame Number", "radius.rsp.dup", FT_FRAMENUM, BASE_NONE, NULL, 0x0,
2742 NULL, HFILL }},
2743 { &hf_radius_ascend_data_filter,
2744 { "Ascend Data Filter", "radius.ascenddatafilter", FT_BYTES, BASE_NONE, NULL, 0x0,
2745 NULL, HFILL }},
2746 { &hf_radius_ascend_data_filter_type,
2747 { "Type", "radius.ascenddatafilter.type", FT_UINT8, BASE_DEC, VALS(ascenddf_filtertype), 0x0,
2748 NULL, HFILL }},
2749 { &hf_radius_ascend_data_filter_filteror,
2750 { "Filter or forward", "radius.ascenddatafilter.filteror", FT_UINT8, BASE_DEC, VALS(ascenddf_filteror), 0x0,
2751 NULL, HFILL }},
2752 { &hf_radius_ascend_data_filter_inout,
2753 { "Indirection", "radius.ascenddatafilter.inout", FT_UINT8, BASE_DEC, VALS(ascenddf_inout), 0x0,
2754 NULL, HFILL }},
2755 { &hf_radius_ascend_data_filter_spare,
2756 { "Spare", "radius.ascenddatafilter.spare", FT_UINT8, BASE_DEC, NULL, 0x0,
2757 NULL, HFILL }},
2758 { &hf_radius_ascend_data_filter_src_ipv4,
2759 { "Source IPv4 address", "radius.ascenddatafilter.src_ipv4", FT_IPv4, BASE_NONE, NULL, 0x0,
2760 NULL, HFILL }},
2761 { &hf_radius_ascend_data_filter_dst_ipv4,
2762 { "Destination IPv4 address", "radius.ascenddatafilter.dst_ipv4", FT_IPv4, BASE_NONE, NULL, 0x0,
2763 NULL, HFILL }},
2764 { &hf_radius_ascend_data_filter_src_ipv6,
2765 { "Source IPv6 address", "radius.ascenddatafilter.src_ipv6", FT_IPv6, BASE_NONE, NULL, 0x0,
2766 NULL, HFILL }},
2767 { &hf_radius_ascend_data_filter_dst_ipv6,
2768 { "Destination IPv6 address", "radius.ascenddatafilter.dst_ipv6", FT_IPv6, BASE_NONE, NULL, 0x0,
2769 NULL, HFILL }},
2770 { &hf_radius_ascend_data_filter_src_ip_prefix,
2771 { "Source IP prefix", "radius.ascenddatafilter.src_prefix_ip", FT_UINT8, BASE_DEC, NULL, 0x0,
2772 NULL, HFILL }},
2773 { &hf_radius_ascend_data_filter_dst_ip_prefix,
2774 { "Destination IP prefix", "radius.ascenddatafilter.dst_prefix_ip", FT_UINT8, BASE_DEC, NULL, 0x0,
2775 NULL, HFILL }},
2776 { &hf_radius_ascend_data_filter_protocol,
2777 { "Protocol", "radius.ascenddatafilter.protocol", FT_UINT8, BASE_DEC, VALS(ascenddf_proto), 0x0,
2778 NULL, HFILL }},
2779 { &hf_radius_ascend_data_filter_established,
2780 { "Established", "radius.ascenddatafilter.established", FT_UINT8, BASE_DEC, NULL, 0x0,
2781 NULL, HFILL }},
2782 { &hf_radius_ascend_data_filter_src_port,
2783 { "Source Port", "radius.ascenddatafilter.src_port", FT_UINT16, BASE_DEC, NULL, 0x0,
2784 NULL, HFILL }},
2785 { &hf_radius_ascend_data_filter_dst_port,
2786 { "Destination Port", "radius.ascenddatafilter.dst_port", FT_UINT16, BASE_DEC, NULL, 0x0,
2787 NULL, HFILL }},
2788 { &hf_radius_ascend_data_filter_src_port_qualifier,
2789 { "Source Port Qualifier", "radius.ascenddatafilter.src_port_qualifier", FT_UINT8, BASE_DEC, VALS(ascenddf_portq), 0x0,
2790 NULL, HFILL }},
2791 { &hf_radius_ascend_data_filter_dst_port_qualifier,
2792 { "Destination Port Qualifier", "radius.ascenddatafilter.dst_port_qualifier", FT_UINT8, BASE_DEC, VALS(ascenddf_portq), 0x0,
2793 NULL, HFILL }},
2794 { &hf_radius_ascend_data_filter_reserved,
2795 { "Reserved", "radius.ascenddatafilter.reserved", FT_UINT16, BASE_HEX, NULL, 0x0,
2796 NULL, HFILL }},
2797 { &hf_radius_vsa_fragment,
2798 { "VSA fragment", "radius.vsa_fragment", FT_BYTES, BASE_NONE, NULL, 0x0,
2799 NULL, HFILL }},
2800 { &hf_radius_eap_fragment,
2801 { "EAP fragment", "radius.eap_fragment", FT_BYTES, BASE_NONE, NULL, 0x0,
2802 NULL, HFILL }},
2803 { &hf_radius_avp,
2804 { "AVP", "radius.avp", FT_BYTES, BASE_NONE, NULL, 0x0,
2805 NULL, HFILL }},
2806 { &hf_radius_avp_length,
2807 { "Length", "radius.avp.length", FT_UINT8, BASE_DEC, NULL, 0x0,
2808 NULL, HFILL }},
2809 { &hf_radius_avp_type,
2810 { "Type", "radius.avp.type", FT_UINT8, BASE_DEC, NULL, 0x0,
2811 NULL, HFILL }},
2812 { &hf_radius_avp_vendor_id,
2813 { "Vendor ID", "radius.avp.vendor_id", FT_UINT32, BASE_ENTERPRISES, STRINGS_ENTERPRISES, 0x0,
2814 NULL, HFILL }},
2815 { &hf_radius_avp_vendor_type,
2816 { "Type", "radius.avp.vendor_type", FT_UINT8, BASE_DEC, NULL, 0x0,
2817 NULL, HFILL }},
2818 { &hf_radius_avp_vendor_len,
2819 { "Length", "radius.avp.vendor_len", FT_UINT8, BASE_DEC, NULL, 0x0,
2820 NULL, HFILL }},
2821 { &hf_radius_avp_extended_type,
2822 { "Extended Type", "radius.avp.extended_type", FT_UINT8, BASE_DEC, NULL, 0x0,
2823 NULL, HFILL }},
2824 { &hf_radius_avp_extended_more,
2825 { "Extended More", "radius.avp.extended_more", FT_BOOLEAN, 8, NULL, 0x80,
2826 NULL, HFILL }},
2827 { &hf_radius_egress_vlanid_tag,
2828 { "Tag", "radius.egress_vlanid_tag", FT_UINT32, BASE_HEX, VALS(egress_vlan_tag_vals), 0xFF000000,
2829 NULL, HFILL }},
2830 { &hf_radius_egress_vlanid_pad,
2831 { "Pad", "radius.egress_vlanid_pad", FT_UINT32, BASE_HEX, NULL, 0x00FFF000,
2832 NULL, HFILL }},
2833 { &hf_radius_egress_vlanid,
2834 { "Vlan ID", "radius.egress_vlanid", FT_UINT32, BASE_DEC, NULL, 0x00000FFF,
2835 NULL, HFILL }},
2836 { &hf_radius_egress_vlan_name_tag,
2837 { "Tag", "radius.egress_vlan_name_tag", FT_UINT8, BASE_HEX, VALS(egress_vlan_tag_vals), 0x0,
2838 NULL, HFILL }},
2839 { &hf_radius_egress_vlan_name,
2840 { "Vlan Name", "radius.egress_vlan_name", FT_STRING, BASE_NONE, NULL, 0x0,
2841 NULL, HFILL }},
2842 { &hf_radius_3gpp_ms_tmime_zone,
2843 { "Timezone", "radius.3gpp_ms_tmime_zone", FT_BYTES, BASE_NONE, NULL, 0x0,
2844 NULL, HFILL }},
2847 int *base_ett[] = {
2848 &ett_radius,
2849 &ett_radius_avp,
2850 &ett_radius_authenticator,
2851 &ett_radius_ascend,
2852 &ett_eap,
2853 &ett_chap,
2854 &(no_dictionary_entry.ett),
2855 &(no_vendor.ett),
2858 static ei_register_info ei[] = {
2860 &ei_radius_invalid_length, { "radius.invalid_length", PI_MALFORMED, PI_ERROR, "Invalid length", EXPFILL }},
2863 expert_module_t *expert_radius;
2864 hfett_t ri;
2865 char *dir = NULL;
2867 ri.hf = wmem_array_new(wmem_epan_scope(), sizeof(hf_register_info));
2868 ri.ett = wmem_array_new(wmem_epan_scope(), sizeof(int *));
2869 ri.vend_vs = wmem_array_new(wmem_epan_scope(), sizeof(value_string));
2871 wmem_array_append(ri.hf, base_hf, array_length(base_hf));
2872 wmem_array_append(ri.ett, base_ett, array_length(base_ett));
2875 dir = get_datafile_path("radius");
2876 _radius_load_dictionary(dir);
2877 g_free(dir);
2878 dir = get_persconffile_path("radius", false);
2879 _radius_load_dictionary(dir);
2880 g_free(dir);
2882 g_hash_table_foreach(dict->attrs_by_id, register_attrs, &ri);
2883 g_hash_table_foreach(dict->vendors_by_id, register_vendors, &ri);
2885 proto_register_field_array(proto_radius, (hf_register_info *)wmem_array_get_raw(ri.hf), wmem_array_get_count(ri.hf));
2886 proto_register_subtree_array((int **)wmem_array_get_raw(ri.ett), wmem_array_get_count(ri.ett));
2887 expert_radius = expert_register_protocol(proto_radius);
2888 expert_register_field_array(expert_radius, ei, array_length(ei));
2891 * Handle attributes that have a special format.
2893 radius_register_avp_dissector(0, 3, dissect_chap_password);
2894 radius_register_avp_dissector(0, 8, dissect_framed_ip_address);
2895 radius_register_avp_dissector(0, 14, dissect_login_ip_host);
2896 radius_register_avp_dissector(0, 23, dissect_framed_ipx_network);
2897 radius_register_avp_dissector(0, 56, dissect_rfc4675_egress_vlanid);
2898 radius_register_avp_dissector(0, 58, dissect_rfc4675_egress_vlan_name);
2900 radius_register_avp_dissector(VENDOR_COSINE, 5, dissect_cosine_vpvc);
2903 * XXX - we should special-case Cisco attribute 252; see the comment in
2904 * dictionary.cisco.
2906 radius_register_avp_dissector(VENDOR_THE3GPP, 1, dissect_radius_3gpp_imsi);
2907 radius_register_avp_dissector(VENDOR_THE3GPP, 23, dissect_radius_3gpp_ms_tmime_zone);
2911 void
2912 proto_register_radius(void)
2914 module_t *radius_module;
2916 proto_radius = proto_register_protocol("RADIUS Protocol", "RADIUS", "radius");
2917 radius_handle = register_dissector("radius", dissect_radius, proto_radius);
2918 register_init_routine(&radius_init_protocol);
2919 register_shutdown_routine(radius_shutdown);
2920 radius_module = prefs_register_protocol(proto_radius, NULL);
2921 prefs_register_string_preference(radius_module, "shared_secret", "Shared Secret",
2922 "Shared secret used to decode User Passwords and validate Accounting Request and Response Authenticators",
2923 &shared_secret);
2924 prefs_register_bool_preference(radius_module, "validate_authenticator", "Validate Authenticator and Message-Authenticator",
2925 "Whether to check or not if Authenticator and Message-Authenticator are correct. You need to define shared secret for this to work.",
2926 &validate_authenticator);
2927 prefs_register_bool_preference(radius_module, "show_length", "Show AVP Lengths",
2928 "Whether to add or not to the tree the AVP's payload length",
2929 &show_length);
2931 * For now this preference allows supporting legacy Ascend AVPs and others
2932 * who might use these attribute types (not complying with IANA allocation).
2934 prefs_register_bool_preference(radius_module, "disable_extended_attributes", "Disable extended attribute space (RFC 6929)",
2935 "Whether to interpret 241-246 as extended attributes according to RFC 6929",
2936 &disable_extended_attributes);
2937 prefs_register_obsolete_preference(radius_module, "request_ttl");
2939 radius_tap = register_tap("radius");
2940 proto_register_prefix("radius", register_radius_fields);
2942 dict = g_new(radius_dictionary_t, 1);
2944 * IDs map to names and vice versa. The attribute and vendor is stored
2945 * only once, but referenced by both name and ID mappings.
2946 * See also radius_dictionary_t in packet-radius.h
2948 dict->attrs_by_id = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_radius_attr_info);
2949 dict->attrs_by_name = g_hash_table_new(g_str_hash, g_str_equal);
2950 dict->vendors_by_id = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_radius_vendor_info);
2951 dict->vendors_by_name = g_hash_table_new(g_str_hash, g_str_equal);
2952 dict->tlvs_by_name = g_hash_table_new(g_str_hash, g_str_equal);
2954 radius_calls = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), radius_call_hash, radius_call_equal);
2956 register_rtd_table(proto_radius, NULL, RADIUS_CAT_NUM_TIMESTATS, 1, radius_message_code, radiusstat_packet, NULL);
2959 void
2960 proto_reg_handoff_radius(void)
2962 eap_handle = find_dissector_add_dependency("eap", proto_radius);
2963 dissector_add_uint_range_with_preference("udp.port", DEFAULT_RADIUS_PORT_RANGE, radius_handle);
2967 * Editor modelines - https://www.wireshark.org/tools/modelines.html
2969 * Local variables:
2970 * c-basic-offset: 8
2971 * tab-width: 8
2972 * indent-tabs-mode: t
2973 * End:
2975 * vi: set shiftwidth=8 tabstop=8 noexpandtab:
2976 * :indentSize=8:tabSize=8:noTabs=false: