The RADIUS Client is included both for testing the server and making requests of other servers.
The client included with the server uses the same methods to construct and examining attribute lists. Extended packets beyond the usual Access-Request and Accounting-Request may also be sent.
There are two basic constructors, one with a default timeout and one you may set for yourself. It's possible, especially if passing through several proxy servers that it may take several seconds for a request to make the round trip. Usually it's much faster, especially with a local server. The client does not perform retries. Those must be performed in your code.
The EAPMD5 client is a complete EAP MD5 authentication implementation.
The EAPClient class holds base methods for handling EAP transactions and can be extended for other EAP authentiation methods.
The EAPClient's constructor sets up the basic information to contact the server and provide standard attributes to accompany the EAP transaction - User-Name, NAS-IP-Address / NAS-Identifier, Message-Authenticator, and all EAP-Message attributes generated.
Methods:
addAttributes()
Add additional attributes to the RADIUS client EAP response. These would be hints to the server about the kind of service requested - it must not include any of the attributes listed above in the EAPClient constructor description.
createPacketId()
Create a randomly generated intial packet identifier. This is used on the first packet that's sent to the RADIUS server.
debug(boolean enable)
Enable or disable debugging - this enables the debugging output of the RADIUS client.
getAttributes()
After a packet is sent to the server any response attributes can be retrieved using this method.
getEAPPacket()
Retrieve the EAPPacket object containing the EAP data from the server. The code, type, and data fields are available using the EAPPacket class.
reset(byte[] name)
Reset the EAPClient with a new identity (User-Name attribute). This resets the RADIUS client and intializes the EAPClient with a new name for the Identity packet.
send(AttributeList requestList)
Send the completed EAP packet through the RADIUS client. The requestList is created by an EAPClient method like createIdentityResponse() or createMD5Response().
The EAPMD5Client is an example of a simple EAP transaction. It first sends the Identity response to identify the entity. The server responds with an EAP MD5 challenge packet. The password encoded using the challenge and packet id is sent to the server. The server responds with success or failure.
The EAPMD5Client constructor is the same as the EAPClient constructor.
authenticate(byte[] password)
This method is used to authenticate using the name passed in the constructor. If you're creating an instance of the EAPMD5Client for each authentication this is the method to use. This is safe to use for retries using the same name.
authenticate(byte[] name, byte[] password)
If the class instance is to be used more than once for different entities use this method for subsequent authentications.
setAttributes(AttributeList local)
Add additional attributes to the RADIUS client EAP response. These would be hints to the server about the kind of service requested - it must not include any of the attributes listed above in the EAPClient constructor description.
The RADIUS client can be used to perform logins using the class com.theorem.radius3.login.RADIUSLogin as an implementation of the javax.security.auth.spi.LoginModule. This requires JDK 1.4+ or JAAS to compile and run. There is an example of use in the directory com/theorem/radius3/examples/login. The security configuration file is found in com/theorem/radius3/login/RADIUSLogin.config and LoginContext is RADIUSLogin. The options include configuring the client as well as including any request attributes in the Access-Request packet. The RADIUSLogin class requires the support of a CallBackHandler. There is an example of a CallbackHandler in the directory com/theorem/radserver3/examples/login.
There are a number of handlers for this class. A number are similarly to the defined callbacks:
The remainder facilitate fine grain control over the RADIUS session:
Unlike many CallbackHandler implementations the RADIUS CallBackHandler will be called more than once. All callbacks from the RADIUSLogin module use the same method to determine if the callback is active - isReady(). It this returns true the callback is active and the data for the specific callback may be read or written. This mechanism allows the handler to be re-entrant.
Some Callback entries in the call back table will be null and should be ignored. These handlers (RADIUSTextInputCallback, RADIUSTextOutputCallback, and RADIUSConfirmationCallback) will not necessarily have values until they're actually needed.
Here's an example of an AccessAccept callback and it's check for readiness.
public void handle(Callback[] callbacks) throws IOException,
UnsupportedCallbackExceptionThe AccessChallenge class provides a method for introducing attributes suitable for
a challenge response. The attributes provided by the RADIUSLogin.config are included in
the Access-Challenge packet but not those provided by the SetRequestAttributes handler.
...
} else if (cb instanceof AccessAccept){
// Just pick up the response attributes object for now.
// It'll be populated after authentication.
AccessAccept accessAccept = ((AccessAccept) cb);
if (! accessAccept.isReady())
continue;
// Save the latest attributes.
lastAttributes = accessAccept.getResponseAttributes();
}
The ClientException handler is called whenever a client related exception is called.With two exceptions this is merely informative as the a LoginException will be thrown anyway. If the ClientException's .getException() method returns an instance of ClientReceiveException or ClientSendException the CallbackHandler may request that the packet be resent. After some number of retries (maybe 3 to 10) the ClientException handler should stop attempting retries. Some ClientReceiveException's may be spurious if the server has received the packet but hasn't yet responded. In this case the server may be dropping duplicate packets.
The file RADIUSLogin.config is the security configuration for the RADIUSLogin class. It can either be used on the command line like this: -Djava.security.auth.login.config==<path to the file>/login/RADIUSLogin.config
Or it can be assigned as a system property in a program.
For an example of a CallbackHandler implementation look at com/theorem/radius3/examples/login/RADIUSHandler.java.
If your system is unable to process the Login Module because the JDK/JVM is prior to
version 1.4 you may remove it from the jar file using any ZIP utility. If you are
unable to compile it remove the contents of the login directory.
First, a word about sending packets and sending reties of the same packet. According to the current RFC a fresh request must use either a different UDP port number or a different message identifier in the packet. A retry of the same packet must keep the same port and message identifier. This greatly assists in detecting duplicate packets arriving at the server.
A fresh request should either look like:
int port = RADIUSClient.AUTH_PORT;
String secret = "bananas";
int result;
RADIUSClient r = new RADIUSClient("192.168.1.1", port, secret);
// Set some attributes.
AttributeList aList = new AttributeList();
alist.addAttribute(Attribute.NAS_Identifier, clientID);
...
result = r.authenticate(aList);
// Send another authentication.
// Set up new attributes.
alist.clearAttributes();
// Reset the client connection.
r.reset();
result = r.authenticate(aList);
In the above example the r.reset() was called before the next authentication. If it is not called message identifier from the previous packet will be reused possibly resulting in the server detecting a duplicate packet.
Retries should look like this:
RADIUSClient r = new RADIUSClient("192.168.1.1", port, secret);
// Set some attributes.
AttributeList aList = new AttributeList();
alist.addAttribute(Attribute.NAS_Identifier, clientID);
while (true) try {
result = r.authenticate(aList);
break;
} catch (Exception e) {
; // Do nothing, just retry.
}
Each retry is just a repeat of the previous attempt with no reset(). The first attempt has the unique message identifier.
Two exceptions are thrown by the accounting() and authenticate() methods. Both use IOException as the super class so both can be caught with Exception or IOException. The ClientSendException and ClientReceiveException are for those who want to know which end of the conversation was dropped.
A client socket may be bound to a particular host interface if required. Normally it will bind to any interface handy on a multihomed machine. The bind() method handles this.
The result of an authentication or accounting may be 0 or the type Access_BadPacket. Access_BadPacket has a value of 0 and matches no permitted RADIUS packet type. The Access_BadPacket will be returned for the following reasons:
This can mean that there has been some damage to the packet, the authenticator is incorrect, the server is broken, or enemy action. The method RADIUSClient.getErrorString() will return a descriptive error. RADIUSClient.getError() returns a numerical value for the error.
The maximum size of a packet can be increased (or decreased) from the default value of 4096 bytes (including header and attributes) by using the method setMaximumPacketSize(). Setting this larger than 4096 requires that the server also allow larger packets. The RFC says that packets longer than expected will be truncated.
Attributes are generally kept in a list, the class AttributeList. Individual attribute data may be obtained from the AttributeList class and new attributes added. Both the Attribute and AttributeList have toString() methods that display attribute information. A discussion on attributes is available here.
PAP (encoded password), CHAP (encoded password and a challenge), MSCHAP / MSCHAP2, and EAP-MD5 are supported directly by the client. There are specific methods for generating the attributes necessary for each authentication method. For example createCHAP() creates the CHAP attributes, createMSCHAP() and createMSCHAP2() create attributes necessary for MSCHAP versions 1 and 2. EAP-MD5 has a special class EAPMD5Client() that's similar to the RADIUSClient in methods.