diff options
Diffstat (limited to 'usr/local/captiveportal')
-rwxr-xr-x | usr/local/captiveportal/index.php | 407 | ||||
-rw-r--r-- | usr/local/captiveportal/radius_accounting.inc | 241 | ||||
-rw-r--r-- | usr/local/captiveportal/radius_authentication.inc | 128 |
3 files changed, 776 insertions, 0 deletions
diff --git a/usr/local/captiveportal/index.php b/usr/local/captiveportal/index.php new file mode 100755 index 0000000..c264625 --- /dev/null +++ b/usr/local/captiveportal/index.php @@ -0,0 +1,407 @@ +#!/usr/local/bin/php +<?php +/* + index.php + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>. + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + +require("globals.inc"); +require("util.inc"); +require("config.inc"); +require("radius_authentication.inc") ; +require("radius_accounting.inc") ; + +header("Expires: 0"); +header("Cache-Control: no-store, no-cache, must-revalidate"); +header("Cache-Control: post-check=0, pre-check=0", false); +header("Pragma: no-cache"); + +$orig_host = $_ENV['HTTP_HOST']; +$orig_request = $_ENV['CAPTIVE_REQPATH']; +$lockfile = "{$g['varrun_path']}/captiveportal.lock"; +$clientip = $_ENV['REMOTE_ADDR']; + +if (!$clientip) { + /* not good - bail out */ + exit; +} + +/* find MAC address for client */ +$clientmac = arp_get_mac_by_ip($clientip); +if (!$clientmac && !isset($config['captiveportal']['nomacfilter'])) { + /* unable to find MAC address - shouldn't happen! - bail out */ + exit; +} + +if ($clientmac && portal_mac_fixed($clientmac)) { + /* punch hole in ipfw for pass thru mac addresses */ + portal_allow($clientip, $clientmac, "unauthenticated"); + +} else if ($_POST['accept'] && file_exists("{$g['vardb_path']}/captiveportal_radius.db")) { + + /* authenticate against radius server */ + $radiusservers = captiveportal_get_radius_servers(); + + if ($_POST['auth_user'] && $_POST['auth_pass']) { + $auth_val = RADIUS_AUTHENTICATION($_POST['auth_user'], + $_POST['auth_pass'], + $radiusservers[0]['ipaddr'], + $radiusservers[0]['port'], + $radiusservers[0]['key']); + if ($auth_val == 2) { + $sessionid = portal_allow($clientip, $clientmac, $_POST['auth_user']); + if (isset($config['captiveportal']['radacct_enable']) && isset($radiusservers[0])) { + $auth_val = RADIUS_ACCOUNTING_START($_POST['auth_user'], + $sessionid, + $radiusservers[0]['ipaddr'], + $radiusservers[0]['acctport'], + $radiusservers[0]['key']); + } + } else { + readfile("{$g['varetc_path']}/captiveportal-error.html"); + } + } else { + readfile("{$g['varetc_path']}/captiveportal-error.html"); + } + +} else if ($_POST['accept'] && $clientip) { + portal_allow($clientip, $clientmac, "unauthenticated"); +} else if ($_POST['logout_id']) { + disconnect_client($_POST['logout_id']); + echo <<<EOD +<HTML> +<HEAD><TITLE>Disconnecting...</TITLE></HEAD> +<BODY BGCOLOR="#435370"> +<SPAN STYLE="color: #ffffff; font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;"> +<B>You've been disconnected.</B> +</SPAN> +<SCRIPT LANGUAGE="JavaScript"> +<!-- +setTimeout('window.close();',5000) ; +--> +</SCRIPT> +</BODY> +</HTML> + +EOD; +} else if (($_ENV['SERVER_PORT'] != 8001) && isset($config['captiveportal']['httpslogin'])) { + /* redirect to HTTPS login page */ + header("Location: https://{$config['captiveportal']['httpsname']}:8001/?redirurl=" . urlencode("http://{$orig_host}{$orig_request}")); +} else { + /* display captive portal page */ + $htmltext = file_get_contents("{$g['varetc_path']}/captiveportal.html"); + + /* substitute variables */ + if (isset($config['captiveportal']['httpslogin'])) + $htmltext = str_replace("\$PORTAL_ACTION\$", "https://{$config['captiveportal']['httpsname']}:8001/", $htmltext); + else + $htmltext = str_replace("\$PORTAL_ACTION\$", "", $htmltext); + + if (preg_match("/redirurl=(.*)/", $orig_request, $matches)) + $redirurl = urldecode($matches[1]); + else + $redirurl = "http://{$orig_host}{$orig_request}"; + $htmltext = str_replace("\$PORTAL_REDIRURL\$", htmlspecialchars($redirurl), $htmltext); + + echo $htmltext; +} + +exit; + +function portal_mac_fixed($clientmac) { + global $g ; + + /* open captive portal mac db */ + if (file_exists("{$g['vardb_path']}/captiveportal_mac.db")) { + $fd = @fopen("{$g['vardb_path']}/captiveportal_mac.db","r") ; + if (!$fd) { + return FALSE; + } + while (!feof($fd)) { + $mac = trim(fgets($fd)) ; + if(strcasecmp($clientmac, $mac) == 0) { + fclose($fd) ; + return TRUE ; + } + } + fclose($fd) ; + } + return FALSE ; +} + +function portal_allow($clientip,$clientmac,$clientuser) { + + global $orig_host, $orig_request, $g, $config; + + /* user has accepted AUP - let him in */ + portal_lock(); + + /* get next ipfw rule number */ + if (file_exists("{$g['vardb_path']}/captiveportal.nextrule")) + $ruleno = trim(file_get_contents("{$g['vardb_path']}/captiveportal.nextrule")); + if (!$ruleno) + $ruleno = 10000; /* first rule number */ + + $saved_ruleno = $ruleno; + + /* generate unique session ID */ + $tod = gettimeofday(); + $sessionid = substr(md5(mt_rand() . $tod['sec'] . $tod['usec'] . $clientip . $clientmac), 0, 16); + + /* add ipfw rules for layer 3 */ + exec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from $clientip to any in"); + exec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from any to $clientip out"); + + /* add ipfw rules for layer 2 */ + if (!isset($config['captiveportal']['nomacfilter'])) { + $l2ruleno = $ruleno + 10000; + exec("/sbin/ipfw add $l2ruleno set 3 deny all from $clientip to any not MAC any $clientmac layer2 in"); + exec("/sbin/ipfw add $l2ruleno set 3 deny all from any to $clientip not MAC $clientmac any layer2 out"); + } + + /* read in client database */ + $cpdb = array(); + + $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r"); + if ($fd) { + while (!feof($fd)) { + $line = trim(fgets($fd)) ; + if($line) { + $cpdb[] = explode(",",$line); + } + } + fclose($fd); + } + + $radiusservers = captiveportal_get_radius_servers(); + + /* find an existing entry and delete it */ + for ($i = 0; $i < count($cpdb); $i++) { + if(!strcasecmp($cpdb[$i][2],$clientip)) { + if(isset($config['captiveportal']['radacct_enable']) && isset($radiusservers[0])) { + RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno + $cpdb[$i][4], // username + $cpdb[$i][5], // sessionid + $cpdb[$i][0], // start time + $radiusservers[0]['ipaddr'], + $radiusservers[0]['acctport'], + $radiusservers[0]['key']); + } + mwexec("/sbin/ipfw delete " . $cpdb[$i][1] . " " . ($cpdb[$i][1]+10000)); + unset($cpdb[$i]); + break; + } + } + + /* rewrite information to database */ + $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w"); + if ($fd) { + foreach ($cpdb as $cpent) { + fwrite($fd, join(",", $cpent) . "\n"); + } + /* write in this new entry */ + fwrite($fd, time().",{$ruleno},{$clientip},{$clientmac},{$clientuser},{$sessionid}\n") ; + fclose($fd); + } + + /* write next rule number */ + $fd = @fopen("{$g['vardb_path']}/captiveportal.nextrule", "w"); + if ($fd) { + $ruleno++; + if ($ruleno > 19899) + $ruleno = 10000; /* wrap around */ + fwrite($fd, $ruleno); + fclose($fd); + } + + portal_unlock(); + + /* redirect user to desired destination */ + if ($config['captiveportal']['redirurl']) + $redirurl = $config['captiveportal']['redirurl']; + else if ($_POST['redirurl']) + $redirurl = $_POST['redirurl']; + else + $redirurl = "http://{$orig_host}{$orig_request}"; + + if(isset($config['captiveportal']['logoutwin_enable'])) { + + if (isset($config['captiveportal']['httpslogin'])) + $logouturl = "https://{$config['captiveportal']['httpsname']}:8001/"; + else + $logouturl = "http://{$config['interfaces'][$config['captiveportal']['interface']]['ipaddr']}:8000/"; + + echo <<<EOD +<HTML> +<HEAD><TITLE>Redirecting...</TITLE></HEAD> +<BODY> +<SPAN STYLE="font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;"> +<B>Redirecting to <A HREF="{$redirurl}">{$redirurl}</A>...</B> +</SPAN> +<SCRIPT LANGUAGE="JavaScript"> +<!-- +LogoutWin = window.open('', 'Logout', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=256,height=64'); +if (LogoutWin) { + LogoutWin.document.write('<HTML>'); + LogoutWin.document.write('<HEAD><TITLE>Logout</TITLE></HEAD>') ; + LogoutWin.document.write('<BODY BGCOLOR="#435370">'); + LogoutWin.document.write('<DIV ALIGN="center" STYLE="color: #ffffff; font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">') ; + LogoutWin.document.write('<B>Click the button below to disconnect</B><P>'); + LogoutWin.document.write('<FORM METHOD="POST" ACTION="{$logouturl}">'); + LogoutWin.document.write('<INPUT NAME="logout_id" TYPE="hidden" VALUE="{$sessionid}">'); + LogoutWin.document.write('<INPUT NAME="logout" TYPE="submit" VALUE="Logout">'); + LogoutWin.document.write('</FORM>'); + LogoutWin.document.write('</DIV></BODY>'); + LogoutWin.document.write('</HTML>'); + LogoutWin.document.close(); +} + +document.location.href="{$redirurl}"; +--> +</SCRIPT> +</BODY> +</HTML> + +EOD; + } else { + header("Location: " . $redirurl); + } + + return $sessionid; +} + +/* read RADIUS servers into array */ +function captiveportal_get_radius_servers() { + + global $g; + + if (file_exists("{$g['vardb_path']}/captiveportal_radius.db")) { + $fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db","r"); + if ($fd) { + $radiusservers = array(); + while (!feof($fd)) { + $line = trim(fgets($fd)); + if ($line) { + $radsrv = array(); + list($radsrv['ipaddr'],$radsrv['port'],$radsrv['acctport'],$radsrv['key']) = explode(",",$line); + $radiusservers[] = $radsrv; + } + } + fclose($fd); + + return $radiusservers; + } + } + + return false; +} + +/* lock captive portal information, decide that the lock file is stale after + 10 seconds */ +function portal_lock() { + + global $lockfile; + + $n = 0; + while ($n < 10) { + /* open the lock file in append mode to avoid race condition */ + if ($fd = @fopen($lockfile, "x")) { + /* succeeded */ + fclose($fd); + return; + } else { + /* file locked, wait and try again */ + sleep(1); + $n++; + } + } +} + +/* unlock captive portal information file */ +function portal_unlock() { + + global $lockfile; + + if (file_exists($lockfile)) + unlink($lockfile); +} + +/* remove a single client by session ID + by Dinesh Nair + */ +function disconnect_client($sessionid) { + + global $g, $config; + + portal_lock(); + + /* read database */ + $cpdb = array() ; + $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r"); + if ($fd) { + while (!feof($fd)) { + $line = trim(fgets($fd)) ; + if($line) { + $cpdb[] = explode(",",$line); + } + } + fclose($fd); + } + + $radiusservers = captiveportal_get_radius_servers(); + + /* find entry */ + for ($i = 0; $i < count($cpdb); $i++) { + if ($cpdb[$i][5] == $sessionid) { + /* this client needs to be deleted - remove ipfw rules */ + if(isset($config['captiveportal']['radacct_enable']) && isset($radiusservers[0])) { + RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno + $cpdb[$i][4], // username + $cpdb[$i][5], // sessionid + $cpdb[$i][0], // start time + $radiusservers[0]['ipaddr'], + $radiusservers[0]['acctport'], + $radiusservers[0]['key']); + } + mwexec("/sbin/ipfw delete " . $cpdb[$i][1] . " " . ($cpdb[$i][1]+10000)); + unset($cpdb[$i]); + break; + } + } + + /* rewrite information to database */ + $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w"); + if ($fd) { + foreach ($cpdb as $cpent) { + fwrite($fd, join(",", $cpent) . "\n"); + } + fclose($fd); + } + + portal_unlock(); +} +?> diff --git a/usr/local/captiveportal/radius_accounting.inc b/usr/local/captiveportal/radius_accounting.inc new file mode 100644 index 0000000..7004971 --- /dev/null +++ b/usr/local/captiveportal/radius_accounting.inc @@ -0,0 +1,241 @@ +<?php +/* + radius_accounting.inc + part of m0n0wall (http://m0n0.ch/wall) + + Copyright (C) 2004 Dinesh Nair <dinesh@alphaque.com> + All rights reserved. + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, + OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF + SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS + INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN + CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) + ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. +*/ + + +function RADIUS_ACCOUNTING_START($username,$sessionid,$radiusip,$radiusport,$radiuskey) { + $sharedsecret=$radiuskey ; + # $debug = 1 ; + + exec("/bin/hostname", $nasHostname) ; + if(!$nasHostname[0]) + $nasHostname[0] = "m0n0wall" ; + + $fd = @fsockopen("udp://$radiusip",$radiusport,$errno,$errstr,3) ; + if(!$fd) + return 1 ; /* error return */ + + /* set 5 second timeout on socket i/o */ + stream_set_timeout($fd, 5) ; + + if ($debug) + echo "<br>radius-port: $radiusport<br>radius-host: $radiusip<br>username: $username<hr>\n"; + + $thisidentifier=rand()%256; + + $length=4+ // header + 16+ // auth code + 6+ // service type + 2+strlen($username)+ // username + 2+strlen($nasHostname[0])+ // nasIdentifier + 6+ // nasPort + 6+ // nasPortType + 6+ // Acct Status Type + 6+ // Acct RADIUS Authenticated + 2+strlen($sessionid); // Acct SessionID + + // v v v v v v v v v 1 v + // Line # 1 2 3 4 5 6 7 8 9 0 E + $data=pack("CCCCNNNNCCCCCCCCa*CCa*CCCCCCCCCCCCCCCCCCCCCCCCCCa*", + 4,$thisidentifier,$length/256,$length%256, // header + 0,0,0,0, // authcode + 6,6,0,0,0,1, // service type + 1,2+strlen($username),$username, // username + 32,2+strlen($nasHostname[0]),$nasHostname[0], // nasIdentifier + 5,6,0,0,0,0, // nasPort + 61,6,0,0,0,15, // nasPortType = Ethernet + 40,6,0,0,0,1, // Acct Status Type = Start + 45,6,0,0,0,1, // Acct RADIUS Authenticated + 44,2+strlen($sessionid),$sessionid // Acct Session ID + ); + + /* Generate Accounting Request Authenticator */ + $RA = md5($data.$radiuskey) ; + + // v v v v v v v v v 1 v + // Line # 1 2 3 4 5 6 7 8 9 0 E + $data=pack("CCCCH*CCCCCCCCa*CCa*CCCCCCCCCCCCCCCCCCCCCCCCCCa*", + 4,$thisidentifier,$length/256,$length%256, // header + $RA, // authcode + 6,6,0,0,0,1, // service type + 1,2+strlen($username),$username, // username + 32,2+strlen($nasHostname[0]),$nasHostname[0], // nasIdentifier + 5,6,0,0,0,0, // nasPort + 61,6,0,0,0,15, // nasPortType = Ethernet + 40,6,0,0,0,1, // Acct Status Type = Start + 45,6,0,0,0,1, // Acct RADIUS Authenticated + 44,2+strlen($sessionid),$sessionid // Acct Session ID + ); + + if($debug) { + echo "username is $username with len " . strlen($username) ."\n" ; + echo "nasHostname is {$nasHostname[0]} with len " . strlen($nasHostname[0]) ."\n" ; + } + + $ret = fwrite($fd,$data) ; + if( !$ret || ($ret != $length) ) + return 1; /* error return */ + + if ($debug) + echo "<br>writing $length bytes<hr>\n"; + + $readdata = fgets($fd,2) ; /* read 1 byte */ + $status = socket_get_status($fd) ; + fclose($fd) ; + + if($status['timed_out']) + $retvalue = 1 ; + else + $retvalue = ord($readdata) ; + + return $retvalue ; + // 5 -> Accounting-Response + // See RFC2866 for this. +} + +function RADIUS_ACCOUNTING_STOP($ruleno,$username,$sessionid,$start_time,$radiusip,$radiusport,$radiuskey) { + $sharedsecret=$radiuskey ; + # $debug = 1 ; + + exec("/bin/hostname", $nasHostname) ; + if(!$nasHostname[0]) + $nasHostname[0] = "quewall" ; + + $input_pkts = $input_bytes = $output_pkts = $output_bytes = 0 ; + + exec("/sbin/ipfw show {$ruleno}", $ipfw) ; + preg_match("/(\d+)\s+(\d+)\s+(\d+)\s+skipto/", $ipfw[0], $matches) ; + $output_pkts = $matches[2] ; + $output_bytes = $matches[3] ; + + unset($matches) ; + preg_match("/(\d+)\s+(\d+)\s+(\d+)\s+skipto/", $ipfw[1], $matches) ; + $input_pkts = $matches[2] ; + $input_bytes = $matches[3] ; + + $fd = @fsockopen("udp://$radiusip",$radiusport,$errno,$errstr,3) ; + if(!$fd) + return 1 ; /* error return */ + + /* set 5 second timeout on socket i/o */ + stream_set_timeout($fd, 5) ; + + if ($debug) + echo "<br>radius-port: $radiusport<br>radius-host: $radiusip<br>username: $username<hr>\n"; + + $thisidentifier=rand()%256; + + $length=4+ // header + 16+ // auth code + 6+ // service type + 2+strlen($username)+ // username + 2+strlen($nasHostname[0])+ // nasIdentifier + 6+ // nasPort + 6+ // nasPortType + 6+ // Acct Status Type + 6+ // Acct RADIUS Authenticated + 2+strlen($sessionid)+ // Acct SessionID + 6+ // Acct terminate + 6+ // Session time + 6+ // input bytes + 6+ // input packets + 6+ // output bytes + 6; // output packets + + // v v v v v v v v v 1 1 1 1 1 1 1 v + // Line # 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 E + $data=pack("CCCCNNNNCCCCCCCCa*CCa*CCCCCCCCCCCCCCCCCCCCCCCCCCa*CCNCCNCCNCCNCCNCCN", + 4,$thisidentifier,$length/256,$length%256, // header + 0,0,0,0, // authcode + 6,6,0,0,0,1, // service type + 1,2+strlen($username),$username, // username + 32,2+strlen($nasHostname[0]),$nasHostname[0], // nasIdentifier + 5,6,0,0,0,0, // nasPort + 61,6,0,0,0,15, // nasPortType = Ethernet + 40,6,0,0,0,2, // Acct Status Type = Stop + 45,6,0,0,0,1, // Acct RADIUS Authenticated + 44,2+strlen($sessionid),$sessionid, // Acct Session ID + 49,6,1, // Acct Terminate = User Request + 46,6,time() - $start_time, // Session Time + 42,6,$input_bytes, // Input Octets + 47,6,$input_pkts, // Input Packets + 43,6,$output_bytes, // Output Octets + 48,6,$output_pkts // Output Packets + ); + + /* Generate Accounting Request Authenticator */ + $RA = md5($data.$radiuskey) ; + + // v v v v v v v v v 1 1 1 1 1 1 1 v + // Line # 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 E + $data=pack("CCCCH*CCCCCCCCa*CCa*CCCCCCCCCCCCCCCCCCCCCCCCCCa*CCNCCNCCNCCNCCNCCN", + 4,$thisidentifier,$length/256,$length%256, // header + $RA, // authcode + 6,6,0,0,0,1, // service type + 1,2+strlen($username),$username, // username + 32,2+strlen($nasHostname[0]),$nasHostname[0], // nasIdentifier + 5,6,0,0,0,0, // nasPort + 61,6,0,0,0,15, // nasPortType = Ethernet + 40,6,0,0,0,2, // Acct Status Type = Stop + 45,6,0,0,0,1, // Acct RADIUS Authenticated + 44,2+strlen($sessionid),$sessionid, // Acct Session ID + 49,6,1, // Acct Terminate = User Request + 46,6,time() - $start_time, // Session Time + 42,6,$input_bytes, // Input Octets + 47,6,$input_pkts, // Input Packets + 43,6,$output_bytes, // Output Octets + 48,6,$output_pkts // Output Packets + ); + + if($debug) { + echo "username is $username with len " . strlen($username) ."\n" ; + echo "nasHostname is {$nasHostname[0]} with len " . strlen($nasHostname[0]) ."\n" ; + } + + $ret = fwrite($fd,$data) ; + if( !$ret || ($ret != $length) ) + return 1; /* error return */ + + if ($debug) + echo "<br>writing $length bytes<hr>\n"; + + $readdata = fgets($fd,2) ; /* read 1 byte */ + $status = socket_get_status($fd) ; + fclose($fd) ; + + if($status['timed_out']) + $retvalue = 1 ; + else + $retvalue = ord($readdata) ; + + return $retvalue ; + // 5 -> Accounting-Response + // See RFC2866 for this. +} +?> diff --git a/usr/local/captiveportal/radius_authentication.inc b/usr/local/captiveportal/radius_authentication.inc new file mode 100644 index 0000000..c106da3 --- /dev/null +++ b/usr/local/captiveportal/radius_authentication.inc @@ -0,0 +1,128 @@ +<?php + // + // $Id$ + // + // radius authentication v1.0 by Edwin Groothuis (edwin@mavetju.org) + // + // If you didn't get this file via http://www.mavetju.org, please + // check for the availability of newer versions. + // + // See LICENSE for distribution issues. If this file isn't in + // the distribution, please inform me about it. + // + // If you want to use this script, fill in the configuration in + // radius_authentication.conf and call the function + // RADIUS_AUTHENTICATION() with the username and password + // provided by the user. If it returns a 2, the authentication + // was successfull! + + // If you want to use this, make sure that you have raw sockets + // enabled during compile-time: "./configure --enable-sockets". + + // This version has been modified by Dinesh Nair <dinesh@alphaque.com> + // for use in the m0n0wall distribution http://m0n0.ch/wall/ + // + // Changes include moving from raw sockets to fsockopen + // and the removal of dependency on external conf file + // An existing bug which resulted in a malformed RADIUS packet + // was also fixed and patches submitted to Edwin. This bug would + // have caused authentication to fail on every access. + +function RADIUS_AUTHENTICATION($username,$password,$radiusip,$radiusport,$radiuskey) { + $sharedsecret=$radiuskey ; + # $debug = 1 ; + + exec("/bin/hostname", $nasHostname) ; + if(!$nasHostname[0]) + $nasHostname[0] = "m0n0wall" ; + + $fd = @fsockopen("udp://$radiusip",$radiusport,$errno,$errstr,3) ; + if(!$fd) + return 1 ; /* error return */ + + /* set 5 second timeout on socket i/o */ + stream_set_timeout($fd, 5) ; + + if ($debug) + echo "<br>radius-port: $radiusport<br>radius-host: $radiusip<br>username: $username<hr>\n"; + + $RA=pack("CCCCCCCCCCCCCCCC", // auth code + 1+rand()%255, 1+rand()%255, 1+rand()%255, 1+rand()%255, + 1+rand()%255, 1+rand()%255, 1+rand()%255, 1+rand()%255, + 1+rand()%255, 1+rand()%255, 1+rand()%255, 1+rand()%255, + 1+rand()%255, 1+rand()%255, 1+rand()%255, 1+rand()%255); + + $encryptedpassword=Encrypt($password,$sharedsecret,$RA); + + $length=4+ // header + 16+ // auth code + 6+ // service type + 2+strlen($username)+ // username + 2+strlen($encryptedpassword)+ // userpassword + 2+strlen($nasHostname[0])+ // nasIdentifier + 6+ // nasPort + 6; // nasPortType + + $thisidentifier=rand()%256; + // v v v v v v v v v + // Line # 1 2 3 4 5 6 7 8 E + $data=pack("CCCCa*CCCCCCCCa*CCa*CCa*CCCCCCCCCCCC", + 1,$thisidentifier,$length/256,$length%256, // header + $RA, // authcode + 6,6,0,0,0,1, // service type + 1,2+strlen($username),$username, // username + 2,2+strlen($encryptedpassword),$encryptedpassword, // userpassword + 32,2+strlen($nasHostname[0]),$nasHostname[0], // nasIdentifier + 5,6,0,0,0,0, // nasPort + 61,6,0,0,0,15 // nasPortType = Ethernet + ); + + if($debug) { + echo "username is $username with len " . strlen($username) ."\n" ; + echo "encryptedpassword is $encryptedpassword with len " . strlen($encryptedpassword) ."\n" ; + echo "nasHostname is {$nasHostname[0]} with len " . strlen($nasHostname[0]) ."\n" ; + } + + $ret = fwrite($fd,$data) ; + if( !$ret || ($ret != $length) ) + return 1; /* error return */ + + if ($debug) + echo "<br>writing $length bytes<hr>\n"; + + $readdata = fgets($fd,2) ; /* read 1 byte */ + $status = socket_get_status($fd) ; + fclose($fd) ; + + if($status['timed_out']) + $retvalue = 1 ; + else + $retvalue = ord($readdata) ; + + return $retvalue ; + // 2 -> Access-Accept + // 3 -> Access-Reject + // See RFC2865 for this. +} + +function Encrypt($password,$key,$RA) { + global $debug; + + $keyRA=$key.$RA; + + if ($debug) + echo "<br>key: $key<br>password: $password<hr>\n"; + + $md5checksum=md5($keyRA); + $output=""; + + for ($i=0;$i<=15;$i++) { + if (2*$i>strlen($md5checksum)) $m=0; else $m=hexdec(substr($md5checksum,2*$i,2)); + if ($i>strlen($keyRA)) $k=0; else $k=ord(substr($keyRA,$i,1)); + if ($i>strlen($password)) $p=0; else $p=ord(substr($password,$i,1)); + $c=$m^$p; + $output.=chr($c); + } + return $output; +} +?> |