Source code: RadiusFormat.java
|
[ index | 560 lines | javadoc ]
|
package nl.west.aaa.radius;
import nl.west.aaa.*;
/*
* based of rfc2865 and rfc2866
*/
import java.io.*;
import java.util.*;
public class RadiusFormat
implements RecordFormat
{
// Types of RADIUS messages
public static final int MSG_ACCESS_REQUEST = 1;
public static final int MSG_ACCESS_ACCEPT = 2;
public static final int MSG_ACCESS_REJECT = 3;
public static final int MSG_ACCOUNTING_REQUEST = 4;
public static final int MSG_ACCOUNTING_RESPONSE = 5;
public static final int MSG_ACCESS_CHALLENGE = 11;
// Types of AVP formats
public static final int AVP_TYPE_TEXT = 1;
public static final int AVP_TYPE_STRING = 2;
public static final int AVP_TYPE_ADDRESS = 3;
public static final int AVP_TYPE_INTEGER = 4;
public static final int AVP_TYPE_DATA = 5;
// Types of AVPs
public static final int AVP_USER_NAME = 1;
public static final int AVP_USER_PASSWORD = 2;
public static final int AVP_CHAP_PASSWORD = 3;
public static final int AVP_NAS_IP_ADDRESS = 4;
public static final int AVP_NAS_PORT = 5;
public static final int AVP_SERVICE_TYPE = 6;
public static final int AVP_FRAMED_PROTOCOL = 7;
public static final int AVP_FRAMED_IP_ADDRESS = 8;
public static final int AVP_FRAMED_IP_NETMASK = 9;
public static final int AVP_FRAMED_ROUTING = 10;
public static final int AVP_FILTER_ID = 11;
public static final int AVP_FRAMED_MTU = 12;
public static final int AVP_FRAMED_COMPRESSION = 13;
public static final int AVP_LOGIN_IP_HOST = 14;
public static final int AVP_LOGIN_SERVICE = 15;
public static final int AVP_LOGIN_TCP_PORT = 16;
public static final int AVP_REPLY_MESSAGE = 18;
public static final int AVP_CALLBACK_NUMBER = 19;
public static final int AVP_CALLBACK_ID = 20;
public static final int AVP_FRAMED_ROUTE = 22;
public static final int AVP_FRAMED_IPX_NETWORK = 23;
public static final int AVP_STATE = 24;
public static final int AVP_CLASS = 25;
public static final int AVP_VENDOR_SPECIFIC = 26;
public static final int AVP_SESSION_TIMEOUT = 27;
public static final int AVP_IDLE_TIMEOUT = 28;
public static final int AVP_TERMINATION_ACTION = 29;
public static final int AVP_CALLED_STATION_ID = 30;
public static final int AVP_CALLING_STATION_ID = 31;
public static final int AVP_NAS_IDENTIFIER = 32;
public static final int AVP_PROXY_STATE = 33;
public static final int AVP_LOGIN_LAT_SERVICE = 34;
public static final int AVP_LOGIN_LAT_NODE = 35;
public static final int AVP_LOGIN_LAT_GROUP = 36;
public static final int AVP_FRAMED_APPLETALK_LINK = 37;
public static final int AVP_FRAMED_APPLETALK_NETWORK = 38;
public static final int AVP_FRAMED_APPLETALK_ZONE = 39;
public static final int AVP_ACCT_STATUS_TYPE = 40;
public static final int AVP_ACCT_DELAY_TIME = 41;
public static final int AVP_ACCT_INPUT_OCTETS = 42;
public static final int AVP_ACCT_OUTPUT_OCTETS = 43;
public static final int AVP_ACCT_SESSION_ID = 44;
public static final int AVP_ACCT_AUTHENTIC = 45;
public static final int AVP_ACCT_SESSION_TIME = 46;
public static final int AVP_ACCT_INPUT_PACKETS = 47;
public static final int AVP_ACCT_OUTPUT_PACKETS = 48;
public static final int AVP_ACCT_TERMINATE_CAUSE = 49;
public static final int AVP_ACCT_MULTI_SESSION_ID = 50;
public static final int AVP_ACCT_LINK_COUNT = 51;
public static final int AVP_CHAP_CHALLENGE = 60;
public static final int AVP_NAS_PORT_TYPE = 61;
public static final int AVP_PORT_LIMIT = 62;
public static final int AVP_LOGIN_LAT_PORT = 63;
private static final String[] avpNames={
"", // 0
"User-Name", // 1
"User-Password", // 2
"CHAP-Password", // 3
"NAS-IP-Address", // 4
"NAS-Port", // 5
"Service-Type", // 6
"Framed-Protocol", // 7
"Framed-IP-Address", // 8
"Framed-IP-Netmask", // 9
"Framed-Routing", // 10
"Filter-Id", // 11
"Framed-MTU", // 12
"Framed-Compression", // 13
"Login-IP-Host", // 14
"Login-Service", // 15
"Login-TCP-Port", // 16
"", // 17
"Reply-Message", // 18
"Callback-Number", // 19
"Callback-Id", // 20
"", // 21
"Framed-Route", // 22
"Framed-IPX-Network", // 23
"State", // 24
"Class", // 25
"Vendor-Specific", // 26
"Session-Timeout", // 27
"Idle-Timeout", // 28
"Termination-Action", // 29
"Called-Station-Id", // 30
"Calling-Station-Id", // 31
"NAS-Identifier", // 32
"Proxy-State", // 33
"Login-LAT-Service", // 34
"Login-LAT-Node", // 35
"Login-LAT-Group", // 36
"Framed-AppleTalk-Link", // 37
"Framed-AppleTalk-Network", // 38
"Framed-AppleTalk-Zone", // 39
"Acct-Status-Type", // 40
"Acct-Delay-Time", // 41
"Acct-Input-Octets", // 42
"Acct-Output-Octets", // 43
"Acct-Session-Id", // 44
"Acct-Authentic", // 45
"Acct-Session-Time", // 46
"Acct-Input-Packets", // 47
"Acct-Output-Packets", // 48
"Acct-Terminate-Cause", // 49
"Acct-Multi-Session-Id", // 50
"Acct-Link-Count", // 51
"", //52
"", //53
"", //54
"", //55
"", //56
"", //57
"", //58
"", //59
"CHAP-Challenge", // 60
"NAS-Port-Type", // 61
"Port-Limit", // 62
"Login-LAT-Port" // 63
};
private int nextIdentifier=(new Random()).nextInt(256);
/**
* Translater the RADIUS message code value
* to a Message.XXXX value.
* If an illegal messagecode is given
* the method throws a RuntimeException.
*/
public static int getMessageType(int messageCode)
{
switch(messageCode)
{
case MSG_ACCESS_REQUEST:
// is allso something of an AUTORISATION_REQUEST
return Message.AUTHENTICATION_REQUEST;
case MSG_ACCESS_ACCEPT:
// is allso something of an AUTORISATION_ACCEPT
return Message.AUTHENTICATION_ACCEPT;
case MSG_ACCESS_REJECT:
// could allso be a AUTORISATION_REJECT
return Message.AUTHENTICATION_REJECT;
case MSG_ACCOUNTING_REQUEST:
return Message.ACCOUNTING_REQUEST;
case MSG_ACCOUNTING_RESPONSE:
return Message.ACCOUNTING_REPLY;
case MSG_ACCESS_CHALLENGE:
// may allso be AUTHORISATION_CHALLENGE;
return Message.AUTHENTICATION_CHALLENGE;
default:
// throw someting
throw new RuntimeException("Invalid MessageCode");
}
}
/**
* Translater the Message.XXX messagetype
* to a valid RADIUS messagecode.
* If an unsupported messagecode is given
* the method return -1.
*/
public static int getRadiusMessageCode(int type)
{
switch(type)
{
case Message.AUTHENTICATION_REQUEST:
case Message.AUTHORISATION_REQUEST:
return MSG_ACCESS_REQUEST;
case Message.AUTHENTICATION_ACCEPT:
case Message.AUTHORISATION_ACCEPT:
return MSG_ACCESS_ACCEPT;
case Message.AUTHENTICATION_REJECT:
case Message.AUTHORISATION_REJECT:
return MSG_ACCESS_REJECT;
case Message.AUTHENTICATION_CHALLENGE:
case Message.AUTHORISATION_CHALLENGE:
return MSG_ACCESS_CHALLENGE;
case Message.ACCOUNTING_REQUEST:
return MSG_ACCOUNTING_REQUEST;
case Message.ACCOUNTING_REPLY:
return MSG_ACCOUNTING_RESPONSE;
case Message.AUTHENTICATION_REDO:
case Message.AUTHORISATION_REDO:
case Message.MESSAGES_REQUEST:
case Message.MESSAGES_READY:
case Message.MESSAGE_REJECT:
case Message.ACCOUNTING_POLL:
default:
return -1;
}
}
/**
* Translates the AVP code to a AVP name.
*/
public static String getAVPName(int code)
{
if ( (code>=1) && (code<=63) )
if(avpNames[code].length()>0)
return avpNames[code];
return "Radius-Unknown-"+code;
}
/**
* Translate a AVP name to a AVP code.
* Returns -1 for unknown names.
*/
public static int getAVPCode(String name)
{
for (int i=1;i<=63;i++)
if (avpNames[i].equals(name))
return i;
return -1;
}
/**
* Figure out the type according to the code.
*/
public static int getAVPType(int code)
{
switch(code)
{
// TEXT AVP's
case AVP_FILTER_ID:
case AVP_REPLY_MESSAGE:
case AVP_FRAMED_ROUTE:
case AVP_ACCT_SESSION_ID:
return AVP_TYPE_TEXT;
// STRING AVP's
case AVP_USER_NAME:
case AVP_USER_PASSWORD:
case AVP_CALLBACK_NUMBER:
case AVP_CALLBACK_ID:
case AVP_STATE:
case AVP_CLASS:
case AVP_CALLED_STATION_ID:
case AVP_CALLING_STATION_ID:
case AVP_NAS_IDENTIFIER:
case AVP_PROXY_STATE:
case AVP_LOGIN_LAT_SERVICE:
case AVP_LOGIN_LAT_NODE:
case AVP_LOGIN_LAT_GROUP:
case AVP_FRAMED_APPLETALK_ZONE:
case AVP_ACCT_MULTI_SESSION_ID:
case AVP_CHAP_CHALLENGE:
case AVP_LOGIN_LAT_PORT:
return AVP_TYPE_STRING;
// ADDRESS AVP's
case AVP_NAS_IP_ADDRESS:
case AVP_FRAMED_IP_ADDRESS:
case AVP_FRAMED_IP_NETMASK:
case AVP_LOGIN_IP_HOST:
return AVP_TYPE_ADDRESS;
// INTEGER AVP's
case AVP_NAS_PORT:
case AVP_SERVICE_TYPE:
case AVP_FRAMED_PROTOCOL:
case AVP_FRAMED_ROUTING:
case AVP_FRAMED_MTU:
case AVP_FRAMED_COMPRESSION:
case AVP_LOGIN_SERVICE:
case AVP_LOGIN_TCP_PORT:
case AVP_FRAMED_IPX_NETWORK:
case AVP_SESSION_TIMEOUT:
case AVP_IDLE_TIMEOUT:
case AVP_TERMINATION_ACTION:
case AVP_FRAMED_APPLETALK_LINK:
case AVP_FRAMED_APPLETALK_NETWORK:
case AVP_ACCT_STATUS_TYPE:
case AVP_ACCT_DELAY_TIME:
case AVP_ACCT_INPUT_OCTETS:
case AVP_ACCT_OUTPUT_OCTETS:
case AVP_ACCT_AUTHENTIC:
case AVP_ACCT_SESSION_TIME:
case AVP_ACCT_INPUT_PACKETS:
case AVP_ACCT_OUTPUT_PACKETS:
case AVP_ACCT_TERMINATE_CAUSE:
case AVP_ACCT_LINK_COUNT:
case AVP_NAS_PORT_TYPE:
case AVP_PORT_LIMIT:
return AVP_TYPE_INTEGER;
// DATA AVP's (other formats)
case AVP_CHAP_PASSWORD:
case AVP_VENDOR_SPECIFIC:
return AVP_TYPE_DATA;
default:
return AVP_TYPE_DATA;
}
}
/**
* Check if the message has sufficient attributes
* for it's type.
*/
private boolean UNIMPLEMENTED_isValidRadiusMessage(Message msg)
{
switch(msg.getMessageType())
{
case Message.AUTHENTICATION_REQUEST:
case Message.AUTHENTICATION_ACCEPT:
case Message.AUTHENTICATION_REJECT:
case Message.AUTHENTICATION_CHALLENGE:
case Message.AUTHENTICATION_REDO:
case Message.AUTHORISATION_REQUEST:
case Message.AUTHORISATION_ACCEPT:
case Message.AUTHORISATION_REJECT:
case Message.AUTHORISATION_CHALLENGE:
case Message.AUTHORISATION_REDO:
case Message.ACCOUNTING_REQUEST:
case Message.ACCOUNTING_REPLY:
case Message.ACCOUNTING_POLL:
case Message.MESSAGES_REQUEST:
case Message.MESSAGES_READY:
case Message.MESSAGE_REJECT:
default:
return false;
}
}
public byte[] encodeMessage(Message msg)
{
// get message attributes
Hashtable atts=msg.getAttributes();
// get message attributes
int messageCode=getRadiusMessageCode(msg.getMessageType());
if(messageCode<0)
return null;
Integer messageIDInteger=(Integer)atts.get("message_id");
int messageID;
if(messageIDInteger==null)
messageID=nextIdentifier();
else
messageID=messageIDInteger.intValue();
byte[] authenticator=(byte[])atts.get("radius_authenticator");
if (authenticator==null)
authenticator=newAuthenticator();
// make the buffer
RadiusMessageBuffer buf=new RadiusMessageBuffer(
messageCode,messageID,authenticator);
// store all the other attributes
Enumeration e=atts.keys();
while(e.hasMoreElements())
{
String key=(String)e.nextElement();
Object val=atts.get(key);
if( key.equals("message_id") ||
key.equals("radius_authenticator") )
{
// these attributes are allready stored
}
else
{
int avpCode=getAVPCode(key);
if(avpCode<0)
{
// no radius attribute, store as vendor-spec
buf.addSpecialAVP(key,val.toString());
}
else
{
switch(getAVPType(avpCode))
{
case AVP_TYPE_TEXT:
buf.addTextAVP(avpCode,(String)val);
break;
case AVP_TYPE_STRING:
buf.addStringAVP(avpCode,(String)val);
break;
case AVP_TYPE_ADDRESS:
buf.addAddressAVP(avpCode,(String)val);
break;
case AVP_TYPE_INTEGER:
if(val instanceof Integer)
buf.addIntegerAVP(avpCode,((Integer)val).intValue());
else
buf.addIntegerAVP(avpCode,Integer.parseInt((String)val));
break;
case AVP_TYPE_DATA:
buf.addDataAVP(avpCode,(byte[])val);
break;
}
}
}
}
return buf.getBytes();
}
public Message decodeMessage(byte[] data,Identifier from)
{
// TODO:add checks from other decodeMessage to check for
// valid format
// make a buffer out of the data
RadiusMessageBuffer mbuf=new RadiusMessageBuffer(data);
// make a message out of it
Hashtable att=new Hashtable();
att.put("message_id",new Integer(mbuf.getMessageId()));
att.put("sid","RADIUS:"+from+":someport:"+mbuf.getMessageId());
att.put("radius_authenticator",mbuf.getMessageAuthenticator());
mbuf.setFirstAVP();
while(mbuf.hasMoreAVPs())
{
int code=mbuf.getAVPCode();
if(code==AVP_VENDOR_SPECIFIC)
{
if(mbuf.isSpecialAVP())
{
// special attribute
String avpn=mbuf.getAVPSpecialName();
String avpo=mbuf.getAVPSpecialValue();
att.put(avpn,avpo);
}
else
{
// "normal" vendor attribute
// TODO: implement
}
}
else
{
// "normal" attribute
String avpn=getAVPName(code);
Object avpo=null;
switch(getAVPType(code))
{
case AVP_TYPE_TEXT:
avpo=mbuf.getAVPText();
break;
case AVP_TYPE_STRING:
avpo=mbuf.getAVPString();
break;
case AVP_TYPE_ADDRESS:
avpo=mbuf.getAVPAddress();
break;
case AVP_TYPE_INTEGER:
avpo=new Integer(mbuf.getAVPInteger());
break;
case AVP_TYPE_DATA:
avpo=mbuf.getAVPData();
break;
}
att.put(avpn,avpo);
}
mbuf.nextAVP();
}
return new Message(getMessageType(mbuf.getMessageCode()),att);
}
/**
* Read a RADIUS message from the stream.
* The algorithm used to check wether it is realy a
* RADIUS message is <b>NOT</b> very good, since RADIUS
* has no real identifying header.
* I suggest you
* use this RecordFormat last in the list to test
* for valid recordformats.
*/
public Message decodeMessage(InputStream in,Identifier from)
throws IOException
{
in.mark(30); // mark for reset()
// check for valid codes
int type=in.read();
switch(type)
{
case MSG_ACCESS_REQUEST:
case MSG_ACCESS_ACCEPT:
case MSG_ACCESS_REJECT:
case MSG_ACCOUNTING_REQUEST:
case MSG_ACCOUNTING_RESPONSE:
case MSG_ACCESS_CHALLENGE:
break; // ok
default:
// other types of requests are not supported and
// deemed invalid RADIUS messages
in.reset();
return null;
}
// skip identifyer
int ident=in.read();
// check if length is senseble
int len=in.read();
len=(len<<8)|in.read();
if(len<20||len>1024)
{
in.reset();
return null;
}
// read the whole stuff
in.reset();
byte[] buf=new byte[len];
in.read(buf);
// parse this
return decodeMessage(buf,from);
}
private int nextIdentifier()
{
nextIdentifier=(nextIdentifier+1)&0xff;
return nextIdentifier;
}
/**
* Generates a new random authenticator.
*/
public static byte[] newAuthenticator()
{
byte[] res=new byte[16];
(new Random()).nextBytes(res);
return res;
}
}
|