dpsapao

/etc/inc/captiveportal.inc

Feb 8th, 2016
433
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. <?php
  2. /*
  3.     captiveportal.inc
  4.     part of pfSense (https://www.pfsense.org)
  5.     Copyright (C) 2004-2011 Scott Ullrich <sullrich@gmail.com>
  6.     Copyright (C) 2009-2012 Ermal Lu�i <eri@pfsense.org>
  7.     Copyright (C) 2003-2006 Manuel Kasper <mk@neon1.net>.
  8.  
  9.     originally part of m0n0wall (http://m0n0.ch/wall)
  10.     All rights reserved.
  11.  
  12.     Redistribution and use in source and binary forms, with or without
  13.     modification, are permitted provided that the following conditions are met:
  14.  
  15.     1. Redistributions of source code must retain the above copyright notice,
  16.        this list of conditions and the following disclaimer.
  17.  
  18.     2. Redistributions in binary form must reproduce the above copyright
  19.        notice, this list of conditions and the following disclaimer in the
  20.        documentation and/or other materials provided with the distribution.
  21.  
  22.     THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
  23.     INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
  24.     AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
  25.     AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
  26.     OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  27.     SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  28.     INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  29.     CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  30.     ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  31.     POSSIBILITY OF SUCH DAMAGE.
  32.  
  33.     This version of captiveportal.inc has been modified by Rob Parker
  34.     <rob.parker@keycom.co.uk> to include changes for per-user bandwidth management
  35.     via returned RADIUS attributes. This page has been modified to delete any
  36.     added rules which may have been created by other per-user code (index.php, etc).
  37.     These changes are (c) 2004 Keycom PLC.
  38.    
  39.     pfSense_BUILDER_BINARIES:   /sbin/ipfw  /sbin/sysctl
  40.     pfSense_BUILDER_BINARIES:   /usr/local/sbin/lighttpd    /usr/local/bin/minicron /sbin/pfctl
  41.     pfSense_BUILDER_BINARIES:   /bin/hostname   /bin/cp
  42.     pfSense_MODULE: captiveportal
  43. */
  44.  
  45. /* include all configuration functions */
  46. require_once("config.inc");
  47. require_once("functions.inc");
  48. require_once("filter.inc");
  49. require_once("radius.inc");
  50. require_once("voucher.inc");
  51.  
  52. function get_default_captive_portal_html() {
  53.     global $config, $g, $cpzone;
  54.  
  55.     $htmltext = <<<EOD
  56. <html>
  57. <body>
  58. <form method="post" action="\$PORTAL_ACTION\$">
  59.     <input name="redirurl" type="hidden" value="\$PORTAL_REDIRURL\$">
  60.     <input name="zone" type="hidden" value="\$PORTAL_ZONE\$">
  61.     <center>
  62.     <table cellpadding="6" cellspacing="0" width="550" height="380" style="border:1px solid #000000">
  63.     <tr height="10" bgcolor="#990000">
  64.         <td style="border-bottom:1px solid #000000">
  65.             <font color='white'>
  66.             <b>
  67.                 {$g['product_name']} captive portal
  68.             </b>
  69.             </font>
  70.         </td>
  71.     </tr>
  72.     <tr>
  73.         <td>
  74.             <div id="mainlevel">
  75.             <center>
  76.             <table width="100%" border="0" cellpadding="5" cellspacing="0">
  77.             <tr>
  78.                 <td>
  79.                     <center>
  80.                     <div id="mainarea">
  81.                     <center>
  82.                     <table width="100%" border="0" cellpadding="5" cellspacing="5">
  83.                     <tr>
  84.                         <td>
  85.                             <div id="maindivarea">
  86.                             <center>
  87.                                 <div id='statusbox'>
  88.                                     <font color='red' face='arial' size='+1'>
  89.                                     <b>
  90.                                         \$PORTAL_MESSAGE\$
  91.                                     </b>
  92.                                     </font>
  93.                                 </div>
  94.                                 <br/>
  95.                                 <div id='loginbox'>
  96.                                 <table>
  97.                                     <tr><td colspan="2"><center>Welcome to the {$g['product_name']} Captive Portal!</td></tr>
  98.                                     <tr><td>&nbsp;</td></tr>
  99.                                     <tr><td align="right">Username:</td><td><input name="auth_user" type="text" style="border: 1px dashed;"></td></tr>
  100.                                     <tr><td align="right">Password:</td><td><input name="auth_pass" type="password" style="border: 1px dashed;"></td></tr>
  101.                                     <tr><td>&nbsp;</td></tr>
  102.  
  103. EOD;
  104.  
  105.     if(isset($config['voucher'][$cpzone]['enable'])) {
  106.     $htmltext .= <<<EOD
  107.                                     <tr>
  108.                                         <td align="right">Enter Voucher Code: </td>
  109.                                         <td><input name="auth_voucher" type="text" style="border:1px dashed;" size="22"></td>
  110.                                     </tr>
  111.  
  112. EOD;
  113.     }
  114.  
  115.     $htmltext .= <<<EOD
  116.                                     <tr>
  117.                                         <td colspan="2"><center><input name="accept" type="submit" value="Continue"></center></td>
  118.                                     </tr>
  119.                                 </table>
  120.                                 </div>
  121.                             </center>
  122.                             </div>
  123.                         </td>
  124.                     </tr>
  125.                     </table>
  126.                     </center>
  127.                     </div>
  128.                     </center>
  129.                 </td>
  130.             </tr>
  131.             </table>
  132.             </center>
  133.             </div>
  134.         </td>
  135.     </tr>
  136.     </table>
  137.     </center>
  138. </form>
  139. </body>
  140. </html>
  141.  
  142. EOD;
  143.  
  144.     return $htmltext;
  145. }
  146.  
  147. function captiveportal_load_modules() {
  148.     global $config;
  149.  
  150.     mute_kernel_msgs();
  151.     if (!is_module_loaded("ipfw.ko")) {
  152.         mwexec("/sbin/kldload ipfw");
  153.         /* make sure ipfw is not on pfil hooks */
  154.         mwexec("/sbin/sysctl net.inet.ip.pfil.inbound=\"pf\" net.inet6.ip6.pfil.inbound=\"pf\"" .
  155.                " net.inet.ip.pfil.outbound=\"pf\" net.inet6.ip6.pfil.outbound=\"pf\"");
  156.     }
  157.     /* Activate layer2 filtering */
  158.     mwexec("/sbin/sysctl net.link.ether.ipfw=1 net.inet.ip.fw.one_pass=1");
  159.  
  160.     /* Always load dummynet now that even allowed ip and mac passthrough use it. */
  161.     if (!is_module_loaded("dummynet.ko")) {
  162.         mwexec("/sbin/kldload dummynet");
  163.         mwexec("/sbin/sysctl net.inet.ip.dummynet.io_fast=1 net.inet.ip.dummynet.hash_size=256");
  164.     }
  165.     unmute_kernel_msgs();
  166.  
  167.     /* XXX: This are not used in pfSense, if needed can be tuned
  168.     if($config['system']['maximumstates'] <> "" && is_numeric($config['system']['maximumstates'])) {
  169.             mwexec("sysctl net.inet.ip.fw.dyn_max={$config['system']['maximumstates']}");
  170.     } else {
  171.             mwexec("sysctl net.inet.ip.fw.dyn_max=10000");
  172.     }
  173.     */
  174. }
  175.  
  176. function captiveportal_configure() {
  177.     global $config, $cpzone;
  178.  
  179.     if (is_array($config['captiveportal'])) {
  180.         foreach ($config['captiveportal'] as $cpkey => $cp) {
  181.             $cpzone = $cpkey;
  182.             captiveportal_configure_zone($cp);
  183.         }
  184.     } else
  185.         mwexec("/sbin/sysctl net.link.ether.ipfw=0");
  186. }
  187.  
  188. function captiveportal_configure_zone($cpcfg) {
  189.     global $config, $g, $cpzone;
  190.  
  191.     $captiveportallck = lock("captiveportal{$cpzone}", LOCK_EX);
  192.    
  193.     if (isset($cpcfg['enable'])) {
  194.  
  195.         if ($g['booting']) {
  196.             echo "Starting captive portal({$cpcfg['zone']})... ";
  197.  
  198.             /* remove old information */
  199.             unlink_if_exists("{$g['vardb_path']}/captiveportal{$cpzone}.db");
  200.         } else
  201.             captiveportal_syslog("Reconfiguring captive portal({$cpcfg['zone']}).");
  202.  
  203.         /* init ipfw rules */
  204.         captiveportal_init_rules(true);
  205.  
  206.         /* kill any running minicron */
  207.         killbypid("{$g['varrun_path']}/cp_prunedb_{$cpzone}.pid");
  208.  
  209.         /* initialize minicron interval value */
  210.         $croninterval = $cpcfg['croninterval'] ? $cpcfg['croninterval'] : 60;
  211.  
  212.         /* double check if the $croninterval is numeric and at least 10 seconds. If not we set it to 60 to avoid problems */
  213.         if ((!is_numeric($croninterval)) || ($croninterval < 10))
  214.             $croninterval = 60;
  215.  
  216.         /* write portal page */
  217.         if (is_array($cpcfg['page']) && $cpcfg['page']['htmltext'])
  218.             $htmltext = base64_decode($cpcfg['page']['htmltext']);
  219.         else {
  220.             /* example/template page */
  221.             $htmltext = get_default_captive_portal_html();
  222.         }
  223.  
  224.         $fd = @fopen("{$g['varetc_path']}/captiveportal_{$cpzone}.html", "w");
  225.         if ($fd) {
  226.             // Special case handling.  Convert so that we can pass this page
  227.             // through the PHP interpreter later without clobbering the vars.
  228.             $htmltext = str_replace("\$PORTAL_ZONE\$", "#PORTAL_ZONE#", $htmltext);
  229.             $htmltext = str_replace("\$PORTAL_REDIRURL\$", "#PORTAL_REDIRURL#", $htmltext);
  230.             $htmltext = str_replace("\$PORTAL_MESSAGE\$", "#PORTAL_MESSAGE#", $htmltext);
  231.             $htmltext = str_replace("\$CLIENT_MAC\$", "#CLIENT_MAC#", $htmltext);
  232.             $htmltext = str_replace("\$CLIENT_IP\$", "#CLIENT_IP#", $htmltext);
  233.             $htmltext = str_replace("\$ORIGINAL_PORTAL_IP\$", "#ORIGINAL_PORTAL_IP#", $htmltext);
  234.             $htmltext = str_replace("\$PORTAL_ACTION\$", "#PORTAL_ACTION#", $htmltext);
  235.             if($cpcfg['preauthurl']) {
  236.                 $htmltext = str_replace("\$PORTAL_REDIRURL\$", "{$cpcfg['preauthurl']}", $htmltext);
  237.                 $htmltext = str_replace("#PORTAL_REDIRURL#", "{$cpcfg['preauthurl']}", $htmltext);
  238.             }
  239.             fwrite($fd, $htmltext);
  240.             fclose($fd);
  241.         }
  242.         unset($htmltext);
  243.  
  244.         /* write error page */
  245.         if (is_array($cpcfg['page']) && $cpcfg['page']['errtext'])
  246.             $errtext = base64_decode($cpcfg['page']['errtext']);
  247.         else {
  248.             /* example page  */
  249.             $errtext = get_default_captive_portal_html();
  250.         }
  251.  
  252.         $fd = @fopen("{$g['varetc_path']}/captiveportal-{$cpzone}-error.html", "w");
  253.         if ($fd) {
  254.             // Special case handling.  Convert so that we can pass this page
  255.             // through the PHP interpreter later without clobbering the vars.
  256.             $errtext = str_replace("\$PORTAL_ZONE\$", "#PORTAL_ZONE#", $errtext);
  257.             $errtext = str_replace("\$PORTAL_REDIRURL\$", "#PORTAL_REDIRURL#", $errtext);
  258.             $errtext = str_replace("\$PORTAL_MESSAGE\$", "#PORTAL_MESSAGE#", $errtext);
  259.             $errtext = str_replace("\$CLIENT_MAC\$", "#CLIENT_MAC#", $errtext);
  260.             $errtext = str_replace("\$CLIENT_IP\$", "#CLIENT_IP#", $errtext);
  261.             $errtext = str_replace("\$ORIGINAL_PORTAL_IP\$", "#ORIGINAL_PORTAL_IP#", $errtext);
  262.             $errtext = str_replace("\$PORTAL_ACTION\$", "#PORTAL_ACTION#", $errtext);
  263.             if($cpcfg['preauthurl']) {
  264.                 $errtext = str_replace("\$PORTAL_REDIRURL\$", "{$cpcfg['preauthurl']}", $errtext);
  265.                 $errtext = str_replace("#PORTAL_REDIRURL#", "{$cpcfg['preauthurl']}", $errtext);
  266.             }
  267.             fwrite($fd, $errtext);
  268.             fclose($fd);
  269.         }
  270.         unset($errtext);
  271.  
  272.         /* write logout page */
  273.         if (is_array($cpcfg['page']) && $cpcfg['page']['logouttext'])
  274.             $logouttext = base64_decode($cpcfg['page']['logouttext']);
  275.         else {
  276.             /* example page */
  277.             $logouttext = <<<EOD
  278. <HTML>
  279. <HEAD><TITLE>Redirecting...</TITLE></HEAD>
  280. <BODY>
  281. <SPAN STYLE="font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">
  282. <B>Redirecting to <A HREF="<?=\$my_redirurl;?>"><?=\$my_redirurl;?></A>...</B>
  283. </SPAN>
  284. <SCRIPT LANGUAGE="JavaScript">
  285. <!--
  286. LogoutWin = window.open('', 'Logout', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=256,height=64');
  287. if (LogoutWin) {
  288.     LogoutWin.document.write('<HTML>');
  289.     LogoutWin.document.write('<HEAD><TITLE>Logout</TITLE></HEAD>') ;
  290.     LogoutWin.document.write('<BODY BGCOLOR="#435370">');
  291.     LogoutWin.document.write('<DIV ALIGN="center" STYLE="color: #ffffff; font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">') ;
  292.     LogoutWin.document.write('<B>Click the button below to disconnect</B><P>');
  293.     LogoutWin.document.write('<FORM METHOD="POST" ACTION="<?=\$logouturl;?>">');
  294.     LogoutWin.document.write('<INPUT NAME="logout_id" TYPE="hidden" VALUE="<?=\$sessionid;?>">');
  295.     LogoutWin.document.write('<INPUT NAME="zone" TYPE="hidden" VALUE="<?=\$cpzone;?>">');
  296.     LogoutWin.document.write('<INPUT NAME="logout" TYPE="submit" VALUE="Logout">');
  297.     LogoutWin.document.write('</FORM>');
  298.     LogoutWin.document.write('</DIV></BODY>');
  299.     LogoutWin.document.write('</HTML>');
  300.     LogoutWin.document.close();
  301. }
  302.  
  303. document.location.href="<?=\$my_redirurl;?>";
  304. -->
  305. </SCRIPT>
  306. </BODY>
  307. </HTML>
  308.  
  309. EOD;
  310.         }
  311.  
  312.         $fd = @fopen("{$g['varetc_path']}/captiveportal-{$cpzone}-logout.html", "w");
  313.         if ($fd) {
  314.             fwrite($fd, $logouttext);
  315.             fclose($fd);
  316.         }
  317.         unset($logouttext);
  318.  
  319.         /* write elements */
  320.         captiveportal_write_elements();
  321.  
  322.         /* kill any running mini_httpd */
  323.         killbypid("{$g['varrun_path']}/lighty-{$cpzone}-CaptivePortal.pid");
  324.         killbypid("{$g['varrun_path']}/lighty-{$cpzone}-CaptivePortal-SSL.pid");
  325.  
  326.         /* start up the webserving daemon */
  327.         captiveportal_init_webgui_zone($cpcfg);
  328.  
  329.         /* Kill any existing prunecaptiveportal processes */
  330.         if (file_exists("{$g['varrun_path']}/cp_prunedb_{$cpzone}.pid"))
  331.             killbypid("{$g['varrun_path']}/cp_prunedb_{$cpzone}.pid");
  332.  
  333.         /* start pruning process (interval defaults to 60 seconds) */
  334.         mwexec("/usr/local/bin/minicron $croninterval {$g['varrun_path']}/cp_prunedb_{$cpzone}.pid " .
  335.             "/etc/rc.prunecaptiveportal {$cpzone}");
  336.  
  337.         /* generate radius server database */
  338.         unlink_if_exists("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db");
  339.         captiveportal_init_radius_servers();
  340.  
  341.         if ($g['booting']) {
  342.             /* send Accounting-On to server */
  343.             captiveportal_send_server_accounting();
  344.             echo "done\n";
  345.         }
  346.  
  347.     } else {
  348.         killbypid("{$g['varrun_path']}/lighty-{$cpzone}-CaptivePortal.pid");
  349.         killbypid("{$g['varrun_path']}/lighty-{$cpzone}-CaptivePortal-SSL.pid");
  350.         killbypid("{$g['varrun_path']}/cp_prunedb_{$cpzone}.pid");
  351.         @unlink("{$g['varetc_path']}/captiveportal_{$cpzone}.html");
  352.         @unlink("{$g['varetc_path']}/captiveportal-{$cpzone}-error.html");
  353.         @unlink("{$g['varetc_path']}/captiveportal-{$cpzone}-logout.html");
  354.  
  355.         captiveportal_radius_stop_all();
  356.  
  357.         /* send Accounting-Off to server */
  358.         if (!$g['booting']) {
  359.             captiveportal_send_server_accounting(true);
  360.         }
  361.  
  362.         /* remove old information */
  363.         unlink_if_exists("{$g['vardb_path']}/captiveportal{$cpzone}.db");
  364.         unlink_if_exists("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db");
  365.         unlink_if_exists("{$g['vardb_path']}/captiveportal_{$cpzone}.rules");
  366.         /* Release allocated pipes for this zone */
  367.         captiveportal_free_dnrules();
  368.  
  369.         mwexec("/usr/local/sbin/ipfw_context -d {$cpzone}", true);
  370.  
  371.         if (empty($config['captiveportal']))
  372.             mwexec("/sbin/sysctl net.link.ether.ipfw=0");
  373.         else {
  374.             /* Deactivate ipfw(4) if not needed */
  375.             $cpactive = false;
  376.             if (is_array($config['captiveportal'])) {
  377.                 foreach ($config['captiveportal'] as $cpkey => $cp) {
  378.                     if (isset($cp['enable'])) {
  379.                         $cpactive = true;
  380.                         break;
  381.                     }
  382.                 }
  383.             }
  384.             if ($cpactive === false)
  385.                 mwexec("/sbin/sysctl net.link.ether.ipfw=0");
  386.                
  387.         }
  388.     }
  389.  
  390.     unlock($captiveportallck);
  391.    
  392.     return 0;
  393. }
  394.  
  395. function captiveportal_init_webgui() {
  396.     global $config, $cpzone;
  397.  
  398.     if (is_array($config['captiveportal'])) {
  399.         foreach ($config['captiveportal'] as $cpkey => $cp) {
  400.             $cpzone = $cpkey;
  401.             captiveportal_init_webgui_zone($cp);
  402.         }
  403.     }
  404. }
  405.  
  406. function captiveportal_init_webgui_zonename($zone) {
  407.     global $config, $cpzone;
  408.    
  409.     if (isset($config['captiveportal'][$zone])) {
  410.         $cpzone = $zone;
  411.         captiveportal_init_webgui_zone($config['captiveportal'][$zone]);
  412.     }
  413. }
  414.  
  415. function captiveportal_init_webgui_zone($cpcfg) {
  416.     global $g, $config, $cpzone;
  417.  
  418.     if (!isset($cpcfg['enable']))
  419.         return;
  420.  
  421.     if (isset($cpcfg['httpslogin'])) {
  422.         $cert = lookup_cert($cpcfg['certref']);
  423.         $crt = base64_decode($cert['crt']);
  424.         $key = base64_decode($cert['prv']);
  425.         $ca = ca_chain($cert);
  426.  
  427.         /* generate lighttpd configuration */
  428.         $listenporthttps = $cpcfg['listenporthttps'] ? $cpcfg['listenporthttps'] : ($cpcfg['zoneid'] + 1);
  429.         system_generate_lighty_config("{$g['varetc_path']}/lighty-{$cpzone}-CaptivePortal-SSL.conf",
  430.             $crt, $key, $ca, "lighty-{$cpzone}-CaptivePortal-SSL.pid", $listenporthttps, "/usr/local/captiveportal",
  431.             "cert-{$cpzone}-portal.pem", "ca-{$cpzone}-portal.pem", $cpzone);
  432.     }
  433.  
  434.     /* generate lighttpd configuration */
  435.     $listenporthttp = $cpcfg['listenporthttp'] ? $cpcfg['listenporthttp'] : $cpcfg['zoneid'];
  436.     system_generate_lighty_config("{$g['varetc_path']}/lighty-{$cpzone}-CaptivePortal.conf",
  437.         "", "", "", "lighty-{$cpzone}-CaptivePortal.pid", $listenporthttp, "/usr/local/captiveportal",
  438.         "", "", $cpzone);
  439.  
  440.     @unlink("{$g['varrun']}/lighty-{$cpzone}-CaptivePortal.pid");
  441.     /* attempt to start lighttpd */
  442.     $res = mwexec("/usr/local/sbin/lighttpd -f {$g['varetc_path']}/lighty-{$cpzone}-CaptivePortal.conf");
  443.  
  444.     /* fire up https instance */
  445.     if (isset($cpcfg['httpslogin'])) {
  446.         @unlink("{$g['varrun']}/lighty-{$cpzone}-CaptivePortal-SSL.pid");
  447.         $res = mwexec("/usr/local/sbin/lighttpd -f {$g['varetc_path']}/lighty-{$cpzone}-CaptivePortal-SSL.conf");
  448.     }
  449. }
  450.  
  451. /* reinit will disconnect all users, be careful! */
  452. function captiveportal_init_rules($reinit = false) {
  453.     global $config, $g, $cpzone;
  454.  
  455.     if (!isset($config['captiveportal'][$cpzone]['enable']))
  456.         return;
  457.  
  458.     captiveportal_load_modules();
  459.     mwexec("/usr/local/sbin/ipfw_context -a {$cpzone}", true);
  460.  
  461.     $cpips = array();
  462.     $ifaces = get_configured_interface_list();
  463.     $cpinterfaces = explode(",", $config['captiveportal'][$cpzone]['interface']);
  464.     $firsttime = 0;
  465.     foreach ($cpinterfaces as $cpifgrp) {
  466.         if (!isset($ifaces[$cpifgrp]))
  467.             continue;
  468.         $tmpif = get_real_interface($cpifgrp);
  469.         if (!empty($tmpif)) {
  470.             $cpipm = get_interface_ip($cpifgrp);
  471.             if (is_ipaddr($cpipm)) {
  472.                 $carpif = link_ip_to_carp_interface($cpipm);
  473.                 if (!empty($carpif)) {
  474.                     $carpsif = explode(" ", $carpif);
  475.                     foreach ($carpsif as $cpcarp) {
  476.                         mwexec("/usr/local/sbin/ipfw_context -a {$cpzone} -n {$cpcarp}", true);
  477.                         $carpip = find_interface_ip($cpcarp);
  478.                         if (is_ipaddr($carpip))
  479.                             $cpips[] = $carpip;
  480.                     }
  481.                 }
  482.                 $cpips[] = $cpipm;
  483.             }
  484.             mwexec("/usr/local/sbin/ipfw_context -a {$cpzone} -n {$tmpif}", true);
  485.         }
  486.     }
  487.     if (count($cpips) > 0) {
  488.         $cpactive = true;
  489.     } else
  490.         return false;
  491.  
  492.     if ($reinit == false)
  493.         $captiveportallck = lock("captiveportal{$cpzone}");
  494.  
  495.     $cprules =  "add 65291 allow pfsync from any to any\n";
  496.     $cprules .= "add 65292 allow carp from any to any\n";
  497.  
  498.     $cprules .= <<<EOD
  499. # layer 2: pass ARP
  500. add 65301 pass layer2 mac-type arp,rarp
  501. # pfsense requires for WPA
  502. add 65302 pass layer2 mac-type 0x888e,0x88c7
  503. # PPP Over Ethernet Session Stage/Discovery Stage
  504. add 65303 pass layer2 mac-type 0x8863,0x8864
  505.  
  506. # layer 2: block anything else non-IP(v4/v6)
  507. add 65307 deny layer2 not mac-type ip,ipv6
  508.  
  509. EOD;
  510.  
  511.     $rulenum = 65310;
  512.     $ipcount = 0;
  513.     $ips = "";
  514.     foreach ($cpips as $cpip) {
  515.         if($ipcount == 0) {
  516.             $ips = "{$cpip} ";
  517.         } else {
  518.             $ips .= "or {$cpip} ";
  519.         }
  520.         $ipcount++;
  521.     }
  522.     $ips = "{ 255.255.255.255 or {$ips} }";
  523.     $cprules .= "add {$rulenum} pass ip from any to {$ips} in\n";
  524.     $rulenum++;
  525.     $cprules .= "add {$rulenum} pass ip from {$ips} to any out\n";
  526.     $rulenum++;
  527.     $cprules .= "add {$rulenum} pass icmp from {$ips} to any out icmptype 0\n";
  528.     $rulenum++;
  529.     $cprules .= "add {$rulenum} pass icmp from any to {$ips} in icmptype 8 \n";
  530.     $rulenum++;
  531.     /* Allowed ips */
  532.     $cprules .= "add {$rulenum} pipe tablearg ip from table(3) to any in\n";
  533.     $rulenum++;
  534.     $cprules .= "add {$rulenum} pipe tablearg ip from any to table(4) in\n";
  535.     $rulenum++;
  536.     $cprules .= "add {$rulenum} pipe tablearg ip from table(3) to any out\n";
  537.     $rulenum++;
  538.     $cprules .= "add {$rulenum} pipe tablearg ip from any to table(4) out\n";
  539.     $rulenum++;
  540.  
  541.     /* Authenticated users rules. */
  542.     $cprules .= "add {$rulenum} pipe tablearg ip from table(1) to any in\n";
  543.     $rulenum++;
  544.     $cprules .= "add {$rulenum} pipe tablearg ip from any to table(2) out\n";
  545.     $rulenum++;
  546.  
  547.     $listenporthttp =
  548.         $config['captiveportal'][$cpzone]['listenporthttp'] ?
  549.         $config['captiveportal'][$cpzone]['listenporthttp'] :
  550.         $config['captiveportal'][$cpzone]['zoneid'];
  551.  
  552.     if (isset($config['captiveportal'][$cpzone]['httpslogin'])) {
  553.         $listenporthttps = $listenporthttp + 1;
  554.         $cprules .= "add 65531 fwd 127.0.0.1,{$listenporthttps} tcp from any to any dst-port 443 in\n";
  555.     }
  556.    
  557.     $cprules .= <<<EOD
  558.  
  559. # redirect non-authenticated clients to captive portal
  560. add 65532 fwd 127.0.0.1,{$listenporthttp} tcp from any to any dst-port 80 in
  561. # let the responses from the captive portal web server back out
  562. add 65533 pass tcp from any to any out
  563. # block everything else
  564. add 65534 deny all from any to any
  565.  
  566. EOD;
  567.  
  568.     /* generate passthru mac database */
  569.     $cprules .= captiveportal_passthrumac_configure(true);
  570.     $cprules .= "\n";
  571.  
  572.     /* allowed ipfw rules to make allowed ip work */
  573.     $cprules .= captiveportal_allowedip_configure();
  574.  
  575.     /* allowed ipfw rules to make allowed hostnames work */
  576.     $cprules .= captiveportal_allowedhostname_configure();
  577.    
  578.     /* load rules */
  579.     $cprules = "flush\n{$cprules}";
  580.     file_put_contents("{$g['tmp_path']}/ipfw_{$cpzone}.cp.rules", $cprules);
  581.     mwexec("/sbin/ipfw -x {$cpzone} -q {$g['tmp_path']}/ipfw_{$cpzone}.cp.rules", true);
  582.     //@unlink("{$g['tmp_path']}/ipfw_{$cpzone}.cp.rules");
  583.     unset($cprules, $tmprules);
  584.  
  585.     if ($reinit == false)
  586.         unlock($captiveportallck);
  587. }
  588.  
  589. /*
  590.  * Remove clients that have been around for longer than the specified amount of time
  591.  * db file structure:
  592.  * timestamp,ipfw_rule_no,clientip,clientmac,username,sessionid,password,session_timeout,idle_timeout,session_terminate_time,interim_interval
  593.  * (password is in Base64 and only saved when reauthentication is enabled)
  594.  */
  595. function captiveportal_prune_old() {
  596.     global $g, $config, $cpzone;
  597.  
  598.     if (empty($cpzone))
  599.         return;
  600.  
  601.     $cpcfg = $config['captiveportal'][$cpzone];
  602.     $vcpcfg = $config['voucher'][$cpzone];
  603.  
  604.     /* check for expired entries */
  605.     $idletimeout = 0;
  606.     $timeout = 0;
  607.     if (!empty($cpcfg['timeout']) && is_numeric($cpcfg['timeout']))
  608.         $timeout = $cpcfg['timeout'] * 60;
  609.  
  610.     if (!empty($cpcfg['idletimeout']) && is_numeric($cpcfg['idletimeout']))
  611.         $idletimeout = $cpcfg['idletimeout'] * 60;
  612.  
  613.     /* Is there any job to do? */
  614.     if (!$timeout && !$idletimeout && !isset($cpcfg['reauthenticate']) &&
  615.         !isset($cpcfg['radiussession_timeout']) && !isset($vcpcfg['enable']))
  616.         return;
  617.  
  618.     $radiussrvs = captiveportal_get_radius_servers();
  619.  
  620.     /* Read database */
  621.     /* NOTE: while this can be simplified in non radius case keep as is for now */
  622.     $cpdb = captiveportal_read_db();
  623.  
  624.     $unsetindexes = array();
  625.     $voucher_needs_sync = false;
  626.     /*
  627.      * Snapshot the time here to use for calculation to speed up the process.
  628.      * If something is missed next run will catch it!
  629.      */
  630.     $pruning_time = time();
  631.     $stop_time = $pruning_time;
  632.     foreach ($cpdb as $cpentry) {
  633.  
  634.         $timedout = false;
  635.         $term_cause = 1;
  636.         if (empty($cpentry[11]))
  637.             $cpentry[11] = 'first';
  638.         $radiusservers = $radiussrvs[$cpentry[11]];
  639.  
  640.         /* hard timeout? */
  641.         if ($timeout) {
  642.             if (($pruning_time - $cpentry[0]) >= $timeout) {
  643.                 $timedout = true;
  644.                 $term_cause = 5; // Session-Timeout
  645.             }
  646.         }
  647.  
  648.         /* Session-Terminate-Time */
  649.         if (!$timedout && !empty($cpentry[9])) {
  650.             if ($pruning_time >= $cpentry[9]) {
  651.                 $timedout = true;
  652.                 $term_cause = 5; // Session-Timeout
  653.             }
  654.         }
  655.  
  656.         /* check if the radius idle_timeout attribute has been set and if its set change the idletimeout to this value */
  657.         $uidletimeout = (is_numeric($cpentry[8])) ? $cpentry[8] : $idletimeout;
  658.         /* if an idle timeout is specified, get last activity timestamp from ipfw */
  659.         if (!$timedout && $uidletimeout > 0) {
  660.             $lastact = captiveportal_get_last_activity($cpentry[2]);
  661.             /*  If the user has logged on but not sent any traffic they will never be logged out.
  662.              *  We "fix" this by setting lastact to the login timestamp.
  663.              */
  664.             $lastact = $lastact ? $lastact : $cpentry[0];
  665.             if ($lastact && (($pruning_time - $lastact) >= $uidletimeout)) {
  666.                 $timedout = true;
  667.                 $term_cause = 4; // Idle-Timeout
  668.                 $stop_time = $lastact; // Entry added to comply with WISPr
  669.             }
  670.         }
  671.  
  672.         /* if vouchers are configured, activate session timeouts */
  673.         if (!$timedout && isset($vcpcfg['enable']) && !empty($cpentry[7])) {
  674.             if ($pruning_time >= ($cpentry[0] + $cpentry[7])) {
  675.                 $timedout = true;
  676.                 $term_cause = 5; // Session-Timeout
  677.                 $voucher_needs_sync = true;
  678.             }
  679.         }
  680.  
  681.         /* if radius session_timeout is enabled and the session_timeout is not null, then check if the user should be logged out */
  682.         if (!$timedout && isset($cpcfg['radiussession_timeout']) && !empty($cpentry[7])) {
  683.             if ($pruning_time >= ($cpentry[0] + $cpentry[7])) {
  684.                 $timedout = true;
  685.                 $term_cause = 5; // Session-Timeout
  686.             }
  687.         }
  688.  
  689.         if ($timedout) {
  690.             captiveportal_disconnect($cpentry, $radiusservers,$term_cause,$stop_time);
  691.             captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "TIMEOUT");
  692.             $unsetindexes[] = $cpentry[5];
  693.         }
  694.  
  695.         /* do periodic RADIUS reauthentication? */
  696.         if (!$timedout && !empty($radiusservers)) {
  697.             if (isset($cpcfg['radacct_enable'])) {
  698.                 if ($cpcfg['reauthenticateacct'] == "stopstart") {
  699.                     /* stop and restart accounting */
  700.                     RADIUS_ACCOUNTING_STOP($cpentry[1], // ruleno
  701.                         $cpentry[4], // username
  702.                         $cpentry[5], // sessionid
  703.                         $cpentry[0], // start time
  704.                         $radiusservers,
  705.                         $cpentry[2], // clientip
  706.                         $cpentry[3], // clientmac
  707.                         10); // NAS Request
  708.                     $_gb = @pfSense_ipfw_Tableaction($cpzone, IP_FW_TABLE_ZERO_ENTRY_STATS, 1, $cpentry[2]);
  709.                     $_gb = @pfSense_ipfw_Tableaction($cpzone, IP_FW_TABLE_ZERO_ENTRY_STATS, 2, $cpentry[2]);
  710.                     RADIUS_ACCOUNTING_START($cpentry[1], // ruleno
  711.                         $cpentry[4], // username
  712.                         $cpentry[5], // sessionid
  713.                         $radiusservers,
  714.                         $cpentry[2], // clientip
  715.                         $cpentry[3]); // clientmac
  716.                 } else if ($cpcfg['reauthenticateacct'] == "interimupdate") {
  717.                     $session_time = $pruning_time - $cpentry[0];
  718.                     if (!empty($cpentry[10]) && intval($cpentry[10]) > 60)
  719.                         $interval = intval($cpentry[10]);
  720.                     else
  721.                         $interval = 0;
  722.                     $past_interval_min = ($session_time > $interval);
  723.                     if (!empty($interval))
  724.                         $within_interval = ($session_time % $interval >= 0 && $session_time % $interval <= 59);
  725.                     if (empty($interval) || ($interval > 0 && $past_interval_min && $within_interval)) {
  726.                         RADIUS_ACCOUNTING_STOP($cpentry[1], // ruleno
  727.                             $cpentry[4], // username
  728.                             $cpentry[5], // sessionid
  729.                             $cpentry[0], // start time
  730.                             $radiusservers,
  731.                             $cpentry[2], // clientip
  732.                             $cpentry[3], // clientmac
  733.                             10, // NAS Request
  734.                             true); // Interim Updates
  735.                     }
  736.                 }
  737.             }
  738.  
  739.             /* check this user against RADIUS again */
  740.             if (isset($cpcfg['reauthenticate'])) {
  741.                 $auth_list = RADIUS_AUTHENTICATION($cpentry[4], // username
  742.                     base64_decode($cpentry[6]), // password
  743.                     $radiusservers,
  744.                     $cpentry[2], // clientip
  745.                     $cpentry[3], // clientmac
  746.                     $cpentry[1]); // ruleno
  747.                 if ($auth_list['auth_val'] == 3) {
  748.                     captiveportal_disconnect($cpentry, $radiusservers, 17);
  749.                     captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "RADIUS_DISCONNECT", $auth_list['reply_message']);
  750.                     $unsetindexes[] = $cpentry[5];
  751.                 } else if ($auth_list['auth_val'] == 2)
  752.                     captiveportal_reapply_attributes($cpentry, $auth_list);
  753.             }
  754.         }
  755.     }
  756.     unset($cpdb);
  757.  
  758.     captiveportal_prune_old_automac();
  759.  
  760.     if ($voucher_needs_sync == true)
  761.         /* Triger a sync of the vouchers on config */
  762.         send_event("service sync vouchers");
  763.  
  764.     /* write database */
  765.     if (!empty($unsetindexes))
  766.         captiveportal_remove_entries($unsetindexes);
  767. }
  768.  
  769. function captiveportal_prune_old_automac() {
  770.     global $g, $config, $cpzone;
  771.  
  772.     if (is_array($config['captiveportal'][$cpzone]['passthrumac']) && isset($config['captiveportal'][$cpzone]['passthrumacaddusername'])) {
  773.         $tmpvoucherdb = array();
  774.         $macrules = "";
  775.         $writecfg = false;
  776.         foreach ($config['captiveportal'][$cpzone]['passthrumac'] as $eid => $emac) {
  777.             if ($emac['logintype'] == "voucher") {
  778.                 if (isset($config['captiveportal'][$cpzone]['noconcurrentlogins'])) {
  779.                     if (isset($tmpvoucherdb[$emac['username']])) {
  780.                         $temac = $config['captiveportal'][$cpzone]['passthrumac'][$tmpvoucherdb[$emac['username']]];
  781.                         $ruleno = captiveportal_get_ipfw_passthru_ruleno($temac['mac']);
  782.                         $pipeno = captiveportal_get_dn_passthru_ruleno($temac['mac']);
  783.                         if ($ruleno) {
  784.                             captiveportal_free_ipfw_ruleno($ruleno);
  785.                             $macrules .= "delete {$ruleno}";
  786.                             ++$ruleno;
  787.                             $macrules .= "delete {$ruleno}";
  788.                         }
  789.                         if ($pipeno) {
  790.                             captiveportal_free_dn_ruleno($pipeno);
  791.                             $macrules .= "pipe delete {$pipeno}\n";
  792.                             ++$pipeno;
  793.                             $macrules .= "pipe delete {$pipeno}\n";
  794.                         }
  795.                         $writecfg = true;
  796.                         captiveportal_logportalauth($temac['username'], $temac['mac'], $temac['ip'], "DUPLICATE {$temac['username']} LOGIN - TERMINATING OLD SESSION");
  797.                         unset($config['captiveportal'][$cpzone]['passthrumac'][$tmpvoucherdb[$emac['username']]]);
  798.                     }
  799.                     $tmpvoucherdb[$emac['username']] = $eid;
  800.                 }
  801.                 if (voucher_auth($emac['username']) <= 0) {
  802.                     $ruleno = captiveportal_get_ipfw_passthru_ruleno($emac['mac']);
  803.                     $pipeno = captiveportal_get_dn_passthru_ruleno($emac['mac']);
  804.                     if ($ruleno) {
  805.                         captiveportal_free_ipfw_ruleno($ruleno);
  806.                         $macrules .= "delete {$ruleno}";
  807.                         ++$ruleno;
  808.                         $macrules .= "delete {$ruleno}";
  809.                     }
  810.                     if ($pipeno) {
  811.                         captiveportal_free_dn_ruleno($pipeno);
  812.                         $macrules .= "pipe delete {$pipeno}\n";
  813.                         ++$pipeno;
  814.                         $macrules .= "pipe delete {$pipeno}\n";
  815.                     }
  816.                     $writecfg = true;
  817.                     captiveportal_logportalauth($emac['username'], $emac['mac'], $emac['ip'], "EXPIRED {$emac['username']} LOGIN - TERMINATING SESSION");
  818.                     unset($config['captiveportal'][$cpzone]['passthrumac'][$eid]);
  819.                 }
  820.             }
  821.         }
  822.         unset($tmpvoucherdb);
  823.         if (!empty($macrules)) {
  824.             @file_put_contents("{$g['tmp_path']}/macentry.prunerules.tmp", $macrules);
  825.             unset($macrules);
  826.             mwexec("/sbin/ipfw -x {$cpzone} -q {$g['tmp_path']}/macentry.prunerules.tmp");
  827.         }
  828.         if ($writecfg === true)
  829.             write_config("Prune session for auto-added macs");
  830.     }
  831. }
  832.  
  833. /* remove a single client according to the DB entry */
  834. function captiveportal_disconnect($dbent, $radiusservers,$term_cause = 1,$stop_time = null) {
  835.     global $g, $config, $cpzone;
  836.  
  837.     $stop_time = (empty($stop_time)) ? time() : $stop_time;
  838.  
  839.     /* this client needs to be deleted - remove ipfw rules */
  840.     if (isset($config['captiveportal'][$cpzone]['radacct_enable']) && !empty($radiusservers)) {
  841.         RADIUS_ACCOUNTING_STOP($dbent[1], // ruleno
  842.             $dbent[4], // username
  843.             $dbent[5], // sessionid
  844.             $dbent[0], // start time
  845.             $radiusservers,
  846.             $dbent[2], // clientip
  847.             $dbent[3], // clientmac
  848.             $term_cause, // Acct-Terminate-Cause
  849.             false,
  850.             $stop_time);
  851.     }
  852.    
  853.     if (is_ipaddr($dbent[2])) {
  854.         /* Delete client's ip entry from tables 1 and 2. */
  855.         $_gb = @pfSense_ipfw_Tableaction($cpzone, IP_FW_TABLE_DEL, 1, $dbent[2]);
  856.         $_gb = @pfSense_ipfw_Tableaction($cpzone, IP_FW_TABLE_DEL, 2, $dbent[2]);
  857.         /* XXX: Redundant?! Ensure all pf(4) states are killed. */
  858.         $_gb = @pfSense_kill_states($dbent[2]);
  859.         $_gb = @pfSense_kill_srcstates($dbent[2]);
  860.     }
  861.  
  862.     /*
  863.     * These are the pipe numbers we use to control traffic shaping for each logged in user via captive portal
  864.     * We could get an error if the pipe doesn't exist but everything should still be fine
  865.     */
  866.     if (!empty($dbent[1])) {
  867.         $_gb = @pfSense_pipe_action("pipe delete {$dbent[1]}");
  868.         $_gb = @pfSense_pipe_action("pipe delete " . ($dbent[1]+1));
  869.  
  870.         /* Release the ruleno so it can be reallocated to new clients. */
  871.         captiveportal_free_dn_ruleno($dbent[1]);
  872.     }
  873.  
  874.     // XMLRPC Call over to the master Voucher node
  875.     if(!empty($config['voucher'][$cpzone]['vouchersyncdbip'])) {
  876.         $syncip   = $config['voucher'][$cpzone]['vouchersyncdbip'];
  877.         $syncport = $config['voucher'][$cpzone]['vouchersyncport'];
  878.         $syncpass = $config['voucher'][$cpzone]['vouchersyncpass'];
  879.         $vouchersyncusername = $config['voucher'][$cpzone]['vouchersyncusername'];
  880.         $remote_status = xmlrpc_sync_voucher_disconnect($dbent, $syncip, $syncport, $syncpass, $vouchersyncusername, $term_cause, $stop_time);
  881.     }
  882.  
  883. }
  884.  
  885. /* remove a single client by sessionid */
  886. function captiveportal_disconnect_client($sessionid, $term_cause = 1, $logoutReason = "LOGOUT") {
  887.     global $g, $config;
  888.  
  889.     $radiusservers = captiveportal_get_radius_servers();
  890.  
  891.     /* read database */
  892.     $result = captiveportal_read_db("WHERE sessionid = '{$sessionid}'");
  893.  
  894.     /* find entry */
  895.     if (!empty($result)) {
  896.         captiveportal_write_db("DELETE FROM captiveportal WHERE sessionid = '{$sessionid}'");
  897.  
  898.         foreach ($result as $cpentry) {
  899.             if (empty($cpentry[11]))
  900.                 $cpentry[11] = 'first';
  901.             captiveportal_disconnect($cpentry, $radiusservers[$cpentry[11]], $term_cause);
  902.             captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "DISCONNECT");
  903.         }
  904.         unset($result);
  905.     }
  906. }
  907.  
  908. /* send RADIUS acct stop for all current clients */
  909. function captiveportal_radius_stop_all() {
  910.     global $config, $cpzone;
  911.  
  912.     if (!isset($config['captiveportal'][$cpzone]['radacct_enable']))
  913.         return;
  914.  
  915.     $radiusservers = captiveportal_get_radius_servers();
  916.     if (!empty($radiusservers)) {
  917.         $cpdb = captiveportal_read_db();
  918.         foreach ($cpdb as $cpentry) {
  919.             if (empty($cpentry[11]))
  920.                 $cpentry[11] = 'first';
  921.             if (!empty($radiusservers[$cpentry[11]])) {
  922.                 RADIUS_ACCOUNTING_STOP($cpentry[1], // ruleno
  923.                     $cpentry[4], // username
  924.                     $cpentry[5], // sessionid
  925.                     $cpentry[0], // start time
  926.                     $radiusservers[$cpentry[11]],
  927.                     $cpentry[2], // clientip
  928.                     $cpentry[3], // clientmac
  929.                     7); // Admin Reboot
  930.             }
  931.         }
  932.     }
  933. }
  934.  
  935. function captiveportal_passthrumac_configure_entry($macent) {
  936.     global $cpzone, $config;
  937.  
  938.     $bwUp = 0;
  939.     if (!empty($macent['bw_up']))
  940.         $bwUp = $macent['bw_up'];
  941.     else if (isset($config['captiveportal'][$cpzone]['bwdefaultup']))
  942.         $bwUp = $config['captiveportal'][$cpzone]['bwdefaultup'];
  943.     $bwDown = 0;
  944.     if (!empty($macent['bw_down']))
  945.         $bwDown = $macent['bw_down'];
  946.     else if (isset($config['captiveportal'][$cpzone]['bwdefaultdn']))
  947.         $bwDown = $config['captiveportal'][$cpzone]['bwdefaultdn'];
  948.  
  949.     $ruleno = captiveportal_get_next_ipfw_ruleno();
  950.     $pipeno = captiveportal_get_next_dn_ruleno();
  951.  
  952.     $rules = "";
  953.     $pipeup = $pipeno;
  954.     $_gb = @pfSense_pipe_action("pipe {$pipeup} config bw {$bwUp}Kbit/s queue 100 buckets 16");
  955.     $pipedown = $pipeno + 1;
  956.     $_gb = @pfSense_pipe_action("pipe {$pipedown} config bw {$bwDown}Kbit/s queue 100 buckets 16");
  957.     $rules .= "add {$ruleno} pipe {$pipeup} ip from any to any MAC any {$macent['mac']}\n";
  958.     $ruleno++;
  959.     $rules .= "add {$ruleno} pipe {$pipedown} ip from any to any MAC {$macent['mac']} any\n";
  960.  
  961.     return $rules;
  962. }
  963.  
  964. function captiveportal_passthrumac_configure($lock = false) {
  965.     global $config, $g, $cpzone;
  966.  
  967.     $rules = "";
  968.  
  969.     if (is_array($config['captiveportal'][$cpzone]['passthrumac'])) {
  970.         $macdb = array();
  971.         foreach ($config['captiveportal'][$cpzone]['passthrumac'] as $macent) {
  972.             $rules .= captiveportal_passthrumac_configure_entry($macent);
  973.             $macdb[$macent['mac']][$cpzone]['active']  = true;
  974.  
  975.         }
  976.     }
  977.  
  978.     return $rules;
  979. }
  980.  
  981. function captiveportal_passthrumac_findbyname($username) {
  982.     global $config, $cpzone;
  983.  
  984.     if (is_array($config['captiveportal'][$cpzone]['passthrumac'])) {
  985.         foreach ($config['captiveportal'][$cpzone]['passthrumac'] as $macent) {
  986.             if ($macent['username'] == $username)
  987.                 return $macent;
  988.         }
  989.     }
  990.     return NULL;
  991. }
  992.  
  993. /*
  994.  * table (3=IN)/(4=OUT) hold allowed ip's without bw limits
  995.  */
  996. function captiveportal_allowedip_configure_entry($ipent, $ishostname = false) {
  997.     global $g;
  998.  
  999.     /*  Instead of copying this entire function for something
  1000.      *  easy such as hostname vs ip address add this check
  1001.      */
  1002.     if ($ishostname === true) {
  1003.         if (!$g['booting']) {
  1004.             $ipaddress = gethostbyname($ipent['hostname']);
  1005.             if (!is_ipaddr($ipaddress))
  1006.                 return;
  1007.         } else
  1008.             $ipaddress = "";
  1009.     } else
  1010.         $ipaddress = $ipent['ip'];
  1011.  
  1012.     $rules = "";
  1013.     $cp_filterdns_conf = "";
  1014.     $enBwup = 0;
  1015.     if (!empty($ipent['bw_up']))
  1016.         $enBwup = intval($ipent['bw_up']);
  1017.     else if (isset($config['captiveportal'][$cpzone]['bwdefaultup']))
  1018.         $enBwup = $config['captiveportal'][$cpzone]['bwdefaultup'];
  1019.     $enBwdown = 0;
  1020.     if (!empty($ipent['bw_down']))
  1021.         $enBwdown = intval($ipent['bw_down']);
  1022.     else if (isset($config['captiveportal'][$cpzone]['bwdefaultdn']))
  1023.         $enBwdown = $config['captiveportal'][$cpzone]['bwdefaultdn'];
  1024.  
  1025.     $pipeno = captiveportal_get_next_dn_ruleno();
  1026.     $_gb = @pfSense_pipe_action("pipe {$pipeno} config bw {$enBwup}Kbit/s queue 100 buckets 16");
  1027.     $pipedown = $pipeno + 1;
  1028.     $_gb = @pfSense_pipe_action("pipe {$pipedown} config bw {$enBwdown}Kbit/s queue 100 buckets 16");
  1029.     if ($ishostname === true) {
  1030.         $cp_filterdns_conf .= "ipfw {$ipent['hostname']} 3 pipe {$pipeno}\n";
  1031.         $cp_filterdns_conf .= "ipfw {$ipent['hostname']} 4 pipe {$pipedown}\n";
  1032.         if (!is_ipaddr($ipaddress))
  1033.             return array("", $cp_filterdns_conf);
  1034.     }
  1035.     $subnet = "";
  1036.     if (!empty($ipent['sn']))
  1037.         $subnet = "/{$ipent['sn']}";
  1038.     $rules .= "table 3 add {$ipaddress}{$subnet} {$pipeno}\n";
  1039.     $rules .= "table 4 add {$ipaddress}{$subnet} {$pipedown}\n";
  1040.  
  1041.     if ($ishostname === true)
  1042.         return array($rules, $cp_filterdns_conf);
  1043.     else
  1044.         return $rules;
  1045. }
  1046.  
  1047. function captiveportal_allowedhostname_configure() {
  1048.     global $config, $g, $cpzone;
  1049.  
  1050.     $rules = "";
  1051.     if (is_array($config['captiveportal'][$cpzone]['allowedhostname'])) {
  1052.         $rules = "\n# captiveportal_allowedhostname_configure()\n";
  1053.         $cp_filterdns_conf = "";
  1054.         foreach ($config['captiveportal'][$cpzone]['allowedhostname'] as $hostnameent) {
  1055.             $tmprules = captiveportal_allowedip_configure_entry($hostnameent, true);
  1056.             $rules .= $tmprules[0];
  1057.             $cp_filterdns_conf .= $tmprules[1];
  1058.         }
  1059.         $cp_filterdns_filename = "{$g['varetc_path']}/filterdns-{$cpzone}-captiveportal.conf";
  1060.         @file_put_contents($cp_filterdns_filename, $cp_filterdns_conf);
  1061.         unset($cp_filterdns_conf);
  1062.         if (isvalidpid("{$g['varrun_path']}/filterdns-{$cpzone}-cpah.pid"))
  1063.             sigkillbypid("{$g['varrun_path']}/filterdns-{$cpzone}-cpah.pid", "HUP");
  1064.         else
  1065.             mwexec("/usr/local/sbin/filterdns -p {$g['varrun_path']}/filterdns-{$cpzone}-cpah.pid -i 300 -c {$cp_filterdns_filename} -y {$cpzone} -d 1");
  1066.     } else {
  1067.         killbypid("{$g['varrun_path']}/filterdns-{$cpzone}-cpah.pid");
  1068.         @unlink("{$g['varrun_path']}/filterdns-{$cpzone}-cpah.pid");
  1069.     }
  1070.  
  1071.     return $rules;
  1072. }
  1073.  
  1074. function captiveportal_allowedip_configure() {
  1075.     global $config, $g, $cpzone;
  1076.  
  1077.     $rules = "";
  1078.     if (is_array($config['captiveportal'][$cpzone]['allowedip'])) {
  1079.         foreach ($config['captiveportal'][$cpzone]['allowedip'] as $ipent)
  1080.             $rules .= captiveportal_allowedip_configure_entry($ipent);
  1081.     }
  1082.  
  1083.     return $rules;
  1084. }
  1085.  
  1086. /* get last activity timestamp given client IP address */
  1087. function captiveportal_get_last_activity($ip) {
  1088.     global $cpzone;
  1089.  
  1090.     $ipfwoutput = pfSense_ipfw_getTablestats($cpzone, 1, $ip);
  1091.     /* Reading only from one of the tables is enough of approximation. */
  1092.     if (is_array($ipfwoutput)) {
  1093.         return $ipfwoutput['timestamp'];
  1094.     }
  1095.  
  1096.     return 0;
  1097. }
  1098.  
  1099. function captiveportal_init_radius_servers() {
  1100.     global $config, $g, $cpzone;
  1101.  
  1102.     /* generate radius server database */
  1103.     if ($config['captiveportal'][$cpzone]['radiusip'] && (!isset($config['captiveportal'][$cpzone]['auth_method']) ||
  1104.         ($config['captiveportal'][$cpzone]['auth_method'] == "radius"))) {
  1105.         $radiusip = $config['captiveportal'][$cpzone]['radiusip'];
  1106.         $radiusip2 = ($config['captiveportal'][$cpzone]['radiusip2']) ? $config['captiveportal'][$cpzone]['radiusip2'] : null;
  1107.         $radiusip3 = ($config['captiveportal'][$cpzone]['radiusip3']) ? $config['captiveportal'][$cpzone]['radiusip3'] : null;
  1108.         $radiusip4 = ($config['captiveportal'][$cpzone]['radiusip4']) ? $config['captiveportal'][$cpzone]['radiusip4'] : null;
  1109.  
  1110.         if ($config['captiveportal'][$cpzone]['radiusport'])
  1111.             $radiusport = $config['captiveportal'][$cpzone]['radiusport'];
  1112.         else
  1113.             $radiusport = 1812;
  1114.         if ($config['captiveportal'][$cpzone]['radiusacctport'])
  1115.             $radiusacctport = $config['captiveportal'][$cpzone]['radiusacctport'];
  1116.         else
  1117.             $radiusacctport = 1813;
  1118.         if ($config['captiveportal'][$cpzone]['radiusport2'])
  1119.             $radiusport2 = $config['captiveportal'][$cpzone]['radiusport2'];
  1120.         else
  1121.             $radiusport2 = 1812;
  1122.         if ($config['captiveportal'][$cpzone]['radiusport3'])
  1123.             $radiusport3 = $config['captiveportal'][$cpzone]['radiusport3'];
  1124.         else
  1125.             $radiusport3 = 1812;
  1126.         if ($config['captiveportal'][$cpzone]['radiusport4'])
  1127.             $radiusport4 = $config['captiveportal'][$cpzone]['radiusport4'];
  1128.         else
  1129.             $radiusport4 = 1812;
  1130.  
  1131.         $radiuskey = $config['captiveportal'][$cpzone]['radiuskey'];
  1132.         $radiuskey2 = $config['captiveportal'][$cpzone]['radiuskey2'];
  1133.         $radiuskey3 = $config['captiveportal'][$cpzone]['radiuskey3'];
  1134.         $radiuskey4 = $config['captiveportal'][$cpzone]['radiuskey4'];
  1135.  
  1136.         $cprdsrvlck = lock("captiveportalradius{$cpzone}", LOCK_EX);
  1137.         $fd = @fopen("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db", "w");
  1138.         if (!$fd) {
  1139.             captiveportal_syslog("Error: cannot open radius DB file in captiveportal_configure().\n");
  1140.             unlock($cprdsrvlck);
  1141.             return 1;
  1142.         }
  1143.         if (isset($radiusip))
  1144.             fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey . ",first");
  1145.         if (isset($radiusip2))
  1146.             fwrite($fd,"\n" . $radiusip2 . "," . $radiusport2 . "," . $radiusacctport . "," . $radiuskey2 . ",first");
  1147.         if (isset($radiusip3))
  1148.             fwrite($fd,"\n" . $radiusip3 . "," . $radiusport3 . "," . $radiusacctport . "," . $radiuskey3 . ",second");
  1149.         if (isset($radiusip4))
  1150.             fwrite($fd,"\n" . $radiusip4 . "," . $radiusport4 . "," . $radiusacctport . "," . $radiuskey4 . ",second");
  1151.        
  1152.  
  1153.         fclose($fd);
  1154.         unlock($cprdsrvlck);
  1155.     }
  1156. }
  1157.  
  1158. /* read RADIUS servers into array */
  1159. function captiveportal_get_radius_servers() {
  1160.     global $g, $cpzone;
  1161.  
  1162.     $cprdsrvlck = lock("captiveportalradius{$cpzone}");
  1163.     if (file_exists("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db")) {
  1164.         $radiusservers = array();
  1165.         $cpradiusdb = file("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db",
  1166.         FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
  1167.         if ($cpradiusdb) {
  1168.             foreach($cpradiusdb as $cpradiusentry) {
  1169.                 $line = trim($cpradiusentry);
  1170.                 if ($line) {
  1171.                     $radsrv = array();
  1172.                         list($radsrv['ipaddr'],$radsrv['port'],$radsrv['acctport'],$radsrv['key'], $context) = explode(",",$line);
  1173.                 }
  1174.                 if (empty($context)) {
  1175.                     if (!is_array($radiusservers['first']))
  1176.                         $radiusservers['first'] = array();
  1177.                     $radiusservers['first'] = $radsrv;
  1178.                 } else {
  1179.                     if (!is_array($radiusservers[$context]))
  1180.                         $radiusservers[$context] = array();
  1181.                     $radiusservers[$context][] = $radsrv;
  1182.                 }
  1183.             }
  1184.         }
  1185.         unlock($cprdsrvlck);
  1186.         return $radiusservers;
  1187.     }
  1188.  
  1189.     unlock($cprdsrvlck);
  1190.     return false;
  1191. }
  1192.  
  1193. /* log successful captive portal authentication to syslog */
  1194. /* part of this code from php.net */
  1195. function captiveportal_logportalauth($user,$mac,$ip,$status, $message = null) {
  1196.     // Log it
  1197.     if (!$message)
  1198.         $message = "$status: $user, $mac, $ip";
  1199.     else {
  1200.         $message = trim($message);
  1201.         $message = "$status: $user, $mac, $ip, $message";
  1202.     }
  1203.     captiveportal_syslog($message);
  1204. }
  1205.  
  1206. /* log simple messages to syslog */
  1207. function captiveportal_syslog($message) {
  1208.     $message = trim($message);
  1209.     openlog("logportalauth", LOG_PID, LOG_LOCAL4);
  1210.     // Log it
  1211.     syslog(LOG_INFO, $message);
  1212.     closelog();
  1213. }
  1214.  
  1215. function radius($username,$password,$clientip,$clientmac,$type, $radiusctx = null) {
  1216.     global $g, $config;
  1217.  
  1218.     $pipeno = captiveportal_get_next_dn_ruleno();
  1219.  
  1220.     /* If the pool is empty, return appropriate message and fail authentication */
  1221.     if (empty($pipeno)) {
  1222.         $auth_list = array();
  1223.         $auth_list['auth_val'] = 1;
  1224.         $auth_list['error'] = "System reached maximum login capacity";
  1225.         return $auth_list;
  1226.     }
  1227.  
  1228.     $radiusservers = captiveportal_get_radius_servers();
  1229.  
  1230.     if (is_null($radiusctx))
  1231.         $radiusctx = 'first';
  1232.  
  1233.     $auth_list = RADIUS_AUTHENTICATION($username,
  1234.         $password,
  1235.         $radiusservers[$radiusctx],
  1236.         $clientip,
  1237.         $clientmac,
  1238.         $pipeno);
  1239.  
  1240.     if ($auth_list['auth_val'] == 2) {
  1241.         captiveportal_logportalauth($username,$clientmac,$clientip,$type);
  1242.         $sessionid = portal_allow($clientip,
  1243.             $clientmac,
  1244.             $username,
  1245.             $password,
  1246.             $auth_list,
  1247.             $pipeno,
  1248.             $radiusctx);
  1249.     } else {
  1250.              captiveportal_free_dn_ruleno($pipeno);
  1251.            }
  1252.  
  1253.     return $auth_list;
  1254. }
  1255.  
  1256. function captiveportal_opendb() {
  1257.     global $g, $cpzone;
  1258.  
  1259.     if (file_exists("{$g['vardb_path']}/captiveportal{$cpzone}.db"))
  1260.         $DB = @sqlite_open("{$g['vardb_path']}/captiveportal{$cpzone}.db");
  1261.     else {
  1262.         $errormsg = "";
  1263.         $DB = @sqlite_open("{$g['vardb_path']}/captiveportal{$cpzone}.db");
  1264.         if (@sqlite_exec($DB, "CREATE TABLE captiveportal (allow_time INTEGER, pipeno INTEGER, ip TEXT, mac TEXT, username TEXT, sessionid TEXT, bpassword TEXT, session_timeout INTEGER, idle_timeout INTEGER, session_terminate_time INTEGER, interim_interval INTEGER, radiusctx TEXT) ", $errormsg)) {
  1265.             @sqlite_exec($DB, "CREATE UNIQUE INDEX idx_active ON captiveportal (sessionid, username)");
  1266.             @sqlite_exec($DB, "CREATE INDEX user ON captiveportal (username)");
  1267.             @sqlite_exec($DB, "CREATE INDEX ip ON captiveportal (ip)");
  1268.             @sqlite_exec($DB, "CREATE INDEX starttime ON captiveportal (allow_time)");
  1269.             @sqlite_exec($DB, "CREATE INDEX serviceid ON captiveportal (serviceid)");
  1270.         } else
  1271.             captiveportal_syslog("Error during table {$cpzone} creation. Error message: {$errormsg}");
  1272.     }
  1273.  
  1274.     return $DB;
  1275. }
  1276.  
  1277. /* read captive portal DB into array */
  1278. function captiveportal_read_db($query = "") {
  1279.  
  1280.     $DB = captiveportal_opendb();
  1281.     if ($DB) {
  1282.         sqlite_exec($DB, "BEGIN");
  1283.         if (!empty($query))
  1284.             $cpdb = @sqlite_array_query($DB, "SELECT * FROM captiveportal {$query}", SQLITE_NUM);
  1285.         else {
  1286.             $response = @sqlite_unbuffered_query($DB, "SELECT * FROM captiveportal", SQLITE_NUM);
  1287.             $cpdb = @sqlite_fetch_all($response, SQLITE_NUM);
  1288.         }
  1289.         sqlite_exec($DB, "END");
  1290.         @sqlite_close($DB);
  1291.     }
  1292.     if (!$cpdb)
  1293.         $cpdb = array();
  1294.  
  1295.     return $cpdb;
  1296. }
  1297.  
  1298. function captiveportal_remove_entries($remove) {
  1299.  
  1300.     if (!is_array($remove) || empty($remove))
  1301.         return;
  1302.  
  1303.     $query = "DELETE FROM captiveportal WHERE sessionid in (";
  1304.     foreach($remove as $idx => $unindex) {
  1305.         $query .= "'{$unindex}'";
  1306.         if ($idx < (count($remove) - 1))
  1307.             $query .= ",";
  1308.     }
  1309.     $query .= ")";
  1310.     captiveportal_write_db($query);
  1311. }
  1312.  
  1313. /* write captive portal DB */
  1314. function captiveportal_write_db($queries) {
  1315.     global $g;
  1316.  
  1317.     if (is_array($queries))
  1318.         $query = implode(";", $queries);
  1319.     else
  1320.         $query = $queries;
  1321.  
  1322.     $DB = captiveportal_opendb();
  1323.     if ($DB) {
  1324.         $error_msg = "";
  1325.         sqlite_exec($DB, "BEGIN TRANSACTION");
  1326.         $result = @sqlite_exec($DB, $query, $error_msg);
  1327.         if (!$result)
  1328.             captiveportal_syslog("Trying to modify DB returned error: {$error_msg}");
  1329.         else
  1330.             sqlite_exec($DB, "END TRANSACTION");
  1331.         @sqlite_close($DB);
  1332.         return $result;
  1333.     } else
  1334.         return true;
  1335. }
  1336.  
  1337. function captiveportal_write_elements() {
  1338.     global $g, $config, $cpzone;
  1339.    
  1340.     $cpcfg = $config['captiveportal'][$cpzone];
  1341.  
  1342.     if (!is_dir($g['captiveportal_element_path']))
  1343.         @mkdir($g['captiveportal_element_path']);
  1344.  
  1345.     if (is_array($cpcfg['element'])) {
  1346.         conf_mount_rw();
  1347.         foreach ($cpcfg['element'] as $data) {
  1348.             if (!@file_put_contents("{$g['captiveportal_element_path']}/{$data['name']}", base64_decode($data['content']))) {
  1349.                 printf(gettext("Error: cannot open '%s' in captiveportal_write_elements()%s"), $data['name'], "\n");
  1350.                 return 1;
  1351.             }
  1352.             if (!file_exists("{$g['captiveportal_path']}/{$data['name']}"))
  1353.                 @symlink("{$g['captiveportal_element_path']}/{$data['name']}", "{$g['captiveportal_path']}/{$data['name']}");
  1354.         }
  1355.         conf_mount_ro();
  1356.     }
  1357.    
  1358.     return 0;
  1359. }
  1360.  
  1361. function captiveportal_free_dnrules($rulenos_start = 2000, $rulenos_range_max = 64500) {
  1362.     global $cpzone;
  1363.  
  1364.     $cpruleslck = lock("captiveportalrulesdn", LOCK_EX);
  1365.     if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) {
  1366.         $rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules"));
  1367.         $ridx = $rulenos_start;
  1368.         while ($ridx < $rulenos_range_max) {
  1369.             if ($rules[$ridx] == $cpzone) {
  1370.                 $rules[$ridx] = false;
  1371.                 $ridx++;
  1372.                 $rules[$ridx] = false;
  1373.                 $ridx++;
  1374.             } else
  1375.                 $ridx += 2;
  1376.         }
  1377.         file_put_contents("{$g['vardb_path']}/captiveportaldn.rules", serialize($rules));
  1378.         unset($rules);
  1379.     }
  1380.     unlock($cpruleslck);
  1381. }
  1382.  
  1383. function captiveportal_get_next_dn_ruleno($rulenos_start = 2000, $rulenos_range_max = 64500) {
  1384.     global $config, $g, $cpzone;
  1385.  
  1386.     $cpruleslck = lock("captiveportalrulesdn", LOCK_EX);
  1387.     $ruleno = 0;
  1388.     if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) {
  1389.         $rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules"));
  1390.         $ridx = $rulenos_start;
  1391.         while ($ridx < $rulenos_range_max) {
  1392.             if (empty($rules[$ridx])) {
  1393.                 $ruleno = $ridx;
  1394.                 $rules[$ridx] = $cpzone;
  1395.                 $ridx++;
  1396.                 $rules[$ridx] = $cpzone;
  1397.                 break;
  1398.             } else {
  1399.                 $ridx += 2;
  1400.             }
  1401.         }
  1402.     } else {
  1403.         $rules = array_pad(array(), $rulenos_range_max, false);
  1404.         $ruleno = $rulenos_start;
  1405.         $rules[$rulenos_start] = $cpzone;
  1406.         $rulenos_start++;
  1407.         $rules[$rulenos_start] = $cpzone;
  1408.     }
  1409.     file_put_contents("{$g['vardb_path']}/captiveportaldn.rules", serialize($rules));
  1410.     unlock($cpruleslck);
  1411.     unset($rules);
  1412.  
  1413.     return $ruleno;
  1414. }
  1415.  
  1416. function captiveportal_free_dn_ruleno($ruleno) {
  1417.     global $config, $g;
  1418.  
  1419.     $cpruleslck = lock("captiveportalrulesdn", LOCK_EX);
  1420.     if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) {
  1421.         $rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules"));
  1422.         $rules[$ruleno] = false;
  1423.         $ruleno++;
  1424.         $rules[$ruleno] = false;
  1425.         file_put_contents("{$g['vardb_path']}/captiveportaldn.rules", serialize($rules));
  1426.         unset($rules);
  1427.     }
  1428.     unlock($cpruleslck);
  1429. }
  1430.  
  1431. function captiveportal_get_dn_passthru_ruleno($value) {
  1432.     global $config, $g, $cpzone;
  1433.  
  1434.     $cpcfg = $config['captiveportal'][$cpzone];
  1435.     if(!isset($cpcfg['enable']))
  1436.         return NULL;
  1437.  
  1438.     $cpruleslck = lock("captiveportalrulesdn", LOCK_EX);
  1439.     $ruleno = NULL;
  1440.     if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) {
  1441.         $rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules"));
  1442.         unset($output);
  1443.         $_gb = exec("/sbin/ipfw -x {$cpzone} show | /usr/bin/grep {$value} | /usr/bin/grep -v grep | /usr/bin/awk '{print $5}' | /usr/bin/head -n 1", $output);
  1444.         $ruleno = intval($output[0]);
  1445.         if (!$rules[$ruleno])
  1446.             $ruleno = NULL;
  1447.         unset($rules);
  1448.     }
  1449.     unlock($cpruleslck);
  1450.  
  1451.     return $ruleno;
  1452. }
  1453.  
  1454. /*
  1455.  * This function will calculate the lowest free firewall ruleno
  1456.  * within the range specified based on the actual logged on users
  1457.  *
  1458.  */
  1459. function captiveportal_get_next_ipfw_ruleno($rulenos_start = 2, $rulenos_range_max = 64500) {
  1460.     global $config, $g, $cpzone;
  1461.  
  1462.     $cpcfg = $config['captiveportal'][$cpzone];
  1463.     if(!isset($cpcfg['enable']))
  1464.         return NULL;
  1465.  
  1466.     $cpruleslck = lock("captiveportalrules{$cpzone}", LOCK_EX);
  1467.     $ruleno = 0;
  1468.     if (file_exists("{$g['vardb_path']}/captiveportal_{$cpzone}.rules")) {
  1469.         $rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal_{$cpzone}.rules"));
  1470.         $ridx = $rulenos_start;
  1471.         while ($ridx < $rulenos_range_max) {
  1472.             if (empty($rules[$ridx])) {
  1473.                 $ruleno = $ridx;
  1474.                 $rules[$ridx] = $cpzone;
  1475.                 $ridx++;
  1476.                 $rules[$ridx] = $cpzone;
  1477.                 break;
  1478.             } else {
  1479.                 /*
  1480.                  * This allows our traffic shaping pipes to be the in pipe the same as ruleno
  1481.                  * and the out pipe ruleno + 1.
  1482.                  */
  1483.                 $ridx += 2;
  1484.             }
  1485.         }
  1486.     } else {
  1487.         $rules = array_pad(array(), $rulenos_range_max, false);
  1488.         $ruleno = $rulenos_start;
  1489.         $rules[$rulenos_start] = $cpzone;
  1490.         $rulenos_start++;
  1491.         $rules[$rulenos_start] = $cpzone;
  1492.     }
  1493.     file_put_contents("{$g['vardb_path']}/captiveportal_{$cpzone}.rules", serialize($rules));
  1494.     unlock($cpruleslck);
  1495.     unset($rules);
  1496.  
  1497.     return $ruleno;
  1498. }
  1499.  
  1500. function captiveportal_free_ipfw_ruleno($ruleno) {
  1501.     global $config, $g, $cpzone;
  1502.  
  1503.     $cpcfg = $config['captiveportal'][$cpzone];
  1504.     if(!isset($cpcfg['enable']))
  1505.         return NULL;
  1506.  
  1507.     $cpruleslck = lock("captiveportalrules{$cpzone}", LOCK_EX);
  1508.     if (file_exists("{$g['vardb_path']}/captiveportal_{$cpzone}.rules")) {
  1509.         $rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal_{$cpzone}.rules"));
  1510.         $rules[$ruleno] = false;
  1511.         $ruleno++;
  1512.         $rules[$ruleno] = false;
  1513.         file_put_contents("{$g['vardb_path']}/captiveportal_{$cpzone}.rules", serialize($rules));
  1514.     }
  1515.     unlock($cpruleslck);
  1516.     unset($rules);
  1517. }
  1518.  
  1519. function captiveportal_get_ipfw_passthru_ruleno($value) {
  1520.     global $config, $g, $cpzone;
  1521.  
  1522.     $cpcfg = $config['captiveportal'][$cpzone];
  1523.     if(!isset($cpcfg['enable']))
  1524.         return NULL;
  1525.  
  1526.     $cpruleslck = lock("captiveportalrules{$cpzone}", LOCK_EX);
  1527.     $ruleno = NULL;
  1528.     if (file_exists("{$g['vardb_path']}/captiveportal_{$cpzone}.rules")) {
  1529.         $rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal_{$cpzone}.rules"));
  1530.         unset($output);
  1531.         $_gb = exec("/sbin/ipfw -x {$cpzone} show | /usr/bin/grep {$value} | /usr/bin/grep -v grep | /usr/bin/awk '{print $1}' | /usr/bin/head -n 1", $output);
  1532.         $ruleno = intval($output[0]);
  1533.         if (!$rules[$ruleno])
  1534.             $ruleno = NULL;
  1535.     }
  1536.     unlock($cpruleslck);
  1537.     unset($rules);
  1538.  
  1539.     return $ruleno;
  1540. }
  1541.  
  1542. /**
  1543.  * This function will calculate the traffic produced by a client
  1544.  * based on its firewall rule
  1545.  *
  1546.  * Point of view: NAS
  1547.  *
  1548.  * Input means: from the client
  1549.  * Output means: to the client
  1550.  *
  1551.  */
  1552.  
  1553. function getVolume($ip) {
  1554.     global $config, $cpzone;
  1555.  
  1556.     $reverse = empty($config['captiveportal'][$cpzone]['reverseacct']) ? false : true;
  1557.     $volume = array();
  1558.     // Initialize vars properly, since we don't want NULL vars
  1559.     $volume['input_pkts'] = $volume['input_bytes'] = $volume['output_pkts'] = $volume['output_bytes'] = 0 ;
  1560.  
  1561.     $ipfw = pfSense_ipfw_getTablestats($cpzone, 1, $ip);
  1562.     if (is_array($ipfw)) {
  1563.         if ($reverse) {
  1564.             $volume['output_pkts'] = $ipfw['packets'];
  1565.             $volume['output_bytes'] = $ipfw['bytes'];
  1566.         }
  1567.         else {
  1568.             $volume['input_pkts'] = $ipfw['packets'];
  1569.             $volume['input_bytes'] = $ipfw['bytes'];
  1570.         }
  1571.     }
  1572.  
  1573.     $ipfw = pfSense_ipfw_getTablestats($cpzone, 2, $ip);
  1574.     if (is_array($ipfw)) {
  1575.         if ($reverse) {
  1576.             $volume['input_pkts'] = $ipfw['packets'];
  1577.             $volume['input_bytes'] = $ipfw['bytes'];
  1578.         }
  1579.         else {
  1580.             $volume['output_pkts'] = $ipfw['packets'];
  1581.             $volume['output_bytes'] = $ipfw['bytes'];
  1582.         }
  1583.     }
  1584.  
  1585.     return $volume;
  1586. }
  1587.  
  1588. /**
  1589.  * Get the NAS-IP-Address based on the current wan address
  1590.  *
  1591.  * Use functions in interfaces.inc to find this out
  1592.  *
  1593.  */
  1594.  
  1595. function getNasIP()
  1596. {
  1597.     global $config, $cpzone;
  1598.  
  1599.     if (empty($config['captiveportal'][$cpzone]['radiussrcip_attribute'])) {
  1600.             $nasIp = get_interface_ip();
  1601.     } else {
  1602.         if (is_ipaddr($config['captiveportal'][$cpzone]['radiussrcip_attribute']))
  1603.             $nasIp = $config['captiveportal'][$cpzone]['radiussrcip_attribute'];
  1604.         else
  1605.             $nasIp = get_interface_ip($config['captiveportal'][$cpzone]['radiussrcip_attribute']);
  1606.     }
  1607.        
  1608.     if(!is_ipaddr($nasIp))
  1609.         $nasIp = "0.0.0.0";
  1610.  
  1611.     return $nasIp;
  1612. }
  1613.  
  1614. function portal_ip_from_client_ip($cliip) {
  1615.     global $config, $cpzone;
  1616.  
  1617.     $isipv6 = is_ipaddrv6($cliip);
  1618.     $interfaces = explode(",", $config['captiveportal'][$cpzone]['interface']);
  1619.     foreach ($interfaces as $cpif) {
  1620.         if ($isipv6) {
  1621.             $ip = get_interface_ipv6($cpif);
  1622.             $sn = get_interface_subnetv6($cpif);
  1623.         } else {
  1624.             $ip = get_interface_ip($cpif);
  1625.             $sn = get_interface_subnet($cpif);
  1626.         }
  1627.         if (ip_in_subnet($cliip, "{$ip}/{$sn}"))
  1628.             return $ip;
  1629.     }
  1630.  
  1631.     $inet = ($isipv6) ? '-inet6' : '-inet';
  1632.     $iface = exec_command("/sbin/route -n get {$inet} {$cliip} | /usr/bin/awk '/interface/ { print \$2; };'");
  1633.     $iface = trim($iface, "\n");
  1634.     if (!empty($iface)) {
  1635.         $ip = ($isipv6) ? find_interface_ipv6($iface) : find_interface_ip($iface);
  1636.         if (is_ipaddr($ip))
  1637.             return $ip;
  1638.     }
  1639.  
  1640.     // doesn't match up to any particular interface
  1641.     // so let's set the portal IP to what PHP says
  1642.     // the server IP issuing the request is.
  1643.     // allows same behavior as 1.2.x where IP isn't
  1644.     // in the subnet of any CP interface (static routes, etc.)
  1645.     // rather than forcing to DNS hostname resolution
  1646.     $ip = $_SERVER['SERVER_ADDR'];
  1647.     if (is_ipaddr($ip))
  1648.         return $ip;
  1649.  
  1650.     return false;
  1651. }
  1652.  
  1653. function portal_hostname_from_client_ip($cliip) {
  1654.     global $config, $cpzone;
  1655.  
  1656.     $cpcfg = $config['captiveportal'][$cpzone];
  1657.  
  1658.     if (isset($cpcfg['httpslogin'])) {
  1659.         $listenporthttps = $cpcfg['listenporthttps'] ? $cpcfg['listenporthttps'] : ($cpcfg['zoneid'] + 1);
  1660.         $ourhostname = $cpcfg['httpsname'];
  1661.        
  1662.         if ($listenporthttps != 443)
  1663.             $ourhostname .= ":" . $listenporthttps;
  1664.     } else {
  1665.         $listenporthttp  = $cpcfg['listenporthttp']  ? $cpcfg['listenporthttp']  : $cpcfg['zoneid'];
  1666.         $ifip = portal_ip_from_client_ip($cliip);
  1667.         if (!$ifip)
  1668.             $ourhostname = "{$config['system']['hostname']}.{$config['system']['domain']}";
  1669.         else
  1670.             $ourhostname = (is_ipaddrv6($ifip)) ? "[{$ifip}]" : "{$ifip}";
  1671.        
  1672.         if ($listenporthttp != 80)
  1673.             $ourhostname .= ":" . $listenporthttp;
  1674.     }
  1675.    
  1676.     return $ourhostname;
  1677. }
  1678.  
  1679. /* functions move from index.php */
  1680. function already_connected($sessionid)
  1681. {
  1682.     global $g, $config, $cpzone;
  1683.     if ($sessionid != "")   {
  1684.         $query = "WHERE sessionid = '{$sessionid}'";
  1685.         $cpdb = captiveportal_read_db($query);
  1686.         foreach ($cpdb as $cpentry) {
  1687.             if ($cpentry[5] == $sessionid) {
  1688.                 captiveportal_logportalauth($cpentry[4], $sessionid, $cpentry[2], "Found sessionid");
  1689.                 return true;
  1690.             }
  1691.         }
  1692.     }
  1693.     return false;
  1694. }
  1695.  
  1696. function portal_reply_page($redirurl, $type = null, $message = null, $clientmac = null, $clientip = null, $username = null, $password = null) {
  1697.     global $g, $config, $cpzone;
  1698.  
  1699.     /* Get captive portal layout */
  1700.     if ($type == "redir") {
  1701.         header("Location: {$redirurl}");
  1702.         return;
  1703.     } else if ($type == "login")
  1704.         $htmltext = get_include_contents("{$g['varetc_path']}/captiveportal_{$cpzone}.html");
  1705.     else if ($type == "already_connected") {
  1706.         $sessionid = $_COOKIE['cookie_portal'];
  1707.             $htmltext = get_include_contents("{$g['captiveportal_path']}/captiveportal-{$cpzone}-already-connected.html");
  1708.         } else
  1709.         $htmltext = get_include_contents("{$g['varetc_path']}/captiveportal-{$cpzone}-error.html");
  1710.  
  1711.     $cpcfg = $config['captiveportal'][$cpzone];
  1712.  
  1713.     /* substitute the PORTAL_REDIRURL variable */
  1714.     if ($cpcfg['preauthurl']) {
  1715.         $htmltext = str_replace("\$PORTAL_REDIRURL\$", "{$cpcfg['preauthurl']}", $htmltext);
  1716.         $htmltext = str_replace("#PORTAL_REDIRURL#", "{$cpcfg['preauthurl']}", $htmltext);
  1717.     }
  1718.  
  1719.     /* substitute other variables */
  1720.     $ourhostname = portal_hostname_from_client_ip($clientip);
  1721.     $protocol = (isset($cpcfg['httpslogin'])) ? 'https://' : 'http://';
  1722.     $htmltext = str_replace("\$PORTAL_ACTION\$", "{$protocol}{$ourhostname}/", $htmltext);
  1723.     $htmltext = str_replace("#PORTAL_ACTION#", "{$protocol}{$ourhostname}/", $htmltext);
  1724.  
  1725.     $htmltext = str_replace("\$PORTAL_ZONE\$", htmlspecialchars($cpzone), $htmltext);
  1726.     $htmltext = str_replace("\$PORTAL_REDIRURL\$", htmlspecialchars($redirurl), $htmltext);
  1727.     $htmltext = str_replace("\$PORTAL_MESSAGE\$", htmlspecialchars($message), $htmltext);
  1728.     $htmltext = str_replace("\$CLIENT_MAC\$", htmlspecialchars($clientmac), $htmltext);
  1729.     $htmltext = str_replace("\$CLIENT_IP\$", htmlspecialchars($clientip), $htmltext);
  1730.     $htmltext = str_replace("\$PORTAL_SESSION\$", htmlspecialchars($sessionid), $htmltext);
  1731.  
  1732.     // Special handling case for captive portal master page so that it can be ran
  1733.     // through the PHP interpreter using the include method above.  We convert the
  1734.     // $VARIABLE$ case to #VARIABLE# in /etc/inc/captiveportal.inc before writing out.
  1735.     $htmltext = str_replace("#PORTAL_ZONE#", htmlspecialchars($cpzone), $htmltext);
  1736.     $htmltext = str_replace("#PORTAL_REDIRURL#", htmlspecialchars($redirurl), $htmltext);
  1737.     $htmltext = str_replace("#PORTAL_MESSAGE#", htmlspecialchars($message), $htmltext);
  1738.     $htmltext = str_replace("#CLIENT_MAC#", htmlspecialchars($clientmac), $htmltext);
  1739.     $htmltext = str_replace("#CLIENT_IP#", htmlspecialchars($clientip), $htmltext);
  1740.     $htmltext = str_replace("#PORTAL_SESSION#", htmlspecialchars($sessionid), $htmltext);
  1741.     $htmltext = str_replace("#USERNAME#", htmlspecialchars($username), $htmltext);
  1742.     $htmltext = str_replace("#PASSWORD#", htmlspecialchars($password), $htmltext);
  1743.  
  1744.     echo $htmltext;
  1745. }
  1746.  
  1747. function portal_mac_radius($clientmac,$clientip) {
  1748.     global $config, $cpzone;
  1749.  
  1750.     $radmac_secret = $config['captiveportal'][$cpzone]['radmac_secret'];
  1751.  
  1752.     /* authentication against the radius server */
  1753.     $username = mac_format($clientmac);
  1754.     $auth_list = radius($username,$radmac_secret,$clientip,$clientmac,"MACHINE LOGIN");
  1755.     if ($auth_list['auth_val'] == 2)
  1756.         return TRUE;
  1757.  
  1758.     if (!empty($auth_list['url_redirection']))
  1759.         portal_reply_page($auth_list['url_redirection'], "redir");
  1760.  
  1761.     return FALSE;
  1762. }
  1763.  
  1764. function captiveportal_reapply_attributes($cpentry, $attributes) {
  1765.     global $config, $cpzone, $g;
  1766.  
  1767.     $dwfaultbw_up = isset($config['captiveportal'][$cpzone]['bwdefaultup']) ? $config['captiveportal'][$cpzone]['bwdefaultup'] : 0;
  1768.     $dwfaultbw_down = isset($config['captiveportal'][$cpzone]['bwdefaultdn']) ? $config['captiveportal'][$cpzone]['bwdefaultdn'] : 0;
  1769.     $bw_up = isset($attributes['bw_up']) ? round(intval($attributes['bw_up'])/1000, 2) : $dwfaultbw_up;
  1770.     $bw_down = isset($attributes['bw_down']) ? round(intval($attributes['bw_down'])/1000, 2) : $dwfaultbw_down;
  1771.     $bw_up_pipeno = $cpentry[1];
  1772.     $bw_down_pipeno = $cpentry[1]+1;
  1773.  
  1774.     $_gb = @pfSense_pipe_action("pipe {$bw_up_pipeno} config bw {$bw_up}Kbit/s queue 100 buckets 16");
  1775.     $_gb = @pfSense_pipe_action("pipe {$bw_down_pipeno} config bw {$bw_down}Kbit/s queue 100 buckets 16");
  1776.     //captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "RADIUS_BANDWIDTH_REAPPLY", "{$bw_up}/{$bw_down}");
  1777.  
  1778.     unset($bw_up_pipeno, $bw_down_pipeno, $bw_up, $bw_down);
  1779. }
  1780.  
  1781. function portal_allow($clientip,$clientmac,$username,$password = null, $attributes = null, $pipeno = null, $radiusctx = null)  {
  1782.     global $redirurl, $g, $config, $type, $passthrumac, $_POST, $cpzone;
  1783.  
  1784.     // Ensure we create an array if we are missing attributes
  1785.     if (!is_array($attributes))
  1786.         $attributes = array();
  1787.  
  1788.     unset($sessionid);
  1789.  
  1790.     /* Do not allow concurrent login execution. */
  1791.     $cpdblck = lock("captiveportaldb{$cpzone}", LOCK_EX);
  1792.  
  1793.     if ($attributes['voucher'])
  1794.         $remaining_time = $attributes['session_timeout'];
  1795.  
  1796.     $writecfg = false;
  1797.     /* Find an existing session */
  1798.     if ((isset($config['captiveportal'][$cpzone]['noconcurrentlogins'])) && $passthrumac) {
  1799.         if (isset($config['captiveportal'][$cpzone]['passthrumacadd'])) {
  1800.             $mac = captiveportal_passthrumac_findbyname($username);
  1801.             if (!empty($mac)) {
  1802.                 if ($_POST['replacemacpassthru']) {
  1803.                     foreach ($config['captiveportal'][$cpzone]['passthrumac'] as $idx => $macent) {
  1804.                         if ($macent['mac'] == $mac['mac']) {
  1805.                             $macrules = "";
  1806.                             $ruleno = captiveportal_get_ipfw_passthru_ruleno($mac['mac']);
  1807.                             $pipeno = captiveportal_get_dn_passthru_ruleno($mac['mac']);
  1808.                             if ($ruleno) {
  1809.                                 captiveportal_free_ipfw_ruleno($ruleno);
  1810.                                 $macrules .= "delete {$ruleno}\n";
  1811.                                 ++$ruleno;
  1812.                                 $macrules .= "delete {$ruleno}\n";
  1813.                             }
  1814.                             if ($pipeno) {
  1815.                                 captiveportal_free_dn_ruleno($pipeno);
  1816.                                 $macrules .= "pipe delete {$pipeno}\n";
  1817.                                 ++$pipeno;
  1818.                                 $macrules .= "pipe delete {$pipeno}\n";
  1819.                             }
  1820.                             unset($config['captiveportal'][$cpzone]['passthrumac'][$idx]);
  1821.                             $mac['mac'] = $clientmac;
  1822.                             $config['captiveportal'][$cpzone]['passthrumac'][] = $mac;
  1823.                             $macrules .= captiveportal_passthrumac_configure_entry($mac);
  1824.                             file_put_contents("{$g['tmp_path']}/macentry_{$cpzone}.rules.tmp", $macrules);
  1825.                             mwexec("/sbin/ipfw -x {$cpzone} -q {$g['tmp_path']}/macentry_{$cpzone}.rules.tmp");
  1826.                             $writecfg = true;
  1827.                             $sessionid = true;
  1828.                             break;
  1829.                         }
  1830.                     }
  1831.                 } else {
  1832.                     portal_reply_page($redirurl, "error", "Username: {$username} is already authenticated using another MAC address.",
  1833.                         $clientmac, $clientip, $username, $password);
  1834.                     unlock($cpdblck);
  1835.                     return;
  1836.                 }
  1837.             }
  1838.         }
  1839.     }
  1840.  
  1841.     /* read in client database */
  1842.     $query = "WHERE ip = '{$clientip}'";
  1843.     $tmpusername = strtolower($username);
  1844.     if (isset($config['captiveportal'][$cpzone]['noconcurrentlogins']))
  1845.         $query .= " OR (username != 'unauthenticated' AND lower(username) = '{$tmpusername}')";
  1846.     $cpdb = captiveportal_read_db($query);
  1847.  
  1848.     /* Snapshot the timestamp */
  1849.     $allow_time = time();
  1850.     $radiusservers = captiveportal_get_radius_servers();
  1851.     $unsetindexes = array();
  1852.     if (is_null($radiusctx))
  1853.         $radiusctx = 'first';
  1854.  
  1855.     foreach ($cpdb as $cpentry) {
  1856.         if (empty($cpentry[11])) {
  1857.             $cpentry[11] = 'first';
  1858.         }
  1859.         /* on the same ip */
  1860.         if ($cpentry[2] == $clientip) {
  1861.             if (isset($config['captiveportal'][$cpzone]['nomacfilter']) || $cpentry[3] == $clientmac)
  1862.                 captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - REUSING OLD SESSION");
  1863.             else
  1864.                 captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - REUSING IP {$cpentry[2]} WITH DIFFERENT MAC ADDRESS {$cpentry[3]}");
  1865.             $sessionid = $cpentry[5];
  1866.             break;
  1867.         }
  1868.         elseif (($attributes['voucher']) && ($username != 'unauthenticated') && ($cpentry[4] == $username)) {
  1869.             // user logged in with an active voucher. Check for how long and calculate
  1870.             // how much time we can give him (voucher credit - used time)
  1871.             $remaining_time = $cpentry[0] + $cpentry[7] - $allow_time;
  1872.             if ($remaining_time < 0)    // just in case.
  1873.                 $remaining_time = 0;
  1874.  
  1875.             /* This user was already logged in so we disconnect the old one */
  1876.             captiveportal_disconnect($cpentry,$radiusservers[$cpentry[11]],13);
  1877.             captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - TERMINATING OLD SESSION");
  1878.             $unsetindexes[] = $cpentry[5];
  1879.             break;
  1880.         }
  1881.         elseif ((isset($config['captiveportal'][$cpzone]['noconcurrentlogins'])) && ($username != 'unauthenticated')) {
  1882.             /* on the same username */
  1883.             if (strcasecmp($cpentry[4], $username) == 0) {
  1884.                 /* This user was already logged in so we disconnect the old one */
  1885.                 captiveportal_disconnect($cpentry,$radiusservers[$cpentry[11]],13);
  1886.                 captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - TERMINATING OLD SESSION");
  1887.                 $unsetindexes[] = $cpentry[5];
  1888.                 break;
  1889.             }
  1890.         }
  1891.     }
  1892.     unset($cpdb);
  1893.  
  1894.     if (!empty($unsetindexes))
  1895.         captiveportal_remove_entries($unsetindexes);
  1896.  
  1897.     if ($attributes['voucher'] && $remaining_time <= 0)
  1898.         return 0;       // voucher already used and no time left
  1899.  
  1900.     if (!isset($sessionid)) {
  1901.         /* generate unique session ID */
  1902.         $tod = gettimeofday();
  1903.         $sessionid = substr(md5(mt_rand() . $tod['sec'] . $tod['usec'] . $clientip . $clientmac), 0, 16);
  1904.  
  1905.         if ($passthrumac) {
  1906.             $mac = array();
  1907.             $mac['mac'] = $clientmac;
  1908.             $mac['ip'] = $clientip; /* Used only for logging */
  1909.             if (isset($config['captiveportal'][$cpzone]['passthrumacaddusername'])) {
  1910.                 $mac['username'] = $username;
  1911.                 if ($attributes['voucher'])
  1912.                     $mac['logintype'] = "voucher";
  1913.             }
  1914.             $mac['descr'] =  "Auto added pass-through MAC for user {$username}";
  1915.             if (!empty($bw_up))
  1916.                 $mac['bw_up'] = $bw_up;
  1917.             if (!empty($bw_down))
  1918.                 $mac['bw_down'] = $bw_down;
  1919.             if (!is_array($config['captiveportal'][$cpzone]['passthrumac']))
  1920.                 $config['captiveportal'][$cpzone]['passthrumac'] = array();
  1921.             $config['captiveportal'][$cpzone]['passthrumac'][] = $mac;
  1922.             unlock($cpdblck);
  1923.             $macrules = captiveportal_passthrumac_configure_entry($mac);
  1924.             file_put_contents("{$g['tmp_path']}/macentry_{$cpzone}.rules.tmp", $macrules);
  1925.             mwexec("/sbin/ipfw -x {$cpzone} -q {$g['tmp_path']}/macentry_{$cpzone}.rules.tmp");
  1926.             $writecfg = true;
  1927.         } else {
  1928.             /* See if a pipeno is passed, if not start sessions because this means there isn't one atm */
  1929.             if (is_null($pipeno))
  1930.                 $pipeno = captiveportal_get_next_dn_ruleno();
  1931.  
  1932.             /* if the pool is empty, return appropriate message and exit */
  1933.             if (is_null($pipeno)) {
  1934.                 portal_reply_page($redirurl, "error", "System reached maximum login capacity");
  1935.                 log_error("WARNING!  Captive portal has reached maximum login capacity");
  1936.                 unlock($cpdblck);
  1937.                 return;
  1938.             }
  1939.  
  1940.             $dwfaultbw_up = isset($config['captiveportal'][$cpzone]['bwdefaultup']) ? $config['captiveportal'][$cpzone]['bwdefaultup'] : 0;
  1941.             $dwfaultbw_down = isset($config['captiveportal'][$cpzone]['bwdefaultdn']) ? $config['captiveportal'][$cpzone]['bwdefaultdn'] : 0;
  1942.             $bw_up = isset($attributes['bw_up']) ? round(intval($attributes['bw_up'])/1000, 2) : $dwfaultbw_up;
  1943.             $bw_down = isset($attributes['bw_down']) ? round(intval($attributes['bw_down'])/1000, 2) : $dwfaultbw_down;
  1944.  
  1945.             $bw_up_pipeno = $pipeno;
  1946.             $bw_down_pipeno = $pipeno + 1;
  1947.             //$bw_up /= 1000; // Scale to Kbit/s
  1948.             $_gb = @pfSense_pipe_action("pipe {$bw_up_pipeno} config bw {$bw_up}Kbit/s queue 100 buckets 16");
  1949.             $_gb = @pfSense_pipe_action("pipe {$bw_down_pipeno} config bw {$bw_down}Kbit/s queue 100 buckets 16");
  1950.  
  1951.             $clientsn = (is_ipaddrv6($clientip)) ? 128 : 32;
  1952.             if (!isset($config['captiveportal'][$cpzone]['nomacfilter']))
  1953.                 $_gb = @pfSense_ipfw_Tableaction($cpzone, IP_FW_TABLE_ADD, 1, $clientip, $clientsn, $clientmac, $bw_up_pipeno);
  1954.             else
  1955.                 $_gb = @pfSense_ipfw_Tableaction($cpzone, IP_FW_TABLE_ADD, 1, $clientip, $clientsn, NULL, $bw_up_pipeno);
  1956.  
  1957.             if (!isset($config['captiveportal'][$cpzone]['nomacfilter']))
  1958.                 $_gb = @pfSense_ipfw_Tableaction($cpzone, IP_FW_TABLE_ADD, 2, $clientip, $clientsn, $clientmac, $bw_down_pipeno);
  1959.             else
  1960.                 $_gb = @pfSense_ipfw_Tableaction($cpzone, IP_FW_TABLE_ADD, 2, $clientip, $clientsn, NULL, $bw_down_pipeno);
  1961.  
  1962.             if ($attributes['voucher'])
  1963.                 $attributes['session_timeout'] = $remaining_time;
  1964.            
  1965.             /* handle empty attributes */
  1966.             $session_timeout = (!empty($attributes['session_timeout'])) ? $attributes['session_timeout'] : 'NULL';
  1967.             $idle_timeout = (!empty($attributes['idle_timeout'])) ? $attributes['idle_timeout'] : 'NULL';
  1968.             $session_terminate_time = (!empty($attributes['session_terminate_time'])) ? $attributes['session_terminate_time'] : 'NULL';
  1969.             $interim_interval = (!empty($attributes['interim_interval'])) ? $attributes['interim_interval'] : 'NULL';
  1970.  
  1971.             /* escape username */
  1972.             $safe_username = sqlite_escape_string($username);
  1973.  
  1974.             /* encode password in Base64 just in case it contains commas */
  1975.             $bpassword = base64_encode($password);
  1976.             $insertquery  = "INSERT INTO captiveportal (allow_time, pipeno, ip, mac, username, sessionid, bpassword, session_timeout, idle_timeout, session_terminate_time, interim_interval, radiusctx) ";
  1977.             $insertquery .= "VALUES ({$allow_time}, {$pipeno}, '{$clientip}', '{$clientmac}', '{$safe_username}', '{$sessionid}', '{$bpassword}', ";
  1978.             $insertquery .= "{$session_timeout}, {$idle_timeout}, {$session_terminate_time}, {$interim_interval}, '{$radiusctx}')";
  1979.  
  1980.             /* store information to database */
  1981.             captiveportal_write_db($insertquery);
  1982.             unlock($cpdblck);
  1983.             unset($insertquery, $bpassword);
  1984.  
  1985.             if (isset($config['captiveportal'][$cpzone]['radacct_enable']) && !empty($radiusservers[$radiusctx])) {
  1986.                 $acct_val = RADIUS_ACCOUNTING_START($pipeno, $username, $sessionid, $radiusservers[$radiusctx], $clientip, $clientmac);
  1987.                 if ($acct_val == 1)
  1988.                     captiveportal_logportalauth($username,$clientmac,$clientip,$type,"RADIUS ACCOUNTING FAILED");
  1989.             }
  1990.         }
  1991.     } else {
  1992.         /* NOTE: #3062-11 If the pipeno has been allocated free it to not DoS the CP and maintain proper operation as in radius() case */
  1993.         if (!is_null($pipeno))
  1994.             captiveportal_free_dn_ruleno($pipeno);
  1995.  
  1996.         unlock($cpdblck);
  1997.     }
  1998.  
  1999.     if ($writecfg == true)
  2000.         write_config();
  2001.  
  2002.     $timeout = 0;
  2003.     if (!empty($config['captiveportal'][$cpzone]['timeout']) && is_numeric($config['captiveportal'][$cpzone]['timeout'])) {
  2004.         $timeout = time() + $config['captiveportal'][$cpzone]['timeout'] * 60;
  2005.         setcookie("cookie_portal", $sessionid, $timeout);
  2006.     } else
  2007.         setcookie("cookie_portal", $sessionid, $timeout);  
  2008.        
  2009.     /* redirect user to desired destination */
  2010.     if (!empty($attributes['url_redirection']))
  2011.         $my_redirurl = $attributes['url_redirection'];
  2012.     else if (!empty($redirurl))
  2013.         $my_redirurl = $redirurl;
  2014.     else if (!empty($config['captiveportal'][$cpzone]['redirurl']))
  2015.         $my_redirurl = $config['captiveportal'][$cpzone]['redirurl'];
  2016.  
  2017.     if(isset($config['captiveportal'][$cpzone]['logoutwin_enable']) && !$passthrumac) {
  2018.         $ourhostname = portal_hostname_from_client_ip($clientip);
  2019.         $protocol = (isset($config['captiveportal'][$cpzone]['httpslogin'])) ? 'https://' : 'http://';
  2020.         $logouturl = "{$protocol}{$ourhostname}/";
  2021.  
  2022.         if (isset($attributes['reply_message']))
  2023.             $message = $attributes['reply_message'];
  2024.         else
  2025.             $message = 0;
  2026.  
  2027.         include("{$g['varetc_path']}/captiveportal-{$cpzone}-logout.html");
  2028.  
  2029.     } else {
  2030.         portal_reply_page($my_redirurl, "redir", "Just redirect the user.");
  2031.     }
  2032.  
  2033.     return $sessionid;
  2034. }
  2035.  
  2036.  
  2037. /*
  2038.  * Used for when pass-through credits are enabled.
  2039.  * Returns true when there was at least one free login to deduct for the MAC.
  2040.  * Expired entries are removed as they are seen.
  2041.  * Active entries are updated according to the configuration.
  2042.  */
  2043. function portal_consume_passthrough_credit($clientmac) {
  2044.     global $config, $cpzone;
  2045.  
  2046.     if (!empty($config['captiveportal'][$cpzone]['freelogins_count']) && is_numeric($config['captiveportal'][$cpzone]['freelogins_count']))
  2047.         $freeloginscount = $config['captiveportal'][$cpzone]['freelogins_count'];
  2048.     else
  2049.         return false;
  2050.  
  2051.     if (!empty($config['captiveportal'][$cpzone]['freelogins_resettimeout']) && is_numeric($config['captiveportal'][$cpzone]['freelogins_resettimeout']))
  2052.         $resettimeout = $config['captiveportal'][$cpzone]['freelogins_resettimeout'];
  2053.     else
  2054.         return false;
  2055.  
  2056.     if ($freeloginscount < 1 || $resettimeout <= 0 || !$clientmac)
  2057.         return false;
  2058.  
  2059.     $updatetimeouts = isset($config['captiveportal'][$cpzone]['freelogins_updatetimeouts']);
  2060.  
  2061.     /*
  2062.      * Read database of used MACs.  Lines are a comma-separated list
  2063.      * of the time, MAC, then the count of pass-through credits remaining.
  2064.      */
  2065.     $usedmacs = captiveportal_read_usedmacs_db();
  2066.  
  2067.     $currenttime = time();
  2068.     $found = false;
  2069.     foreach ($usedmacs as $key => $usedmac) {
  2070.         $usedmac = explode(",", $usedmac);
  2071.  
  2072.         if ($usedmac[1] == $clientmac) {
  2073.             if ($usedmac[0] + ($resettimeout * 3600) > $currenttime) {
  2074.                 if ($usedmac[2] < 1) {
  2075.                     if ($updatetimeouts) {
  2076.                         $usedmac[0] = $currenttime;
  2077.                         unset($usedmacs[$key]);
  2078.                         $usedmacs[] = implode(",", $usedmac);
  2079.                         captiveportal_write_usedmacs_db($usedmacs);
  2080.                     }
  2081.  
  2082.                     return false;
  2083.                 } else {
  2084.                     $usedmac[2] -= 1;
  2085.                     $usedmacs[$key] = implode(",", $usedmac);
  2086.                 }
  2087.  
  2088.                 $found = true;
  2089.             } else
  2090.                 unset($usedmacs[$key]);
  2091.  
  2092.             break;
  2093.         } else if ($usedmac[0] + ($resettimeout * 3600) <= $currenttime)
  2094.                 unset($usedmacs[$key]);
  2095.     }
  2096.  
  2097.     if (!$found) {
  2098.         $usedmac = array($currenttime, $clientmac, $freeloginscount - 1);
  2099.         $usedmacs[] = implode(",", $usedmac);
  2100.     }
  2101.  
  2102.     captiveportal_write_usedmacs_db($usedmacs);
  2103.     return true;
  2104. }
  2105.  
  2106. function captiveportal_read_usedmacs_db() {
  2107.     global $g, $cpzone;
  2108.  
  2109.     $cpumaclck = lock("captiveusedmacs{$cpzone}");
  2110.     if (file_exists("{$g['vardb_path']}/captiveportal_usedmacs_{$cpzone}.db")) {
  2111.         $usedmacs = file("{$g['vardb_path']}/captiveportal_usedmacs_{$cpzone}.db", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
  2112.         if (!$usedmacs)
  2113.             $usedmacs = array();
  2114.     } else
  2115.         $usedmacs = array();
  2116.  
  2117.     unlock($cpumaclck);
  2118.     return $usedmacs;
  2119. }
  2120.  
  2121. function captiveportal_write_usedmacs_db($usedmacs) {
  2122.     global $g, $cpzone;
  2123.  
  2124.     $cpumaclck = lock("captiveusedmacs{$cpzone}", LOCK_EX);
  2125.     @file_put_contents("{$g['vardb_path']}/captiveportal_usedmacs_{$cpzone}.db", implode("\n", $usedmacs));
  2126.     unlock($cpumaclck);
  2127. }
  2128.  
  2129. function captiveportal_send_server_accounting($off = false) {
  2130.     global $cpzone, $config;
  2131.  
  2132.     if (!isset($config['captiveportal'][$cpzone]['radacct_enable'])) {
  2133.         return;
  2134.     }
  2135.     if ($off) {
  2136.         $racct = new Auth_RADIUS_Acct_Off;
  2137.     } else {
  2138.         $racct = new Auth_RADIUS_Acct_On;
  2139.     }
  2140.     $radiusservers = captiveportal_get_radius_servers();
  2141.     if (empty($radiusservers)) {
  2142.         return;
  2143.     }
  2144.     foreach ($radiusservers['first'] as $radsrv) {
  2145.         // Add a new server to our instance
  2146.         $racct->addServer($radsrv['ipaddr'], $radsrv['acctport'], $radsrv['key']);
  2147.     }
  2148.     if (PEAR::isError($racct->start())) {
  2149.         $retvalue['acct_val'] = 1;
  2150.         $retvalue['error'] = $racct->getMessage();
  2151.  
  2152.         // If we encounter an error immediately stop this function and go back
  2153.         $racct->close();
  2154.         return $retvalue;
  2155.     }
  2156.     // Send request
  2157.     $result = $racct->send();
  2158.     // Evaluation of the response
  2159.     // 5 -> Accounting-Response
  2160.     // See RFC2866 for this.
  2161.     if (PEAR::isError($result)) {
  2162.         $retvalue['acct_val'] = 1;
  2163.         $retvalue['error'] = $result->getMessage();
  2164.     } else if ($result === true) {
  2165.         $retvalue['acct_val'] = 5 ;
  2166.     } else {
  2167.         $retvalue['acct_val'] = 1 ;
  2168.     }
  2169.    
  2170.     $racct->close();
  2171.     return $retvalue;
  2172. }
  2173.  
  2174. function captiveportal_isip_logged($clientip) {
  2175.     global $g, $cpzone;
  2176.     /* read in client database */
  2177.     $query = "WHERE ip = '{$clientip}'";
  2178.     $cpdb = captiveportal_read_db($query);
  2179.     foreach ($cpdb as $cpentry) {
  2180.         return $cpentry;
  2181.     }
  2182. }
  2183. ?>
Add Comment
Please, Sign In to add comment