Authentication

Interface

Most of the programmer's authentication code resides in a class that extends AccessImpl.   The AuthInfo class object is passed to the authenticate() method the programmer defines.  The AuthInfo class has a number of methods to facilitate authentication, logging and other functions.  AuthInfo extends the PacketInfo class.  PacketInfo has methods common to AuthInfo, ProxyInfo, AccountingInfo, ExtendedInfo, and other classes. the common methods listed below are not exhaustive but cover most of the methods useful in Authentication.  The section 'Authentication specific methods' covers the methods defined in the AuthInfo class.

Common Methods

getUserName()

Get the User-Name attribute as a string.  This is the raw name as it arrived. It may contain realm information as proxy information.  Proxy information customarily appears after an '@' sign as in vivian@radius3.com. The proxy information is called the realm.  If there is no User-Name present a null is returned.

getName()

Get only the name portion of the User-Name attribute.  This will discard any realm information.  GetName() would return vivian from the above example. If there is no User-Name attribute present an empty string is returned.

getRealm()

Get the realm information from the User-Name attribute. The will remove the name and only the realm information will be returned. GetRealm() would return radius3.com from the above example.  If there is no realm information an empty string is returned.

getPrefix()

Get the prefix information. This is usually some routing information that precedes the name and often separated by either a forward or reverse slash (or two). It might look something like this texas/vivian@radius3.com.

getDecodedUserPassword()

PAP passwords are never sent in clear text.  They are encoded using the RADIUS packet authenticator and the secret shared between the client and server. This method returns the decoded User-Password attribute as a byte array.  The encoding pads the end of the password with zero valued octets.  Use the trim() method below to remove these empty bytes. This is only suitable for PAP authentication. All other authentication methods use a one way encoding that prevents ever easily determining the original password.

trim(byte[] password)

Trim zeroed octets from the end of a decoded password.  If the password being compared is a binary password you may not want to use this method if the password ends in empty octets.  In this case it's better to encode your local binary password and then compare passwords using cmp() found below.

cmp(byte[] passwordA, byte[] passwordB)

Compare two encoded or plain text passwords. You may be encoding a local password using the encode() method (see below) and need to compare the resulting password against the packet password. This method performs a byte by byte comparison and return true if the comparison succeeded.

encode(byte[] password)

Encode a local plain text or plain binary password with the same encoding used to create the User-Password attribute.  The result should be identical to the User-Password.  A cmp() of the two passwords will return true. This is only suitable for PAP authentication.

getRequestAttributeList() / getRequestAttributes()

Either of these methods will return a list of the attributes in the Access-Request packet. The former returns an AttributeList class and the latter returns an array of attributes.

setResponseAttributes(Attribute[] aList) / setResponseAttributes(AttributeList aList)

Once an authentication has been verified the client may require some configuration information.  Build a list of response attributes and apply them to the response packet using this method.

Authentication specific methods

isAccessRequest()

Ask if this is an Access-Request packet.  Access-Requests are looking for authentication. These packets may be responded to in several ways: by an Access-Accept if the authentication succeeded, challenged by returning an Access-Challenge packet, rejected using an AccessRejectException, or silently dropped by issuing an AccessDropException.

Access-Challenge packets will issue an Access-Request packet as a response.  Use the isAccessChallengeResponse() to tell the difference between an Access-Request and a challenge response. The challenge response differs from an Access-Request by the presence of the State attribute.  More about the State attribute can be found below.

isAccessChallengeResponse()

An Access-Request looks exactly like a response to an Access-Challenge packet.   The vital difference is the presence of the State attribute.  This attribute is used by the authentication method match responses to requests.  The value of the State attribute is regarded as opaque by the client and the client is obliged to return this attribute untouched. See setAccessChallenge() for more information on the State attribute.

setAccessAccept()

Set the response packet type as Access-Accept if the Access-Request passed the validation tests.  This is also sent as a successful conclusion to an Access-Request from an access challenge response.

setAccessChallenge()

Set this packet type to and Access-Challenge packet.  It MUST include a State attribute to be effective. If the State attribute is not sent or the client does not return the State attribute it will be impossible to match the last challenge to this response since otherwise it will appear to be a fresh Access-Request.

State attribute contents:  It should be a temporally global object such that it will not appear again nor can any other request generate the same State.  A time stamp or other constantly increasing token would suffice.  Something that can match the initial request to a later response is also required.  Since the RADIUS protocol is stateless it can't tell one request from another.  The State attribute is your only link to the request and may contain something like a database record Id or other token  used to match the request to the response.

The State attribute should probably be encoded to keep the actual state information secret.

Multiple State attributes may be used, one a time stamp, the other the identifier.

cmpCHAP()

CHAP authentications can use this method to determine if the plain text password matches the CHAP information (CHAP-Password / CHAP-Challenge attributes).  The method returns true if the calculated MD5 digest matches the CHAP-Password data. For CHAP to be used you must have access to plain text passwords. A CHAP request to a system that cannot access plain text passwords must return an Access-Reject packet.

