/**************************************************************************** ** 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; }