/* packet-radius.c * * Routines for RADIUS packet disassembly * Copyright 1999 Johan Feyaerts * Changed 03/12/2003 Rui Carmo (http://the.taoofmac.com - added all 3GPP VSAs, some parsing) * Changed 07/2005 Luis Ontanon - use FreeRADIUS' dictionary * Changed 10/2006 Alejandro Vaquero - add Conversations support * Changed 08/2015 Didier Arenzana - add response authenticator validation * * Wireshark - Network traffic analyzer * By Gerald Combs * Copyright 1998 Gerald Combs * * SPDX-License-Identifier: GPL-2.0-or-later * * References: * * RFC 2865 - Remote Authentication Dial In User Service (RADIUS) * RFC 2866 - RADIUS Accounting * RFC 2867 - RADIUS Accounting Modifications for Tunnel Protocol Support * RFC 2868 - RADIUS Attributes for Tunnel Protocol Support * RFC 2869 - RADIUS Extensions * RFC 3162 - RADIUS and IPv6 * RFC 3576 - Dynamic Authorization Extensions to RADIUS * RFC 6929 - Remote Authentication Dial-In User Service (RADIUS) Protocol Extensions * * See also * * http://www.iana.org/assignments/radius-types * * and see * * http://freeradius.org/radiusd/man/dictionary.html * * for the dictionary file syntax. */ /* TO (re)DO: (see svn rev 14786) - dissect_3gpp_ipv6_dns_servers() */ #include "config.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "packet-radius.h" #include "packet-e212.h" void proto_register_radius(void); void proto_reg_handoff_radius(void); typedef struct _e_radiushdr { guint8 rh_code; guint8 rh_ident; guint16 rh_pktlength; } e_radiushdr; typedef struct { wmem_array_t *hf; wmem_array_t *ett; wmem_array_t *vend_vs; } hfett_t; #define AUTHENTICATOR_LENGTH 16 #define RD_HDR_LENGTH 4 #define HDR_LENGTH (RD_HDR_LENGTH + AUTHENTICATOR_LENGTH) /* Item of request list */ typedef struct _radius_call_t { guint code; guint ident; guint8 req_authenticator[AUTHENTICATOR_LENGTH]; guint32 req_num; /* frame number request seen */ guint32 rsp_num; /* frame number response seen */ guint32 rspcode; nstime_t req_time; gboolean responded; } radius_call_t; /* Container for tapping relevant data */ typedef struct _radius_info_t { guint code; guint ident; nstime_t req_time; gboolean is_duplicate; gboolean request_available; guint32 req_num; /* frame number request seen */ guint32 rspcode; } radius_info_t; /* * Default RADIUS ports: * 1645 (Authentication, pre RFC 2865) * 1646 (Accounting, pre RFC 2866) * 1812 (Authentication, RFC 2865) * 1813 (Accounting, RFC 2866) * 1700 (Dynamic Authorization Extensions, pre RFC 3576) * 3799 (Dynamic Authorization Extensions, RFC 3576) */ #define DEFAULT_RADIUS_PORT_RANGE "1645,1646,1700,1812,1813,3799" static radius_dictionary_t *dict = NULL; static int proto_radius = -1; static int hf_radius_req = -1; static int hf_radius_rsp = -1; static int hf_radius_req_frame = -1; static int hf_radius_rsp_frame = -1; static int hf_radius_time = -1; static int hf_radius_dup = -1; static int hf_radius_req_dup = -1; static int hf_radius_rsp_dup = -1; static int hf_radius_id = -1; static int hf_radius_code = -1; static int hf_radius_length = -1; static int hf_radius_authenticator = -1; static int hf_radius_authenticator_valid = -1; static int hf_radius_authenticator_invalid = -1; static int hf_radius_chap_password = -1; static int hf_radius_chap_ident = -1; static int hf_radius_chap_string = -1; static int hf_radius_framed_ip_address = -1; static int hf_radius_login_ip_host = -1; static int hf_radius_framed_ipx_network = -1; static int hf_radius_cosine_vpi = -1; static int hf_radius_cosine_vci = -1; static int hf_radius_ascend_data_filter = -1; static int hf_radius_ascend_data_filter_type = -1; static int hf_radius_ascend_data_filter_filteror = -1; static int hf_radius_ascend_data_filter_inout = -1; static int hf_radius_ascend_data_filter_spare = -1; static int hf_radius_ascend_data_filter_src_ipv4 = -1; static int hf_radius_ascend_data_filter_dst_ipv4 = -1; static int hf_radius_ascend_data_filter_src_ipv6 = -1; static int hf_radius_ascend_data_filter_dst_ipv6 = -1; static int hf_radius_ascend_data_filter_src_ip_prefix = -1; static int hf_radius_ascend_data_filter_dst_ip_prefix = -1; static int hf_radius_ascend_data_filter_protocol = -1; static int hf_radius_ascend_data_filter_established = -1; static int hf_radius_ascend_data_filter_src_port = -1; static int hf_radius_ascend_data_filter_dst_port = -1; static int hf_radius_ascend_data_filter_src_port_qualifier = -1; static int hf_radius_ascend_data_filter_dst_port_qualifier = -1; static int hf_radius_ascend_data_filter_reserved = -1; static int hf_radius_vsa_fragment = -1; static int hf_radius_eap_fragment = -1; static int hf_radius_avp = -1; static int hf_radius_avp_length = -1; static int hf_radius_avp_type = -1; static int hf_radius_avp_vendor_id = -1; static int hf_radius_avp_vendor_type = -1; static int hf_radius_avp_vendor_len = -1; static int hf_radius_avp_extended_type = -1; static int hf_radius_avp_extended_more = -1; static int hf_radius_3gpp_ms_tmime_zone = -1; static int hf_radius_egress_vlanid_tag = -1; static int hf_radius_egress_vlanid_pad = -1; static int hf_radius_egress_vlanid = -1; static int hf_radius_egress_vlan_name_tag = -1; static int hf_radius_egress_vlan_name = -1; static gint ett_radius = -1; static gint ett_radius_avp = -1; static gint ett_radius_authenticator = -1; static gint ett_radius_ascend = -1; static gint ett_eap = -1; static gint ett_chap = -1; static expert_field ei_radius_invalid_length = EI_INIT; /* * Define the tap for radius */ static int radius_tap = -1; static radius_vendor_info_t no_vendor = {"Unknown Vendor", 0, NULL, -1, 1, 1, FALSE}; static radius_attr_info_t no_dictionary_entry = {"Unknown-Attribute", { { 0, 0 } }, FALSE, FALSE, radius_octets, NULL, NULL, -1, -1, -1, -1, -1, NULL }; static dissector_handle_t eap_handle; static dissector_handle_t radius_handle; static const gchar *shared_secret = ""; static gboolean validate_authenticator = FALSE; static gboolean show_length = FALSE; static gboolean disable_extended_attributes = FALSE; static guint8 authenticator[AUTHENTICATOR_LENGTH]; /* http://www.iana.org/assignments/radius-types */ static const value_string radius_pkt_type_codes[] = { {RADIUS_PKT_TYPE_ACCESS_REQUEST, "Access-Request"}, /* 1 RFC2865 */ {RADIUS_PKT_TYPE_ACCESS_ACCEPT, "Access-Accept"}, /* 2 RFC2865 */ {RADIUS_PKT_TYPE_ACCESS_REJECT, "Access-Reject"}, /* 3 RFC2865 */ {RADIUS_PKT_TYPE_ACCOUNTING_REQUEST, "Accounting-Request"}, /* 4 RFC2865 */ {RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE, "Accounting-Response"}, /* 5 RFC2865 */ {RADIUS_PKT_TYPE_ACCOUNTING_STATUS, "Accounting-Status"}, /* 6 RFC3575 */ {RADIUS_PKT_TYPE_PASSWORD_REQUEST, "Password-Request"}, /* 7 RFC3575 */ {RADIUS_PKT_TYPE_PASSWORD_ACK, "Password-Ack"}, /* 8 RFC3575 */ {RADIUS_PKT_TYPE_PASSWORD_REJECT, "Password-Reject"}, /* 9 RFC3575 */ {RADIUS_PKT_TYPE_ACCOUNTING_MESSAGE, "Accounting-Message"}, /* 10 RFC3575 */ {RADIUS_PKT_TYPE_ACCESS_CHALLENGE, "Access-Challenge"}, /* 11 RFC2865 */ {RADIUS_PKT_TYPE_STATUS_SERVER, "Status-Server"}, /* 12 RFC2865 */ {RADIUS_PKT_TYPE_STATUS_CLIENT, "Status-Client"}, /* 13 RFC2865 */ {RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST, "Resource-Free-Request"}, /* 21 RFC3575 */ {RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE, "Resource-Free-Response"}, /* 22 RFC3575 */ {RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST, "Resource-Query-Request"}, /* 23 RFC3575 */ {RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE, "Query_Response"}, /* 24 RFC3575 */ {RADIUS_PKT_TYPE_ALTERNATE_RESOURCE_RECLAIM_REQUEST, "Alternate-Resource-Reclaim-Request"}, /* 25 RFC3575 */ {RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST, "NAS-Reboot-Request"}, /* 26 RFC3575 */ {RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE, "NAS-Reboot-Response"}, /* 27 RFC3575 */ {RADIUS_PKT_TYPE_NEXT_PASSCODE, "Next-Passcode"}, /* 29 RFC3575 */ {RADIUS_PKT_TYPE_NEW_PIN, "New-Pin"}, /* 30 RFC3575 */ {RADIUS_PKT_TYPE_TERMINATE_SESSION, "Terminate-Session"}, /* 31 RFC3575 */ {RADIUS_PKT_TYPE_PASSWORD_EXPIRED, "Password-Expired"}, /* 32 RFC3575 */ {RADIUS_PKT_TYPE_EVENT_REQUEST, "Event-Request"}, /* 33 RFC3575 */ {RADIUS_PKT_TYPE_EVENT_RESPONSE, "Event-Response"}, /* 34 RFC3575|RFC5176 */ {RADIUS_PKT_TYPE_DISCONNECT_REQUEST, "Disconnect-Request"}, /* 40 RFC3575|RFC5176 */ {RADIUS_PKT_TYPE_DISCONNECT_ACK, "Disconnect-ACK"}, /* 41 RFC3575|RFC5176 */ {RADIUS_PKT_TYPE_DISCONNECT_NAK, "Disconnect-NAK"}, /* 42 RFC3575|RFC5176 */ {RADIUS_PKT_TYPE_COA_REQUEST, "CoA-Request"}, /* 43 RFC3575|RFC5176 */ {RADIUS_PKT_TYPE_COA_ACK, "CoA-ACK"}, /* 44 RFC3575|RFC5176 */ {RADIUS_PKT_TYPE_COA_NAK, "CoA-NAK"}, /* 45 RFC3575|RFC5176 */ {RADIUS_PKT_TYPE_IP_ADDRESS_ALLOCATE, "IP-Address-Allocate"}, /* 50 RFC3575 */ {RADIUS_PKT_TYPE_IP_ADDRESS_RELEASE, "IP-Address-Release"}, /* 51 RFC3575 */ {RADIUS_PKT_TYPE_ALU_STATE_REQUEST, "ALU-State-Request"}, /* 129 ALU AAA */ {RADIUS_PKT_TYPE_ALU_STATE_ACCEPT, "ALU-State-Accept"}, /* 130 ALU AAA */ {RADIUS_PKT_TYPE_ALU_STATE_REJECT, "ALU-State-Reject"}, /* 131 ALU AAA */ {RADIUS_PKT_TYPE_ALU_STATE_ERROR, "ALU-State-Error"}, /* 132 ALU AAA */ /* 250-253 Experimental Use [RFC3575] 254-255 Reserved [RFC3575] */ {0, NULL} }; static value_string_ext radius_pkt_type_codes_ext = VALUE_STRING_EXT_INIT(radius_pkt_type_codes); typedef enum _radius_category { RADIUS_CAT_OVERALL = 0, RADIUS_CAT_ACCESS, RADIUS_CAT_ACCOUNTING, RADIUS_CAT_PASSWORD, RADIUS_CAT_RESOURCE_FREE, RADIUS_CAT_RESOURCE_QUERY, RADIUS_CAT_NAS_REBOOT, RADIUS_CAT_EVENT, RADIUS_CAT_DISCONNECT, RADIUS_CAT_COA, RADIUS_CAT_OTHERS, RADIUS_CAT_NUM_TIMESTATS } radius_category; static const value_string radius_message_code[] = { { RADIUS_CAT_OVERALL, "Overall"}, { RADIUS_CAT_ACCESS, "Access"}, { RADIUS_CAT_ACCOUNTING, "Accounting"}, { RADIUS_CAT_PASSWORD, "Password"}, { RADIUS_CAT_RESOURCE_FREE, "Resource Free"}, { RADIUS_CAT_RESOURCE_QUERY, "Resource Query"}, { RADIUS_CAT_NAS_REBOOT, "NAS Reboot"}, { RADIUS_CAT_EVENT, "Event"}, { RADIUS_CAT_DISCONNECT, "Disconnect"}, { RADIUS_CAT_COA, "CoA"}, { RADIUS_CAT_OTHERS, "Other"}, { 0, NULL} }; static tap_packet_status radiusstat_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pri, tap_flags_t flags _U_) { rtd_data_t *rtd_data = (rtd_data_t *)prs; rtd_stat_table *rs = &rtd_data->stat_table; const radius_info_t *ri = (const radius_info_t *)pri; nstime_t delta; radius_category radius_cat = RADIUS_CAT_OTHERS; tap_packet_status ret = TAP_PACKET_DONT_REDRAW; switch (ri->code) { case RADIUS_PKT_TYPE_ACCESS_REQUEST: case RADIUS_PKT_TYPE_ACCESS_ACCEPT: case RADIUS_PKT_TYPE_ACCESS_REJECT: radius_cat = RADIUS_CAT_ACCESS; break; case RADIUS_PKT_TYPE_ACCOUNTING_REQUEST: case RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE: radius_cat = RADIUS_CAT_ACCOUNTING; break; case RADIUS_PKT_TYPE_PASSWORD_REQUEST: case RADIUS_PKT_TYPE_PASSWORD_ACK: case RADIUS_PKT_TYPE_PASSWORD_REJECT: radius_cat = RADIUS_CAT_PASSWORD; break; case RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST: case RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE: radius_cat = RADIUS_CAT_RESOURCE_FREE; break; case RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST: case RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE: radius_cat = RADIUS_CAT_RESOURCE_QUERY; break; case RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST: case RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE: radius_cat = RADIUS_CAT_NAS_REBOOT; break; case RADIUS_PKT_TYPE_EVENT_REQUEST: case RADIUS_PKT_TYPE_EVENT_RESPONSE: radius_cat = RADIUS_CAT_EVENT; break; case RADIUS_PKT_TYPE_DISCONNECT_REQUEST: case RADIUS_PKT_TYPE_DISCONNECT_ACK: case RADIUS_PKT_TYPE_DISCONNECT_NAK: radius_cat = RADIUS_CAT_DISCONNECT; break; case RADIUS_PKT_TYPE_COA_REQUEST: case RADIUS_PKT_TYPE_COA_ACK: case RADIUS_PKT_TYPE_COA_NAK: radius_cat = RADIUS_CAT_COA; break; } switch (ri->code) { case RADIUS_PKT_TYPE_ACCESS_REQUEST: case RADIUS_PKT_TYPE_ACCOUNTING_REQUEST: case RADIUS_PKT_TYPE_PASSWORD_REQUEST: case RADIUS_PKT_TYPE_EVENT_REQUEST: case RADIUS_PKT_TYPE_DISCONNECT_REQUEST: case RADIUS_PKT_TYPE_COA_REQUEST: if (ri->is_duplicate) { /* Duplicate is ignored */ rs->time_stats[RADIUS_CAT_OVERALL].req_dup_num++; rs->time_stats[radius_cat].req_dup_num++; } else { rs->time_stats[RADIUS_CAT_OVERALL].open_req_num++; rs->time_stats[radius_cat].open_req_num++; } break; case RADIUS_PKT_TYPE_ACCESS_ACCEPT: case RADIUS_PKT_TYPE_ACCESS_REJECT: case RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE: case RADIUS_PKT_TYPE_PASSWORD_ACK: case RADIUS_PKT_TYPE_PASSWORD_REJECT: case RADIUS_PKT_TYPE_EVENT_RESPONSE: case RADIUS_PKT_TYPE_DISCONNECT_ACK: case RADIUS_PKT_TYPE_DISCONNECT_NAK: case RADIUS_PKT_TYPE_COA_ACK: case RADIUS_PKT_TYPE_COA_NAK: if (ri->is_duplicate) { /* Duplicate is ignored */ rs->time_stats[RADIUS_CAT_OVERALL].rsp_dup_num++; rs->time_stats[radius_cat].rsp_dup_num++; } else if (!ri->request_available) { /* no request was seen */ rs->time_stats[RADIUS_CAT_OVERALL].disc_rsp_num++; rs->time_stats[radius_cat].disc_rsp_num++; } else { rs->time_stats[RADIUS_CAT_OVERALL].open_req_num--; rs->time_stats[radius_cat].open_req_num--; /* calculate time delta between request and response */ nstime_delta(&delta, &pinfo->abs_ts, &ri->req_time); time_stat_update(&(rs->time_stats[RADIUS_CAT_OVERALL].rtd[0]),&delta, pinfo); time_stat_update(&(rs->time_stats[radius_cat].rtd[0]),&delta, pinfo); ret = TAP_PACKET_REDRAW; } break; default: break; } return ret; } /* * Init Hash table stuff for conversation */ typedef struct _radius_call_info_key { guint code; guint ident; conversation_t *conversation; nstime_t req_time; } radius_call_info_key; static wmem_map_t *radius_calls; typedef struct _radius_vsa_buffer_key { guint32 vendor_id; guint32 vsa_type; } radius_vsa_buffer_key; typedef struct _radius_vsa_buffer { radius_vsa_buffer_key key; guint8 *data; guint seg_num; guint len; } radius_vsa_buffer; static gint radius_vsa_equal(gconstpointer k1, gconstpointer k2) { const radius_vsa_buffer_key *key1 = (const radius_vsa_buffer_key *) k1; const radius_vsa_buffer_key *key2 = (const radius_vsa_buffer_key *) k2; return (((key1->vendor_id == key2->vendor_id) && (key1->vsa_type == key2->vsa_type) ) ? TRUE : FALSE); } static guint radius_vsa_hash(gconstpointer k) { const radius_vsa_buffer_key *key = (const radius_vsa_buffer_key *) k; return key->vendor_id + key->vsa_type; } /* Compare 2 keys */ static gboolean radius_call_equal(gconstpointer k1, gconstpointer k2) { const radius_call_info_key *key1 = (const radius_call_info_key *) k1; const radius_call_info_key *key2 = (const radius_call_info_key *) k2; if (key1->ident == key2->ident && key1->conversation == key2->conversation) { if (key1->code == key2->code) return TRUE; /* check the request and response are of the same code type */ if ((key1->code == RADIUS_PKT_TYPE_ACCESS_REQUEST) && ((key2->code == RADIUS_PKT_TYPE_ACCESS_ACCEPT) || (key2->code == RADIUS_PKT_TYPE_ACCESS_REJECT) || (key2->code == RADIUS_PKT_TYPE_ACCESS_CHALLENGE))) return TRUE; if ((key2->code == RADIUS_PKT_TYPE_ACCESS_REQUEST) && ((key1->code == RADIUS_PKT_TYPE_ACCESS_ACCEPT) || (key1->code == RADIUS_PKT_TYPE_ACCESS_REJECT) || (key1->code == RADIUS_PKT_TYPE_ACCESS_CHALLENGE))) return TRUE; if ((key1->code == RADIUS_PKT_TYPE_ACCOUNTING_REQUEST) && (key2->code == RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE)) return TRUE; if ((key2->code == RADIUS_PKT_TYPE_ACCOUNTING_REQUEST) && (key1->code == RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE)) return TRUE; if ((key1->code == RADIUS_PKT_TYPE_PASSWORD_REQUEST) && ((key2->code == RADIUS_PKT_TYPE_PASSWORD_ACK) || (key2->code == RADIUS_PKT_TYPE_PASSWORD_REJECT))) return TRUE; if ((key2->code == RADIUS_PKT_TYPE_PASSWORD_REQUEST) && ((key1->code == RADIUS_PKT_TYPE_PASSWORD_ACK) || (key1->code == RADIUS_PKT_TYPE_PASSWORD_REJECT))) return TRUE; if ((key1->code == RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST) && (key2->code == RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE)) return TRUE; if ((key2->code == RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST) && (key1->code == RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE)) return TRUE; if ((key1->code == RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST) && (key2->code == RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE)) return TRUE; if ((key2->code == RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST) && (key1->code == RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE)) return TRUE; if ((key1->code == RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST) && (key2->code == RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE)) return TRUE; if ((key2->code == RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST) && (key1->code == RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE)) return TRUE; if ((key1->code == RADIUS_PKT_TYPE_EVENT_REQUEST) && (key2->code == RADIUS_PKT_TYPE_EVENT_RESPONSE)) return TRUE; if ((key2->code == RADIUS_PKT_TYPE_EVENT_REQUEST) && (key1->code == RADIUS_PKT_TYPE_EVENT_RESPONSE)) return TRUE; if ((key1->code == RADIUS_PKT_TYPE_DISCONNECT_REQUEST) && ((key2->code == RADIUS_PKT_TYPE_DISCONNECT_ACK) || (key2->code == RADIUS_PKT_TYPE_DISCONNECT_NAK))) return TRUE; if ((key2->code == RADIUS_PKT_TYPE_DISCONNECT_REQUEST) && ((key1->code == RADIUS_PKT_TYPE_DISCONNECT_ACK) || (key1->code == RADIUS_PKT_TYPE_DISCONNECT_NAK))) return TRUE; if ((key1->code == RADIUS_PKT_TYPE_COA_REQUEST) && ((key2->code == RADIUS_PKT_TYPE_COA_ACK) || (key2->code == RADIUS_PKT_TYPE_COA_NAK))) return TRUE; if ((key2->code == RADIUS_PKT_TYPE_COA_REQUEST) && ((key1->code == RADIUS_PKT_TYPE_COA_ACK) || (key1->code == RADIUS_PKT_TYPE_COA_NAK))) return TRUE; if ((key1->code == RADIUS_PKT_TYPE_ALU_STATE_REQUEST) && ((key2->code == RADIUS_PKT_TYPE_ALU_STATE_ACCEPT) || (key2->code == RADIUS_PKT_TYPE_ALU_STATE_REJECT) || (key2->code == RADIUS_PKT_TYPE_ALU_STATE_ERROR))) return TRUE; if ((key2->code == RADIUS_PKT_TYPE_ALU_STATE_REQUEST) && ((key1->code == RADIUS_PKT_TYPE_ALU_STATE_ACCEPT) || (key1->code == RADIUS_PKT_TYPE_ALU_STATE_REJECT) || (key1->code == RADIUS_PKT_TYPE_ALU_STATE_ERROR))) return TRUE; } return FALSE; } /* Calculate a hash key */ static guint radius_call_hash(gconstpointer k) { const radius_call_info_key *key = (const radius_call_info_key *) k; return key->ident + key->conversation->conv_index; } static const gchar * dissect_chap_password(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_) { int len; proto_item *ti; proto_tree *chap_tree; len = tvb_reported_length(tvb); if (len != 17) return "[wrong length for CHAP-Password]"; ti = proto_tree_add_item(tree, hf_radius_chap_password, tvb, 0, len, ENC_NA); chap_tree = proto_item_add_subtree(ti, ett_chap); proto_tree_add_item(chap_tree, hf_radius_chap_ident, tvb, 0, 1, ENC_BIG_ENDIAN); proto_tree_add_item(chap_tree, hf_radius_chap_string, tvb, 1, 16, ENC_NA); return (tvb_bytes_to_str(wmem_packet_scope(), tvb, 0, len)); } static const gchar * dissect_framed_ip_address(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_) { int len; guint32 ip; guint32 ip_h; const gchar *str; len = tvb_reported_length(tvb); if (len != 4) return "[wrong length for IP address]"; ip = tvb_get_ipv4(tvb, 0); ip_h = g_ntohl(ip); if (ip_h == 0xFFFFFFFF) { str = "Negotiated"; proto_tree_add_ipv4_format_value(tree, hf_radius_framed_ip_address, tvb, 0, len, ip, "%s", str); } else if (ip_h == 0xFFFFFFFE) { str = "Assigned"; proto_tree_add_ipv4_format_value(tree, hf_radius_framed_ip_address, tvb, 0, len, ip, "%s", str); } else { str = tvb_ip_to_str(pinfo->pool, tvb, 0); proto_tree_add_item(tree, hf_radius_framed_ip_address, tvb, 0, len, ENC_BIG_ENDIAN); } return str; } static const gchar * dissect_login_ip_host(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_) { int len; guint32 ip; guint32 ip_h; const gchar *str; len = tvb_reported_length(tvb); if (len != 4) return "[wrong length for IP address]"; ip = tvb_get_ipv4(tvb, 0); ip_h = g_ntohl(ip); if (ip_h == 0xFFFFFFFF) { str = "User-selected"; proto_tree_add_ipv4_format_value(tree, hf_radius_login_ip_host, tvb, 0, len, ip, "%s", str); } else if (ip_h == 0) { str = "NAS-selected"; proto_tree_add_ipv4_format_value(tree, hf_radius_login_ip_host, tvb, 0, len, ip, "%s", str); } else { str = tvb_ip_to_str(pinfo->pool, tvb, 0); proto_tree_add_item(tree, hf_radius_login_ip_host, tvb, 0, len, ENC_BIG_ENDIAN); } return str; } static const value_string ascenddf_filtertype[] = { {0, "generic"}, {1, "ipv4"}, {3, "ipv6"}, {0, NULL} }; static const value_string ascenddf_filteror[] = { {0, "drop"}, {1, "forward"}, {0, NULL} }; static const value_string ascenddf_inout[] = { {0, "out"}, {1, "in"}, {0, NULL} }; static const value_string ascenddf_proto[] = { {1, "icmp"}, {6, "tcp"}, {17, "udp"}, {0, NULL} }; static const value_string ascenddf_portq[] = { {1, "lt"}, {2, "eq"}, {3, "gt"}, {4, "ne"}, {0, NULL} }; static const gchar * dissect_ascend_data_filter(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_) { wmem_strbuf_t *filterstr; proto_item *ti; proto_tree *ascend_tree; int len; guint8 type, proto, srclen, dstlen; address srcip, dstip; guint16 srcport, dstport; guint8 srcportq, dstportq; guint8 iplen = 4; guint offset = 0; len=tvb_reported_length(tvb); if (len != 24 && len != 48) { return wmem_strdup_printf(wmem_packet_scope(), "Wrong attribute length %d", len); } filterstr = wmem_strbuf_new_sized(wmem_packet_scope(), 128); ti = proto_tree_add_item(tree, hf_radius_ascend_data_filter, tvb, 0, -1, ENC_NA); ascend_tree = proto_item_add_subtree(ti, ett_radius_ascend); proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_type, tvb, offset, 1, ENC_BIG_ENDIAN); type = tvb_get_guint8(tvb, 0); offset += 1; if (type == 3) { /* IPv6 */ iplen = 16; } proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_filteror, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_inout, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_spare, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; if (type == 3) { /* IPv6 */ proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_src_ipv6, tvb, offset, 16, ENC_NA); offset += 16; proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_dst_ipv6, tvb, offset, 16, ENC_NA); offset += 16; } else { /* IPv4 */ proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_src_ipv4, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_dst_ipv4, tvb, offset, 4, ENC_BIG_ENDIAN); offset += 4; } proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_src_ip_prefix, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_dst_ip_prefix, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_protocol, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_established, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_src_port, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_dst_port, tvb, offset, 2, ENC_BIG_ENDIAN); offset += 2; proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_src_port_qualifier, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_dst_port_qualifier, tvb, offset, 1, ENC_BIG_ENDIAN); offset += 1; proto_tree_add_item(ascend_tree, hf_radius_ascend_data_filter_reserved, tvb, offset, 2, ENC_BIG_ENDIAN); wmem_strbuf_append_printf(filterstr, "%s %s %s", val_to_str(type, ascenddf_filtertype, "%u"), val_to_str(tvb_get_guint8(tvb, 2), ascenddf_inout, "%u"), val_to_str(tvb_get_guint8(tvb, 1), ascenddf_filteror, "%u")); proto = tvb_get_guint8(tvb, 6+iplen*2); if (proto) { wmem_strbuf_append_printf(filterstr, " %s", val_to_str(proto, ascenddf_proto, "%u")); } if (type == 3) { /* IPv6 */ set_address_tvb(&srcip, AT_IPv6, 16, tvb, 4); } else { set_address_tvb(&srcip, AT_IPv4, 4, tvb, 4); } srclen = tvb_get_guint8(tvb, 4+iplen*2); srcport = tvb_get_ntohs(tvb, 9+iplen*2); srcportq = tvb_get_guint8(tvb, 12+iplen*2); if (srclen || srcportq) { wmem_strbuf_append_printf(filterstr, " srcip %s/%d", address_to_display(wmem_packet_scope(), &srcip), srclen); if (srcportq) wmem_strbuf_append_printf(filterstr, " srcport %s %d", val_to_str(srcportq, ascenddf_portq, "%u"), srcport); } if (type == 3) { /* IPv6-*/ set_address_tvb(&dstip, AT_IPv6, 16, tvb, 4+iplen); } else { set_address_tvb(&dstip, AT_IPv4, 4, tvb, 4+iplen); } dstlen = tvb_get_guint8(tvb, 5+iplen*2); dstport = tvb_get_ntohs(tvb, 10+iplen*2); dstportq = tvb_get_guint8(tvb, 13+iplen*2); if (dstlen || dstportq) { wmem_strbuf_append_printf(filterstr, " dstip %s/%d", address_to_display(wmem_packet_scope(), &dstip), dstlen); if (dstportq) wmem_strbuf_append_printf(filterstr, " dstport %s %d", val_to_str(dstportq, ascenddf_portq, "%u"), dstport); } return wmem_strbuf_get_str(filterstr); } static const gchar * dissect_framed_ipx_network(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_) { int len; guint32 net; const gchar *str; len = tvb_reported_length(tvb); if (len != 4) return "[wrong length for IPX network]"; net = tvb_get_ntohl(tvb, 0); if (net == 0xFFFFFFFE) str = "NAS-selected"; else str = wmem_strdup_printf(wmem_packet_scope(), "0x%08X", net); proto_tree_add_ipxnet_format_value(tree, hf_radius_framed_ipx_network, tvb, 0, len, net, "Framed-IPX-Network: %s", str); return str; } static const gchar * dissect_cosine_vpvc(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_) { guint vpi, vci; if (tvb_reported_length(tvb) != 4) return "[Wrong Length for VP/VC AVP]"; vpi = tvb_get_ntohs(tvb, 0); vci = tvb_get_ntohs(tvb, 2); proto_tree_add_uint(tree, hf_radius_cosine_vpi, tvb, 0, 2, vpi); proto_tree_add_uint(tree, hf_radius_cosine_vci, tvb, 2, 2, vci); return wmem_strdup_printf(wmem_packet_scope(), "%u/%u", vpi, vci); } static const value_string daylight_saving_time_vals[] = { {0, "No adjustment"}, {1, "+1 hour adjustment for Daylight Saving Time"}, {2, "+2 hours adjustment for Daylight Saving Time"}, {3, "Reserved"}, {0, NULL} }; static const gchar * dissect_radius_3gpp_imsi(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo) { return dissect_e212_utf8_imsi(tvb, pinfo, tree, 0, tvb_reported_length(tvb)); } static const gchar * dissect_radius_3gpp_ms_tmime_zone(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_) { int offset = 0; guint8 oct, daylight_saving_time; char sign; /* 3GPP TS 23.040 version 6.6.0 Release 6 * 9.2.3.11 TP-Service-Centre-Time-Stamp (TP-SCTS) * : * The Time Zone indicates the difference, expressed in quarters of an hour, * between the local time and GMT. In the first of the two semi-octets, * the first bit (bit 3 of the seventh octet of the TP-Service-Centre-Time-Stamp field) * represents the algebraic sign of this difference (0: positive, 1: negative). */ oct = tvb_get_guint8(tvb, offset); sign = (oct & 0x08) ? '-' : '+'; oct = (oct >> 4) + (oct & 0x07) * 10; daylight_saving_time = tvb_get_guint8(tvb, offset+1) & 0x3; proto_tree_add_bytes_format_value(tree, hf_radius_3gpp_ms_tmime_zone, tvb, offset, 2, NULL, "GMT %c%d hours %d minutes %s", sign, oct / 4, oct % 4 * 15, val_to_str_const(daylight_saving_time, daylight_saving_time_vals, "Unknown")); return wmem_strdup_printf(wmem_packet_scope(), "Timezone: GMT %c%d hours %d minutes %s ", sign, oct / 4, oct % 4 * 15, val_to_str_const(daylight_saving_time, daylight_saving_time_vals, "Unknown")); } static const value_string egress_vlan_tag_vals[] = { { 0x31, "Tagged"}, { 0x32, "Untagged"}, { 0, NULL} }; static const gchar * dissect_rfc4675_egress_vlanid(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_) { int len; guint32 vlanid; len = tvb_reported_length(tvb); if (len != 4) return "[wrong length for Egress-VLANID ]"; proto_tree_add_item(tree, hf_radius_egress_vlanid_tag, tvb, 0, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_radius_egress_vlanid_pad, tvb, 0, 4, ENC_BIG_ENDIAN); proto_tree_add_item(tree, hf_radius_egress_vlanid, tvb, 0, 4, ENC_BIG_ENDIAN); vlanid = tvb_get_ntohl(tvb, 0); return wmem_strdup_printf(wmem_packet_scope(), "%s, Vlan ID: %u", val_to_str_const(((vlanid&0xFF000000)>>24), egress_vlan_tag_vals, "Unknown"), vlanid&0xFFF); } static const gchar * dissect_rfc4675_egress_vlan_name(proto_tree *tree, tvbuff_t *tvb, packet_info *pinfo _U_) { int len; guint8 tag; const guint8 *name; len = tvb_reported_length(tvb); if (len < 2) return "[wrong length for Egress-VLAN-Name ]"; proto_tree_add_item(tree, hf_radius_egress_vlan_name_tag, tvb, 0, 1, ENC_BIG_ENDIAN); tag = tvb_get_guint8(tvb, 0); len -= 1; proto_tree_add_item_ret_string(tree, hf_radius_egress_vlan_name, tvb, 1, len, ENC_ASCII|ENC_NA, wmem_packet_scope(), &name); return wmem_strdup_printf(wmem_packet_scope(), "%s, Vlan Name: %s", val_to_str_const(tag, egress_vlan_tag_vals, "Unknown"), name); } static void radius_decrypt_avp(gchar *dest, int dest_len, tvbuff_t *tvb, int offset, int length) { gcry_md_hd_t md5_handle; guint8 digest[HASH_MD5_LENGTH]; int i, j; gint totlen = 0, returned_length, padded_length; guint8 *pd; guchar c; DISSECTOR_ASSERT(dest_len > 0); dest[0] = '\0'; if (length <= 0) return; /* The max avp length is 253 (255 - 2 for type & length), but only the * User-Password is marked with encrypt=1 in dictionary.rfc2865, and the * User-Password max length is only 128 (130 - 2 for type & length) per * tools.ietf.org/html/rfc2865#section-5.2, so enforce that limit here. */ if (length > 128) length = 128; if (gcry_md_open(&md5_handle, GCRY_MD_MD5, 0)) { return; } gcry_md_write(md5_handle, (const guint8 *)shared_secret, (int)strlen(shared_secret)); gcry_md_write(md5_handle, authenticator, AUTHENTICATOR_LENGTH); memcpy(digest, gcry_md_read(md5_handle, 0), HASH_MD5_LENGTH); padded_length = length + ((length % AUTHENTICATOR_LENGTH) ? (AUTHENTICATOR_LENGTH - (length % AUTHENTICATOR_LENGTH)) : 0); pd = (guint8 *)wmem_alloc0(wmem_packet_scope(), padded_length); tvb_memcpy(tvb, pd, offset, length); for (i = 0; i < padded_length; i += AUTHENTICATOR_LENGTH) { for (j = 0; j < AUTHENTICATOR_LENGTH; j++) { c = pd[i + j] ^ digest[j]; if (g_ascii_isprint(c)) { returned_length = snprintf(&dest[totlen], dest_len - totlen, "%c", c); totlen += MIN(returned_length, dest_len - totlen - 1); } else if (c) { returned_length = snprintf(&dest[totlen], dest_len - totlen, "\\%03o", c); totlen += MIN(returned_length, dest_len - totlen - 1); } } gcry_md_reset(md5_handle); gcry_md_write(md5_handle, (const guint8 *)shared_secret, (int)strlen(shared_secret)); gcry_md_write(md5_handle, &pd[i], AUTHENTICATOR_LENGTH); memcpy(digest, gcry_md_read(md5_handle, 0), HASH_MD5_LENGTH); } gcry_md_close(md5_handle); } static void 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, guint32 avp_length, guint32 offset); void radius_integer(radius_attr_info_t *a, proto_tree *tree, packet_info *pinfo _U_, tvbuff_t *tvb, int offset, int len, proto_item *avp_item) { guint32 uintv; switch (len) { case 1: uintv = tvb_get_guint8(tvb, offset); break; case 2: uintv = tvb_get_ntohs(tvb, offset); break; case 3: uintv = tvb_get_ntoh24(tvb, offset); break; case 4: uintv = tvb_get_ntohl(tvb, offset); break; case 8: { guint64 uintv64 = tvb_get_ntoh64(tvb, offset); proto_tree_add_uint64(tree, a->hf_alt, tvb, offset, len, uintv64); proto_item_append_text(avp_item, "%" PRIu64, uintv64); return; } default: proto_item_append_text(avp_item, "[unhandled integer length(%u)]", len); return; } proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_BIG_ENDIAN); if (a->vs) { proto_item_append_text(avp_item, "%s(%u)", val_to_str_const(uintv, a->vs, "Unknown"), uintv); } else { proto_item_append_text(avp_item, "%u", uintv); } } void radius_signed(radius_attr_info_t *a, proto_tree *tree, packet_info *pinfo _U_, tvbuff_t *tvb, int offset, int len, proto_item *avp_item) { guint32 uintv; switch (len) { case 1: uintv = tvb_get_guint8(tvb, offset); break; case 2: uintv = tvb_get_ntohs(tvb, offset); break; case 3: uintv = tvb_get_ntoh24(tvb, offset); break; case 4: uintv = tvb_get_ntohl(tvb, offset); break; case 8: { guint64 uintv64 = tvb_get_ntoh64(tvb, offset); proto_tree_add_int64(tree, a->hf_alt, tvb, offset, len, uintv64); proto_item_append_text(avp_item, "%" PRIu64, uintv64); return; } default: proto_item_append_text(avp_item, "[unhandled signed integer length(%u)]", len); return; } proto_tree_add_int(tree, a->hf, tvb, offset, len, uintv); if (a->vs) { proto_item_append_text(avp_item, "%s(%d)", val_to_str_const(uintv, a->vs, "Unknown"), uintv); } else { proto_item_append_text(avp_item, "%d", uintv); } } void radius_string(radius_attr_info_t *a, proto_tree *tree, packet_info *pinfo _U_, tvbuff_t *tvb, int offset, int len, proto_item *avp_item) { switch (a->encrypt) { case 0: /* not encrypted */ proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_UTF_8|ENC_NA); proto_item_append_text(avp_item, "%s", tvb_format_text(pinfo->pool, tvb, offset, len)); break; case 1: /* encrypted like User-Password as defined in RFC 2865 */ if (*shared_secret == '\0') { proto_item_append_text(avp_item, "Encrypted"); proto_tree_add_item(tree, a->hf_alt, tvb, offset, len, ENC_NA); } else { gchar *buffer; buffer = (gchar *)wmem_alloc(wmem_packet_scope(), 1024); /* an AVP value can be at most 253 bytes */ radius_decrypt_avp(buffer, 1024, tvb, offset, len); proto_item_append_text(avp_item, "Decrypted: %s", buffer); proto_tree_add_string(tree, a->hf, tvb, offset, len, buffer); } break; case 2: /* encrypted like Tunnel-Password as defined in RFC 2868 */ proto_item_append_text(avp_item, "Encrypted"); proto_tree_add_item(tree, a->hf_alt, tvb, offset, len, ENC_NA); break; case 3: /* encrypted like Ascend-Send-Secret as defined by Ascend^WLucent^WAlcatel-Lucent */ proto_item_append_text(avp_item, "Encrypted"); proto_tree_add_item(tree, a->hf_alt, tvb, offset, len, ENC_NA); break; } } void radius_octets(radius_attr_info_t *a, proto_tree *tree, packet_info *pinfo _U_, tvbuff_t *tvb, int offset, int len, proto_item *avp_item) { if (len == 0) { proto_item_append_text(avp_item, "[wrong length]"); return; } proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA); proto_item_append_text(avp_item, "%s", tvb_bytes_to_str(wmem_packet_scope(), tvb, offset, len)); } void radius_ipaddr(radius_attr_info_t *a, proto_tree *tree, packet_info *pinfo _U_, tvbuff_t *tvb, int offset, int len, proto_item *avp_item) { if (len != 4) { proto_item_append_text(avp_item, "[wrong length for IP address]"); return; } proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_BIG_ENDIAN); proto_item_append_text(avp_item, "%s", tvb_ip_to_str(pinfo->pool, tvb, offset)); } void radius_ipv6addr(radius_attr_info_t *a, proto_tree *tree, packet_info *pinfo _U_, tvbuff_t *tvb, int offset, int len, proto_item *avp_item) { if (len != 16) { proto_item_append_text(avp_item, "[wrong length for IPv6 address]"); return; } proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA); proto_item_append_text(avp_item, "%s", tvb_ip6_to_str(pinfo->pool, tvb, offset)); } void radius_ipv6prefix(radius_attr_info_t *a, proto_tree *tree, packet_info *pinfo _U_, tvbuff_t *tvb, int offset, int len, proto_item *avp_item) { ws_in6_addr ipv6_buff; gchar txtbuf[256]; guint8 n; if ((len < 2) || (len > 18)) { proto_item_append_text(avp_item, "[wrong length for IPv6 prefix]"); return; } /* first byte is reserved == 0x00 */ if (tvb_get_guint8(tvb, offset)) { proto_item_append_text(avp_item, "[invalid reserved byte for IPv6 prefix]"); return; } /* this is the prefix length */ n = tvb_get_guint8(tvb, offset + 1); if (n > 128) { proto_item_append_text(avp_item, "[invalid IPv6 prefix length]"); return; } proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA); /* cannot use tvb_get_ipv6() here, since the prefix most likely is truncated */ memset(&ipv6_buff, 0, sizeof ipv6_buff); tvb_memcpy(tvb, &ipv6_buff, offset + 2, len - 2); ip6_to_str_buf(&ipv6_buff, txtbuf, sizeof(txtbuf)); proto_item_append_text(avp_item, "%s/%u", txtbuf, n); } void radius_combo_ip(radius_attr_info_t *a, proto_tree *tree, packet_info *pinfo _U_, tvbuff_t *tvb, int offset, int len, proto_item *avp_item) { if (len == 4) { proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_BIG_ENDIAN); proto_item_append_text(avp_item, "%s", tvb_ip_to_str(pinfo->pool, tvb, offset)); } else if (len == 16) { proto_tree_add_item(tree, a->hf_alt, tvb, offset, len, ENC_NA); proto_item_append_text(avp_item, "%s", tvb_ip6_to_str(pinfo->pool, tvb, offset)); } else { proto_item_append_text(avp_item, "[wrong length for both of IPv4 and IPv6 address]"); return; } } void radius_ipxnet(radius_attr_info_t *a, proto_tree *tree, packet_info *pinfo _U_, tvbuff_t *tvb, int offset, int len, proto_item *avp_item) { guint32 net; if (len != 4) { proto_item_append_text(avp_item, "[wrong length for IPX network]"); return; } net = tvb_get_ntohl(tvb, offset); proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA); proto_item_append_text(avp_item, "0x%08X", net); } void radius_date(radius_attr_info_t *a, proto_tree *tree, packet_info *pinfo _U_, tvbuff_t *tvb, int offset, int len, proto_item *avp_item) { nstime_t time_ptr; if (len != 4) { proto_item_append_text(avp_item, "[wrong length for timestamp]"); return; } time_ptr.secs = tvb_get_ntohl(tvb, offset); time_ptr.nsecs = 0; proto_tree_add_time(tree, a->hf, tvb, offset, len, &time_ptr); proto_item_append_text(avp_item, "%s", abs_time_to_str(wmem_packet_scope(), &time_ptr, ABSOLUTE_TIME_LOCAL, TRUE)); } /* * "abinary" is Ascend's binary format for filters. See dissect_ascend_data_filter(). */ void radius_abinary(radius_attr_info_t *a, proto_tree *tree, packet_info *pinfo _U_, tvbuff_t *tvb, int offset, int len, proto_item *avp_item) { if (a->code.u8_code[0] == 242) { add_avp_to_tree_with_dissector(tree, avp_item, pinfo, tvb, dissect_ascend_data_filter, len, offset); return; } proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA); proto_item_append_text(avp_item, "%s", tvb_bytes_to_str(wmem_packet_scope(), tvb, offset, len)); } void radius_ether(radius_attr_info_t *a, proto_tree *tree, packet_info *pinfo _U_, tvbuff_t *tvb, int offset, int len, proto_item *avp_item) { if (len != 6) { proto_item_append_text(avp_item, "[wrong length for ethernet address]"); return; } proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA); proto_item_append_text(avp_item, "%s", tvb_ether_to_str(pinfo->pool, tvb, offset)); } void radius_ifid(radius_attr_info_t *a, proto_tree *tree, packet_info *pinfo _U_, tvbuff_t *tvb, int offset, int len, proto_item *avp_item) { proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_NA); proto_item_append_text(avp_item, "%s", tvb_bytes_to_str(wmem_packet_scope(), tvb, offset, len)); } static void add_tlv_to_tree(proto_tree *tlv_tree, proto_item *tlv_item, packet_info *pinfo, tvbuff_t *tvb, radius_attr_info_t *dictionary_entry, guint32 tlv_length, guint32 offset) { proto_item_append_text(tlv_item, ": "); dictionary_entry->type(dictionary_entry, tlv_tree, pinfo, tvb, offset, tlv_length, tlv_item); } void radius_tlv(radius_attr_info_t *a, proto_tree *tree, packet_info *pinfo _U_, tvbuff_t *tvb, int offset, int len, proto_item *avp_item) { gint tlv_num = 0; while (len > 0) { radius_attr_info_t *dictionary_entry = NULL; guint32 tlv_type; guint32 tlv_length; proto_item *tlv_item; proto_item *tlv_len_item; proto_tree *tlv_tree; if (len < 2) { proto_tree_add_expert_format(tree, pinfo, &ei_radius_invalid_length, tvb, offset, 0, "Not enough room in packet for TLV header"); return; } tlv_type = tvb_get_guint8(tvb, offset); tlv_length = tvb_get_guint8(tvb, offset+1); if (tlv_length < 2) { proto_tree_add_expert_format(tree, pinfo, &ei_radius_invalid_length, tvb, offset, 0, "TLV too short: length %u < 2", tlv_length); return; } if (len < (gint)tlv_length) { proto_tree_add_expert_format(tree, pinfo, &ei_radius_invalid_length, tvb, offset, 0, "Not enough room in packet for TLV"); return; } len -= tlv_length; if (a->tlvs_by_id) dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(a->tlvs_by_id, GUINT_TO_POINTER(tlv_type)); if (!dictionary_entry) { dictionary_entry = &no_dictionary_entry; } tlv_tree = proto_tree_add_subtree_format(tree, tvb, offset, tlv_length, dictionary_entry->ett, &tlv_item, "TLV: t=%s(%u) l=%u ", dictionary_entry->name, tlv_type, tlv_length); tlv_length -= 2; offset += 2; if (show_length) { tlv_len_item = proto_tree_add_uint(tlv_tree, dictionary_entry->hf_len, tvb, 0, 0, tlv_length); proto_item_set_generated(tlv_len_item); } add_tlv_to_tree(tlv_tree, tlv_item, pinfo, tvb, dictionary_entry, tlv_length, offset); offset += tlv_length; tlv_num++; } proto_item_append_text(avp_item, "%d TLV(s) inside", tlv_num); } static void 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, guint32 avp_length, guint32 offset) { tvbuff_t *tvb_value; const gchar *str; tvb_value = tvb_new_subset_length(tvb, offset, avp_length); str = avp_dissector(avp_tree, tvb_value, pinfo); proto_item_append_text(avp_item, "%s", str); } static void add_avp_to_tree(proto_tree *avp_tree, proto_item *avp_item, packet_info *pinfo, tvbuff_t *tvb, radius_attr_info_t *dictionary_entry, guint32 avp_length, guint32 offset) { if (dictionary_entry->tagged) { guint tag; if (avp_length == 0) { proto_tree_add_expert_format(avp_tree, pinfo, &ei_radius_invalid_length, tvb, offset, 0, "AVP too short for tag"); return; } tag = tvb_get_guint8(tvb, offset); if (tag <= 0x1f) { proto_tree_add_uint(avp_tree, dictionary_entry->hf_tag, tvb, offset, 1, tag); proto_item_append_text(avp_item, " Tag=0x%.2x", tag); offset++; avp_length--; } } proto_item_append_text(avp_item, " val="); if (dictionary_entry->dissector) { add_avp_to_tree_with_dissector(avp_tree, avp_item, pinfo, tvb, dictionary_entry->dissector, avp_length, offset); return; } dictionary_entry->type(dictionary_entry, avp_tree, pinfo, tvb, offset, avp_length, avp_item); } static gboolean vsa_buffer_destroy(gpointer k _U_, gpointer v, gpointer p _U_) { radius_vsa_buffer *vsa_buffer = (radius_vsa_buffer *)v; g_free((gpointer)vsa_buffer->data); g_free(v); return TRUE; } static void eap_buffer_free_indirect(void *context) { guint8 *eap_buffer = *(guint8 **)context; g_free(eap_buffer); } static void vsa_buffer_table_destroy_indirect(void *context) { GHashTable *vsa_buffer_table = *(GHashTable **)context; if (vsa_buffer_table) { g_hash_table_foreach_remove(vsa_buffer_table, vsa_buffer_destroy, NULL); g_hash_table_destroy(vsa_buffer_table); } } void dissect_attribute_value_pairs(proto_tree *tree, packet_info *pinfo, tvbuff_t *tvb, int offset, guint length) { gboolean last_eap = FALSE; guint8 *eap_buffer = NULL; guint eap_seg_num = 0; guint eap_tot_len_captured = 0; guint eap_tot_len = 0; proto_tree *eap_tree = NULL; tvbuff_t *eap_tvb = NULL; GHashTable *vsa_buffer_table = NULL; if (hf_radius_code == -1) proto_registrar_get_byname("radius.code"); /* * In case we throw an exception, clean up whatever stuff we've * allocated (if any). */ CLEANUP_PUSH_PFX(la, eap_buffer_free_indirect, &eap_buffer); CLEANUP_PUSH_PFX(lb, vsa_buffer_table_destroy_indirect, &vsa_buffer_table); while (length > 0) { radius_attr_info_t *dictionary_entry = NULL; guint32 avp_type0 = 0, avp_type1 = 0; radius_attr_type_t avp_type; guint32 avp_length; guint32 vendor_id; gboolean avp_is_extended = FALSE; int avp_offset_start = offset; proto_item *avp_item; proto_item *avp_len_item; proto_tree *avp_tree; if (length < 2) { proto_tree_add_expert_format(tree, pinfo, &ei_radius_invalid_length, tvb, offset, 0, "Not enough room in packet for AVP header"); break; /* exit outer loop, then cleanup & return */ } avp_type0 = tvb_get_guint8(tvb, offset); avp_length = tvb_get_guint8(tvb, offset+1); avp_is_extended = RADIUS_ATTR_TYPE_IS_EXTENDED(avp_type0); if (avp_is_extended) { avp_type1 = tvb_get_guint8(tvb, offset+2); } memset(&avp_type, 0, sizeof(avp_type)); avp_type.u8_code[0] = avp_type0; avp_type.u8_code[1] = avp_type1; if (disable_extended_attributes) { avp_is_extended = FALSE; avp_type.u8_code[1] = 0; } if (avp_length < 2) { proto_tree_add_expert_format(tree, pinfo, &ei_radius_invalid_length, tvb, offset, 0, "AVP too short: length %u < 2", avp_length); break; /* exit outer loop, then cleanup & return */ } if (avp_is_extended && avp_length < 3) { proto_tree_add_expert_format(tree, pinfo, &ei_radius_invalid_length, tvb, offset, 0, "Extended AVP too short: length %u < 3", avp_length); break; /* exit outer loop, then cleanup & return */ } if (length < avp_length) { proto_tree_add_expert_format(tree, pinfo, &ei_radius_invalid_length, tvb, offset, 0, "Not enough room in packet for AVP"); break; /* exit outer loop, then cleanup & return */ } length -= avp_length; dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(dict->attrs_by_id, GUINT_TO_POINTER(avp_type.value)); if (!dictionary_entry) { dictionary_entry = &no_dictionary_entry; } avp_item = proto_tree_add_bytes_format_value(tree, hf_radius_avp, tvb, offset, avp_length, NULL, "t=%s", dictionary_entry->name); if (avp_is_extended) proto_item_append_text(avp_item, "(%u.%u)", avp_type0, avp_type1); else proto_item_append_text(avp_item, "(%u)", avp_type0); proto_item_append_text(avp_item, " l=%u", avp_length); avp_length -= 2; offset += 2; if (avp_is_extended) { avp_length -= 1; offset += 1; if (RADIUS_ATTR_TYPE_IS_EXTENDED_LONG(avp_type0)) { avp_length -= 1; offset += 1; } } if (avp_type0 == RADIUS_ATTR_TYPE_VENDOR_SPECIFIC || (avp_is_extended && avp_type1 == RADIUS_ATTR_TYPE_VENDOR_SPECIFIC)) { radius_vendor_info_t *vendor; proto_tree *vendor_tree; gint max_offset = offset + avp_length; const gchar *vendor_str; int vendor_offset; /* XXX TODO: handle 2 byte codes for USR */ if (avp_length < 4) { expert_add_info_format(pinfo, avp_item, &ei_radius_invalid_length, "AVP too short; no room for vendor ID"); offset += avp_length; continue; /* while (length > 0) */ } vendor_id = tvb_get_ntohl(tvb, offset); avp_length -= 4; offset += 4; vendor = (radius_vendor_info_t *)g_hash_table_lookup(dict->vendors_by_id, GUINT_TO_POINTER(vendor_id)); vendor_str = enterprises_lookup(vendor_id, "Unknown"); if (!vendor) { vendor = &no_vendor; } proto_item_append_text(avp_item, " vnd=%s(%u)", vendor_str, vendor_id); vendor_tree = proto_item_add_subtree(avp_item, vendor->ett); vendor_offset = avp_offset_start; proto_tree_add_item(vendor_tree, hf_radius_avp_type, tvb, vendor_offset, 1, ENC_BIG_ENDIAN); proto_tree_add_item(vendor_tree, hf_radius_avp_length, tvb, vendor_offset+1, 1, ENC_BIG_ENDIAN); vendor_offset += 2; if (avp_is_extended) { proto_tree_add_item(vendor_tree, hf_radius_avp_extended_type, tvb, vendor_offset, 1, ENC_BIG_ENDIAN); vendor_offset += 1; if (RADIUS_ATTR_TYPE_IS_EXTENDED_LONG(avp_type0)) { proto_tree_add_item(vendor_tree, hf_radius_avp_extended_more, tvb, vendor_offset, 1, ENC_BIG_ENDIAN); vendor_offset += 1; } } 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); vendor_offset += 4; while (offset < max_offset) { radius_attr_type_t vendor_type; guint32 avp_vsa_type; guint32 avp_vsa_len; guint8 avp_vsa_flags = 0; guint32 avp_vsa_header_len; guint32 vendor_attribute_len; switch (vendor->type_octets) { case 1: avp_vsa_type = tvb_get_guint8(tvb, offset++); break; case 2: avp_vsa_type = tvb_get_ntohs(tvb, offset); offset += 2; break; case 4: avp_vsa_type = tvb_get_ntohl(tvb, offset); offset += 4; break; default: /* vendor->type_octets = 1; */ DISSECTOR_ASSERT_NOT_REACHED(); break; } if (!avp_is_extended) { switch (vendor->length_octets) { case 1: avp_vsa_len = tvb_get_guint8(tvb, offset++); break; case 0: avp_vsa_len = avp_length; break; case 2: avp_vsa_len = tvb_get_ntohs(tvb, offset); offset += 2; break; default: /* vendor->length_octets = 1; */ DISSECTOR_ASSERT_NOT_REACHED(); break; } avp_vsa_header_len = vendor->type_octets + vendor->length_octets + (vendor->has_flags ? 1 : 0); } else { avp_vsa_len = avp_length; avp_vsa_header_len = vendor->type_octets + (vendor->has_flags ? 1 : 0); } if (vendor->has_flags) { avp_vsa_flags = tvb_get_guint8(tvb, offset++); } if (avp_vsa_len < avp_vsa_header_len) { proto_tree_add_expert_format(tree, pinfo, &ei_radius_invalid_length, tvb, offset+1, 1, "VSA too short"); break; /* exit while (offset < max_offset) loop */ } avp_vsa_len -= avp_vsa_header_len; memset(&vendor_type, 0, sizeof(vendor_type)); if (avp_is_extended) { vendor_type.u8_code[0] = avp_type.u8_code[0]; vendor_type.u8_code[1] = avp_vsa_type; } else { vendor_type.u8_code[0] = avp_vsa_type; vendor_type.u8_code[1] = 0; } if (vendor->attrs_by_id) { dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(vendor->attrs_by_id, GUINT_TO_POINTER(vendor_type.value)); } else { dictionary_entry = NULL; } if (!dictionary_entry) { dictionary_entry = &no_dictionary_entry; } if (vendor->has_flags) { avp_tree = proto_tree_add_subtree_format(vendor_tree, tvb, offset-avp_vsa_header_len, avp_vsa_len+avp_vsa_header_len, dictionary_entry->ett, &avp_item, "VSA: t=%s(%u) l=%u C=0x%02x", dictionary_entry->name, avp_vsa_type, avp_vsa_len+avp_vsa_header_len, avp_vsa_flags); } else if (avp_is_extended) { avp_tree = proto_tree_add_subtree_format(vendor_tree, tvb, offset-avp_vsa_header_len, avp_vsa_len+avp_vsa_header_len, dictionary_entry->ett, &avp_item, "EVS: t=%s(%u) l=%u", dictionary_entry->name, avp_vsa_type, avp_vsa_len+avp_vsa_header_len); } else { avp_tree = proto_tree_add_subtree_format(vendor_tree, tvb, offset-avp_vsa_header_len, avp_vsa_len+avp_vsa_header_len, dictionary_entry->ett, &avp_item, "VSA: t=%s(%u) l=%u", dictionary_entry->name, avp_vsa_type, avp_vsa_len+avp_vsa_header_len); } proto_tree_add_item(avp_tree, hf_radius_avp_vendor_type, tvb, vendor_offset, vendor->type_octets, ENC_BIG_ENDIAN); vendor_offset += vendor->type_octets; if (!avp_is_extended && vendor->length_octets) { 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); vendor_offset += (vendor_attribute_len - vendor->type_octets); } if (show_length) { avp_len_item = proto_tree_add_uint(avp_tree, dictionary_entry->hf_len, tvb, 0, 0, avp_length); proto_item_set_generated(avp_len_item); } if (vendor->has_flags) { /* * WiMAX VSA's have a non-standard format: * * type 1 octet * length 1 octet * continuation 1 octet 0bcrrrrrrr * value 1+ octets * * If the high bit of the "continuation" field is set, then * the next attribute of the same WiMAX type should have it's * value concatenated to this one. * * See "dictionary.wimax" from FreeRADIUS for details and references. */ radius_vsa_buffer_key key; radius_vsa_buffer *vsa_buffer = NULL; key.vendor_id = vendor_id; key.vsa_type = avp_vsa_type; if (!vsa_buffer_table) { vsa_buffer_table = g_hash_table_new(radius_vsa_hash, radius_vsa_equal); } vsa_buffer = (radius_vsa_buffer *)g_hash_table_lookup(vsa_buffer_table, &key); if (vsa_buffer) { vsa_buffer->data = (guint8 *)g_realloc(vsa_buffer->data, vsa_buffer->len + avp_vsa_len); tvb_memcpy(tvb, vsa_buffer->data + vsa_buffer->len, offset, avp_vsa_len); vsa_buffer->len += avp_vsa_len; vsa_buffer->seg_num++; } if (avp_vsa_flags & 0x80) { if (!vsa_buffer) { vsa_buffer = g_new(radius_vsa_buffer, 1); vsa_buffer->key.vendor_id = vendor_id; vsa_buffer->key.vsa_type = avp_vsa_type; vsa_buffer->len = avp_vsa_len; vsa_buffer->seg_num = 1; vsa_buffer->data = (guint8 *)g_malloc(avp_vsa_len); tvb_memcpy(tvb, vsa_buffer->data, offset, avp_vsa_len); g_hash_table_insert(vsa_buffer_table, &(vsa_buffer->key), vsa_buffer); } proto_tree_add_item(avp_tree, hf_radius_vsa_fragment, tvb, offset, avp_vsa_len, ENC_NA); proto_item_append_text(avp_item, ": VSA fragment[%u]", vsa_buffer->seg_num); } else { if (vsa_buffer) { tvbuff_t *vsa_tvb = NULL; proto_tree_add_item(avp_tree, hf_radius_vsa_fragment, tvb, offset, avp_vsa_len, ENC_NA); proto_item_append_text(avp_item, ": Last VSA fragment[%u]", vsa_buffer->seg_num); vsa_tvb = tvb_new_child_real_data(tvb, vsa_buffer->data, vsa_buffer->len, vsa_buffer->len); tvb_set_free_cb(vsa_tvb, g_free); add_new_data_source(pinfo, vsa_tvb, "Reassembled VSA"); add_avp_to_tree(avp_tree, avp_item, pinfo, vsa_tvb, dictionary_entry, vsa_buffer->len, 0); g_hash_table_remove(vsa_buffer_table, &(vsa_buffer->key)); g_free(vsa_buffer); } else { add_avp_to_tree(avp_tree, avp_item, pinfo, tvb, dictionary_entry, avp_vsa_len, offset); } } } else { add_avp_to_tree(avp_tree, avp_item, pinfo, tvb, dictionary_entry, avp_vsa_len, offset); } offset += avp_vsa_len; } /* while (offset < max_offset) */ continue; /* while (length > 0) */ } avp_tree = proto_item_add_subtree(avp_item, dictionary_entry->ett); proto_tree_add_item(avp_tree, hf_radius_avp_type, tvb, avp_offset_start, 1, ENC_BIG_ENDIAN); proto_tree_add_item(avp_tree, hf_radius_avp_length, tvb, avp_offset_start+1, 1, ENC_BIG_ENDIAN); if (show_length) { avp_len_item = proto_tree_add_uint(avp_tree, dictionary_entry->hf_len, tvb, 0, 0, avp_length); proto_item_set_generated(avp_len_item); } if (avp_is_extended) { proto_tree_add_item(avp_tree, hf_radius_avp_extended_type, tvb, avp_offset_start+2, 1, ENC_BIG_ENDIAN); if (RADIUS_ATTR_TYPE_IS_EXTENDED_LONG(avp_type0)) { proto_tree_add_item(avp_tree, hf_radius_avp_extended_more, tvb, avp_offset_start+3, 1, ENC_BIG_ENDIAN); } } if (avp_type0 == RADIUS_ATTR_TYPE_EAP_MESSAGE) { gint tvb_len; eap_seg_num++; tvb_len = tvb_captured_length_remaining(tvb, offset); if ((gint)avp_length < tvb_len) tvb_len = avp_length; /* Show this as an EAP fragment. */ proto_tree_add_item(avp_tree, hf_radius_eap_fragment, tvb, offset, tvb_len, ENC_NA); if (eap_tvb != NULL) { /* * Oops, a non-consecutive EAP-Message * attribute. */ proto_item_append_text(avp_item, " (non-consecutive)"); } else { /* * RFC 2869 says, in section 5.13, describing * the EAP-Message attribute: * * The NAS places EAP messages received * from the authenticating peer into one * or more EAP-Message attributes and * forwards them to the RADIUS Server * within an Access-Request message. * If multiple EAP-Messages are * contained within an Access-Request or * Access-Challenge packet, they MUST be * in order and they MUST be consecutive * attributes in the Access-Request or * Access-Challenge packet. * * ... * * The String field contains EAP packets, * as defined in [3]. If multiple * EAP-Message attributes are present * in a packet their values should be * concatenated; this allows EAP packets * longer than 253 octets to be passed * by RADIUS. * * Do reassembly of EAP-Message attributes. * We just concatenate all the attributes, * and when we see either the end of the * attribute list or a non-EAP-Message * attribute, we know we're done. */ if (eap_buffer == NULL) eap_buffer = (guint8 *)g_malloc(eap_tot_len_captured + tvb_len); else eap_buffer = (guint8 *)g_realloc(eap_buffer, eap_tot_len_captured + tvb_len); tvb_memcpy(tvb, eap_buffer + eap_tot_len_captured, offset, tvb_len); eap_tot_len_captured += tvb_len; eap_tot_len += avp_length; if (tvb_bytes_exist(tvb, offset + avp_length + 1, 1)) { guint8 next_type = tvb_get_guint8(tvb, offset + avp_length); if (next_type != RADIUS_ATTR_TYPE_EAP_MESSAGE) { /* Non-EAP-Message attribute */ last_eap = TRUE; } } else { /* * No more attributes, either because * we're at the end of the packet or * because we're at the end of the * captured packet data. */ last_eap = TRUE; } if (last_eap && eap_buffer) { gboolean save_writable; proto_item_append_text(avp_item, " Last Segment[%u]", eap_seg_num); eap_tree = proto_item_add_subtree(avp_item, ett_eap); eap_tvb = tvb_new_child_real_data(tvb, eap_buffer, eap_tot_len_captured, eap_tot_len); tvb_set_free_cb(eap_tvb, g_free); add_new_data_source(pinfo, eap_tvb, "Reassembled EAP"); /* * Don't free this when we're done - * it's associated with a tvbuff. */ eap_buffer = NULL; /* * Set the columns non-writable, * so that the packet list shows * this as an RADIUS packet, not * as an EAP packet. */ save_writable = col_get_writable(pinfo->cinfo, -1); col_set_writable(pinfo->cinfo, -1, FALSE); call_dissector(eap_handle, eap_tvb, pinfo, eap_tree); col_set_writable(pinfo->cinfo, -1, save_writable); } else { proto_item_append_text(avp_item, " Segment[%u]", eap_seg_num); } } offset += avp_length; continue; } add_avp_to_tree(avp_tree, avp_item, pinfo, tvb, dictionary_entry, avp_length, offset); offset += avp_length; } /* while (length > 0) */ CLEANUP_CALL_AND_POP_PFX(lb); /* vsa_buffer_table_destroy_indirect(&vsa_buffer_table) */ /* * Call the cleanup handler to free any reassembled data we haven't * attached to a tvbuff, and pop the handler. */ CLEANUP_CALL_AND_POP_PFX(la); /* eap_buffer_free_indirect(&eap_buffer); */ } /* This function tries to determine whether a packet is radius or not */ static gboolean is_radius(tvbuff_t *tvb) { guint8 code; guint16 length; code = tvb_get_guint8(tvb, 0); if (try_val_to_str_ext(code, &radius_pkt_type_codes_ext) == NULL) { return FALSE; } /* Check for valid length value: * Length * * The Length field is two octets. It indicates the length of the * packet including the Code, Identifier, Length, Authenticator and * Attribute fields. Octets outside the range of the Length field * MUST be treated as padding and ignored on reception. If the * packet is shorter than the Length field indicates, it MUST be * silently discarded. The minimum length is 20 and maximum length * is 4096. */ length = tvb_get_ntohs(tvb, 2); if ((length < 20) || (length > 4096)) { return FALSE; } return TRUE; } /* * returns true if the response or accounting request authenticator is valid * input: tvb of the response, corresponding request authenticator (not used for request), * uses the shared secret to calculate the authenticator * and checks with the current. * see RFC 2865, packet format page 16 * see RFC 2866, Request Authenticator page 7 */ static gboolean valid_authenticator(tvbuff_t *tvb, guint8 request_authenticator[], int request) { gcry_md_hd_t md5_handle; guint8 *digest; gboolean result; guint tvb_length; guint8 *payload; tvb_length = tvb_captured_length(tvb); /* should it be tvb_reported_length ? */ /* copy response into payload */ payload = (guint8 *)tvb_memdup(wmem_packet_scope(), tvb, 0, tvb_length); if (request) { /* reset authenticator field */ memset(payload+4, 0, AUTHENTICATOR_LENGTH); } else { /* replace authenticator in reply with the one in request */ memcpy(payload+4, request_authenticator, AUTHENTICATOR_LENGTH); } /* calculate MD5 hash (payload+shared_secret) */ if (gcry_md_open(&md5_handle, GCRY_MD_MD5, 0)) { return FALSE; } gcry_md_write(md5_handle, payload, tvb_length); gcry_md_write(md5_handle, shared_secret, strlen(shared_secret)); digest = gcry_md_read(md5_handle, 0); result = !memcmp(digest, authenticator, AUTHENTICATOR_LENGTH); gcry_md_close(md5_handle); return result; } static int dissect_radius(tvbuff_t *tvb, packet_info *pinfo, proto_tree *tree, void *data _U_) { proto_tree *radius_tree = NULL; proto_tree *avptree = NULL; proto_item *ti, *hidden_item, *authenticator_item = NULL; guint avplength; e_radiushdr rh; radius_info_t *rad_info; conversation_t *conversation; radius_call_info_key radius_call_key; radius_call_info_key *new_radius_call_key; wmem_tree_t *radius_call_tree; radius_call_t *radius_call = NULL; static address null_address = ADDRESS_INIT_NONE; /* does this look like radius ? */ if (!is_radius(tvb)) { return 0; } col_set_str(pinfo->cinfo, COL_PROTOCOL, "RADIUS"); col_clear(pinfo->cinfo, COL_INFO); rh.rh_code = tvb_get_guint8(tvb, 0); rh.rh_ident = tvb_get_guint8(tvb, 1); rh.rh_pktlength = tvb_get_ntohs(tvb, 2); /* Initialise stat info for passing to tap */ rad_info = wmem_new(wmem_packet_scope(), radius_info_t); rad_info->req_time.secs = 0; rad_info->req_time.nsecs = 0; rad_info->is_duplicate = FALSE; rad_info->request_available = FALSE; rad_info->req_num = 0; /* frame number request seen */ rad_info->rspcode = 0; /* tap stat info */ rad_info->code = rh.rh_code; rad_info->ident = rh.rh_ident; tap_queue_packet(radius_tap, pinfo, rad_info); col_add_fstr(pinfo->cinfo, COL_INFO, "%s id=%d", val_to_str_ext_const(rh.rh_code, &radius_pkt_type_codes_ext, "Unknown Packet"), rh.rh_ident); /* Load header fields if not already done */ if (hf_radius_code == -1) proto_registrar_get_byname("radius.code"); ti = proto_tree_add_item(tree, proto_radius, tvb, 0, rh.rh_pktlength, ENC_NA); radius_tree = proto_item_add_subtree(ti, ett_radius); proto_tree_add_uint(radius_tree, hf_radius_code, tvb, 0, 1, rh.rh_code); proto_tree_add_uint_format(radius_tree, hf_radius_id, tvb, 1, 1, rh.rh_ident, "Packet identifier: 0x%01x (%d)", rh.rh_ident, rh.rh_ident); /* * Make sure the length is sane. */ if (rh.rh_pktlength < HDR_LENGTH) { proto_tree_add_uint_format_value(radius_tree, hf_radius_length, tvb, 2, 2, rh.rh_pktlength, "%u (bogus, < %u)", rh.rh_pktlength, HDR_LENGTH); return tvb_captured_length(tvb); } avplength = rh.rh_pktlength - HDR_LENGTH; proto_tree_add_uint(radius_tree, hf_radius_length, tvb, 2, 2, rh.rh_pktlength); authenticator_item = proto_tree_add_item(radius_tree, hf_radius_authenticator, tvb, 4, AUTHENTICATOR_LENGTH, ENC_NA); tvb_memcpy(tvb, authenticator, 4, AUTHENTICATOR_LENGTH); /* Conversation support REQUEST/RESPONSES */ switch (rh.rh_code) { case RADIUS_PKT_TYPE_ACCESS_REQUEST: case RADIUS_PKT_TYPE_ACCOUNTING_REQUEST: case RADIUS_PKT_TYPE_PASSWORD_REQUEST: case RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST: case RADIUS_PKT_TYPE_RESOURCE_QUERY_REQUEST: case RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST: case RADIUS_PKT_TYPE_EVENT_REQUEST: case RADIUS_PKT_TYPE_DISCONNECT_REQUEST: case RADIUS_PKT_TYPE_COA_REQUEST: case RADIUS_PKT_TYPE_ALU_STATE_REQUEST: /* Don't bother creating conversations if we're encapsulated within * an error packet, such as an ICMP destination unreachable */ if (pinfo->flags.in_error_pkt) break; hidden_item = proto_tree_add_boolean(radius_tree, hf_radius_req, tvb, 0, 0, TRUE); proto_item_set_hidden(hidden_item); /* Keep track of the address and port whence the call came * so that we can match up requests with replies. * * Because it is UDP and the reply can come from any IP * and port (not necessarily the request dest), we only * track the source IP and port of the request to match * the reply. */ /* * XXX - can we just use NO_ADDR_B? Unfortunately, * you currently still have to pass a non-null * pointer for the second address argument even * if you do that. */ conversation = find_conversation(pinfo->num, &pinfo->src, &null_address, conversation_pt_to_conversation_type(pinfo->ptype), pinfo->srcport, pinfo->destport, 0); if (conversation == NULL) { /* It's not part of any conversation - create a new one. */ conversation = conversation_new(pinfo->num, &pinfo->src, &null_address, conversation_pt_to_conversation_type(pinfo->ptype), pinfo->srcport, pinfo->destport, 0); } /* Prepare the key data */ radius_call_key.code = rh.rh_code; radius_call_key.ident = rh.rh_ident; radius_call_key.conversation = conversation; radius_call_key.req_time = pinfo->abs_ts; /* Look up the tree of calls with this ident */ radius_call_tree = (wmem_tree_t *)wmem_map_lookup(radius_calls, &radius_call_key); if (!radius_call_tree) { radius_call_tree = wmem_tree_new(wmem_file_scope()); new_radius_call_key = wmem_new(wmem_file_scope(), radius_call_info_key); *new_radius_call_key = radius_call_key; wmem_map_insert(radius_calls, new_radius_call_key, radius_call_tree); } /* Find the last call we've seen (for this ident in this conversation) */ radius_call = (radius_call_t *)wmem_tree_lookup32_le(radius_call_tree, pinfo->num); if (radius_call != NULL) { /* We found a request with the same ident (in this conversation). * Is it really a duplicate? */ if (pinfo->num != radius_call->req_num && !memcmp(radius_call->req_authenticator, authenticator, AUTHENTICATOR_LENGTH)) { /* Yes, mark it as such */ rad_info->is_duplicate = TRUE; rad_info->req_num = radius_call->req_num; col_append_fstr(pinfo->cinfo, COL_INFO, ", Duplicate Request"); if (tree) { proto_item *item; hidden_item = proto_tree_add_uint(radius_tree, hf_radius_dup, tvb, 0, 0, rh.rh_ident); proto_item_set_hidden(hidden_item); item = proto_tree_add_uint(radius_tree, hf_radius_req_dup, tvb, 0, 0, radius_call->req_num); proto_item_set_generated(item); } } /* Accounting Request Authenticator Validation */ if (rh.rh_code == RADIUS_PKT_TYPE_ACCOUNTING_REQUEST && validate_authenticator && *shared_secret != '\0') { proto_item *authenticator_tree, *item; int valid; valid = valid_authenticator(tvb, radius_call->req_authenticator, 1); proto_item_append_text(authenticator_item, " [%s]", valid? "correct" : "incorrect"); authenticator_tree = proto_item_add_subtree(authenticator_item, ett_radius_authenticator); item = proto_tree_add_boolean(authenticator_tree, hf_radius_authenticator_valid, tvb, 4, AUTHENTICATOR_LENGTH, valid ? TRUE : FALSE); proto_item_set_generated(item); item = proto_tree_add_boolean(authenticator_tree, hf_radius_authenticator_invalid, tvb, 4, AUTHENTICATOR_LENGTH, valid ? FALSE : TRUE); proto_item_set_generated(item); if (!valid) { col_append_fstr(pinfo->cinfo, COL_INFO, " [incorrect authenticator]"); } } } if (!PINFO_FD_VISITED(pinfo) && (radius_call == NULL || !rad_info->is_duplicate)) { /* Prepare the value data. * "req_num" and "rsp_num" are frame numbers; * frame numbers are 1-origin, so we use 0 * to mean "we don't yet know in which frame * the reply for this call appears". */ radius_call = wmem_new(wmem_file_scope(), radius_call_t); radius_call->req_num = pinfo->num; radius_call->rsp_num = 0; radius_call->ident = rh.rh_ident; radius_call->code = rh.rh_code; memcpy(radius_call->req_authenticator, authenticator, AUTHENTICATOR_LENGTH); radius_call->responded = FALSE; radius_call->req_time = pinfo->abs_ts; radius_call->rspcode = 0; /* Store it */ wmem_tree_insert32(radius_call_tree, pinfo->num, radius_call); } if (radius_call && radius_call->rsp_num) { proto_item *item; item = proto_tree_add_uint_format(radius_tree, hf_radius_rsp_frame, tvb, 0, 0, radius_call->rsp_num, "The response to this request is in frame %u", radius_call->rsp_num); proto_item_set_generated(item); } break; case RADIUS_PKT_TYPE_ACCESS_ACCEPT: case RADIUS_PKT_TYPE_ACCESS_REJECT: case RADIUS_PKT_TYPE_ACCOUNTING_RESPONSE: case RADIUS_PKT_TYPE_PASSWORD_ACK: case RADIUS_PKT_TYPE_PASSWORD_REJECT: case RADIUS_PKT_TYPE_RESOURCE_FREE_RESPONSE: case RADIUS_PKT_TYPE_RESOURCE_QUERY_RESPONSE: case RADIUS_PKT_TYPE_NAS_REBOOT_RESPONSE: case RADIUS_PKT_TYPE_EVENT_RESPONSE: case RADIUS_PKT_TYPE_DISCONNECT_ACK: case RADIUS_PKT_TYPE_DISCONNECT_NAK: case RADIUS_PKT_TYPE_COA_ACK: case RADIUS_PKT_TYPE_COA_NAK: case RADIUS_PKT_TYPE_ACCESS_CHALLENGE: case RADIUS_PKT_TYPE_ALU_STATE_ACCEPT: case RADIUS_PKT_TYPE_ALU_STATE_REJECT: case RADIUS_PKT_TYPE_ALU_STATE_ERROR: /* Don't bother finding conversations if we're encapsulated within * an error packet, such as an ICMP destination unreachable */ if (pinfo->flags.in_error_pkt) break; hidden_item = proto_tree_add_boolean(radius_tree, hf_radius_rsp, tvb, 0, 0, TRUE); proto_item_set_hidden(hidden_item); /* Check for RADIUS response. A response must match a call that * we've seen, and the response must be sent to the same * port and address that the call came from. * * Because it is UDP and the reply can come from any IP * and port (not necessarily the request dest), we only * track the source IP and port of the request to match * the reply. */ /* XXX - can we just use NO_ADDR_B? Unfortunately, * you currently still have to pass a non-null * pointer for the second address argument even * if you do that. */ conversation = find_conversation(pinfo->num, &null_address, &pinfo->dst, conversation_pt_to_conversation_type(pinfo->ptype), pinfo->srcport, pinfo->destport, 0); if (conversation == NULL) { /* Nothing more to do here */ break; } /* Prepare the key data */ radius_call_key.code = rh.rh_code; radius_call_key.ident = rh.rh_ident; radius_call_key.conversation = conversation; radius_call_key.req_time = pinfo->abs_ts; /* Look up the tree of calls with this ident */ radius_call_tree = (wmem_tree_t *)wmem_map_lookup(radius_calls, &radius_call_key); if (radius_call_tree == NULL) { /* Nothing more to do here */ break; } /* Find the last call we've seen (for this ident in this conversation) */ radius_call = (radius_call_t *)wmem_tree_lookup32_le(radius_call_tree, pinfo->num); if (radius_call == NULL) { /* Nothing more to do here */ break; } /* Indicate the frame to which this is a reply. */ if (radius_call->req_num) { nstime_t delta; proto_item *item; rad_info->request_available = TRUE; rad_info->req_num = radius_call->req_num; radius_call->responded = TRUE; item = proto_tree_add_uint_format(radius_tree, hf_radius_req_frame, tvb, 0, 0, radius_call->req_num, "This is a response to a request in frame %u", radius_call->req_num); proto_item_set_generated(item); nstime_delta(&delta, &pinfo->abs_ts, &radius_call->req_time); item = proto_tree_add_time(radius_tree, hf_radius_time, tvb, 0, 0, &delta); proto_item_set_generated(item); /* Response Authenticator Validation */ if (validate_authenticator && *shared_secret != '\0') { proto_item *authenticator_tree; int valid; valid = valid_authenticator(tvb, radius_call->req_authenticator, 0); proto_item_append_text(authenticator_item, " [%s]", valid? "correct" : "incorrect"); authenticator_tree = proto_item_add_subtree(authenticator_item, ett_radius_authenticator); item = proto_tree_add_boolean(authenticator_tree, hf_radius_authenticator_valid, tvb, 4, AUTHENTICATOR_LENGTH, valid ? TRUE : FALSE); proto_item_set_generated(item); item = proto_tree_add_boolean(authenticator_tree, hf_radius_authenticator_invalid, tvb, 4, AUTHENTICATOR_LENGTH, valid ? FALSE : TRUE); proto_item_set_generated(item); if (!valid) { col_append_fstr(pinfo->cinfo, COL_INFO, " [incorrect authenticator]"); } } } if (radius_call->rsp_num == 0) { /* We have not yet seen a response to that call, so this must be the first response; remember its frame number. */ radius_call->rsp_num = pinfo->num; } else { /* We have seen a response to this call - but was it *this* response? (disregard provisional responses) */ if ((radius_call->rsp_num != pinfo->num) && (radius_call->rspcode == rh.rh_code)) { /* No, so it's a duplicate response. Mark it as such. */ rad_info->is_duplicate = TRUE; col_append_fstr(pinfo->cinfo, COL_INFO, ", Duplicate Response"); if (tree) { proto_item *item; hidden_item = proto_tree_add_uint(radius_tree, hf_radius_dup, tvb, 0, 0, rh.rh_ident); proto_item_set_hidden(hidden_item); item = proto_tree_add_uint(radius_tree, hf_radius_rsp_dup, tvb, 0, 0, radius_call->rsp_num); proto_item_set_generated(item); } } } /* Now store the response code (after comparison above) */ radius_call->rspcode = rh.rh_code; rad_info->rspcode = rh.rh_code; break; default: break; } if (radius_call) { rad_info->req_time = radius_call->req_time; } if (avplength > 0) { /* list the attribute value pairs */ avptree = proto_tree_add_subtree(radius_tree, tvb, HDR_LENGTH, avplength, ett_radius_avp, NULL, "Attribute Value Pairs"); dissect_attribute_value_pairs(avptree, pinfo, tvb, HDR_LENGTH, avplength); } return tvb_captured_length(tvb); } void free_radius_attr_info(gpointer data) { radius_attr_info_t* attr = (radius_attr_info_t*)data; value_string *vs = (value_string *)attr->vs; g_free(attr->name); if (attr->tlvs_by_id) { g_hash_table_destroy(attr->tlvs_by_id); } if (vs) { for (; vs->strptr; vs++) { g_free((gpointer)vs->strptr); } g_free((gpointer)attr->vs); } g_free(attr); } static void free_radius_vendor_info(gpointer data) { radius_vendor_info_t* vendor = (radius_vendor_info_t*)data; g_free(vendor->name); if (vendor->attrs_by_id) g_hash_table_destroy(vendor->attrs_by_id); g_free(vendor); } static void register_attrs(gpointer k _U_, gpointer v, gpointer p) { radius_attr_info_t *a = (radius_attr_info_t *)v; int i; gint *ett = &(a->ett); gchar *abbrev = wmem_strdup_printf(wmem_epan_scope(), "radius.%s", a->name); hf_register_info hfri[] = { { NULL, { NULL, NULL, FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { NULL, { NULL, NULL, FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { NULL, { NULL, NULL, FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { NULL, { NULL, NULL, FT_NONE, BASE_NONE, NULL, 0x0, NULL, HFILL }} }; guint len_hf = 2; hfett_t *ri = (hfett_t *)p; for(i=0; abbrev[i]; i++) { if (abbrev[i] == '-') abbrev[i] = '_'; if (abbrev[i] == '/') abbrev[i] = '_'; } hfri[0].p_id = &(a->hf); hfri[1].p_id = &(a->hf_len); hfri[0].hfinfo.name = a->name; hfri[0].hfinfo.abbrev = abbrev; hfri[1].hfinfo.name = "Length"; hfri[1].hfinfo.abbrev = wmem_strdup_printf(wmem_epan_scope(), "%s.len", abbrev); hfri[1].hfinfo.blurb = wmem_strdup_printf(wmem_epan_scope(), "%s Length", a->name); if (a->type == radius_integer) { hfri[0].hfinfo.type = FT_UINT32; hfri[0].hfinfo.display = BASE_DEC; hfri[2].p_id = &(a->hf_alt); hfri[2].hfinfo.name = wmem_strdup(wmem_epan_scope(), a->name); hfri[2].hfinfo.abbrev = abbrev; hfri[2].hfinfo.type = FT_UINT64; hfri[2].hfinfo.display = BASE_DEC; if (a->vs) { hfri[0].hfinfo.strings = VALS(a->vs); } len_hf++; } else if (a->type == radius_signed) { hfri[0].hfinfo.type = FT_INT32; hfri[0].hfinfo.display = BASE_DEC; hfri[2].p_id = &(a->hf_alt); hfri[2].hfinfo.name = wmem_strdup(wmem_epan_scope(), a->name); hfri[2].hfinfo.abbrev = abbrev; hfri[2].hfinfo.type = FT_INT64; hfri[2].hfinfo.display = BASE_DEC; if (a->vs) { hfri[0].hfinfo.strings = VALS(a->vs); } len_hf++; } else if (a->type == radius_string) { hfri[0].hfinfo.type = FT_STRING; hfri[0].hfinfo.display = BASE_NONE; if (a->encrypt != 0) { /* * This attribute is encrypted, so create an * alternative field for the encrypted value. */ hfri[2].p_id = &(a->hf_alt); hfri[2].hfinfo.name = wmem_strdup_printf(wmem_epan_scope(), "%s (encrypted)", a->name); hfri[2].hfinfo.abbrev = wmem_strdup_printf(wmem_epan_scope(), "%s_encrypted", abbrev); hfri[2].hfinfo.type = FT_BYTES; hfri[2].hfinfo.display = BASE_NONE; len_hf++; } } else if (a->type == radius_octets) { hfri[0].hfinfo.type = FT_BYTES; hfri[0].hfinfo.display = BASE_NONE; } else if (a->type == radius_ipaddr) { hfri[0].hfinfo.type = FT_IPv4; hfri[0].hfinfo.display = BASE_NONE; } else if (a->type == radius_ipv6addr) { hfri[0].hfinfo.type = FT_IPv6; hfri[0].hfinfo.display = BASE_NONE; } else if (a->type == radius_ipv6prefix) { hfri[0].hfinfo.type = FT_BYTES; hfri[0].hfinfo.display = BASE_NONE; } else if (a->type == radius_ipxnet) { hfri[0].hfinfo.type = FT_IPXNET; hfri[0].hfinfo.display = BASE_NONE; } else if (a->type == radius_date) { hfri[0].hfinfo.type = FT_ABSOLUTE_TIME; hfri[0].hfinfo.display = ABSOLUTE_TIME_LOCAL; } else if (a->type == radius_abinary) { hfri[0].hfinfo.type = FT_BYTES; hfri[0].hfinfo.display = BASE_NONE; } else if (a->type == radius_ifid) { hfri[0].hfinfo.type = FT_BYTES; hfri[0].hfinfo.display = BASE_NONE; } else if (a->type == radius_combo_ip) { hfri[0].hfinfo.type = FT_IPv4; hfri[0].hfinfo.display = BASE_NONE; hfri[2].p_id = &(a->hf_alt); hfri[2].hfinfo.name = wmem_strdup(wmem_epan_scope(), a->name); hfri[2].hfinfo.abbrev = wmem_strdup(wmem_epan_scope(), abbrev); hfri[2].hfinfo.type = FT_IPv6; hfri[2].hfinfo.display = BASE_NONE; len_hf++; #if 0 /* Fix -Wduplicated-branches */ } else if (a->type == radius_tlv) { hfri[0].hfinfo.type = FT_BYTES; hfri[0].hfinfo.display = BASE_NONE; #endif } else { hfri[0].hfinfo.type = FT_BYTES; hfri[0].hfinfo.display = BASE_NONE; } if (a->tagged) { hfri[len_hf].p_id = &(a->hf_tag); hfri[len_hf].hfinfo.name = "Tag"; hfri[len_hf].hfinfo.abbrev = wmem_strdup_printf(wmem_epan_scope(), "%s.tag", abbrev); hfri[len_hf].hfinfo.blurb = wmem_strdup_printf(wmem_epan_scope(), "%s Tag", a->name); hfri[len_hf].hfinfo.type = FT_UINT8; hfri[len_hf].hfinfo.display = BASE_HEX; len_hf++; } wmem_array_append(ri->hf, hfri, len_hf); wmem_array_append_one(ri->ett, ett); if (a->tlvs_by_id) { g_hash_table_foreach(a->tlvs_by_id, register_attrs, ri); } } static void register_vendors(gpointer k _U_, gpointer v, gpointer p) { radius_vendor_info_t *vnd = (radius_vendor_info_t *)v; hfett_t *ri = (hfett_t *)p; value_string vnd_vs; gint *ett_p = &(vnd->ett); vnd_vs.value = vnd->code; vnd_vs.strptr = vnd->name; wmem_array_append_one(ri->vend_vs, vnd_vs); wmem_array_append_one(ri->ett, ett_p); g_hash_table_foreach(vnd->attrs_by_id, register_attrs, ri); } extern void radius_register_avp_dissector(guint32 vendor_id, guint32 _attribute_id, radius_avp_dissector_t radius_avp_dissector) { radius_vendor_info_t *vendor; radius_attr_info_t *dictionary_entry; GHashTable *by_id; radius_attr_type_t attribute_id; DISSECTOR_ASSERT(radius_avp_dissector != NULL); memset(&attribute_id, 0, sizeof(attribute_id)); attribute_id.u8_code[0] = _attribute_id; if (vendor_id) { vendor = (radius_vendor_info_t *)g_hash_table_lookup(dict->vendors_by_id, GUINT_TO_POINTER(vendor_id)); if (!vendor) { vendor = g_new(radius_vendor_info_t, 1); vendor->name = ws_strdup_printf("%s-%u", enterprises_lookup(vendor_id, "Unknown"), vendor_id); vendor->code = vendor_id; vendor->attrs_by_id = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_radius_attr_info); vendor->ett = no_vendor.ett; /* XXX: Default "standard" values: Should be parameters ? */ vendor->type_octets = 1; vendor->length_octets = 1; vendor->has_flags = FALSE; g_hash_table_insert(dict->vendors_by_id, GUINT_TO_POINTER(vendor->code), vendor); g_hash_table_insert(dict->vendors_by_name, (gpointer)(vendor->name), vendor); } dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(vendor->attrs_by_id, GUINT_TO_POINTER(attribute_id.value)); by_id = vendor->attrs_by_id; } else { dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(dict->attrs_by_id, GUINT_TO_POINTER(attribute_id.value)); by_id = dict->attrs_by_id; } if (!dictionary_entry) { dictionary_entry = g_new(radius_attr_info_t, 1); dictionary_entry->name = ws_strdup_printf("Unknown-Attribute-%u", attribute_id.value); dictionary_entry->code = attribute_id; dictionary_entry->encrypt = 0; dictionary_entry->type = NULL; dictionary_entry->vs = NULL; dictionary_entry->hf = no_dictionary_entry.hf; dictionary_entry->tagged = 0; dictionary_entry->hf_tag = -1; dictionary_entry->hf_len = no_dictionary_entry.hf_len; dictionary_entry->ett = no_dictionary_entry.ett; dictionary_entry->tlvs_by_id = NULL; g_hash_table_insert(by_id, GUINT_TO_POINTER(dictionary_entry->code.value), dictionary_entry); } dictionary_entry->dissector = radius_avp_dissector; } /* Discard and init any state we've saved */ static void radius_init_protocol(void) { module_t *radius_module = prefs_find_module("radius"); pref_t *alternate_port; if (radius_module) { /* Find alternate_port preference and mark it obsolete (thus hiding it from a user) */ alternate_port = prefs_find_preference(radius_module, "alternate_port"); if (! prefs_get_preference_obsolete(alternate_port)) prefs_set_preference_obsolete(alternate_port); } } static void radius_shutdown(void) { if (dict != NULL) { g_hash_table_destroy(dict->attrs_by_id); g_hash_table_destroy(dict->attrs_by_name); g_hash_table_destroy(dict->vendors_by_id); g_hash_table_destroy(dict->vendors_by_name); g_hash_table_destroy(dict->tlvs_by_name); g_free(dict); } } static void _radius_load_dictionary(gchar* dir) { gchar *dict_err_str = NULL; if (!dir || test_for_directory(dir) != EISDIR) { return; } radius_load_dictionary(dict, dir, "dictionary", &dict_err_str); if (dict_err_str) { report_failure("radius: %s", dict_err_str); g_free(dict_err_str); } } static void register_radius_fields(const char *unused _U_) { hf_register_info base_hf[] = { { &hf_radius_req, { "Request", "radius.req", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "TRUE if RADIUS request", HFILL }}, { &hf_radius_rsp, { "Response", "radius.rsp", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "TRUE if RADIUS response", HFILL }}, { &hf_radius_req_frame, { "Request Frame", "radius.reqframe", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_REQUEST), 0, NULL, HFILL }}, { &hf_radius_rsp_frame, { "Response Frame", "radius.rspframe", FT_FRAMENUM, BASE_NONE, FRAMENUM_TYPE(FT_FRAMENUM_RESPONSE), 0, NULL, HFILL }}, { &hf_radius_time, { "Time from request", "radius.time", FT_RELATIVE_TIME, BASE_NONE, NULL, 0, "Timedelta between Request and Response", HFILL }}, { &hf_radius_code, { "Code", "radius.code", FT_UINT8, BASE_DEC|BASE_EXT_STRING, &radius_pkt_type_codes_ext, 0x0, NULL, HFILL }}, { &hf_radius_id, { "Identifier", "radius.id", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_radius_authenticator, { "Authenticator", "radius.authenticator", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_radius_authenticator_valid, { "Valid Authenticator", "radius.authenticator.valid", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "TRUE if Authenticator is valid", HFILL }}, { &hf_radius_authenticator_invalid, { "Invalid Authenticator", "radius.authenticator.invalid", FT_BOOLEAN, BASE_NONE, NULL, 0x0, "TRUE if Authenticator is invalid", HFILL }}, { &hf_radius_length, { "Length", "radius.length", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &(no_dictionary_entry.hf), { "Unknown-Attribute", "radius.Unknown_Attribute", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &(no_dictionary_entry.hf_len), { "Unknown-Attribute Length", "radius.Unknown_Attribute.length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_radius_chap_password, { "CHAP-Password", "radius.CHAP_Password", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_radius_chap_ident, { "CHAP Ident", "radius.CHAP_Ident", FT_UINT8, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_radius_chap_string, { "CHAP String", "radius.CHAP_String", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_radius_framed_ip_address, { "Framed-IP-Address", "radius.Framed-IP-Address", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_radius_login_ip_host, { "Login-IP-Host", "radius.Login-IP-Host", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_radius_framed_ipx_network, { "Framed-IPX-Network", "radius.Framed-IPX-Network", FT_IPXNET, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_radius_cosine_vpi, { "Cosine-VPI", "radius.Cosine-Vpi", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_radius_cosine_vci, { "Cosine-VCI", "radius.Cosine-Vci", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_radius_dup, { "Duplicate Message ID", "radius.dup", FT_UINT32, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_radius_req_dup, { "Duplicate Request Frame Number", "radius.req.dup", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_radius_rsp_dup, { "Duplicate Response Frame Number", "radius.rsp.dup", FT_FRAMENUM, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_radius_ascend_data_filter, { "Ascend Data Filter", "radius.ascenddatafilter", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_radius_ascend_data_filter_type, { "Type", "radius.ascenddatafilter.type", FT_UINT8, BASE_DEC, VALS(ascenddf_filtertype), 0x0, NULL, HFILL }}, { &hf_radius_ascend_data_filter_filteror, { "Filter or forward", "radius.ascenddatafilter.filteror", FT_UINT8, BASE_DEC, VALS(ascenddf_filteror), 0x0, NULL, HFILL }}, { &hf_radius_ascend_data_filter_inout, { "Indirection", "radius.ascenddatafilter.inout", FT_UINT8, BASE_DEC, VALS(ascenddf_inout), 0x0, NULL, HFILL }}, { &hf_radius_ascend_data_filter_spare, { "Spare", "radius.ascenddatafilter.spare", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_radius_ascend_data_filter_src_ipv4, { "Source IPv4 address", "radius.ascenddatafilter.src_ipv4", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_radius_ascend_data_filter_dst_ipv4, { "Destination IPv4 address", "radius.ascenddatafilter.dst_ipv4", FT_IPv4, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_radius_ascend_data_filter_src_ipv6, { "Source IPv6 address", "radius.ascenddatafilter.src_ipv6", FT_IPv6, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_radius_ascend_data_filter_dst_ipv6, { "Destination IPv6 address", "radius.ascenddatafilter.dst_ipv6", FT_IPv6, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_radius_ascend_data_filter_src_ip_prefix, { "Source IP prefix", "radius.ascenddatafilter.src_prefix_ip", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_radius_ascend_data_filter_dst_ip_prefix, { "Destination IP prefix", "radius.ascenddatafilter.dst_prefix_ip", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_radius_ascend_data_filter_protocol, { "Protocol", "radius.ascenddatafilter.protocol", FT_UINT8, BASE_DEC, VALS(ascenddf_proto), 0x0, NULL, HFILL }}, { &hf_radius_ascend_data_filter_established, { "Established", "radius.ascenddatafilter.established", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_radius_ascend_data_filter_src_port, { "Source Port", "radius.ascenddatafilter.src_port", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_radius_ascend_data_filter_dst_port, { "Destination Port", "radius.ascenddatafilter.dst_port", FT_UINT16, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_radius_ascend_data_filter_src_port_qualifier, { "Source Port Qualifier", "radius.ascenddatafilter.src_port_qualifier", FT_UINT8, BASE_DEC, VALS(ascenddf_portq), 0x0, NULL, HFILL }}, { &hf_radius_ascend_data_filter_dst_port_qualifier, { "Destination Port Qualifier", "radius.ascenddatafilter.dst_port_qualifier", FT_UINT8, BASE_DEC, VALS(ascenddf_portq), 0x0, NULL, HFILL }}, { &hf_radius_ascend_data_filter_reserved, { "Reserved", "radius.ascenddatafilter.reserved", FT_UINT16, BASE_HEX, NULL, 0x0, NULL, HFILL }}, { &hf_radius_vsa_fragment, { "VSA fragment", "radius.vsa_fragment", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_radius_eap_fragment, { "EAP fragment", "radius.eap_fragment", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_radius_avp, { "AVP", "radius.avp", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_radius_avp_length, { "Length", "radius.avp.length", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_radius_avp_type, { "Type", "radius.avp.type", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_radius_avp_vendor_id, { "Vendor ID", "radius.avp.vendor_id", FT_UINT32, BASE_ENTERPRISES, STRINGS_ENTERPRISES, 0x0, NULL, HFILL }}, { &hf_radius_avp_vendor_type, { "Type", "radius.avp.vendor_type", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_radius_avp_vendor_len, { "Length", "radius.avp.vendor_len", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_radius_avp_extended_type, { "Extended Type", "radius.avp.extended_type", FT_UINT8, BASE_DEC, NULL, 0x0, NULL, HFILL }}, { &hf_radius_avp_extended_more, { "Extended More", "radius.avp.extended_more", FT_BOOLEAN, 8, NULL, 0x80, NULL, HFILL }}, { &hf_radius_egress_vlanid_tag, { "Tag", "radius.egress_vlanid_tag", FT_UINT32, BASE_HEX, VALS(egress_vlan_tag_vals), 0xFF000000, NULL, HFILL }}, { &hf_radius_egress_vlanid_pad, { "Pad", "radius.egress_vlanid_pad", FT_UINT32, BASE_HEX, NULL, 0x00FFF000, NULL, HFILL }}, { &hf_radius_egress_vlanid, { "Vlan ID", "radius.egress_vlanid", FT_UINT32, BASE_DEC, NULL, 0x00000FFF, NULL, HFILL }}, { &hf_radius_egress_vlan_name_tag, { "Tag", "radius.egress_vlan_name_tag", FT_UINT8, BASE_HEX, VALS(egress_vlan_tag_vals), 0x0, NULL, HFILL }}, { &hf_radius_egress_vlan_name, { "Vlan Name", "radius.egress_vlan_name", FT_STRING, BASE_NONE, NULL, 0x0, NULL, HFILL }}, { &hf_radius_3gpp_ms_tmime_zone, { "Timezone", "radius.3gpp_ms_tmime_zone", FT_BYTES, BASE_NONE, NULL, 0x0, NULL, HFILL }}, }; gint *base_ett[] = { &ett_radius, &ett_radius_avp, &ett_radius_authenticator, &ett_radius_ascend, &ett_eap, &ett_chap, &(no_dictionary_entry.ett), &(no_vendor.ett), }; static ei_register_info ei[] = { { &ei_radius_invalid_length, { "radius.invalid_length", PI_MALFORMED, PI_ERROR, "Invalid length", EXPFILL }}, }; expert_module_t *expert_radius; hfett_t ri; char *dir = NULL; ri.hf = wmem_array_new(wmem_epan_scope(), sizeof(hf_register_info)); ri.ett = wmem_array_new(wmem_epan_scope(), sizeof(gint *)); ri.vend_vs = wmem_array_new(wmem_epan_scope(), sizeof(value_string)); wmem_array_append(ri.hf, base_hf, array_length(base_hf)); wmem_array_append(ri.ett, base_ett, array_length(base_ett)); dir = get_datafile_path("radius"); _radius_load_dictionary(dir); g_free(dir); dir = get_persconffile_path("radius", FALSE); _radius_load_dictionary(dir); g_free(dir); g_hash_table_foreach(dict->attrs_by_id, register_attrs, &ri); g_hash_table_foreach(dict->vendors_by_id, register_vendors, &ri); proto_register_field_array(proto_radius, (hf_register_info *)wmem_array_get_raw(ri.hf), wmem_array_get_count(ri.hf)); proto_register_subtree_array((gint **)wmem_array_get_raw(ri.ett), wmem_array_get_count(ri.ett)); expert_radius = expert_register_protocol(proto_radius); expert_register_field_array(expert_radius, ei, array_length(ei)); /* * Handle attributes that have a special format. */ radius_register_avp_dissector(0, 3, dissect_chap_password); radius_register_avp_dissector(0, 8, dissect_framed_ip_address); radius_register_avp_dissector(0, 14, dissect_login_ip_host); radius_register_avp_dissector(0, 23, dissect_framed_ipx_network); radius_register_avp_dissector(0, 56, dissect_rfc4675_egress_vlanid); radius_register_avp_dissector(0, 58, dissect_rfc4675_egress_vlan_name); radius_register_avp_dissector(VENDOR_COSINE, 5, dissect_cosine_vpvc); /* * XXX - we should special-case Cisco attribute 252; see the comment in * dictionary.cisco. */ radius_register_avp_dissector(VENDOR_THE3GPP, 1, dissect_radius_3gpp_imsi); radius_register_avp_dissector(VENDOR_THE3GPP, 23, dissect_radius_3gpp_ms_tmime_zone); } void proto_register_radius(void) { module_t *radius_module; proto_radius = proto_register_protocol("RADIUS Protocol", "RADIUS", "radius"); radius_handle = register_dissector("radius", dissect_radius, proto_radius); register_init_routine(&radius_init_protocol); register_shutdown_routine(radius_shutdown); radius_module = prefs_register_protocol(proto_radius, NULL); prefs_register_string_preference(radius_module, "shared_secret", "Shared Secret", "Shared secret used to decode User Passwords and validate Accounting Request and Response Authenticators", &shared_secret); prefs_register_bool_preference(radius_module, "validate_authenticator", "Validate Accounting Request and Response Authenticator", "Whether to check or not if Accounting Request and Response Authenticator are correct. You need to define shared secret for this to work.", &validate_authenticator); prefs_register_bool_preference(radius_module, "show_length", "Show AVP Lengths", "Whether to add or not to the tree the AVP's payload length", &show_length); /* * For now this preference allows supporting legacy Ascend AVPs and others * who might use these attribute types (not complying with IANA allocation). */ prefs_register_bool_preference(radius_module, "disable_extended_attributes", "Disable extended attribute space (RFC 6929)", "Whether to interpret 241-246 as extended attributes according to RFC 6929", &disable_extended_attributes); prefs_register_obsolete_preference(radius_module, "request_ttl"); radius_tap = register_tap("radius"); proto_register_prefix("radius", register_radius_fields); dict = g_new(radius_dictionary_t, 1); /* * IDs map to names and vice versa. The attribute and vendor is stored * only once, but referenced by both name and ID mappings. * See also radius_dictionary_t in packet-radius.h */ dict->attrs_by_id = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_radius_attr_info); dict->attrs_by_name = g_hash_table_new(g_str_hash, g_str_equal); dict->vendors_by_id = g_hash_table_new_full(g_direct_hash, g_direct_equal, NULL, free_radius_vendor_info); dict->vendors_by_name = g_hash_table_new(g_str_hash, g_str_equal); dict->tlvs_by_name = g_hash_table_new(g_str_hash, g_str_equal); radius_calls = wmem_map_new_autoreset(wmem_epan_scope(), wmem_file_scope(), radius_call_hash, radius_call_equal); register_rtd_table(proto_radius, NULL, RADIUS_CAT_NUM_TIMESTATS, 1, radius_message_code, radiusstat_packet, NULL); } void proto_reg_handoff_radius(void) { eap_handle = find_dissector_add_dependency("eap", proto_radius); dissector_add_uint_range_with_preference("udp.port", DEFAULT_RADIUS_PORT_RANGE, radius_handle); } /* * Editor modelines - https://www.wireshark.org/tools/modelines.html * * Local variables: * c-basic-offset: 8 * tab-width: 8 * indent-tabs-mode: t * End: * * vi: set shiftwidth=8 tabstop=8 noexpandtab: * :indentSize=8:tabSize=8:noTabs=false: */