I'm using TinyRadius to authenticate my Java WebServer (as Radius Client) to a Windows Server (as Radius Server).
I successfully sent the user Access-Request to the server and received the passcode back.
final RadiusClient client = new RadiusClient(
new RadiusEndpoint(
new InetSocketAddress(RADIUS_SERVER_ADDRESS, PORT),
SHARED_SECRET
)
);
final AccessRequest request = new AccessRequest(
USERNAME,
USER_PASS
);
request.setAuthProtocol(AccessRequest.AUTH_PAP);
request.addAttribute("NAS-IP-Address", RADIUS_CLIENT_ADDRESS);
RadiusPacket packet = null;
try {
packet = client.authenticate(request);
} catch (final RadiusException | IOException ex) {
System.out.println(ex.getMessage());
ex.printStackTrace();
}
This code is followed by a log which states that the user authentication was approved by the server.
Sep 29, 2017 10:05:32 AM org.tinyradius.util.RadiusClient authenticate
INFO: send Access-Request packet: Access-Request, ID 1
User-Name: mp
NAS-IP-Address: 192.168.0.58
Sep 29, 2017 10:05:33 AM org.tinyradius.util.RadiusClient authenticate
INFO: received packet: Access-Challenge, ID 1
State: 0x7b41324244344539362d453139332d344539392d413134322d4134423536364441443938437d
Reply-Message: Enter PASSCODE
The Event Viewer on my Windows Server, too states that the Access-Request was accepted.
In fact I correctly receive the passcode on my mobile application (I'm using SMS Passcode by Censornet, altought this is irrelevant).
Unfortunately, I could not find any TinyRadius Passcode example on the web, but while browsing for other libraries, I stumbled upon this python library, which states that:
The ChallengeResponse exception has
messages
andstate
attributesmessages
can be displayed to the user to prompt them for their challenge response.state
must be echoed back as a RADIUS attribute.
and
Finally authenticate again using the challenge response from the user in place of the password.
So all I did is to use the same code above. I put PASSCODE instead of USER_PASS and I add the state attribute to my AccessRequest.
final RadiusAttribute stateAttr = new RadiusAttribute(24, STATE.getBytes());
request.addAttribute(stateAttr);
I send the request to the server and I can see this log.
Sep 29, 2017 10:34:04 AM org.tinyradius.util.RadiusClient authenticate
INFO: send Access-Request packet: Access-Request, ID 1
User-Name: mp
NAS-IP-Address: 192.168.0.58
State: 0x307837623431343133353330333433363334333832643433333433343339326433343432343633303264343234343335343532643330343533323337343633383332333333373332333933373764
Sep 29, 2017 10:34:04 AM org.tinyradius.util.RadiusClient authenticate
INFO: received packet: Access-Reject, ID 1
Reply-Message: Session is unknown or has expired
The Windows Event Viewer states:
Event description:
Event type: Authentication request
Result: Failure
Failure reason: Password validation failed
So I read the RFC 2865 Access-Challenge Chapter at Section 4.4, and they state that:
the receipt of a valid Access-Challenge indicates that a new Access-Request SHOULD be sent [...] with the User-Password Attribute replaced by the user's response (encrypted), and including the State Attribute from the Access-Challenge, if any. Only 0 or 1 instances of the State Attribute can be present in an Access-Request.
I'm therefore doing everything right, I guess. May anyone help me here?
There are a few issues in how I was sending the second request to the radius server.
Doing it this way did not work as STATE.getBytes()
returned a differently-encoded String
:
final RadiusAttribute stateAttr = new RadiusAttribute(24, STATE.getBytes()); // BAD
request.addAttribute(stateAttr);
What I had to do was: first store the response into a new Packet
.
final AccessRequest request = new AccessRequest(
USERNAME,
USER_PASS
);
packet = client.authenticate(request);
Then, create a new AccessRequest
for answering the challenge and set the state in the following way.
final AccessRequest challengeResponseRequest = new AccessRequest(
USER,
PASSCODE
);
challengeResponseRequest.addAttribute(
new RadiusAttribute(24, packet.getAttribute(24).getAttributeData()) // GOOD
);
(Note: 24 is the attribute code for STATUS)
By sending the newer AccessRequest
:
client.authenticate(challengeResponseRequest);
the log confirms the authentication was successful:
Sep 29, 2017 2:05:13 PM org.tinyradius.util.RadiusClient authenticate
INFO: received packet: Access-Accept, ID 2
Class: 0x8f8007ad0000013700010200c0a800050000000024db5d173578383201d3379907bd2a500000000000000098