Feature #14119
openCorrect or fully implement, in Captive Portal authentication routines, the Tunnel attributes related to the freeRadius VLAN ID setting
0%
Description
This may be either a bug or the completion of a partially implemented feature to support freeRadius users.
The captive portal authentication routines do not respect the VLAN ID setting in freeRadius, resulting in any freeRadius user being able to log into any Captive Portal that uses that freeRadius server for authentication. I assume the existence of the VLAN ID variable in freeRadius was intended to support Captive Portal authentication validation for a specified Captive Portal (i.e. VLan) and it does not do so because the required Tunnel variables are not passed through to the authentication routine. Specifically, as an example:
/* --NEW CODE --------Constant Definitions added here for now ------------------ */
define("RADIUS_TUNNEL_TYPE", 64 );
define("RADIUS_TUNNEL_MEDIUM_TYPE", 65 );
define("RADIUS_TUNNEL_PRIVATE_GROUP_ID", 81 );
/* ----------------------------------------------------------------------------- */
I suggest (while acknowledging the 4096 byte accounting packet limit) that this information needs to be included in accounting packets and incorporated into the $attrivutes array for evaluation by the captiveportal.inc routine:
/* Authenticate users using Authentication Backend */
function captiveportal_authenticate_user(&$login = '', &$password = '', $clientmac = '', $clientip = '', $pipeno = 'null', $context = 'first') {
which calls the authenticate_user( function and/or the Radius.php public function getAttributes().
Once incorporated into the $attributes (or session, etc arrays) it will be possible to test for a match in the captiveportal_authenticate_user( function against either the existing NAS-Identifier (32) value or even to add the "RADIUS_TUNNEL_PRIVATE_GROUP_ID", 81 value to a new cell for VLAN ID in the Captive Portal GUI to match the syntax used in the freeRadius GUI and properly associate it with that tunnel.
During the authentication check any time after line 1295 in captiveportal.inc you would then be able to check this attribute against that set in the Captive Portal NAS-Identifier (simplest solution as it already exists) or the new Captive Portal GUI variable for VLAN ID in the Captive Portal GUI, resulting in the freeRadius user being rejected if attempting to log into a Captive Portal other than the one identified by the VLAN ID tunnel in the freeRadius user definition.
This is required to properly support multiple captive portals authenticating with the same, single freeRadius server. Although it may be possible to achieve this through more complex implementations, clearly this is the simplest way to properly support multiple Captive Portals authentication against a single machine, single instance of freeRadius and is already partially implemented in pfSense.
Updated by Dale Harron almost 2 years ago
WRT ##14093's rejection,
"The solution here is to set each portal to use the RADIUS server in a different way, either with a different NAS Identifier in the portal settings, or something along those lines (maybe separate definitions that send a different NAS IP), and then filter that as you want on the RADIUS server.
tl;dr the authentication server should be making the authentication decisions, not the portal."
We are already sending a different NAS ID (32) as in CaptivePortal-vlan50 and CaptivePortal-vlan60 for example. Both have an IP from different subnets 192.168.1.50.XX and 192.168.1.60.XX for example. freeRadius is authenticating every time, not rejecting; despite being in possession of that data so there is clearly a handshaking issue, independent upon whom is going to decide to approve or reject the authentication request. The typical pfSense user only has access to the freeRadius GUI built into pfSense to implement "something along those lines". What am I missing here?
Updated by Dale Harron almost 2 years ago
If freeRadius was correctly parsing the attributes sent in the accounting communication to freeRadius, the following code inserted into function captiveportal_send_server_accounting( in captiveportal.inc 2411) at 2447 as shown below should have triggered a reject response from freeRadius and it does not.
$racct->putAttribute(RADIUS_NAS_IDENTIFIER, empty($cpcfg["radiusnasid"]) ? "CaptivePortal-{$cpzone}" : $cpcfg["radiusnasid"] );
/* --NEW CODE --------Constant Definitions added here for now ------------------ */
define("RADIUS_TUNNEL_TYPE", 64 );
define("RADIUS_TUNNEL_MEDIUM_TYPE", 65 );
define("RADIUS_TUNNEL_PRIVATE_GROUP_ID", 81 );
$racct->putAttribute(RADIUS_TUNNEL_TYPE, "VLAN");
$racct->putAttribute(RADIUS_TUNNEL_MEDIUM_TYPE, "IEEE-802");
$racct->putAttribute(RADIUS_TUNNEL_PRIVATE_GROUP_ID, empty($cpcfg["radiusnasid"]) ? "CaptivePortal-{$cpzone}" : $cpcfg["radiusnasid"] );
/* ----------------------------------------------------------------------------- */
if (is_int($ruleno)) {
I do achieve the desired result by testing for NAS ID in captiveportal.inc function captiveportal_authenticate_user( after line 1309 in captiveportal.inc with the following test:
/* ----------------------------------------------------- New Code ------------------------------------------- */
/* Check to see if this user is a monthly/router user (vlan60) and deny access to DATA (vlan50) and vice versa*/
/*
$user_file = "/var/log/radacct/datacounter/monthly/max-octets-";
$user_file .= $login;
if (file_exists($user_file) && ($cpzone == "vlan50")) {
$status = "FAILURE";
$result = false;
}
$user_file = "/var/log/radacct/datacounter/forever/max-octets-";
$user_file .= $login;
if (file_exists($user_file) && ($cpzone == "vlan60")) {
$status = "FAILURE";
$result = false;
}
/
/ --------------------------------------------------------------------------------------------------------------- */
The above is just a site specific kludge because a response from freeRadius containing the "RADIUS_TUNNEL_PRIVATE_GROUP_ID" is not available to assess in the $attributes array. If it was available, we could solve the issue in the captiveportal_authenticate_user( routine by simply testing the attribute for "RADIUS_TUNNEL_PRIVATE_GROUP_ID"(81) against the NAS ID (32). Obviously either of the above approaches would be a solution to this concern.