/****************************************************************************
** File: radius.c
**
** Author: Mike Borella
**
** Comments: Support for decoding RADIUS packets.
**
** $Id: radius.c,v 1.4 2002/01/03 18:12:13 mborella Exp $
**
** 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 Library 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
**
*****************************************************************************/
#include "radius.h"
#include "iana.h"
#include "radius_3gpp2.h"
/*
* Types of RADIUS codes
*/
#define RADIUS_CODE_ACCESSREQUEST 1
#define RADIUS_CODE_ACCESSACCEPT 2
#define RADIUS_CODE_ACCESSREJECT 3
#define RADIUS_CODE_ACCOUNTINGREQUEST 4
#define RADIUS_CODE_ACCOUNTINGRESPONSE 5
#define RADIUS_CODE_ACCESSCHALLENGE 11
/*
* RADIUS code map
*/
strmap_t radius_code_map[] =
{
{ RADIUS_CODE_ACCESSREQUEST, "access request" },
{ RADIUS_CODE_ACCESSACCEPT, "access accept" },
{ RADIUS_CODE_ACCESSREJECT, "access reject" },
{ RADIUS_CODE_ACCOUNTINGREQUEST, "accounting request" },
{ RADIUS_CODE_ACCOUNTINGRESPONSE, "accounting response" },
{ RADIUS_CODE_ACCESSCHALLENGE, "access challenge" },
{ 0, ""}
};
/*
* RADIUS attributes
*/
#define RADIUS_ATTR_USERNAME 1
#define RADIUS_ATTR_USERPASSWORD 2
#define RADIUS_ATTR_CHAPPASSWORD 3
#define RADIUS_ATTR_NASIPADDR 4
#define RADIUS_ATTR_NASPORT 5
#define RADIUS_ATTR_SERVICETYPE 6
#define RADIUS_ATTR_FRAMEDPROTOCOL 7
#define RADIUS_ATTR_FRAMEDIPADDR 8
#define RADIUS_ATTR_FRAMEDIPNETMASK 9
#define RADIUS_ATTR_CLASS 25
#define RADIUS_ATTR_VENDORSPECIFIC 26
#define RADIUS_ATTR_CALLINGSTATIONID 31
#define RADIUS_ATTR_LOGINLATSERVICE 34
#define RADIUS_ATTR_ACCTSTATUSTYPE 40
#define RADIUS_ATTR_ACCTINPUTOCTETS 42
#define RADIUS_ATTR_ACCTOUTPUTOCTETS 43
#define RADIUS_ATTR_ACCTSESSIONID 44
#define RADIUS_ATTR_ACCTSESSIONTIME 46
#define RADIUS_ATTR_EVENTTIMESTAMP 55
#define RADIUS_ATTR_CHAPCHALLENGE 60
#define RADIUS_ATTR_NASPORTTYPE 61
/*
* RADIUS attribute map
*/
strmap_t radius_attr_map[] =
{
{ RADIUS_ATTR_USERNAME, "user name" },
{ RADIUS_ATTR_USERPASSWORD, "user password" },
{ RADIUS_ATTR_CHAPPASSWORD, "CHAP password" },
{ RADIUS_ATTR_NASIPADDR, "NAS IP address" },
{ RADIUS_ATTR_NASPORT, "NAS port" },
{ RADIUS_ATTR_SERVICETYPE, "service type" },
{ RADIUS_ATTR_FRAMEDPROTOCOL, "framed protocol" },
{ RADIUS_ATTR_FRAMEDIPADDR, "framed IP address" },
{ RADIUS_ATTR_FRAMEDIPNETMASK, "framed IP netmask" },
{ RADIUS_ATTR_CLASS, "class" },
{ RADIUS_ATTR_VENDORSPECIFIC, "vendor specific" },
{ RADIUS_ATTR_CALLINGSTATIONID, "calling station ID" },
{ RADIUS_ATTR_LOGINLATSERVICE, "login-LAT-service" },
{ RADIUS_ATTR_ACCTSTATUSTYPE, "accounting status type" },
{ RADIUS_ATTR_ACCTSESSIONID, "accounting session id" },
{ RADIUS_ATTR_ACCTINPUTOCTETS, "accounting input octets" },
{ RADIUS_ATTR_ACCTOUTPUTOCTETS, "accounting output octets" },
{ RADIUS_ATTR_ACCTSESSIONTIME, "accounting session time" },
{ RADIUS_ATTR_EVENTTIMESTAMP, "event timestamp" },
{ RADIUS_ATTR_CHAPCHALLENGE, "CHAP challenge" },
{ RADIUS_ATTR_NASPORTTYPE, "NAS port type" },
{ 0, ""}
};
/*
* RADIUS packet format
*/
typedef struct radius_
{
u_int8_t code;
u_int8_t identifier;
u_int16_t length;
u_int8_t authenticator[16];
} radius_header_t;
extern struct arg_t * my_args;
extern strmap_t iana_enterprise_map[];
/*----------------------------------------------------------------------------
**
** dump_radius_vendorspecific()
**
** Parse and dump a vendor specific RADIUS attribute.
** Returns the number of bytes read or -1 on error.
**
**----------------------------------------------------------------------------
*/
void dump_radius_vendorspecific(packet_t * pkt, u_int8_t type, u_int8_t length)
{
u_int32_t vendor;
if (get_packet_bytes((u_int8_t *) &vendor, pkt, 4) == 0)
return;
/* Conversions */
vendor = ntohl(vendor);
/* Get the vendor specific info */
switch(vendor)
{
case IANA_ENTERPRISE_3GPP2:
dump_radius_3gpp2(pkt, type, length);
break;
default:
{
u_int8_t * value;
/* Allocate memory for the value then get it */
value = my_malloc(length-5);
if (get_packet_bytes(value, pkt, length-6) == 0)
return;
value[length-6] = '\0';
/* Display */
if (my_args->m)
{
display_minimal_string(map2str(radius_attr_map, type));
display_minimal_string("[");
display_minimal_string(map2str(iana_enterprise_map, vendor));
display_minimal_string("]: ");
display_minimal(value, length-6, DISP_HEX);
}
else
{
display_strmap("Attribute type", type, radius_attr_map);
display(" Length", &length, 1, DISP_DEC);
display_strmap(" Vendor", vendor, iana_enterprise_map);
display(" Value", value, length-6, DISP_HEX_MULTILINE);
}
my_free(value);
}
break;
} /* switch */
}
/*----------------------------------------------------------------------------
**
** dump_radius_attribute()
**
** Parse and dump a single RADIUS attribute. Returns the number of bytes read
** or -1 on error.
**
**----------------------------------------------------------------------------
*/
int dump_radius_attribute(packet_t * pkt)
{
u_int8_t type;
u_int8_t length;
u_int8_t * generic_value;
/* Get the code */
if (get_packet_bytes(&type, pkt, 1) == 0)
return -1;
/* Get the length */
if (get_packet_bytes(&length, pkt, 1) == 0)
return 1;
/* Decide how to proceed based on the code */
switch(type)
{
/* These cases are for all attributes to be printed as text strings */
case RADIUS_ATTR_USERNAME:
case RADIUS_ATTR_CLASS:
case RADIUS_ATTR_CALLINGSTATIONID:
case RADIUS_ATTR_LOGINLATSERVICE:
{
u_int8_t * username;
/* Allocate memory for the username then get it */
username = my_malloc(length-1);
if (get_packet_bytes(username, pkt, length-2) == 0)
return -1;
username[length-2] = '\0';
/* Display */
if (my_args->m)
{
display_minimal_string(map2str(radius_attr_map, type));
display_minimal_string(": ");
display_minimal_string(username);
}
else
{
display_strmap("Attribute type", type, radius_attr_map);
display(" Length", &length, 1, DISP_DEC);
display_string(" Value", username);
}
my_free(username);
}
break;
/* These cases are for all attributes to be printed as 4 byte ints */
case RADIUS_ATTR_NASPORT:
case RADIUS_ATTR_SERVICETYPE: /* should have its own parser */
case RADIUS_ATTR_FRAMEDPROTOCOL: /* should have its own parser */
case RADIUS_ATTR_NASPORTTYPE: /* should have its own parser */
case RADIUS_ATTR_EVENTTIMESTAMP:
case RADIUS_ATTR_ACCTSESSIONTIME:
case RADIUS_ATTR_ACCTINPUTOCTETS:
case RADIUS_ATTR_ACCTOUTPUTOCTETS:
{
u_int32_t byte4;
if (get_packet_bytes((u_int8_t *) &byte4, pkt, 4) == 0)
return -1;
/* Conversions */
byte4 = ntohl(byte4);
/* Display */
if (my_args->m)
{
display_minimal_string(map2str(radius_attr_map, type));
display_minimal_string(": ");
display_minimal((u_int8_t *) &byte4, 4, DISP_DEC);
}
else
{
display_strmap("Attribute type", type, radius_attr_map);
display(" Length", &length, 1, DISP_DEC);
display(" Value", (u_int8_t *) &byte4, 4, DISP_DEC);
}
}
break;
/* These cases are for all attributes to be printed as IP addresses */
case RADIUS_ATTR_NASIPADDR:
case RADIUS_ATTR_FRAMEDIPADDR:
case RADIUS_ATTR_FRAMEDIPNETMASK:
{
u_int32_t addr;
if (get_packet_bytes((u_int8_t *) &addr, pkt, 4) == 0)
return -1;
/* Display */
if (my_args->m)
{
display_minimal_string(map2str(radius_attr_map, type));
display_minimal_string(": ");
display_minimal((u_int8_t *) &addr, 4, DISP_DOTTEDDEC);
}
else
{
display_strmap("Attribute type", type, radius_attr_map);
display(" Length", &length, 1, DISP_DEC);
display(" Address", (u_int8_t *) &addr, 4, DISP_DOTTEDDEC);
}
}
break;
case RADIUS_ATTR_VENDORSPECIFIC:
dump_radius_vendorspecific(pkt, type, length);
break;
/* These cases are for all attributes to be printed as hex */
case RADIUS_ATTR_CHAPCHALLENGE:
case RADIUS_ATTR_USERPASSWORD:
case RADIUS_ATTR_CHAPPASSWORD:
default:
{
/* Allocate memory for the value then get it */
generic_value = my_malloc(length-2);
if (get_packet_bytes(generic_value, pkt, length-2) == 0)
return -1;
/* Display */
if (my_args->m)
{
display_minimal_string(map2str(radius_attr_map, type));
display_minimal_string(": ");
display_minimal(generic_value, length-2, DISP_HEX);
}
else
{
display_strmap("Attribute type", type, radius_attr_map);
display(" Length", &length, 1, DISP_DEC);
display(" Value", generic_value, length-2, DISP_HEX_MULTILINE);
}
/* Free the memory of the value */
my_free(generic_value);
}
break;
}
return length;
}
/*----------------------------------------------------------------------------
**
** dump_radius()
**
** Parse and dump RADIUS packets
**
**----------------------------------------------------------------------------
*/
void dump_radius(packet_t *pkt)
{
radius_header_t radius;
int len;
int first;
/* Set the layer */
set_layer(LAYER_TRANSPORT);
/* Get the RADIUS header */
if (get_packet_bytes((u_int8_t *) &radius, pkt, sizeof(radius_header_t))
== 0)
return;
/* Conversions */
radius.length = ntohs(radius.length);
/* Display */
if (my_args->m)
{
display_minimal_string("| RADIUS ");
display_minimal_string(map2str(radius_code_map, radius.code));
display_minimal_string(" ");
display_minimal(&radius.identifier, 1, DISP_DEC);
display_minimal_string(" (");
}
else
{
display_header_banner("RADIUS");
display_strmap("Code", radius.code, radius_code_map);
display("Identifier", &radius.identifier, 1, DISP_DEC);
display("Length", (u_int8_t *) &radius.length, 2, DISP_DEC);
display("Authenticator", (u_int8_t *) &radius.authenticator, 16,
DISP_HEX);
}
/* Read all of the attributes */
len = radius.length - sizeof(radius_header_t);
first = 1;
while(1)
{
if (len <= 0)
break;
if (first != 1 && my_args->m)
display_minimal_string(", ");
len = len - dump_radius_attribute(pkt);
first = 0;
}
/* Final close paren for minimal mode */
if (my_args->m)
display_minimal_string(")");
/* Dump the hex buffer */
hexbuffer_flush();
return;
}
syntax highlighted by Code2HTML, v. 0.9.1