Implement Small Program for 802.1X RADIUS Client Authentication on Windows
In this article, we will discuss how to implement a small program for 802.1X RADIUS client authentication on Windows. The program will allow users to enter their username and password, and then perform the authentication process using a free RADIUS server.
What is 802.1X and RADIUS?
802.1X is a standard for port-based network access control that provides authenticated access to the network. It utilizes the Extensible Authentication Protocol (EAP) to transmit authentication messages between the supplicant (client) and the authenticator (network device).
RADIUS (Remote Authentication Dial-In User Service) is a networking protocol used to provide centralized Authentication, Authorization, and Accounting (AAA) management for users who connect and use a network service.
Setting up a Free RADIUS Server
There are several free RADIUS servers available, including FreeRADIUS and Daloradius. For this article, we will use FreeRADIUS, which can be downloaded from the official website.
After downloading and installing FreeRADIUS, you will need to configure it to accept authentication requests. This typically involves editing the configuration files located in the /etc/raddb/ directory.
Implementing the Small Program
To implement the small program, we can use the EapHost class in the EapPeer namespace, which is part of the Windows API. This class provides the functionality required for 802.1X supplicant authentication.
using System;
using System.Runtime.InteropServices;
using EapPeer;
public class Program
{
[DllImport("EapHost.dll", CharSet = CharSet.Auto)]
private static extern int EapHostGetVersion(out int majorVersion, out int minorVersion);
[DllImport("EapHost.dll", CharSet = CharSet.Auto)]
private static extern int EapHostOpen(ref IntPtr eapHandle, EapHostOpenFlags flags, out IntPtr config);
[DllImport("EapHost.dll", CharSet = CharSet.Auto)]
private static extern int EapHostClose(IntPtr eapHandle);
[DllImport("EapHost.dll", CharSet = CharSet.Auto)]
private static extern int EapHostStart(IntPtr eapHandle, out IntPtr sessionHandle);
[DllImport("EapHost.dll", CharSet = CharSet.Auto)]
private static extern int EapHostStop(IntPtr sessionHandle);
[DllImport("EapHost.dll", CharSet = CharSet.Auto)]
private static extern int EapHostSetProperty(IntPtr sessionHandle, EapProperties property, IntPtr propertyValue);
[DllImport("EapHost.dll", CharSet = CharSet.Auto)]
private static extern int EapHostGetProperty(IntPtr sessionHandle, EapProperties property, out IntPtr propertyValue);
private const EapHostOpenFlags EAP_HOST_OPEN_FLAG_USE_WINLOGON_CREDENTIALS = 0x00000001;
private const EapProperties EAP_PROPERTY_USERNAME = 2;
private const EapProperties EAP_PROPERTY_PASSWORD = 3;
private const EapProperties EAP_PROPERTY_SIMULTANEOUS_AUTHENTICATIONS = 16;
private enum EapMethodId : uint
{
EapTypePeap = 0x00000012,
EapTypeMschapv2 = 0x0000006c,
}
private enum EapPhaseState
{
Entry = 0,
Phase1,
Phase2,
Completed
}
public static void Main(string[] args)
{
int majorVersion, minorVersion;
EapHostGetVersion(out majorVersion, out minorVersion);
Console.WriteLine("EapHost Version: {0}.{1}", majorVersion, minorVersion);
IntPtr eapHandle;
EapHostOpen(ref eapHandle, EAP_HOST_OPEN_FLAG_USE_WINLOGON_CREDENTIALS, out IntPtr config);
Console.WriteLine("EapHostOpen: 0x{0:x}", EapHostGetError(eapHandle));
IntPtr sessionHandle;
EapHostStart(eapHandle, out sessionHandle);
Console.WriteLine("EapHostStart: 0x{0:x}", EapHostGetError(eapHandle));
IntPtr propertyValue;
EapHostGetProperty(sessionHandle, EAP_PROPERTY_SIMULTANEOUS_AUTHENTICATIONS, out propertyValue);
int simultanousAuth = (int)Marshal.PtrToStructure(propertyValue, typeof(int));
Console.WriteLine("Simultaneous Authentications: {0}", simultanousAuth);
EapHostSetProperty(sessionHandle, EAP_PROPERTY_SIMULTANEOUS_AUTHENTICATIONS, new IntPtr(1));
EapHostSetProperty(sessionHandle, EAP_PROPERTY_USERNAME, new IntPtr("username"));
EapHostSetProperty(sessionHandle, EAP_PROPERTY_PASSWORD, new IntPtr("password"));
EapMethodId[] methods = { EapMethodId.EapTypePeap };
int result = EapHostSelectEapMethod(sessionHandle, methods, (uint)methods.Length, 0).ToInt32();
Console.WriteLine("EapHostSelectEapMethod: 0x{0:x}", result);
EapPhaseState phaseState;
GCHandle phaseStateHandle = GCHandle.Alloc(phaseState, GCHandleType.Pinned);
EapHostGetState(sessionHandle, EapPeer.EapHostGetStateFlags.IncludePeerInfo,PhaseState.Entry, phaseStateHandle.AddrOfPinnedObject());
Console.WriteLine("Phase: {0}", phaseState);
// Implement the authentication loop here
EapHostStop(sessionHandle);
EapHostClose(eapHandle);
}
private static Int32 EapHostGetError(IntPtr eapHandle)
{
uint errorCode;
if (EapHostIsError(eapHandle, out errorCode))
{
return (Int32)errorCode;
}
return -1;
}
private static bool EapHostIsError(IntPtr eapHandle, out uint errorCode)
{
Int32 result = EapHostGetProperty(eapHandle, EapPeer.EapHostGetPropertyFlags.IsError, out IntPtr propertyValue);
if (result == EapPeer.ERROR_BUFFER_TOO_SMALL)
{
Int32 minSize = EapHostGetPropertyMinimumBufferSize(eapHandle, EapPeer.EapHostGetPropertyFlags.IsError);
if (minSize > 0)
{
IntPtr newPropertyValue = Marshal.ReallocateHGlobal(propertyValue, minSize);
result = EapHostGetProperty(eapHandle, EapPeer.EapHostGetPropertyFlags.IsError, out newPropertyValue);
marshalData = newPropertyValue;
}
}
errorCode = (uint)result;
return (errorCode == 0 ? false : true);
}
}
The above code implements a small program for 802.1X RADIUS client authentication on Windows. The EapHostOpen function is used to open a session with the RADIUS server, and the EapHostStart function is used to start the authentication process.
The EapHostSetProperty function is used to set the username and password for the authentication process, and the EapHostSelectEapMethod function is used to select the EAP method used for the authentication process. For this example, we are using PEAP with MSCHAPv2.
After the authentication loop is implemented, the EapHostStop function is used to stop the authentication process, and the EapHostClose function is used to close the session.