wireshark/epan/dissectors/packet-radius.c

2324 lines
74 KiB
C

/* 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 <luis@ontanon.org> - use FreeRADIUS' dictionary
* Changed 10/2006 Alejandro Vaquero <alejandrovaquero@yahoo.com> - add Conversations support
*
* Wireshark - Network traffic analyzer
* By Gerald Combs <gerald@wireshark.org>
* Copyright 1998 Gerald Combs
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
*
* 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
*
* 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 <stdlib.h>
#include <errno.h>
#include <epan/packet.h>
#include <epan/exceptions.h>
#include <epan/expert.h>
#include <epan/prefs.h>
#include <epan/sminmpec.h>
#include <epan/conversation.h>
#include <epan/tap.h>
#include <epan/rtd_table.h>
#include <epan/addr_resolv.h>
#include <wsutil/filesystem.h>
#include <wsutil/report_err.h>
#include <wsutil/md5.h>
#include "packet-radius.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)
#define UDP_PORT_RADIUS 1645
#define UDP_PORT_RADIUS_NEW 1812
#define UDP_PORT_RADACCT 1646
#define UDP_PORT_RADACCT_NEW 1813
#define UDP_PORT_DAE_OLD 1700 /* DAE: pre RFC */
#define UDP_PORT_DAE 3799 /* DAE: rfc3576 */
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_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_vsa_fragment = -1;
static int hf_radius_eap_fragment = -1;
static int hf_radius_avp = -1;
static int hf_radius_3gpp_ms_tmime_zone = -1;
static gint ett_radius = -1;
static gint ett_radius_avp = -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,FALSE,FALSE,radius_octets, NULL, NULL, -1, -1, -1, -1, -1, NULL };
static dissector_handle_t eap_handle;
static const gchar* shared_secret = "";
static gboolean show_length = FALSE;
static guint alt_port_pref = 0;
static guint request_ttl = 5;
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 int
radiusstat_packet(void *prs, packet_info *pinfo, epan_dissect_t *edt _U_, const void *pri)
{
rtd_data_t* rtd_data = (rtd_data_t*)prs;
rtd_stat_table* rs = &rtd_data->stat_table;
const radius_info_t *ri=(radius_info_t *)pri;
nstime_t delta;
radius_category radius_cat = RADIUS_CAT_OTHERS;
int ret = 0;
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->fd->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 = 1;
}
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 GHashTable *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) {
nstime_t delta;
nstime_delta(&delta, &key1->req_time, &key2->req_time);
if (abs( (int) nstime_to_sec(&delta)) > (double) request_ttl) return 0;
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 ((key1->code == RADIUS_PKT_TYPE_ACCOUNTING_REQUEST) &&
(key2->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 ((key1->code == RADIUS_PKT_TYPE_RESOURCE_FREE_REQUEST) &&
(key2->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 ((key1->code == RADIUS_PKT_TYPE_NAS_REBOOT_REQUEST) &&
(key2->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 ((key1->code == RADIUS_PKT_TYPE_DISCONNECT_REQUEST) &&
((key2->code == RADIUS_PKT_TYPE_DISCONNECT_ACK) || (key2->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 ((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;
}
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->code + */ key->conversation->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_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(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(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, "ip"}, {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;
int len;
guint8 proto, srclen, dstlen;
guint32 srcip, dstip;
guint16 srcport, dstport;
guint8 srcportq, dstportq;
len=tvb_length(tvb);
if (len != 24) {
return wmem_strdup_printf(wmem_packet_scope(), "Wrong attribute length %d", len);
}
filterstr=wmem_strbuf_sized_new(wmem_packet_scope(), 64, 64);
proto_tree_add_item(tree, hf_radius_ascend_data_filter, tvb, 0, -1, ENC_NA);
wmem_strbuf_append_printf(filterstr, "%s %s %s",
val_to_str(tvb_get_guint8(tvb, 0), 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, 14);
if (proto) {
wmem_strbuf_append_printf(filterstr, " %s",
val_to_str(proto, ascenddf_proto, "%u"));
}
srcip=tvb_get_ipv4(tvb, 4);
srclen=tvb_get_guint8(tvb, 12);
srcport=tvb_get_ntohs(tvb, 16);
srcportq=tvb_get_guint8(tvb, 20);
if (srcip || srclen || srcportq) {
wmem_strbuf_append_printf(filterstr, " srcip %s/%d", tvb_ip_to_str(tvb, 4), srclen);
if (srcportq)
wmem_strbuf_append_printf(filterstr, " srcport %s %d",
val_to_str(srcportq, ascenddf_portq, "%u"), srcport);
}
dstip=tvb_get_ipv4(tvb, 8);
dstlen=tvb_get_guint8(tvb, 13);
dstport=tvb_get_ntohs(tvb, 18);
dstportq=tvb_get_guint8(tvb, 21);
if (dstip || dstlen || dstportq) {
wmem_strbuf_append_printf(filterstr, " dstip %s/%d", tvb_ip_to_str(tvb, 8), 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_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_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_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 void
radius_decrypt_avp(gchar *dest,int dest_len,tvbuff_t *tvb,int offset,int length)
{
md5_state_t md_ctx, old_md_ctx;
md5_byte_t digest[AUTHENTICATOR_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;
md5_init(&md_ctx);
md5_append(&md_ctx, (const guint8*)shared_secret, (int)strlen(shared_secret));
old_md_ctx = md_ctx;
md5_append(&md_ctx, authenticator, AUTHENTICATOR_LENGTH);
md5_finish(&md_ctx, digest);
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 = g_snprintf(&dest[totlen], dest_len - totlen,
"%c", c);
totlen += MIN(returned_length, dest_len - totlen - 1);
}
else if ( c ) {
returned_length = g_snprintf(&dest[totlen], dest_len - totlen,
"\\%03o", c);
totlen += MIN(returned_length, dest_len - totlen - 1);
}
}
md_ctx = old_md_ctx;
md5_append(&md_ctx, &pd[i], AUTHENTICATOR_LENGTH);
md5_finish(&md_ctx, digest);
}
}
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, "%" G_GINT64_MODIFIER "u", 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, "%" G_GINT64_MODIFIER "u", 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(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) {
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) {
guint32 ip;
gchar buf[MAX_IP_STR_LEN];
if (len != 4) {
proto_item_append_text(avp_item, "[wrong length for IP address]");
return;
}
ip=tvb_get_ipv4(tvb,offset);
proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_BIG_ENDIAN);
ip_to_str_buf((guint8 *)&ip, buf, MAX_IP_STR_LEN);
proto_item_append_text(avp_item, "%s", buf);
}
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) {
struct e_in6_addr ipv6_buff;
gchar txtbuf[256];
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);
tvb_get_ipv6(tvb, offset, &ipv6_buff);
ip6_to_str_buf(&ipv6_buff, txtbuf);
proto_item_append_text(avp_item, "%s", txtbuf);
}
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) {
struct e_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);
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) {
guint32 ip;
struct e_in6_addr ipv6_buff;
gchar buf[256];
if (len == 4){
ip=tvb_get_ipv4(tvb,offset);
proto_tree_add_item(tree, a->hf, tvb, offset, len, ENC_BIG_ENDIAN);
ip_to_str_buf((guint8 *)&ip, buf, MAX_IP_STR_LEN);
proto_item_append_text(avp_item, "%s", buf);
} else if (len == 16) {
proto_tree_add_item(tree, a->hf_alt, tvb, offset, len, ENC_NA);
tvb_get_ipv6(tvb, offset, &ipv6_buff);
ip6_to_str_buf(&ipv6_buff, buf);
proto_item_append_text(avp_item, "%s", buf);
} 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) {
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(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;
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: l=%u t=%s(%u)", tlv_length,
dictionary_entry->name, tlv_type);
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(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--;
}
}
if ( dictionary_entry->dissector ) {
tvbuff_t* tvb_value;
const gchar* str;
tvb_value = tvb_new_subset_length(tvb, offset, avp_length);
str = dictionary_entry->dissector(avp_tree,tvb_value,pinfo);
proto_item_append_text(avp_item, ": %s",str);
} else {
proto_item_append_text(avp_item, ": ");
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 vsa_buffer_table_destroy(void *table) {
if (table) {
g_hash_table_foreach_remove((GHashTable *)table, vsa_buffer_destroy, NULL);
g_hash_table_destroy((GHashTable *)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;
/* Forces load of header fields, if not already done so */
DISSECTOR_ASSERT(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, g_free, eap_buffer);
CLEANUP_PUSH_PFX(lb, vsa_buffer_table_destroy, (void *)vsa_buffer_table);
while (length > 0) {
radius_attr_info_t* dictionary_entry = NULL;
gint tvb_len;
guint32 avp_type;
guint32 avp_length;
guint32 vendor_id;
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_type = tvb_get_guint8(tvb,offset);
avp_length = tvb_get_guint8(tvb,offset+1);
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 (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));
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, "l=%u t=%s(%u)", avp_length,
dictionary_entry->name, avp_type);
avp_length -= 2;
offset += 2;
if (avp_type == RADIUS_ATTR_TYPE_VENDOR_SPECIFIC) {
radius_vendor_info_t* vendor;
proto_tree* vendor_tree;
gint max_offset = offset + avp_length;
const gchar* vendor_str;
/* 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 = val_to_str_ext_const(vendor_id, &sminmpec_values_ext, "Unknown");
if (!vendor) {
vendor = &no_vendor;
}
proto_item_append_text(avp_item, " v=%s(%u)", vendor_str,
vendor_id);
vendor_tree = proto_item_add_subtree(avp_item,vendor->ett);
while (offset < max_offset) {
guint32 avp_vsa_type;
guint32 avp_vsa_len;
guint8 avp_vsa_flags = 0;
guint32 avp_vsa_header_len = vendor->type_octets + vendor->length_octets + (vendor->has_flags ? 1 : 0);
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:
avp_vsa_type = tvb_get_guint8(tvb,offset++);
}
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:
avp_vsa_len = tvb_get_guint8(tvb,offset++);
}
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;
dictionary_entry = (radius_attr_info_t *)g_hash_table_lookup(vendor->attrs_by_id,GUINT_TO_POINTER(avp_vsa_type));
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: l=%u t=%s(%u) C=0x%02x",
avp_vsa_len+avp_vsa_header_len, dictionary_entry->name, avp_vsa_type, avp_vsa_flags);
} 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: l=%u t=%s(%u)",
avp_vsa_len+avp_vsa_header_len, dictionary_entry->name, avp_vsa_type);
}
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) {
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 = (radius_vsa_buffer *)g_malloc(sizeof(radius_vsa_buffer));
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);
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);
}
tvb_len = tvb_length_remaining(tvb, offset);
if ((gint)avp_length < tvb_len)
tvb_len = avp_length;
if (avp_type == RADIUS_ATTR_TYPE_EAP_MESSAGE) {
eap_seg_num++;
/* 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);
col_set_writable(pinfo->cinfo, FALSE);
call_dissector(eap_handle, eap_tvb, pinfo, eap_tree);
col_set_writable(pinfo->cinfo, save_writable);
} else {
proto_item_append_text(avp_item, " Segment[%u]",
eap_seg_num);
}
}
offset += avp_length;
} else {
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(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);
}
/* 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;
}
static void register_radius_fields(const char*);
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;
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;
radius_call_t *radius_call = NULL;
static address null_address = { AT_NONE, 0, NULL };
/* 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->code = 0;
rad_info->ident = 0;
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(%d) (id=%d, l=%d)",
val_to_str_ext_const(rh.rh_code, &radius_pkt_type_codes_ext, "Unknown Packet"),
rh.rh_code, rh.rh_ident, rh.rh_pktlength);
if (tree)
{
/* Forces load of header fields, if not already done so */
DISSECTOR_ASSERT(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)
{
if (tree)
{
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_length(tvb);
}
avplength = rh.rh_pktlength - HDR_LENGTH;
if (tree)
{
proto_tree_add_uint(radius_tree, hf_radius_length, tvb, 2, 2, rh.rh_pktlength);
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;
if (tree)
{
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->fd->num, &pinfo->src,
&null_address, 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->fd->num, &pinfo->src,
&null_address, 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->fd->abs_ts;
/* Look up the request */
radius_call = (radius_call_t *)g_hash_table_lookup(radius_calls, &radius_call_key);
if (radius_call != NULL)
{
/* We've seen a request with this ID, with the same
destination, before - but was it *this* request? */
if (pinfo->fd->num != radius_call->req_num)
{
/* No, so it's a duplicate request. Mark it as such.
FIXME: This is way too simple, as the request number
is only an 8-bit value. See bug#4096 */
rad_info->is_duplicate = TRUE;
rad_info->req_num = radius_call->req_num;
col_append_fstr(pinfo->cinfo, COL_INFO,
", Duplicate Request ID:%u", rh.rh_ident);
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, rh.rh_ident);
PROTO_ITEM_SET_GENERATED(item);
}
}
}
else
{
/* 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". */
new_radius_call_key = wmem_new(wmem_file_scope(), radius_call_info_key);
*new_radius_call_key = radius_call_key;
radius_call = wmem_new(wmem_file_scope(), radius_call_t);
radius_call->req_num = pinfo->fd->num;
radius_call->rsp_num = 0;
radius_call->ident = rh.rh_ident;
radius_call->code = rh.rh_code;
radius_call->responded = FALSE;
radius_call->req_time = pinfo->fd->abs_ts;
radius_call->rspcode = 0;
/* Store it */
g_hash_table_insert(radius_calls, new_radius_call_key, radius_call);
}
if (tree && 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;
if (tree)
{
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->fd->num, &null_address,
&pinfo->dst, pinfo->ptype, pinfo->srcport, pinfo->destport, 0);
if (conversation != NULL)
{
/* Look only for matching request, if
matching conversation is available. */
/* 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->fd->abs_ts;
radius_call = (radius_call_t *)g_hash_table_lookup(radius_calls, &radius_call_key);
if (radius_call)
{
/* Indicate the frame to which this is a reply. */
if (radius_call->req_num)
{
rad_info->request_available = TRUE;
rad_info->req_num = radius_call->req_num;
radius_call->responded = TRUE;
if (tree)
{
nstime_t delta;
proto_item* item;
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->fd->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);
}
}
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->fd->num;
}
else
{
/* We have seen a response to this call - but was it
*this* response? (disregard provisional responses) */
if ( (radius_call->rsp_num != pinfo->fd->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 ID:%u", rh.rh_ident);
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, rh.rh_ident);
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.secs = radius_call->req_time.secs;
rad_info->req_time.nsecs = radius_call->req_time.nsecs;
}
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_length(tvb);
}
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++;
} else if (a->type == radius_tlv) {
hfri[0].hfinfo.type = FT_BYTES;
hfri[0].hfinfo.display = BASE_NONE;
} 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;
DISSECTOR_ASSERT(radius_avp_dissector != NULL);
if (vendor_id) {
vendor = (radius_vendor_info_t *)g_hash_table_lookup(dict->vendors_by_id,GUINT_TO_POINTER(vendor_id));
if ( ! vendor ) {
vendor = (radius_vendor_info_t *)g_malloc(sizeof(radius_vendor_info_t));
vendor->name = g_strdup_printf("%s-%u",
val_to_str_ext_const(vendor_id, &sminmpec_values_ext, "Unknown"),
vendor_id);
vendor->code = vendor_id;
vendor->attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
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));
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));
by_id = dict->attrs_by_id;
}
if (!dictionary_entry) {
dictionary_entry = (radius_attr_info_t *)g_malloc(sizeof(radius_attr_info_t));
dictionary_entry->name = g_strdup_printf("Unknown-Attribute-%u",attribute_id);
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),dictionary_entry);
}
dictionary_entry->dissector = radius_avp_dissector;
}
/* Discard and init any state we've saved */
static void
radius_init_protocol(void)
{
if (radius_calls != NULL)
{
g_hash_table_destroy(radius_calls);
radius_calls = NULL;
}
radius_calls = g_hash_table_new(radius_call_hash, radius_call_equal);
}
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, NULL, 0,
NULL, HFILL }},
{ &hf_radius_rsp_frame,
{ "Response Frame", "radius.rspframe", FT_FRAMENUM, BASE_NONE, NULL, 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_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", "radius.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
{ &hf_radius_req_dup,
{ "Duplicate Request", "radius.req.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
{ &hf_radius_rsp_dup,
{ "Duplicate Response", "radius.rsp.dup", FT_UINT32, BASE_DEC, NULL, 0x0,
NULL, HFILL }},
{ &hf_radius_ascend_data_filter,
{ "Ascend Data Filter", "radius.ascenddatafilter", FT_BYTES, BASE_NONE, 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_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_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;
gchar* dict_err_str = 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_persconffile_path("radius", FALSE);
if (test_for_directory(dir) != EISDIR) {
/* Although dir isn't a directory it may still use memory */
g_free(dir);
dir = get_datafile_path("radius");
if (test_for_directory(dir) != EISDIR) {
g_free(dir);
dir = NULL;
}
}
if (dir) {
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);
}
g_hash_table_foreach(dict->attrs_by_id,register_attrs,&ri);
g_hash_table_foreach(dict->vendors_by_id,register_vendors,&ri);
}
g_free(dir);
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));
no_vendor.attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
/*
* 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(VENDOR_COSINE,5,dissect_cosine_vpvc);
/*
* XXX - should we just call dissect_ascend_data_filter()
* in radius_abinary()?
*
* Note that there is no attribute 242 in dictionary.redback.
*/
radius_register_avp_dissector(VENDOR_ASCEND,242,dissect_ascend_data_filter);
radius_register_avp_dissector(VENDOR_REDBACK,242,dissect_ascend_data_filter);
radius_register_avp_dissector(0,242,dissect_ascend_data_filter);
/*
* XXX - we should special-case Cisco attribute 252; see the comment in
* dictionary.cisco.
*/
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");
new_register_dissector("radius", dissect_radius, proto_radius);
register_init_routine(&radius_init_protocol);
radius_module = prefs_register_protocol(proto_radius, proto_reg_handoff_radius);
prefs_register_string_preference(radius_module,"shared_secret","Shared Secret",
"Shared secret used to decode User Passwords",
&shared_secret);
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);
prefs_register_uint_preference(radius_module, "alternate_port","Alternate Port",
"An alternate UDP port to decode as RADIUS", 10, &alt_port_pref);
prefs_register_uint_preference(radius_module, "request_ttl", "Request TimeToLive",
"Time to live for a radius request used for matching it with a response", 10, &request_ttl);
radius_tap = register_tap("radius");
proto_register_prefix("radius",register_radius_fields);
dict = (radius_dictionary_t *)g_malloc(sizeof(radius_dictionary_t));
dict->attrs_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
dict->attrs_by_name = g_hash_table_new(g_str_hash,g_str_equal);
dict->vendors_by_id = g_hash_table_new(g_direct_hash,g_direct_equal);
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);
register_rtd_table(proto_radius, NULL, RADIUS_CAT_NUM_TIMESTATS, 1, radius_message_code, radiusstat_packet, NULL);
}
void
proto_reg_handoff_radius(void)
{
static gboolean initialized = FALSE;
static dissector_handle_t radius_handle;
static guint alt_port;
if (!initialized) {
radius_handle = find_dissector("radius");
dissector_add_uint("udp.port", UDP_PORT_RADIUS, radius_handle);
dissector_add_uint("udp.port", UDP_PORT_RADIUS_NEW, radius_handle);
dissector_add_uint("udp.port", UDP_PORT_RADACCT, radius_handle);
dissector_add_uint("udp.port", UDP_PORT_RADACCT_NEW, radius_handle);
dissector_add_uint("udp.port", UDP_PORT_DAE_OLD, radius_handle);
dissector_add_uint("udp.port", UDP_PORT_DAE, radius_handle);
eap_handle = find_dissector("eap");
initialized = TRUE;
} else {
if (alt_port != 0)
dissector_delete_uint("udp.port", alt_port, radius_handle);
}
if (alt_port_pref != 0)
dissector_add_uint("udp.port", alt_port_pref, radius_handle);
alt_port = alt_port_pref;
}
/*
* Editor modelines - http://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:
*/