The documentation describing CHAP and it's very name (CHAP Challenge) suggest that this is a two part authentication. It is in fact only a simple Access-Request and Access-Accept/Reject transaction. The 'extra' transaction is performed by the client.

cmpMSCHAP() & cmpMSCHAPV2()

MS-CHAP and MS-CHAP V2 are supported and are similar to the cmpCHAP() method above. MS-CHAP V2 provide mutual authentication - the server authenticates the client and the client uses server's response is used to authenticate the server.

Microsoft Point-to-Point Encryption Protocol (MPPE) keys are automatically provided.

After authentication has succeed the server may still send a failure packet to the client using setMSCHAPErrorCode() to indicate an error found later - perhaps the account has been locked out.

getEAP()

EAP-Message attributes are preprocessed by the EAPInfo object returned from getEAP(). The EAPInfo and EAPPacket classes ease working with EAP transactions.

getState()

Retrieve an AttributeList of State attributes.  You may also get the State attribute(s) from the getRequestAttributeList();  See getStateObject() below for another way to manage State attribute inforamtion.

Class AccessRejectException

Throw this exception if an Access-Reject packet is to be sent as a response. The AccessRejectException.setAttributes() may be used if attributes are to be returned. The message will be sent to the log if you're logging failures.

Class AccessDropException

Throw this exception if no response is to be made.  No response packet will be sent and the client will eventually time out.  This is desirable if the packet contains suspicious information or there is no need to reveal the failure of an authentication attempt.  For example the RADIUS server will drop packets if they arrive from an unknown NAS.  Part of the security of RADIUS is its inscrutability.

Persistent State Attribute Objects

Objects can be associated with the State Attribute used in Access-Challenge packets.   This is handled automatically by the following methods from the AuthInfo class.

Why would you need them? Common authentication methods usually are done in a single pair of transactions using one Access-Request and an Access-Accept/Reject. The RADIUS client may require a number of transactions to perform an authentication. Since it sees these as sequential steps it's able to keep vital information around easily. The server, on the other hand, cannot tell one Access-Request from another. Whether it's a new authentication request or a continuation the server cannot tell the difference. This is why the RADIUS protocol is called stateless - it has no memory of any previous transactions.

To overcome this problem the State attribute is attached to all Access-Challenge responses from the server. This attribute MUST always be returned by the client. The State attribute holds information that allows the server to 'recall' the previous step(s) in the conversation.

For example the LEAP authentication method has six steps to follow. To securely keep track of this the server offers a convenient set of methods to associate Java Objects with the State attribute. The State attribute will hold one or more integers that are associated with one or more Java Objects. These objects are available for recovery when the client sends the next Access-Request.

While these object are 'persistent' in one sense they have a limited life in the server and will be deleted if the response is delayed significantly. For a simple transaction it might be enough to recall the original User-Name and the last challenge information sent. An entire authentication class object may also be attached to the State attribute. The number of objects is limited to 50 (constrained by the maximum length of the State attribute). Usually one is sufficient.

setStateObject() / getStateObject() / removeStateObject()

These methods allow a State attribute to be associated with persistent data.  The related RADIUSServer method is RADIUSServer.setStateTTL() which sets the lifetime of the persistent data.  The setStateObject(Object obj) associates an integer key with the object.

When setStateObject() is called the object is stored by the server and a State attribute with the key is placed in the response attribute list.  When the packet with the same State attribute value is received from a client that data is made available to the authenticate() method in AuthImpl.  For example, the EAP protocol can track any challenges, next-state information and the request identifier for each part of the transaction.  Similarly any type of Access-Challenge and response transaction can 'remember' information between transactions.

An object may be refreshed by removing it and adding it again.

Removing the state object using removeStateObject() helps lower memory usage.  If not explicitly removed it will be removed after the data is stale.  The default lifetime is thirty seconds.  This can be changed using the RADIUSServer.setStateTTL() method.

Other authentication methods

LEAP Authentication

LEAP is an EAP authentication method designed by Cisco. It's considered quite weak and it's use is being discouraged. LEAP is based on the CHAP method which is cryptographically weak with a number of well known attacks and is considered weak.. This means that LEAP is prone to similar attacks and is also weak.

Digest Authentication

The package radserver3.auth contains the Digest authentication methods for both server and client. Digest authentication uses the draft-sterman-aaa-sip-00.txt version suitable for HTTP and SIP authentication. The server receives the Digest-Attributes and decodes the contents into a set of Java Properties. Your code should verify the correctness of the values. The DigestAuthentication.process() actually performs the MD5 authentication and the result is true then the authentication succeeded.

The client authentication is just a matter of setting values in the ClientDigest class and calling the ClientDigest.authenticate() method. There are methods for setAlgorithm(), setMethod(), setNonce() and others.

Extended Access Protocol (EAP)

EAP is also handled by the authentication class (AuthInfo) using the getEAP() method in the AuthInfo class.  See EAP for more specific information.