Summary Status

  • Our original plan to use simple LDAP bind authentication against the AD had to be scrapped. In order to do that, we need the password presented by the user. On the VPN, we use CHAP/MSCHAP/MSCHAPv2, which means that we don't have access to the password presented by the user. Instead, we have only the user's response to an offered challenge and the challenge itself.
  • Our next thought was to retrieve the NTPasswordHash for a user from the AD and use that in computing the MSCHAPv2 response. We scrapped that idea after Marc indicated that it would be difficult (and perhaps not possible) to get the NTPasswordHash attribute from the directory, and it would create some security concerns for him.
  • Andrew stumbled onto a HOW-TO for FreeRADIUS that described using some utility called ntlm_auth to do MSCHAPv2 using Active Directory. After some digging, we realized that ntlm_auth is actually part of Samba, the UNIX toolkit for doing AD and CIFS. With a little more digging, we realized that we could join a UNIX machine to the Active Directory using Samba and then delegate the MSCHAPv2 authentication requests to ntlm_auth. At this point we have successfully joined our FreeBSD-based test system to the AD on the CNS domain and we've successfully interacted with ntlm_auth to simulate an MSCHAPv2 authentication.
  • We're nearing the end of development on a replacement system for our aging authentication, authorization, and accounting (AAA) server that I developed back in 1996-1997. We're now developing a service interface around the ntlm_auth utility so that it can be used as part of the new solution.
  • Meanwhile, Andrew has adapted our existing RADIUS server configuration so that it can delegate MSCHAPv2 requests to a server running Microsoft's IAS. This will give us an interim solution while we finish development on our new AAA server.

Background

You'll recall that our basic plan was to identify that a user wanted to authenticate against the Hokies domain by looking for HOKIES or @*.w2k.vt.edu in the user name; e.g. if I specified my VPN username as HOKIES\ceharris or ceharris@cns.w2k.vt.edu, the VPN authentication system would recognize either of these as indicating authentication against Hokies. Otherwise, the authentication request would be handled in the usual way, against the existing authentication database.

Our first thought was to use and LDAP bind against Active Directory; i.e. we'd search the AD for the user to get a DN for the user's entry in the directory, and then we'd attempt to bind to the directory using simple LDAP authentication using the DN and the password specified by the user. That'd work great for a VPN authentication protocol such as PAP (Password Authentication Protocol) where we have the password specified by the user. But PAP passes the user's credentials between the client host and the VPN server in cleartext, so (obviously) that's out. In fact the VPN is configured to refuse to authenticate with PAP because of the security issues with it.

The VPN supports CHAP, MSCHAP, and MSCHAPv2. These are all challenge-response protocols. What the authentication system receives from the VPN server for these protocols is the challenge that was offered to the user and the user's response to it. The authentication system uses the challenge in the request along with the stored secret for the user to derive the expected response and then compares it to the received response to validate the authentication.

With CHAP, there's no choice but to keep the user's secret in some reversibly encrypted form so that it can be used to derive the expected response to a particular challenge. With MSCHAP/MSCHAPv2, however, the response is derived not from the user's cleartext secret. Instead, MSCHAP/MSCHAPv2 uses the same irreversibly encrypted form of the user's password that is used when the password is stored in Active Directory. MSCHAPv2 uses the so-called NTPasswordHash; the older MSCHAP computes responses using both the LMPasswordHash and the NTPasswordHash.

In our existing use of MSCHAP/MSCHAPv2 on the VPN, we compute the NTPasswordHash using the user's stored CHAP secret and then use the hash to compute the MSCHAP or MSCHAPv2 response. We zero out
the LMPasswordHash in the response to that it can never be used.

Our second thought: Instead of computing the NTPasswordHash from the user's stored secret, we'd retrieve the user's NTPasswordHash from the AD and use the retrieved hash to compute the MSCHAP or MSCHAPv2 response. Obviously, this depends on being able to retrieve the NTPasswordHash from the AD in the first place. Marc indicated that this would pose some difficulties and would create some security concerns.

