Search code examples
javawindows-server-2012radiustinyradius

Radius Protocol - How to correctly set the status in the second request?


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 and state attributes messages 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?


Solution

  • There are a few issues in how I was sending the second request to the 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