Friday, October 3, 2014

Open AM as Radius Server

Update: Wondering how this turned out? See OpenAM Answers its 1st Radius Request and following posts for more detail.


It has been a long productive summer with little time to post. So here comes. I've been delving further into Open AM during that time and need to capture progress for when others come across my contributions and wonder what on earth I was doing with a certain bit of code. Most likely that will be me in a year or two. :-)

One requirement of my team is to implement multi-factor authentication for our enterprise web access infrastructure. And since we were implementing it there, we were asked to implement it for VPN too using the same multi-factor mechanisms. I know, doesn't make sense. They are two completely different technologies. Heavy desktop client versus browser applications. HTTP versus...not sure what at the time. But we dug in to see what was possible.

Open AM Backing our VPN?

Over a couple of weeks there appeared several reasons why this could be possible. First, Open AM supplies a number of Authentication Modules that can be leveraged to protect resources including SMS One Time Passcode (OTP) and Google Authenticator. Just what we wanted to start out with or our second factor. Furthermore, their interface is published for adding your own implementations and contributing them back if desired which has results in quite a number of modules being available in baseline. And these can be "chained" together forcing users to provide other factors after the initial username and password are submitted implementing the multi-factor feature.

Second, using Open AM's rest endpoint to authenticate (see the developer guide) a given authentication module with which a user is interacting during authentication exposes the requirements of the user in JSON along with a session token keeping track of where they are in the authentication process, the labels that should be exposed for a given piece of data expected from a user, and delineated locations within the JSON to inject the user's answers. This structure is then submitted back to the server potentially followed by another such response for the next module in the chain. Not that I'll be using the rest endpoint although it could, but more importantly, Open AM must clearly provide that same information if our solution was going to be running within Open AM and we'd need that information when soliciting additional values from the user when accessing VPN.

Third, it turns out that the VPN software that we use is a desktop application that speaks to a Network Access Server or NAS. The NAS can use two mechanisms on the back end to authentication users for VPN: an LDAP directory or a Radius server. And one of those Open AM authentication modules speaks the Radius protocol allowing Open AM to act as a Radius client and authenticate against a Radius server.

To sum up then, Open AM already had support for speaking the Radius protocol. Open AM authentication can be a multi-step processes by using an authentication chain with a distinct authentication module handling each step with clear information required from the user at each step and allowing the user to move from one step to the next only after they have provided the information required for that step. And VPN clients can speak to Radius servers.

The only remaining question was this: can a VPN interaction be a multi-step process such as submitting username and password followed by a prompt for more information, then more, and so on until an authentication chain was completed and the user successfully authenticated with all authentication modules?

Radius Protocol

At its core, the Radius protocol is quite simple. The RFC is just over 70 pages. There a numerous related RFCs but most appear to be simply adding and defining additional fields that are added to these packets to enable other overlying protocols to use Radius to achieve their authentication. The protocol itself consists of one client initiated packet, an ACCESS_REQUEST, and three potential server responses; ACCESS_ACCEPT, ACCESS_REJECT, and ACCESS_CHALLENGE respectfully for "successfully authenticated", "failed authentication", and "hey, I need more information".  That last one caught my attention.

I dug into Open AM's openam-auth-radius module. It had java objects for each packet type but only supported marshaling from the ACCESS_REQUEST object to the on-the-wire bytes and from the on-the-wire response bytes into the other three objects. I first had to make that marshaling bidirectional which took a day. Then, I added a RadiusListener that opened a UDP server socket to receive requests followed by some state flow classes to response in different ways to see how the VPN client reacted.

It was beautiful! That ACCESS_CHALLENGE response caused the VPN client to present a screen with an input text field labeled "Answer" and a message area presenting the content of a Reply-Message field included in the response and a Continue button. When pressed, the client then issues another ACCESS_REQUEST to the server as shown in the image in the next section. And in that next request the same user name is again submitted and the entered answer comes through in the User-Password field which was odd but works.

Further, the ACCESS_CHALLENGE response packet type also supports the State field which can be filled with state information of my choice that gets passed back "as-is" in the following ACCESS_REQUEST message enabling my prototype Radius Server to be stateless with all state being preserved via the state field in the client between requests. And there appeared to be no limit to the number of challenge/response cycles. The cycle only terminated when an ACCESS_ACCEPT or ACCESS_REJECT was returned.

That answered the question of supporting a multi-step flow. It was a resounding YES!

Multi-Factor Proof Of Concept

The next step was to give this multi-factor authentication a try. But one thing became critically clear pretty early. Provisioning MUST be discussed and planned. By provisioning I mean what multi-factor channels are going to be supported, how are users going to elect which one they can or want to use, and how will the system know for a given user which channel has been selected by them?

To drive that point home I built a POC that intentionally supported four different mechanisms. (Actually I was only going for three and I learned of a fourth, synthesized voice call, while on vacation and dropped it in within a few hours one evening.) The four mechanisms are SMS OTP, voice delivered OTP, Google authenticator, and what I call a Smart Phone Responder which I'll explain in a following post. I've yet to get to the Google Authenticator due to project pressures. And I may not ever get there since my next step is to move this into Open AM and use their authentication modules directly. At time of writing this post the POC is currently a standalone java process.

Having more than one channel demands that provisioning be considered. To push the limits of VPN I decided to implement a flow allowing the users to provision their channel "Just-In-Time". By this I mean if they didn't already have a selected channel they would be presented with an ACCESS_CHALLENGE message to that effect and offering selection of the choices available. That actually worked quite well. At least it did from a developer's point of new. You business analysts and technical writers will no doubt cringe at my content. Here is what a user sees when they submit their username and password and no previously selected channel:


Yeah, that last line being below the view screen bugs me. But there was no way to that I could discover to tell the client to present more space. And the vendor wasn't very helpful on that front so I had to live with it. No worries. Its only a POC. Right?

By entering the corresponding letter, that user has then elected to use that channel. The POC then persists that election in a file. When they try again, it knows what their preference is and they go right to the next factor for authentication. Time to implement some actual channels. I'll cover the different channels that were implemented in the next few posts.

Until then, Enjoy.