CoovaChilli with WPA Captive Portal
This page describes how CoovaChilli can be used as a proxy for WPA authentication with the following twist:
- a user presenting valid credentials get on-line without restriction while
- a user presenting in-valid credentials will still gain WPA access, but be subject to a captive portal and walled garden.
- There are many ways to implement what is described here. For this example, however, we are using CoovaAP combined with FreeRADIUS and JRadius. You will also find this feature in the CoovaAAA services.
CoovaChilli
To enable this feature within chilli itself, an option was defined. By setting the configuration flag wpaguests (in combination with the proxylisten, proxyport, and proxysecret settings) chilli will do a couple things:
sends a ChilliSpot-Config = allow-wpa-guests in proxy RADIUS Access-Request and looks for ChilliSpot-Config = require-uam-auth in the Access-Accept reply such that if not found, the access-accept is passed on normally giving the user full access; if found, the access-accept is passed on, but the session is not marked as authenticated and is subject to ChilliSpots walled garden. If the reply is an Access-Reject, then, of course, that is sent on by proxy and WPA is not granted.
FreeRADIUS
Actual authentication of the WPA RADIUS is handled by FreeRADIUS. It must be configured for EAP, TLS, and PEAP. Additionally, this example uses the JRadius module to easily code our GuestWPA RADIUS logic.
For basics on FreeRADIUS and EAP authentication, see:
- Securing Your WLAN with WPA and FreeRADIUS, Part I
- Securing Your WLAN with WPA and FreeRADIUS, Part II
- 802.1X Port-Based Authentication HOWTO
JRadius
For information on installing and using JRadius with FreeRADIUS, see:
- JRadius - Architecture
- JRadius - Building for FreeRADIUS
- JRadius - Getting started
- JRadius - Developer notes
For this example, the following JRadius handler is defined (abbreviated for readability):
public class WPACaptivePortal extends PacketHandlerBase
{
public boolean handle(JRadiusRequest request)
{
try
{
/*
* Gather some information about the JRadius request
*/
AttributeList ci = request.getConfigItems();
RadiusPacket req = request.getRequestPacket();
RadiusPacket rep = request.getReplyPacket();
/*
* Find the username in the request packet
*/
String username = (String)req.getAttributeValue(Attr_UserName.TYPE);
if (rep instanceof AccessAccept)
{
RadiusLog.info(Allowing WPA access for username: + username);
}
else
{ // Is an Access-Reject
if (allow-wpa-guests.
equals((String)req.getAttributeValue(Attr_ChilliSpotConfig.TYPE)))
{ // Allowing WPA guest access
if (req.findAttribute(Attr_EAPMessage.TYPE) != null)
{ // Is EAP (802.1x)
if (req.findAttribute(Attr_FreeRADIUSProxiedTo.TYPE) != null)
{ // Is the inner request, TLS termianted
rep = new AccessAccept();
rep.addAttribute(new Attr_ChilliSpotConfig(require-uam-auth));
request.setReplyPacket(rep);
ci.add(new Attr_AuthType(Accept));
request.setReturnValue(JRadiusServer.RLM_MODULE_UPDATED);
RadiusLog.error(Allowing Guest WPA access for username: + username);
return true;
}
}
}
RadiusLog.info(Authentication failed for username: + username);
}
}
catch (RadiusException e) { e.printStackTrace(); }
request.setReturnValue(JRadiusServer.RLM_MODULE_UPDATED);
return false;
}
}
This handler is to be ran in the post-auth section of FreeRADIUS, as defined in this JRadius config.xml snippit:
<chain name=post_auth>
<init-session name=post_auth-init-session
description=Initialize The Radius Session/>
<command className=org.coova.jradius.example.WPACaptivePortal />
<class-post-auth name=post_auth-class
description=Post-Auth Class Attribute Handler/>
</chain>
And requires that FreeRADIUS is configured such that Access-Rejects are processed in the post-auth section too (by default, they are not):
# in radiusd.conf
post-auth {
jradius
Post-Auth-Type REJECT {
jradius
}
}
CoovaAP Example
Putting it all together, with a (FreeRADIUS/JRadius) RADIUS server up and running, the setup is tested using a Linksys running the CoovaAP firmware (version beta.4 or better).
On the HotSpot / RADIUS configuration page in the firmware, you can select to allow/enable WPA Guests.
It is important that chilli is started after everything else because it will actually rewrite some configurations (so that the nas program uses chillispot for RADIUS) and restarts other programs.
Some tests Some simple tests using Mac OS X as the client (PEAP).
Example User Login Just using a FreeRADIUS raddb/users entry to define the user:
david User-Password == testing
WISPr-Redirection-URL = http://coova.org/
The user experience, at least on Mac OS X, is summarized by: Found the SSID and was asked for a username, password, and 802.1x authentication type. I entered my valid credentials leaving the auth type on Automatic. I had to acknowledge the SSL (TLS) certificate because it is self signed ;) I was logged in and able to browse the Internet FreeRADIUS/JRadius log (highlights only):
Access-Request packet from host 192.168.100.200:2084, id=2, length=170
User-Name = david
EAP-Message = 0x020200061900
ChilliSpot-Config = allow-wpa-guests
Calling-Station-Id = 00-11-24-90-XX-XX
Called-Station-Id = 00-06-25-C5-XX-XX
NAS-Port-Type = Wireless-802.11
NAS-Port = 1
NAS-IP-Address = 192.168.100.200
NAS-Identifier = 00:06:25:c6:xx:xx
.. EAP Access Challenge/Request and TLS Termination ..
Sending Access-Accept of id 8 to 192.168.100.200 port 2084
User-Name = david
MS-MPPE-Recv-Key = 0x239ae8a15596cc224f6990e0713e3524dcac4cc22de68226d2de01fee7b4e77f
MS-MPPE-Send-Key = 0x938b61d3487a87a6e1a2442767cd6c10036384f6b368d23e553ab53caf13e742
EAP-Message = 0x03080004
Example Guest Login
This time, I did the following:
- Found the SSID and connected to it
- When asked for username and password, I just put in anything
- I still had to acknowledge the SSL (TLS) certificate because it is self signed
- Got onto the WPA network, but got the captive portal page when trying to browse the Internet
FreeRADIUS/JRadius log (highlights only):
Access-Request packet from host 192.168.100.200:2084, id=14, length=171
User-Name = nobody
EAP-Message = 0x020200061900
ChilliSpot-Config = allow-wpa-guests
Calling-Station-Id = 00-11-24-90-XX-XX
Called-Station-Id = 00-06-25-C5-XX-XX
NAS-Port-Type = Wireless-802.11
NAS-Port = 1
NAS-IP-Address = 192.168.100.200
NAS-Identifier = 00:06:25:c6:xx:xx
.. EAP Access Challenge/Request and TLS Termination ..
Sending Access-Accept of id 19 to 192.168.100.200 port 2084
ChilliSpot-Config := require-uam-auth
MS-MPPE-Recv-Key = 0x317f2c50e739bd69d14be1d09ec111abe3f562886879b70eee16395637dc5eb5
MS-MPPE-Send-Key = 0xcbc74e30dce41c343b4afe0ea90b7a6413b659bba3a330bcd265da9df4e85ae7
EAP-Message = 0x03070004
User-Name = nobody