At this point, Andrew discovered a HOW-TO for FreeRADIUS describing the use of ntlm_auth to perform MSCHAPv2 authentication. He showed it to me and after some digging, we realized that ntlm_auth was a Samba component. I did some digging into Samba documentation to figure out how it worked. Samba allows a UNIX machine to partipate in an Active Directory domain. Once the host is joined to the domain, Samba's winbind daemon can be used to perform a variety of different authentication/authorization operations against the AD. The ntlm_auth utility simply interacts with winbind over a UNIX-domain socket to present a simpler command-line and/or interactive interface for it. With a little bit of trial and error, Andrew and Jason Christian managed to get our test UNIX system (running FreeBSD) to join the CNS domain. At that point, Andrew did some testing with ntlm_auth at the command line and confirmed that it would work for us.

Aradia and ntlm_auth

We've been developing a new AAA server for the past several months, under a project named Aradia. The new server is Java based, and makes extensive use of dependency injection (using the Spring Framework) in order to achieve a very high level of modularity. One of the key concepts in Aradia is a complete separation of protocol-dependent concerns (e.g. the specific mechanics of the RADIUS protocol) from protocol-independent concerns (e.g. which directory/service will be used to authenticate a particular user).

In Aradia's protocol-independent layer, we can easily wire in new authentication providers to support new modes of authentication (e.g. token-based authentication). We can also wire in multiple providers of the same authentication mode (e.g. MSCHAPv2) using different underlying data sources or third-party providers. The interface implemented by an authentication provider in Aradia has a method that allows the provider to examine a request and determine whether or not it can support the desired mode of the authentication and attributes associated with it.

public interface AuthenticationProvider {

  boolean supports(AuthenticationRequest request);
 
  void authenticate(AuthenticationRequest request, AuthenticationResponse response) throws Exception;

}

In the protocol-independent processing of an authentication request, the request is offered (via the supports) method, to each configured AuthenticationProvider. The first provider that claims to support the request is called back on its authenticate method to perform the authentication itself.

In order to support an MSCHAPv2 solution using ntlm_auth, we will first create an implementation of AuthenticationProvider. This provider will implement supports so that it returns true if both of the following conditions are met:

  1. request is an instance of ChapAuthenticationRequest with MSCHAPv2 specified as the algorithm
  2. the request context attribute userName specifies a username that contains a Windows domain specification. The provider will be configured with the specific Windows domain criteria to match on.

In order to facilitate the interaction with ntlm_auth , the provider will be wired with a reference to
an NtlmAuthService to which it will delegate the actual request for MSCHAPv2 authentication.

public interface NtlmAuthService extends Service {

  void authenticate(String user, byte[] challenge, byte[] response) throws Exception;

}

The implementation of NtlmAuthService will need to implement the start and stop methods of the Service interface. These methods allow the Aradia ServiceManager to schedule startup and shutdown. In start, the NtlmAuthService implementation will obtain a Runtime reference and use it to create a Process that executes ntlm_auth. The start method will also obtain and store the references to the InputStream and OutputStream objects that represent the stdout and stdin of the ntlm_auth processes. The stop method will close the streams and destroy the process.

In the authenticate method, the NtlmAuthService implementation will engage in the line-oriented protocol specified by ntlm_auth. It will throw NtlmAuthServiceException if there is some error in the interaction with ntlm_auth. It will throw a subclass of AuthenticationException (e.g. InvalidCredentialsException) if ntlm_auth indicates that the authentication has failed. Otherwise, it will simply return to indicate a successful authentication. The reason for the void return is inherent to the filter-chain based processing used by Aradia.

Multiple threads will be requesting MSCHAPv2 authentication concurrently. Since the interaction with ntlm_auth is inherently serialized, simply declaring the authenticate method as synchronized should suffice. If this creates a significant bottleneck of threads waiting for access to the authenticate, we will have to explore having a pool of NtlmAuthService instances available to handle requests.

  • No labels