Nowwhat47

/usr/local/www/services_captiveportal.php - v4 - 2019-05-21

Apr 18th, 2019
401
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
PHP 55.33 KB | None | 0 0
  1. <?php
  2. /*
  3.  * services_captiveportal.php
  4.  *
  5.  * part of pfSense (https://www.pfsense.org)
  6.  * Copyright (c) 2004-2018 Rubicon Communications, LLC (Netgate)
  7.  * All rights reserved.
  8.  *
  9.  * originally based on m0n0wall (http://m0n0.ch/wall)
  10.  * Copyright (c) 2003-2004 Manuel Kasper <mk@neon1.net>.
  11.  * All rights reserved.
  12.  *
  13.  * Licensed under the Apache License, Version 2.0 (the "License");
  14.  * you may not use this file except in compliance with the License.
  15.  * You may obtain a copy of the License at
  16.  *
  17.  * http://www.apache.org/licenses/LICENSE-2.0
  18.  *
  19.  * Unless required by applicable law or agreed to in writing, software
  20.  * distributed under the License is distributed on an "AS IS" BASIS,
  21.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  22.  * See the License for the specific language governing permissions and
  23.  * limitations under the License.
  24.  */
  25.  
  26. ##|+PRIV
  27. ##|*IDENT=page-services-captiveportal
  28. ##|*NAME=Services: Captive Portal
  29. ##|*DESCR=Allow access to the 'Services: Captive Portal' page.
  30. ##|*MATCH=services_captiveportal.php*
  31. ##|-PRIV
  32.  
  33. require_once("functions.inc");
  34. require_once("filter.inc");
  35. require_once("shaper.inc");
  36. require_once("captiveportal.inc");
  37.  
  38. if (substr($_REQUEST['act'], 0, 3) == "get") {
  39.     $nocsrf = true;
  40. }
  41.  
  42. require_once("guiconfig.inc");
  43.  
  44. global $cpzone;
  45. global $cpzoneid;
  46.  
  47. $cpzoneid = 1; /* Just a default */
  48. $cpzone = $_REQUEST['zone'];
  49.  
  50. $cpzone = strtolower($cpzone);
  51.  
  52. if (empty($cpzone) || empty($config['captiveportal'][$cpzone])) {
  53.     header("Location: services_captiveportal_zones.php");
  54.     exit;
  55. }
  56.  
  57. init_config_arr(array('captiveportal'));
  58. $a_cp = &$config['captiveportal'];
  59.  
  60. $pgtitle = array(gettext("Services"), gettext("Captive Portal"), $a_cp[$cpzone]['zone'], gettext("Configuration"));
  61. $pglinks = array("", "services_captiveportal_zones.php", "@self", "@self");
  62. $shortcut_section = "captiveportal";
  63.  
  64. if ($_REQUEST['act'] == "viewhtml") {
  65.     if ($a_cp[$cpzone] && $a_cp[$cpzone]['page']['htmltext']) {
  66.         echo base64_decode($a_cp[$cpzone]['page']['htmltext']);
  67.     }
  68.     exit;
  69. } else if ($_REQUEST['act'] == "gethtmlhtml" && $a_cp[$cpzone] && $a_cp[$cpzone]['page']['htmltext']) {
  70.     $file_data = base64_decode($a_cp[$cpzone]['page']['htmltext']);
  71.     $file_size = strlen($file_data);
  72.  
  73.     header("Content-Type: text/html");
  74.     header("Content-Disposition: attachment; filename=portal.html");
  75.     header("Content-Length: $file_size");
  76.     echo $file_data;
  77.  
  78.     exit;
  79. } else if ($_REQUEST['act'] == "delhtmlhtml" && $a_cp[$cpzone] && $a_cp[$cpzone]['page']['htmltext']) {
  80.     unset($a_cp[$cpzone]['page']['htmltext']);
  81.     write_config(sprintf(gettext("Captive Portal: zone %s: Restore default portal page"), $cpzone));
  82.     header("Location: services_captiveportal.php?zone={$cpzone}");
  83.     exit;
  84. } else if ($_REQUEST['act'] == "viewerrhtml") {
  85.     if ($a_cp[$cpzone] && $a_cp[$cpzone]['page']['errtext']) {
  86.         echo base64_decode($a_cp[$cpzone]['page']['errtext']);
  87.     }
  88.     exit;
  89. } else if ($_REQUEST['act'] == "geterrhtml" && $a_cp[$cpzone] && $a_cp[$cpzone]['page']['errtext']) {
  90.     $file_data = base64_decode($a_cp[$cpzone]['page']['errtext']);
  91.     $file_size = strlen($file_data);
  92.  
  93.     header("Content-Type: text/html");
  94.     header("Content-Disposition: attachment; filename=err.html");
  95.     header("Content-Length: $file_size");
  96.     echo $file_data;
  97.  
  98.     exit;
  99. } else if ($_REQUEST['act'] == "delerrhtml" && $a_cp[$cpzone] && $a_cp[$cpzone]['page']['errtext']) {
  100.     unset($a_cp[$cpzone]['page']['errtext']);
  101.     write_config(sprintf(gettext("Captive Portal: zone %s: Restore default error page"), $cpzone));
  102.     header("Location: services_captiveportal.php?zone={$cpzone}");
  103.     exit;
  104. } else if ($_REQUEST['act'] == "viewlogouthtml") {
  105.     if ($a_cp[$cpzone] && $a_cp[$cpzone]['page']['logouttext']) {
  106.         echo base64_decode($a_cp[$cpzone]['page']['logouttext']);
  107.     }
  108.     exit;
  109. } else if ($_REQUEST['act'] == "getlogouthtml" && $a_cp[$cpzone] && $a_cp[$cpzone]['page']['logouttext']) {
  110.     $file_data = base64_decode($a_cp[$cpzone]['page']['logouttext']);
  111.     $file_size = strlen($file_data);
  112.  
  113.     header("Content-Type: text/html");
  114.     header("Content-Disposition: attachment; filename=logout.html");
  115.     header("Content-Length: $file_size");
  116.     echo $file_data;
  117.  
  118.     exit;
  119. } else if ($_REQUEST['act'] == "dellogouthtml" && $a_cp[$cpzone] && $a_cp[$cpzone]['page']['logouttext']) {
  120.     unset($a_cp[$cpzone]['page']['logouttext']);
  121.     write_config(sprintf(gettext("Captive Portal: zone %s: Restore default logout page"), $cpzone));
  122.     header("Location: services_captiveportal.php?zone={$cpzone}");
  123.     exit;
  124. }
  125.  
  126. init_config_arr(array('ca'));
  127. $a_ca = &$config['ca'];
  128.  
  129. init_config_arr(array('cert'));
  130. $a_cert = &$config['cert'];
  131.  
  132. if ($a_cp[$cpzone]) {
  133.     $cpzoneid = $pconfig['zoneid'] = $a_cp[$cpzone]['zoneid'];
  134.     $pconfig['descr'] = $a_cp[$cpzone]['descr'];
  135.     $pconfig['cinterface'] = $a_cp[$cpzone]['interface'];
  136.     $pconfig['maxproc'] = $a_cp[$cpzone]['maxproc'];
  137.     $pconfig['maxprocperip'] = $a_cp[$cpzone]['maxprocperip'];
  138.     $pconfig['timeout'] = $a_cp[$cpzone]['timeout'];
  139.     $pconfig['idletimeout'] = $a_cp[$cpzone]['idletimeout'];
  140.     $pconfig['trafficquota'] = $a_cp[$cpzone]['trafficquota'];
  141.     $pconfig['freelogins_count'] = $a_cp[$cpzone]['freelogins_count'];
  142.     $pconfig['freelogins_resettimeout'] = $a_cp[$cpzone]['freelogins_resettimeout'];
  143.     $pconfig['freelogins_updatetimeouts'] = isset($a_cp[$cpzone]['freelogins_updatetimeouts']);
  144.     $pconfig['enable'] = isset($a_cp[$cpzone]['enable']);
  145.     $pconfig['auth_method'] = $a_cp[$cpzone]['auth_method'];
  146.     $pconfig['auth_server'] = explode(",", $a_cp[$cpzone]['auth_server']);
  147.     $pconfig['auth_server2'] = explode(",", $a_cp[$cpzone]['auth_server2']);
  148.     $pconfig['localauth_priv'] = isset($a_cp[$cpzone]['localauth_priv']);
  149.     $pconfig['radacct_server'] = $a_cp[$cpzone]['radacct_server'];
  150.     $pconfig['radacct_enable'] = isset($a_cp[$cpzone]['radacct_enable']);
  151.     $pconfig['radmac_secret'] = $a_cp[$cpzone]['radmac_secret'];
  152.     $pconfig['radmac_fallback'] = isset($a_cp[$cpzone]['radmac_fallback']);
  153.     $pconfig['reauthenticate'] = isset($a_cp[$cpzone]['reauthenticate']);
  154.     $pconfig['preservedb'] = isset($a_cp[$cpzone]['preservedb']);
  155.     $pconfig['reauthenticateacct'] = $a_cp[$cpzone]['reauthenticateacct'];
  156.     $pconfig['httpslogin_enable'] = isset($a_cp[$cpzone]['httpslogin']);
  157.     $pconfig['httpsname'] = $a_cp[$cpzone]['httpsname'];
  158.     $pconfig['preauthurl'] = strtolower($a_cp[$cpzone]['preauthurl']);
  159.     $pconfig['blockedmacsurl'] = strtolower($a_cp[$cpzone]['blockedmacsurl']);
  160.     $pconfig['certref'] = $a_cp[$cpzone]['certref'];
  161.     $pconfig['nohttpsforwards'] = isset($a_cp[$cpzone]['nohttpsforwards']);
  162.     $pconfig['logoutwin_enable'] = isset($a_cp[$cpzone]['logoutwin_enable']);
  163.     $pconfig['peruserbw'] = isset($a_cp[$cpzone]['peruserbw']);
  164.     $pconfig['bwdefaultdn'] = $a_cp[$cpzone]['bwdefaultdn'];
  165.     $pconfig['bwdefaultup'] = $a_cp[$cpzone]['bwdefaultup'];
  166.     $pconfig['nomacfilter'] = isset($a_cp[$cpzone]['nomacfilter']);
  167. // edit
  168.     if ( isset($a_cp[$cpzone]['noconcurrentlogins']) )  {
  169.         $pconfig['noconcurrentlogins'] = $a_cp[$cpzone]['noconcurrentlogins'];
  170.     } else {
  171.         $pconfig['noconcurrentlogins'] = 'multiple';
  172.     }
  173. //  $pconfig['noconcurrentlogins'] = isset($a_cp[$cpzone]['noconcurrentlogins']);
  174. // edit
  175.     $pconfig['redirurl'] = $a_cp[$cpzone]['redirurl'];
  176.     $pconfig['radiussession_timeout'] = isset($a_cp[$cpzone]['radiussession_timeout']);
  177.     $pconfig['radiustraffic_quota'] = isset($a_cp[$cpzone]['radiustraffic_quota']);
  178.     $pconfig['radiusperuserbw'] = isset($a_cp[$cpzone]['radiusperuserbw']);
  179.     $pconfig['passthrumacadd'] = isset($a_cp[$cpzone]['passthrumacadd']);
  180.     $pconfig['radmac_format'] = $a_cp[$cpzone]['radmac_format'];
  181.     $pconfig['reverseacct'] = isset($a_cp[$cpzone]['reverseacct']);
  182.     $pconfig['includeidletime'] = isset($a_cp[$cpzone]['includeidletime']);
  183.     $pconfig['radiusnasid'] = $a_cp[$cpzone]['radiusnasid'];
  184.     $pconfig['page'] = array();
  185.     if ($a_cp[$cpzone]['page']['htmltext']) {
  186.         $pconfig['page']['htmltext'] = $a_cp[$cpzone]['page']['htmltext'];
  187.     }
  188.     if ($a_cp[$cpzone]['page']['errtext']) {
  189.         $pconfig['page']['errtext'] = $a_cp[$cpzone]['page']['errtext'];
  190.     }
  191.     if ($a_cp[$cpzone]['page']['logouttext']) {
  192.         $pconfig['page']['logouttext'] = $a_cp[$cpzone]['page']['logouttext'];
  193.     }
  194.     $pconfig['customlogo'] = isset($a_cp[$cpzone]['customlogo']);
  195.     $pconfig['custombg'] = isset($a_cp[$cpzone]['custombg']);
  196.     $pconfig['customhtml'] = isset($pconfig['page']['htmltext']);
  197.     $pconfig['termsconditions'] = base64_decode($a_cp[$cpzone]['termsconditions']);
  198. }
  199.  
  200. if ($_POST['save']) {
  201.  
  202.     unset($input_errors);
  203.     $pconfig = $_POST;
  204.  
  205.     /* input validation */
  206.     if ($_POST['enable']) {
  207.         $reqdfields = explode(" ", "zone cinterface auth_method");
  208.         $reqdfieldsn = array(gettext("Zone name"), gettext("Interface"), gettext("Authentication method"));
  209.  
  210.         do_input_validation($_POST, $reqdfields, $reqdfieldsn, $input_errors);
  211.  
  212.         /* make sure no interfaces are bridged or used on other zones */
  213.         if (is_array($_POST['cinterface'])) {
  214.             foreach ($pconfig['cinterface'] as $cpbrif) {
  215.                 if (link_interface_to_bridge($cpbrif)) {
  216.                     $input_errors[] = sprintf(gettext("The captive portal cannot be used on interface %s since it is part of a bridge."), $cpbrif);
  217.                 }
  218.                 foreach ($a_cp as $cpkey => $cp) {
  219.                     if ($cpkey != $cpzone || empty($cpzone)) {
  220.                         if (in_array($cpbrif, explode(",", $cp['interface']))) {
  221.                             $input_errors[] = sprintf(gettext('The captive portal cannot be used on interface %1$s since it is used already on %2$s instance.'), $cpbrif, $cp['zone']);
  222.                         }
  223.                     }
  224.                 }
  225.             }
  226.         }
  227.  
  228.         if ($_POST['auth_method'] && !in_array($_POST['auth_method'], array('none', 'authserver', 'radmac'))) {
  229.             $input_errors[] = gettext("Authentication method is invalid.");
  230.         }
  231.  
  232.         if ($_POST['httpslogin_enable']) {
  233.             if (!$_POST['certref']) {
  234.                 $input_errors[] = gettext("Certificate must be specified for HTTPS login.");
  235.             }
  236.             if (!$_POST['httpsname'] || !is_domain($_POST['httpsname'])) {
  237.                 $input_errors[] = gettext("The HTTPS server name must be specified for HTTPS login.");
  238.             }
  239.         }
  240.     }
  241.  
  242.     if ($_POST['timeout']) {
  243.         if (!is_numeric($_POST['timeout']) || ($_POST['timeout'] < 1)) {
  244.             $input_errors[] = gettext("The timeout must be at least 1 minute.");
  245.         } else if (isset($config['dhcpd']) && is_array($config['dhcpd'])) {
  246.             foreach ($config['dhcpd'] as $dhcpd_if => $dhcpd_data) {
  247.                 if (!isset($dhcpd_data['enable'])) {
  248.                     continue;
  249.                 }
  250.                 if (!is_array($_POST['cinterface']) || !in_array($dhcpd_if, $_POST['cinterface'])) {
  251.                     continue;
  252.                 }
  253.  
  254.                 $deftime = 7200; // Default lease time
  255.                 if (isset($dhcpd_data['defaultleasetime']) && is_numeric($dhcpd_data['defaultleasetime'])) {
  256.                     $deftime = $dhcpd_data['defaultleasetime'];
  257.                 }
  258.  
  259.                 if ($_POST['timeout'] > $deftime) {
  260.                     $input_errors[] = gettext("Hard timeout must be less than or equal to the Default lease time set on DHCP Server");
  261.                 }
  262.             }
  263.         }
  264.     }
  265.  
  266.     if ($_POST['idletimeout'] && (!is_numeric($_POST['idletimeout']) || ($_POST['idletimeout'] < 1))) {
  267.         $input_errors[] = gettext("The idle timeout must be at least 1 minute.");
  268.     }
  269.  
  270.     if ($_POST['trafficquota'] && (!is_numeric($_POST['trafficquota']) || ($_POST['trafficquota'] < 1))) {
  271.         $input_errors[] = gettext("The traffic quota must be at least 1 megabyte.");
  272.     }
  273.  
  274.     if ($_POST['freelogins_count'] && (!is_numeric($_POST['freelogins_count']))) {
  275.         $input_errors[] = gettext("The pass-through credit count must be a number or left blank.");
  276.     } else if ($_POST['freelogins_count'] && is_numeric($_POST['freelogins_count']) && ($_POST['freelogins_count'] >= 1)) {
  277.         if (empty($_POST['freelogins_resettimeout']) || !is_numeric($_POST['freelogins_resettimeout']) || ($_POST['freelogins_resettimeout'] <= 0)) {
  278.             $input_errors[] = gettext("The waiting period to restore pass-through credits must be above 0 hours.");
  279.         }
  280.     }
  281.  
  282.     if ($_POST['maxproc'] && (!is_numeric($_POST['maxproc']) || ($_POST['maxproc'] < 4) || ($_POST['maxproc'] > 100))) {
  283.         $input_errors[] = gettext("The maximum number of concurrent connections per client IP address may not be larger than the global maximum.");
  284.     }
  285.  
  286.     if ($_POST['auth_method']) {
  287.         if ($_POST['auth_method'] !== 'none' && empty($_POST['auth_server'])) {
  288.             $input_errors[] = gettext("You need to select at least one authentication server.");
  289.         }
  290.         /* If RADMAC auth method is selected : carefully check that the selected server is a RADIUS one */
  291.         if ($_POST['auth_method'] === 'radmac') {
  292.             foreach ($_POST['auth_server'] as $server) {
  293.  
  294.                 $realauthserver = explode(' - ', $server);
  295.                 array_shift($realauthserver);
  296.                 $realauthserver = implode(' - ', $realauthserver);
  297.                 $realauthserver = auth_get_authserver($realauthserver);
  298.  
  299.                 if ($realauthserver === null || $realauthserver['type'] !== 'radius') {
  300.                     $input_errors[] = gettext("RADIUS MAC Authentication can only be performed on a RADIUS server.");
  301.                 }
  302.             }
  303.  
  304.             if (isset($_POST['nomacfilter'])) {
  305.                 $input_errors[] = gettext("RADIUS MAC Authentication cannot be used if MAC filtering is disabled");
  306.             }
  307.         }
  308.     }
  309.  
  310. // edit
  311.  
  312.     if (isset($_POST['noconcurrentlogins']) && !in_array($_POST['noconcurrentlogins'], array('multiple', 'last', 'first'))) {
  313.         $input_errors[] = gettext("You need to select an option for Concurrent user logins !");
  314.     }
  315.  
  316. // edit
  317.  
  318.     if (isset($_POST['radacct_enable']) && empty(auth_get_authserver($_POST['radacct_server']))) {
  319.         $input_errors[] = gettext("You need to select at least one accounting server.");
  320.     }
  321.     if (isset($_POST['radacct_enable']) && !in_array($_POST['reauthenticateacct'], array('none', 'stopstart', 'stopstartfreeradius', 'interimupdate'))) {
  322.         $input_errors[] = gettext("You need to select an option for Accounting Updates !");
  323.     }
  324.     if (trim($_POST['radiusnasid']) !== "" && !preg_match("/^[\x21-\x7e]{3,253}$/i", trim($_POST['radiusnasid']))) {
  325.         $input_errors[] = gettext("The NAS-Identifier must be 3-253 characters long and should only contain ASCII characters.");
  326.     }
  327.  
  328.     if (!$input_errors) {
  329.         init_config_arr(array('captiveportal', $cpzone));
  330.         $newcp = &$a_cp[$cpzone];
  331.         //$newcp['zoneid'] = $a_cp[$cpzone]['zoneid'];
  332.         if (empty($newcp['zoneid'])) {
  333.             $newcp['zoneid'] = 2;
  334.             foreach ($a_cp as $keycpzone => $cp) {
  335.                 if ($cp['zoneid'] == $newcp['zoneid'] && $keycpzone != $cpzone) {
  336.                     $newcp['zoneid'] += 2; /* Reserve space for SSL config if needed */
  337.                 }
  338.             }
  339.  
  340.             $cpzoneid = $newcp['zoneid'];
  341.         }
  342.         if (is_array($_POST['cinterface'])) {
  343.             $newcp['interface'] = implode(",", $_POST['cinterface']);
  344.         }
  345.         $newcp['descr'] = $_POST['descr'];
  346.         $newcp['maxproc'] = $_POST['maxproc'];
  347.         $newcp['maxprocperip'] = $_POST['maxprocperip'] ? $_POST['maxprocperip'] : false;
  348.         $newcp['timeout'] = $_POST['timeout'];
  349.         $newcp['idletimeout'] = $_POST['idletimeout'];
  350.         $newcp['trafficquota'] = $_POST['trafficquota'];
  351.         $newcp['freelogins_count'] = $_POST['freelogins_count'];
  352.         $newcp['freelogins_resettimeout'] = $_POST['freelogins_resettimeout'];
  353.         $newcp['freelogins_updatetimeouts'] = $_POST['freelogins_updatetimeouts'] ? true : false;
  354.         if ($_POST['enable']) {
  355.             $newcp['enable'] = true;
  356.         } else {
  357.             unset($newcp['enable']);
  358.         }
  359.         $newcp['auth_method'] = $_POST['auth_method'];
  360.         $newcp['auth_server'] = '';
  361.         if ($_POST['auth_method'] != 'none') {
  362.             $newcp['auth_server'] = implode(",", $_POST['auth_server']);
  363.         }
  364.         $newcp['auth_server2'] = '';
  365.         if (!empty($_POST['auth_server2']) && $_POST['auth_method'] === 'authserver') {
  366.             $newcp['auth_server2'] = implode(",", $_POST['auth_server2']);
  367.         }
  368.         $newcp['radacct_server'] = $_POST['radacct_server'];
  369.         $newcp['localauth_priv'] = isset($_POST['localauth_priv']);
  370.         $newcp['radacct_enable'] = $_POST['radacct_enable'] ? true : false;
  371.         $newcp['reauthenticate'] = $_POST['reauthenticate'] ? true : false;
  372.         $newcp['preservedb'] = $_POST['preservedb'] ? true : false;
  373.         $newcp['radmac_secret'] = $_POST['radmac_secret'] ? $_POST['radmac_secret'] : false;
  374.         $newcp['radmac_fallback'] = $_POST['radmac_fallback'] ? true : false;
  375.         $newcp['reauthenticateacct'] = $_POST['reauthenticateacct'];
  376.         if ($_POST['httpslogin_enable']) {
  377.             $newcp['httpslogin'] = true;
  378.         } else {
  379.             unset($newcp['httpslogin']);
  380.         }
  381.         $newcp['httpsname'] = $_POST['httpsname'];
  382.         $newcp['preauthurl'] = $_POST['preauthurl'];
  383.         $newcp['blockedmacsurl'] = $_POST['blockedmacsurl'];
  384.         $newcp['peruserbw'] = $_POST['peruserbw'] ? true : false;
  385.         if (isset($_POST['bwdefaultdn'])) {
  386.             $newcp['bwdefaultdn'] = $_POST['bwdefaultdn'];
  387.         } else {
  388.             unset($newcp['bwdefaultdn']);
  389.         }
  390.         if (isset($_POST['bwdefaultup'])) {
  391.             $newcp['bwdefaultup'] = $_POST['bwdefaultup'];
  392.         } else {
  393.             unset($newcp['bwdefaultup']);
  394.         }
  395.         $newcp['certref'] = $_POST['certref'];
  396.         $newcp['nohttpsforwards'] = $_POST['nohttpsforwards'] ? true : false;
  397.         $newcp['logoutwin_enable'] = $_POST['logoutwin_enable'] ? true : false;
  398.         $newcp['nomacfilter'] = $_POST['nomacfilter'] ? true : false;
  399. //edit     
  400. //      $newcp['noconcurrentlogins'] = $_POST['noconcurrentlogins'] ? true : false;
  401.         if ($_POST['noconcurrentlogins'] == 'multiple') {
  402.             unset($newcp['noconcurrentlogins']);
  403.         } else {
  404.             $newcp['noconcurrentlogins'] = $_POST['noconcurrentlogins'];
  405.         }
  406.        
  407. //edit
  408.         $newcp['redirurl'] = $_POST['redirurl'];
  409.         $newcp['radiussession_timeout'] = $_POST['radiussession_timeout'] ? true : false;
  410.         $newcp['radiustraffic_quota'] = $_POST['radiustraffic_quota'] ? true : false;
  411.         $newcp['radiusperuserbw'] = $_POST['radiusperuserbw'] ? true : false;
  412.         $newcp['passthrumacadd'] = $_POST['passthrumacadd'] ? true : false;
  413.         $newcp['radmac_format'] = $_POST['radmac_format'] ? $_POST['radmac_format'] : false;
  414.         $newcp['reverseacct'] = $_POST['reverseacct'] ? true : false;
  415.         $newcp['includeidletime'] = $_POST['includeidletime'] ? true : false;
  416.         $newcp['radiusnasid'] = trim($_POST['radiusnasid']);
  417.         if ($_POST['customhtml']) {
  418.             $newcp['customhtml'] = true;
  419.         } else {
  420.             unset($newcp['customhtml']);
  421.         }
  422.         if ($_POST['customlogo']) {
  423.             $newcp['customlogo'] = true;
  424.         } else {
  425.             unset($newcp['customlogo']);
  426.         }
  427.         if ($_POST['custombg']) {
  428.             $newcp['custombg'] = true;
  429.         } else {
  430.             unset($newcp['custombg']);
  431.         }
  432.         $newcp['termsconditions'] = base64_encode(strip_tags($_POST['termsconditions']));
  433.         if (!is_array($newcp['page'])) {
  434.             $newcp['page'] = array();
  435.         }
  436.  
  437.         /* file upload? */
  438.         if (is_uploaded_file($_FILES['htmlfile']['tmp_name'])) {
  439.             $newcp['page']['htmltext'] = base64_encode(file_get_contents($_FILES['htmlfile']['tmp_name']));
  440.         }
  441.         if (is_uploaded_file($_FILES['errfile']['tmp_name'])) {
  442.             $newcp['page']['errtext'] = base64_encode(file_get_contents($_FILES['errfile']['tmp_name']));
  443.         }
  444.         if (is_uploaded_file($_FILES['logoutfile']['tmp_name'])) {
  445.             $newcp['page']['logouttext'] = base64_encode(file_get_contents($_FILES['logoutfile']['tmp_name']));
  446.         }
  447.  
  448.         // Check for uploaded images for the default CP login
  449.         if (is_uploaded_file($_FILES['logo-img']['tmp_name'])) {
  450.             $ext = pathinfo($_FILES['logo-img']['name'],PATHINFO_EXTENSION);
  451.             $logo_name = "captiveportal-logo." . $ext;
  452.             for ($i = 0; $i < count($a_cp[$cpzone]['element']); $i++) {
  453.                 if (strpos($a_cp[$cpzone]['element'][$i]['name'], "captiveportal-logo.") !== false){
  454.                     // remove old image before replacing it.
  455.                     @unlink("{$g['captiveportal_element_path']}/" . $a_cp[$cpzone]['element'][$i]['name']);
  456.                     @unlink("{$g['captiveportal_path']}/" . $a_cp[$cpzone]['element'][$i]['name']);
  457.                     unset($a_cp[$cpzone]['element'][$i]);
  458.                 }
  459.             }
  460.             $element = array();
  461.             $element['name'] = $logo_name;
  462.             $element['size'] = filesize($_FILES['logo-img']['tmp_name']);
  463.             // Set this so it will still show up in file manager but won't be
  464.             // deleted for having no content.
  465.             $element['nocontent'] = true;
  466.             $newcp['element'][] = $element;
  467.             $target = "{$g['captiveportal_path']}/" . $logo_name;
  468.             move_uploaded_file( $_FILES['logo-img']['tmp_name'], $target);
  469.         }
  470.         if (is_uploaded_file($_FILES['background-img']['tmp_name'])) {
  471.             $ext = pathinfo($_FILES['background-img']['name'],PATHINFO_EXTENSION);
  472.             $background_name = "captiveportal-background." . $ext;
  473.             // is there already a file with that name?
  474.             for ($i = 0; $i < count($a_cp[$cpzone]['element']); $i++) {
  475.                 if (strpos($a_cp[$cpzone]['element'][$i]['name'], "captiveportal-background.") !== false){
  476.                     // remove old image and replace it.
  477.                     @unlink("{$g['captiveportal_element_path']}/" . $a_cp[$cpzone]['element'][$i]['name']);
  478.                     @unlink("{$g['captiveportal_path']}/" . $a_cp[$cpzone]['element'][$i]['name']);
  479.                     unset($a_cp[$cpzone]['element'][$i]);
  480.                 }
  481.             }
  482.  
  483.             $element = array();
  484.             $element['name'] = $background_name;
  485.             $element['size'] = filesize($_FILES['background-img']['tmp_name']);
  486.             // Set this so it will still show up in file manager but won't be
  487.             // deleted for having no content.
  488.             $element['nocontent'] = true;
  489.             $newcp['element'][] = $element;
  490.             $target = "{$g['captiveportal_path']}/" . $background_name;
  491.             move_uploaded_file( $_FILES['background-img']['tmp_name'], $target);
  492.         }
  493.  
  494.         write_config();
  495.  
  496.         captiveportal_configure_zone($newcp);
  497.         unset($newcp);
  498.         filter_configure();
  499.         header("Location: services_captiveportal_zones.php");
  500.         exit;
  501.     } else {
  502.         if (is_array($_POST['cinterface'])) {
  503.             $pconfig['cinterface'] = implode(",", $_POST['cinterface']);
  504.         }
  505.     }
  506. }
  507.  
  508. function build_cert_list() {
  509.     global $a_cert;
  510.  
  511.     $list = array();
  512.  
  513.     foreach ($a_cert as $cert) {
  514.         $list[$cert['refid']] = $cert['descr'];
  515.     }
  516.  
  517.     return($list);
  518. }
  519.  
  520. function build_authserver_list() {
  521.  
  522.     $authlist = auth_get_authserver_list();
  523.     $options = array();
  524.  
  525.     /* auth types are used by javascript */
  526.     foreach ($authlist as $i => $auth) {
  527.         if ($auth['type'] != 'radius' || $auth['type'] == 'radius' && !empty($auth['radius_auth_port'])) {
  528.             $options[$auth['type'].' - '.$auth['name']] = $auth['name'];
  529.         }
  530.     }
  531.     return $options;
  532. }
  533.  
  534. include("head.inc");
  535.  
  536. if ($input_errors) {
  537.     print_input_errors($input_errors);
  538. }
  539.  
  540. $tab_array = array();
  541. $tab_array[] = array(gettext("Configuration"), true, "services_captiveportal.php?zone={$cpzone}");
  542. $tab_array[] = array(gettext("MACs"), false, "services_captiveportal_mac.php?zone={$cpzone}");
  543. $tab_array[] = array(gettext("Allowed IP Addresses"), false, "services_captiveportal_ip.php?zone={$cpzone}");
  544. $tab_array[] = array(gettext("Allowed Hostnames"), false, "services_captiveportal_hostname.php?zone={$cpzone}");
  545. $tab_array[] = array(gettext("Vouchers"), false, "services_captiveportal_vouchers.php?zone={$cpzone}");
  546. $tab_array[] = array(gettext("File Manager"), false, "services_captiveportal_filemanager.php?zone={$cpzone}");
  547. display_top_tabs($tab_array, true);
  548.  
  549. $form = new Form();
  550. $form->setMultipartEncoding();
  551.  
  552. $section = new Form_Section('Captive Portal Configuration');
  553.  
  554. $section->addInput(new Form_Checkbox(
  555.     'enable',
  556.     'Enable',
  557.     'Enable Captive Portal',
  558.     $pconfig['enable']
  559. ));
  560.  
  561. $section->addInput(new Form_Input(
  562.     'descr',
  563.     'Description',
  564.     'text',
  565.     $pconfig['descr']
  566. ))->setHelp('A description may be entered here for administrative reference (not parsed).');
  567.  
  568. $section->addInput(new Form_Select(
  569.     'cinterface',
  570.     '*Interfaces',
  571.     explode(",", $pconfig['cinterface']),
  572.     get_configured_interface_with_descr(),
  573.     true
  574. ))->addClass('general')->setHelp('Select the interface(s) to enable for captive portal.');
  575.  
  576. $section->addInput(new Form_Input(
  577.     'maxprocperip',
  578.     'Maximum concurrent connections',
  579.     'number',
  580.     $pconfig['maxprocperip'],
  581.     ['min' => '0', 'max' => '100']
  582. ))->setHelp('Limits the number of concurrent connections to the captive portal HTTP(S) server. This does not set how many users can be logged in ' .
  583.             'to the captive portal, but rather how many connections a single IP can establish to the portal web server.');
  584.  
  585. $section->addInput(new Form_Input(
  586.     'idletimeout',
  587.     'Idle timeout (Minutes)',
  588.     'number',
  589.     $pconfig['idletimeout']
  590. ))->setHelp('Clients will be disconnected after this amount of inactivity. They may log in again immediately, though. Leave this field blank for no idle timeout.');
  591.  
  592. $section->addInput(new Form_Input(
  593.     'timeout',
  594.     'Hard timeout (Minutes)',
  595.     'number',
  596.     $pconfig['timeout']
  597. ))->setHelp('Clients will be disconnected after this amount of time, regardless of activity. They may log in again immediately, though. ' .
  598.             'Leave this field blank for no hard timeout (not recommended unless an idle timeout is set).');
  599.  
  600. $section->addInput(new Form_Input(
  601.     'trafficquota',
  602.     'Traffic quota (Megabytes)',
  603.     'number',
  604.     $pconfig['trafficquota']
  605. ))->setHelp('Clients will be disconnected after exceeding this amount of traffic, inclusive of both downloads and uploads. They may log in again immediately, though. ' .
  606.             'Leave this field blank for no traffic quota.');
  607.  
  608. $section->addInput(new Form_Input(
  609.     'freelogins_count',
  610.     'Pass-through credits per MAC address.',
  611.     'number',
  612.     $pconfig['freelogins_count']
  613. ))->setHelp('Allows passing through the captive portal without authentication a limited number of times per MAC address. Once used up, ' .
  614.             'the client can only log in with valid credentials until the waiting period specified below has expired. Recommended to set ' .
  615.             'a hard timeout and/or idle timeout when using this for it to be effective.');
  616.  
  617. $section->addInput(new Form_Input(
  618.     'freelogins_resettimeout',
  619.     'Waiting period to restore pass-through credits. (Hours)',
  620.     'number',
  621.     $pconfig['freelogins_resettimeout']
  622. ))->setHelp('Clients will have their available pass-through credits restored to the original count after this amount of time since using the first one. ' .
  623.             'This must be above 0 hours if pass-through credits are enabled.');
  624.  
  625. $section->addInput(new Form_Checkbox(
  626.     'freelogins_updatetimeouts',
  627.     'Reset waiting period',
  628.     'Enable waiting period reset on attempted access',
  629.     $pconfig['freelogins_updatetimeouts']
  630. ))->setHelp('If enabled, the waiting period is reset to the original duration if access is attempted when all pass-through credits have already been exhausted.');
  631.  
  632. $section->addInput(new Form_Checkbox(
  633.     'logoutwin_enable',
  634.     'Logout popup window',
  635.     'Enable logout popup window',
  636.     $pconfig['logoutwin_enable']
  637. ))->setHelp('If enabled, a popup window will appear when clients are allowed through the captive portal. ' .
  638.             'This allows clients to explicitly disconnect themselves before the idle or hard timeout occurs.');
  639.  
  640. $section->addInput(new Form_Input(
  641.     'preauthurl',
  642.     'Pre-authentication redirect URL',
  643.     'text',
  644.     $pconfig['preauthurl']
  645. ))->setHelp('Set a default redirection URL. Visitors will be redirected to this URL after authentication only if the captive portal don\'t know where to redirect them. This field will be accessible through $PORTAL_REDIRURL$ variable in captiveportal\'s HTML pages.');
  646.  
  647. $section->addInput(new Form_Input(
  648.     'redirurl',
  649.     'After authentication Redirection URL',
  650.     'text',
  651.     $pconfig['redirurl']
  652. ))->setHelp('Set a forced redirection URL. Clients will be redirected to this URL instead of the one they initially tried to access after they\'ve authenticated.');
  653.  
  654. $section->addInput(new Form_Input(
  655.     'blockedmacsurl',
  656.     'Blocked MAC address redirect URL',
  657.     'text',
  658.     $pconfig['blockedmacsurl']
  659. ))->setHelp('Blocked MAC addresses will be redirected to this URL when attempting access.');
  660.  
  661. $section->addInput(new Form_Checkbox(
  662.     'preservedb',
  663.     'Preserve users database',
  664.     'Preserve connected users across reboot',
  665.     $pconfig['preservedb']
  666. ))->setHelp('If enabled, connected users won\'t be disconnected during a pfSense reboot.');
  667.  
  668. $group = new Form_Group('Concurrent user logins');
  669.  
  670. $group->addClass('noconcurrentlogins');
  671.  
  672. $group->add(new Form_Checkbox(
  673.     'noconcurrentlogins',
  674.     null,
  675.     'Multiple',
  676.     $pconfig['noconcurrentlogins'] == 'multiple',
  677.     "multiple"
  678. ))->displayasRadio();
  679.  
  680. $group->add(new Form_Checkbox(
  681.     'noconcurrentlogins',
  682.     null,
  683.     'Last login',
  684.     $pconfig['noconcurrentlogins'] == 'last',
  685.     "last"
  686. ))->displayasRadio();
  687.  
  688. $group->add(new Form_Checkbox(
  689.     'noconcurrentlogins',
  690.     null,
  691.     'First login',
  692.     $pconfig['noconcurrentlogins'] == 'first',
  693.     "first"
  694. ))->displayasRadio();
  695.  
  696. $group->setHelp('Multiple : no restrictions to the number of logins per username or voucher will be applied.<br />'.
  697.                 ' Last login : only the most recent login per username or voucher will be granted.'.
  698.                 ' Previous logins will be disconnected.<br />'.
  699.                 ' First login : only the first login per username or voucher will be granted.'.
  700.                 ' Further login attempts using the username or voucher will not be possible while an initial user is already active.');
  701.  
  702. $section->add($group);
  703.  
  704. $section->addInput(new Form_Checkbox(
  705.     'nomacfilter',
  706.     'MAC filtering',
  707.     'Disable MAC filtering',
  708.     $pconfig['nomacfilter']
  709. ))->setHelp('If enabled no attempts will be made to ensure that the MAC address of clients stays the same while they are logged in. ' .
  710.             'This is required when the MAC address of the client cannot be determined (usually because there are routers between pfSense and the clients). ' .
  711.             'If this is enabled, RADIUS MAC authentication cannot be used.');
  712.  
  713. $section->addInput(new Form_Checkbox(
  714.     'passthrumacadd',
  715.     'Pass-through MAC Auto Entry',
  716.     'Enable Pass-through MAC automatic additions',
  717.     $pconfig['passthrumacadd']
  718. ))->setHelp('When enabled, a MAC passthrough entry is automatically added after the user has successfully authenticated. Users of that MAC address will ' .
  719.             'never have to authenticate again. To remove the passthrough MAC entry either log in and remove it manually from the ' .
  720.             '%1$sMAC tab%2$s or send a POST from another system. '  .
  721.             'If this is enabled, the logout window will not be shown.', "<a href=\"services_captiveportal_mac.php?zone={$cpzone}\">", '</a>');
  722.  
  723. $section->addInput(new Form_Checkbox(
  724.     'peruserbw',
  725.     'Per-user bandwidth restriction',
  726.     'Enable per-user bandwidth restriction',
  727.     $pconfig['peruserbw']
  728. ));
  729.  
  730. $section->addInput(new Form_Input(
  731.     'bwdefaultdn',
  732.     'Default download (Kbit/s)',
  733.     'number',
  734.     $pconfig['bwdefaultdn']
  735. ));
  736.  
  737. $section->addInput(new Form_Input(
  738.     'bwdefaultup',
  739.     'Default upload (Kbit/s)',
  740.     'number',
  741.     $pconfig['bwdefaultup']
  742. ))->setHelp('If this option is set, the captive portal will restrict each user who logs in to the specified default bandwidth. ' .
  743.             'RADIUS servers can override the default settings. Leave empty for no limit.');
  744.  
  745. $section->addInput(new Form_Checkbox(
  746.     'customhtml',
  747.     'Use custom captive portal page',
  748.     'Enable to use a custom captive portal login page',
  749.     $pconfig['customhtml']
  750. ))->setHelp('If set a portal.html page must be created and uploaded. If unchecked the default template will be used');
  751.  
  752. $form->add($section);
  753.  
  754. $section = new Form_Section('HTML Page Contents');
  755. $section->addClass('Custom-HTML');
  756.  
  757. $section->addInput(new Form_Input(
  758.     'htmlfile',
  759.     'Portal page contents',
  760.     'file',
  761.     $pconfig['htmlfile']
  762. ))->setHelp('Upload an HTML/PHP file for the portal page here (leave blank to keep the current one). Make sure to include a form (POST to "$PORTAL_ACTION$") ' .
  763.             'with a submit button (name="accept") and a hidden field with name="redirurl" and value="$PORTAL_REDIRURL$". ' .
  764.             'Include the "auth_user" and "auth_pass" and/or "auth_voucher" input fields if authentication is enabled, otherwise it will always fail.%1$s' .
  765.             'Example code for the form: %1$s' .
  766.             '&lt;form method=&quot;post&quot; action=&quot;$PORTAL_ACTION$&quot;&gt;%1$s' .
  767.             '&nbsp;&nbsp;&nbsp;&lt;input name=&quot;auth_user&quot; type=&quot;text&quot;&gt;%1$s' .
  768.             '&nbsp;&nbsp;&nbsp;&lt;input name=&quot;auth_pass&quot; type=&quot;password&quot;&gt;%1$s' .
  769.             '&nbsp;&nbsp;&nbsp;&lt;input name=&quot;auth_voucher&quot; type=&quot;text&quot;&gt;%1$s' .
  770.             '&nbsp;&nbsp;&nbsp;&lt;input name=&quot;redirurl&quot; type=&quot;hidden&quot; value=&quot;$PORTAL_REDIRURL$&quot;&gt;%1$s' .
  771.             '&nbsp;&nbsp;&nbsp;&lt;input name=&quot;zone&quot; type=&quot;hidden&quot; value=&quot;$PORTAL_ZONE$&quot;&gt;%1$s' .
  772.             '&nbsp;&nbsp;&nbsp;&lt;input name=&quot;accept&quot; type=&quot;submit&quot; value=&quot;Continue&quot;&gt;%1$s' .
  773.             '&lt;/form&gt;', '<br />')->addClass('btn btn-info btn-sm');
  774.  
  775. list($host) = explode(":", $_SERVER['HTTP_HOST']);
  776. $zoneid = $pconfig['zoneid'] ? $pconfig['zoneid'] : 8000;
  777. if ($pconfig['httpslogin_enable']) {
  778.     $port = $pconfig['listenporthttps'] ? $pconfig['listenporthttps'] : ($zoneid + 8001);
  779.     $href = "https://{$host}:{$port}/?zone={$cpzone}";
  780. } else {
  781.     $port = $pconfig['listenporthttp'] ? $pconfig['listenporthttp'] : ($zoneid + 8000);
  782.     $href = "http://{$host}:{$port}/?zone={$cpzone}";
  783. }
  784.  
  785. if ($pconfig['page']['htmltext']) {
  786.     $group = new Form_Group('Current Portal Page');
  787.     $group->add(new Form_Button(
  788.         'btnliveview',
  789.         'Live View',
  790.         $href,
  791.         'fa-file-text-o'
  792.     ))->addClass('btn btn-info btn-xs')->setAttribute("target", "_blank");
  793.  
  794.     $group->add(new Form_Button(
  795.         'btnview',
  796.         'View Page Contents',
  797.         '?zone=' . $cpzone . '&act=viewhtml',
  798.         'fa-file-text-o'
  799.     ))->addClass('btn btn-info btn-xs')->setAttribute("target", "_blank");
  800.  
  801.     $group->add(new Form_Button(
  802.         'btndownload',
  803.         'Download',
  804.         '?zone=' . $cpzone . '&act=gethtmlhtml',
  805.         'fa-download'
  806.     ))->addClass('btn btn-primary btn-xs')->setAttribute("target", "_blank");
  807.  
  808.     $group->add(new Form_Button(
  809.         'btndownload',
  810.         'Restore Default Page',
  811.         '?zone=' . $cpzone . '&act=delhtmlhtml',
  812.         'fa-undo'
  813.     ))->addClass('btn btn-danger btn-xs')->setAttribute("target", "_blank");
  814.     $section->add($group);
  815. }
  816.  
  817. $section->addInput(new Form_Input(
  818.     'errfile',
  819.     'Auth error page contents',
  820.     'file',
  821.     $pconfig['errfile']
  822. ))->setHelp('The contents of the HTML/PHP file that is uploaded here are displayed when an authentication error occurs. ' .
  823.             'It may include "$PORTAL_MESSAGE$", which will be replaced by the error or reply messages from the RADIUS ' .
  824.             'server, if any.')->addClass('btn btn-info btn-sm');
  825.  
  826. if ($pconfig['page']['errtext']) {
  827.     $group = new Form_Group('Current Auth Error Page');
  828.     $group->add(new Form_Button(
  829.         'btnview',
  830.         'View Page Contents',
  831.         '?zone=' . $cpzone . '&act=viewerrhtml',
  832.         'fa-file-text-o'
  833.     ))->addClass('btn btn-info btn-xs')->setAttribute("target", "_blank");
  834.  
  835.     $group->add(new Form_Button(
  836.         'btndownload',
  837.         'Download',
  838.         '?zone=' . $cpzone . '&act=geterrhtml',
  839.         'fa-download'
  840.     ))->addClass('btn btn-primary btn-xs')->setAttribute("target", "_blank");
  841.  
  842.     $group->add(new Form_Button(
  843.         'btndownload',
  844.         'Restore Default Page',
  845.         '?zone=' . $cpzone . '&act=delerrhtml',
  846.         'fa-undo'
  847.     ))->addClass('btn btn-danger btn-xs')->setAttribute("target", "_blank");
  848.     $section->add($group);
  849. }
  850.  
  851. $section->addInput(new Form_Input(
  852.     'logoutfile',
  853.     'Logout page contents',
  854.     'file',
  855.     $pconfig['logoutfile']
  856. ))->setHelp('The contents of the HTML/PHP file that is uploaded here are displayed on authentication success when the logout popup is enabled.')->addClass('btn btn-info btn-sm');
  857.  
  858. if ($pconfig['page']['logouttext']) {
  859.     $group = new Form_Group('Current Logout Page');
  860.     $group->add(new Form_Button(
  861.         'btnview',
  862.         'View Page Contents',
  863.         '?zone=' . $cpzone . '&act=viewlogouthtml',
  864.         'fa-file-text-o'
  865.     ))->addClass('btn btn-info btn-xs')->setAttribute("target", "_blank");
  866.  
  867.     $group->add(new Form_Button(
  868.         'btndownload',
  869.         'Download',
  870.         '?zone=' . $cpzone . '&act=getlogouthtml',
  871.         'fa-download'
  872.     ))->addClass('btn btn-primary btn-xs')->setAttribute("target", "_blank");
  873.  
  874.     $group->add(new Form_Button(
  875.         'btndownload',
  876.         'Restore Default Page',
  877.         '?zone=' . $cpzone . '&act=dellogouthtml',
  878.         'fa-undo'
  879.     ))->addClass('btn btn-danger btn-xs')->setAttribute("target", "_blank");
  880.     $section->add($group);
  881. }
  882. $section->addInput(new Form_Input(
  883.     'zone',
  884.     null,
  885.     'hidden',
  886.     $cpzone
  887. ));
  888.  
  889. $form->add($section);
  890.  
  891. $section = new Form_Section('Captive Portal Login Page');
  892. $section->addClass('Default-HTML');
  893.  
  894. $section->addInput(new Form_Checkbox(
  895.     'customlogo',
  896.     'Display custom logo image',
  897.     'Enable to use a custom uploaded logo',
  898.     $pconfig['customlogo']
  899. ));
  900.  
  901. $section->addInput(new Form_Input(
  902.     'logo-img',
  903.     'Logo Image',
  904.     'file',
  905.     ''
  906. ))->setHelp('Add a logo for use in the default portal login screen. File will be renamed captiveportal-logo.* The image will be resized to fit within the given area, It can be of any image type: .png, .jpg, .svg <strong>This image will not be stored in the config</strong>. The default logo will be used if no custom image is present.')->addClass("btn btn-info btn-sm");
  907.  
  908. $section->addInput(new Form_Checkbox(
  909.     'custombg',
  910.     'Display custom background image',
  911.     'Enable to use a custom uploaded background image',
  912.     $pconfig['custombg']
  913. ));
  914.  
  915. $section->addInput(new Form_Input(
  916.     'background-img',
  917.     'Background Image',
  918.     'file',
  919.     ''
  920. ))->setHelp('Add a background image for use in the default portal login screen. File will be renamed captiveportal-background.* The background image will fill the screen. <strong>This image will not be stored in the config</strong>. The default background image will be used if no custom background is present.')->addClass("btn btn-info btn-sm");
  921.  
  922. $section->addInput(new Form_Textarea(
  923.     'termsconditions',
  924.     'Terms and Conditions',
  925.     $pconfig['termsconditions']
  926.     ))->setHelp('Copy and paste terms and conditions for use in the captive portal. HTML tags will be stripped out');
  927.  
  928. $form->add($section);
  929.  
  930. $section = new Form_Section('Authentication');
  931. $section->addClass('Authentication');
  932.  
  933. $group = new Form_Group('*Authentication Method');
  934.  
  935. $options['authserver'] = 'Use an Authentication backend';
  936. $options['none'] = 'None, don\'t authenticate users';
  937. $options['radmac'] = 'Use RADIUS MAC Authentication';
  938.  
  939. $group->add(new Form_Select(
  940.     'auth_method',
  941.     'Authentication Method',
  942.     $pconfig['auth_method'],
  943.     $options
  944. ))->setHelp('Select an Authentication Method to use for this zone. One method must be selected.<br />'.
  945. '- "Authentication backend" will force the login page to be displayed and will authenticate users using their login and password, or using vouchers.<br />'.
  946. '- "None" method will force the login page to be displayed but will accept any visitor that clicks the "submit" button.<br/>'.
  947. '- "RADIUS MAC Authentication" method will try to authenticate devices automatically with their MAC address without displaying any login page.');
  948.  
  949. $section->add($group);
  950.  
  951. $group = new Form_Group('*Authentication Server');
  952. $group->addClass('auth_server');
  953.  
  954. $group->add(new Form_Select(
  955.     'auth_server',
  956.     '*Authentication Server',
  957.     $pconfig['auth_server'],
  958.     build_authserver_list(),
  959.     true
  960. ))->setHelp("You can add a remote authentication server in the <a href=\"/system_authservers.php\">User Manager</a>.<br/>".
  961.     "<span class=\"vouchers_helptext\">Vouchers could also be used, please go to ".
  962.     "the <a href=\"services_captiveportal_vouchers.php?zone={$cpzone}\">Vouchers Page</a> to enable them.</span>");
  963. $section->add($group);
  964.  
  965. $group = new Form_Group('Secondary authentication Server');
  966. $group->addClass('auth_server2');
  967.  
  968. $group->add(new Form_Select(
  969.     'auth_server2',
  970.     '',
  971.     $pconfig['auth_server2'],
  972.     build_authserver_list(),
  973.     true
  974. ))->setHelp("You can optionally select a second set of servers to to authenticate users. Users will then be able to login using separated HTML inputs.<br />".
  975.             "This setting is useful if you want to provide multiple authentication method to your users. If you don't need multiple authentication method, then leave this setting empty.");
  976. $section->add($group);
  977.  
  978. $section->addInput(new Form_Input(
  979.     'radiusnasid',
  980.     'NAS Identifier',
  981.     'text',
  982.     $pconfig['radiusnasid']
  983. ))->setHelp('Specify a NAS identifier to override the default value (CaptivePortal-%s)', $cpzone);
  984.  
  985. $section->addInput(new Form_Checkbox(
  986.     'reauthenticate',
  987.     'Reauthenticate Users',
  988.     'Reauthenticate connected users every minute',
  989.     $pconfig['reauthenticate']
  990. ))->setHelp('If reauthentication is enabled, request are made to the server for each user that is logged in every minute. ' .
  991.             'If an access denied is received for a user, that user is disconnected from the captive portal immediately. ' .
  992.             'Reauthentication requires user credentials to be cached in the captive portal database while a user is logged in; ' .
  993.             'The cached credentials are necessary for the portal to perform automatic reauthentication requests.');
  994.  
  995.  
  996.  
  997. $section->addInput(new Form_Checkbox(
  998.     'localauth_priv',
  999.     'Local Authentication Privileges',
  1000.     'Allow only users/groups with "Captive portal login" privilege set',
  1001.     $pconfig['localauth_priv']
  1002. ));
  1003.  
  1004.  
  1005. $section->addInput(new Form_Input(
  1006.     'radmac_secret',
  1007.     'RADIUS MAC Secret',
  1008.     'text',
  1009.     $pconfig['radmac_secret']
  1010. ))->setHelp('RADIUS MAC will automatically try to authenticate devices with their MAC address as username, and the password entered below as password. Devices will still need to make one HTTP request to get connected, through.');
  1011.  
  1012. $section->addInput(new Form_Checkbox(
  1013.     'radmac_fallback',
  1014.     'Login page Fallback',
  1015.     'Display the login page as fallback if RADIUS MAC authentication failed.',
  1016.     $pconfig['radmac_fallback']
  1017. ))->setHelp('When enabled, users will be redirected to the captive portal login page when RADIUS MAC authentication failed.');
  1018.  
  1019. $section->addInput(new Form_Checkbox(
  1020.     'radiussession_timeout',
  1021.     'Session timeout',
  1022.     'Use RADIUS Session-Timeout attributes',
  1023.     $pconfig['radiussession_timeout']
  1024. ))->setHelp('When enabled, clients will be disconnected after the amount of time retrieved from the RADIUS Session-Timeout attribute.');
  1025.  
  1026. $section->addInput(new Form_Checkbox(
  1027.     'radiustraffic_quota',
  1028.     'Traffic quota',
  1029.     'Use RADIUS pfSense-Max-Total-Octets attribute',
  1030.     $pconfig['radiustraffic_quota']
  1031. ))->setHelp('When enabled, clients will be disconnected after exceeding the amount of traffic, inclusive of both downloads and uploads, retrieved from the RADIUS pfSense-Max-Total-Octets attribute.');
  1032.  
  1033. $section->addInput(new Form_Checkbox(
  1034.     'radiusperuserbw',
  1035.     'Per-user bandwidth restrictions',
  1036.     'Use RADIUS pfSense-Bandwidth-Max-Up and pfSense-Bandwidth-Max-Down attributes',
  1037.     $pconfig['radiusperuserbw']
  1038. ))->setHelp('When enabled, the bandwidth assigned to a client will be limited to the values retrieved from the RADIUS pfSense-Bandwidth-Max-Up and ' .
  1039.             'pfSense-Bandwidth-Max-Down attributes or from the comparable WISPr attributes.');
  1040.  
  1041. $section->addInput(new Form_Select(
  1042.     'radmac_format',
  1043.     'MAC address format',
  1044.     $pconfig['radmac_format'],
  1045.     ['default' => 'Default', 'singledash' => gettext('Single dash'), 'ietf' => 'IETF', 'cisco' => 'Cisco', 'unformatted' => gettext('Unformatted')]
  1046. ))->setHelp('This option changes the MAC address format used when performing a RADIUS authentication. %1$s' .
  1047.             'Default: 00:11:22:33:44:55 %1$s' .
  1048.             'Single dash: 001122-334455 %1$s' .
  1049.             'IETF: 00-11-22-33-44-55 %1$s' .
  1050.             'Cisco: 0011.2233.4455 %1$s' .
  1051.             'Unformatted: 001122334455', '<br />');
  1052.  
  1053. $form->add($section);
  1054.  
  1055. $section = new Form_Section('Accounting');
  1056. $section->addClass('Accounting');
  1057.  
  1058. $section->addInput(new Form_Checkbox(
  1059.     'radacct_enable',
  1060.     'RADIUS',
  1061.     'Send RADIUS accounting packets.',
  1062.     $pconfig['radacct_enable']
  1063. ))->setHelp('If enabled, accounting request will be made for users identified against any RADIUS server.');
  1064.  
  1065.  
  1066. $options = array();
  1067.  
  1068. foreach (auth_get_authserver_list() as $i => $auth) {
  1069.     if ($auth['type'] == 'radius' && !empty($auth['radius_acct_port'])) {
  1070.         $options[$auth['name']] = $auth['name'];
  1071.     }
  1072. }
  1073.  
  1074. $group = new Form_Group('Accounting Server');
  1075.  
  1076.  
  1077. $group->add(new Form_Select(
  1078.     'radacct_server',
  1079.     'Accounting Server',
  1080.     $pconfig['radacct_server'],
  1081.     $options
  1082. ));
  1083.  
  1084. $group->addClass('radacct_enable');
  1085. $group->setHelp('You can add a Radius Accounting server in the <a href="/system_authservers.php">User Manager</a>.');
  1086.  
  1087. $section->add($group);
  1088.  
  1089. $group = new Form_Group('Send accounting updates');
  1090.  
  1091. $group->addClass('reauthenticateacct');
  1092.  
  1093. $group->add(new Form_Checkbox(
  1094.     'reauthenticateacct',
  1095.     null,
  1096.     'No updates',
  1097.     $pconfig['reauthenticateacct'] == 'none',
  1098.     "none"
  1099. ))->displayasRadio();
  1100.  
  1101. $group->add(new Form_Checkbox(
  1102.     'reauthenticateacct',
  1103.     null,
  1104.     'Stop/Start',
  1105.     $pconfig['reauthenticateacct'] == 'stopstart',
  1106.     "stopstart"
  1107. ))->displayasRadio();
  1108.  
  1109. $group->add(new Form_Checkbox(
  1110.     'reauthenticateacct',
  1111.     null,
  1112.     'Stop/Start (FreeRADIUS)',
  1113.     $pconfig['reauthenticateacct'] == 'stopstartfreeradius',
  1114.     "stopstartfreeradius"
  1115. ))->displayasRadio();
  1116.  
  1117. $group->add(new Form_Checkbox(
  1118.     'reauthenticateacct',
  1119.     null,
  1120.     'Interim',
  1121.     $pconfig['reauthenticateacct'] == 'interimupdate',
  1122.     "interimupdate"
  1123. ))->displayasRadio();
  1124.  
  1125. $group->setHelp('This field set the way Accounting Updates should be done : <br />'.
  1126.                 '- If "No updates" is selected, then only one "Accounting Start" and one "Accounting Stop" request will be sent, when any user get connected and disconnected.<br />'.
  1127.                 '- If "Interim" is selected, then "Accounting Update" requests will be send regularly (every minute) to the RADIUS server, for each connected user.<br />'.
  1128.                 '- In some rare cases, you would like to simulate users to disconnect and reconnect every minute (eg, to send an Accounting Stop then an Accounting Start) instead of sending Accounting updates, this is the purpose of "Stop/Start" option. FreeRADIUS does not support this option very well, you should select "Stop/Start (FreeRADIUS)" instead.');
  1129.  
  1130. $section->add($group);
  1131.  
  1132. $section->addInput(new Form_Checkbox(
  1133.     'reverseacct',
  1134.     'Accounting style',
  1135.     'Invert Acct-Input-Octets and Acct-Output-Octets',
  1136.     $pconfig['reverseacct']
  1137. ))->setHelp('When enabled, data counts for RADIUS accounting packets will be taken from the client perspective, not the NAS. ' .
  1138.             'Acct-Input-Octets will represent download, and Acct-Output-Octets will represent upload.');
  1139.  
  1140. $section->addInput(new Form_Checkbox(
  1141.     'includeidletime',
  1142.     'Idle time accounting',
  1143.     'Include idle time when users get disconnected due to idle timeout',
  1144.     $pconfig['includeidletime']
  1145. ))->setHelp('This setting change the stop time that will be send in the Accounting Stop request, when a user get disconnected after exceeding the idle timeout. ' .
  1146.             'If not checked, the sent stop time will be the last activity time.');
  1147. $form->add($section);
  1148.  
  1149. $section = new Form_Section('HTTPS Options');
  1150. $section->addClass('HTTPS');
  1151.  
  1152. $section->addInput(new Form_Checkbox(
  1153.     'httpslogin_enable',
  1154.     'Login',
  1155.     'Enable HTTPS login',
  1156.     $pconfig['httpslogin_enable']
  1157. ))->setHelp('When enabled, the username and password will be transmitted over an HTTPS connection to protect against eavesdroppers. ' .
  1158.             'A server name and certificate must also be specified below.');
  1159.  
  1160. $section->addInput(new Form_Input(
  1161.     'httpsname',
  1162.     '*HTTPS server name',
  1163.     'text',
  1164.     $pconfig['httpsname']
  1165. ))->setHelp('This name will be used in the form action for the HTTPS POST and should match the Common Name (CN) in the certificate ' .
  1166.             '(otherwise, the client browser will most likely display a security warning). ' .
  1167.             'Make sure captive portal clients can resolve this name in DNS and verify on the client that the IP resolves to the correct interface IP on pfSense.');
  1168.  
  1169. $section->addInput(new Form_Select(
  1170.     'certref',
  1171.     '*SSL Certificate',
  1172.     $pconfig['certref'],
  1173.     build_cert_list()
  1174. ))->setHelp('If no certificates are defined, one may be defined here: %1$sSystem &gt; Cert. Manager%2$s', '<a href="system_certmanager.php">', '</a>');
  1175.  
  1176. $section->addInput(new Form_Checkbox(
  1177.     'nohttpsforwards',
  1178.     'HTTPS Forwards',
  1179.     'Disable HTTPS Forwards',
  1180.     $pconfig['nohttpsforwards']
  1181. ))->setHelp('If this option is set, attempts to connect to SSL/HTTPS (Port 443) sites will not be forwarded to the captive portal. ' .
  1182.             'This prevents certificate errors from being presented to the user even if HTTPS logins are enabled. ' .
  1183.             'Users must attempt a connection to an HTTP (Port 80) site to get forwarded to the captive portal. ' .
  1184.             'If HTTPS logins are enabled, the user will be redirected to the HTTPS login page.');
  1185.  
  1186. $form->add($section);
  1187.  
  1188. print($form);
  1189.  
  1190. print_info_box(gettext('Don\'t forget to enable the DHCP server on the captive portal interface! ' .
  1191.                        'Make sure that the default/maximum DHCP lease time is higher than the hard timeout entered on this page. ' .
  1192.                        'Also, the DNS Forwarder or Resolver must be enabled for DNS lookups by unauthenticated clients to work.'));
  1193.  
  1194. ?>
  1195.  
  1196. <script type="text/javascript">
  1197. //<![CDATA[
  1198. events.push(function() {
  1199.  
  1200.     // ------- Show/hide sections based on checkbox settings --------------------------------------
  1201.     function hideSections(hide) {
  1202.         hideClass('Authentication', hide);
  1203.         hideHTTPS();
  1204.         hideClass('HTTPS', hide);
  1205.         hideClass('HTML', hide);
  1206.         hideClass('Custom-HTML', (hide || !$('#customhtml').prop('checked')));
  1207.         hideClass('Default-HTML', (hide || $('#customhtml').prop('checked')));
  1208.         hideGeneral(hide);
  1209.         hideClass('Accounting', hide);
  1210.     }
  1211.  
  1212.     function hideRadius(hide) {
  1213.         hideCheckbox('radiussession_timeout', hide);
  1214.         hideInput('radmac_format', hide);
  1215.         hideCheckbox('radiusperuserbw', hide);
  1216.         hideCheckbox('radiustraffic_quota', hide);
  1217.         hideInput('radiusnasid', hide);
  1218.     }
  1219.  
  1220.     function hideHTTPS() {
  1221.         hide = (!$('#httpslogin_enable').prop('checked') || !$('#enable').prop('checked'));
  1222.  
  1223.         hideInput('httpsname', hide);
  1224.         hideInput('certref', hide);
  1225.         hideCheckbox('nohttpsforwards', hide);
  1226.     }
  1227.  
  1228.     function hideGeneral(hide) {
  1229.         hideMultiClass('general', hide);
  1230.         hideInput('maxprocperip', hide);
  1231.         hideInput('idletimeout', hide);
  1232.         hideInput('timeout', hide);
  1233.         hideInput('trafficquota', hide);
  1234.         hideInput('freelogins_count', hide);
  1235.         hideInput('freelogins_resettimeout', hide);
  1236.         hideCheckbox('freelogins_updatetimeouts', hide);
  1237.         hideCheckbox('logoutwin_enable', hide);
  1238.         hideInput('preauthurl', hide);
  1239.         hideInput('redirurl', hide);
  1240.         hideInput('blockedmacsurl', hide);
  1241.         hideCheckbox('preservedb', hide);
  1242.         hideClass('noconcurrentlogins', hide);
  1243.         hideCheckbox('nomacfilter', hide);
  1244.         hideCheckbox('passthrumacadd', hide);
  1245.         hideCheckbox('peruserbw', hide);
  1246.         hideCheckbox('reauthenticate', hide);
  1247.         hideCheckbox('customhtml', hide);
  1248.     }
  1249.  
  1250.     function hideRadiusAccounting(radiusServerSelected, hide) {
  1251.         if(radiusServerSelected) return;
  1252.  
  1253.         hideInput('radacct_server', hide);
  1254.         hideClass('reauthenticateacct', hide);
  1255.         hideCheckbox('reverseacct', hide);
  1256.         hideCheckbox('includeidletime', hide);
  1257.     }
  1258.  
  1259.     function hidePassthru(hide) {
  1260.         if(!$('#enable').prop('checked')) {
  1261.             hide = true;
  1262.         }
  1263.         else if(!hide) {
  1264.             $('#logoutwin_enable').prop('checked', false);
  1265.         }
  1266.  
  1267.         disableInput("logoutwin_enable", !hide);
  1268.     }
  1269.  
  1270.     function hidePerUserBandwith(hide) {
  1271.         if(!$('#enable').prop('checked')) {
  1272.             hide = true;
  1273.         }
  1274.         hideInput('bwdefaultdn', hide);
  1275.         hideInput('bwdefaultup', hide);
  1276.     }
  1277.  
  1278.     function triggerChangesAuthMethod() {
  1279.         if(!$('#enable').prop('checked')) return;
  1280.  
  1281.         let authserver_list = <?php echo json_encode(build_authserver_list()); ?>;
  1282.         let auth_method =  $('#auth_method').val();
  1283.         let saved_values = $('select[name="auth_server[]"]').val(); // we save the current list of selected servers
  1284.  
  1285.         if(auth_method.indexOf("authserver") === 0) {
  1286.             // If authserver is selected : we display all the server list.
  1287.             $('select[name="auth_server[]"]').find('option').remove();
  1288.             $.each(authserver_list, function(key, value) {
  1289.                 $('<option>').val(key).text(value).appendTo($('select[name="auth_server[]"]'));
  1290.             });
  1291.  
  1292.             hideCheckbox('reauthenticate', false);
  1293.             hideClass('auth_server', false);
  1294.             hideInput('radmac_secret', true);
  1295.             hideCheckbox('radmac_fallback', true);
  1296.             $('.auth_server .vouchers_helptext').removeClass('hidden');
  1297.         }
  1298.         else if(auth_method.indexOf("radmac") === 0) {
  1299.             // If Radmac is selected : only RADIUS servers should be displayed
  1300.             $('select[name="auth_server[]"]').find('option').remove();
  1301.  
  1302.             $.each(authserver_list, function(key, value) {
  1303.                 if(key.indexOf("radius") === 0) {
  1304.                     $('<option>').val(key).text(value).appendTo($('select[name="auth_server[]"]'));
  1305.                 }
  1306.             });
  1307.             hideCheckbox('reauthenticate', false);
  1308.             hideClass('auth_server', false);
  1309.             hideInput('radmac_secret', false);
  1310.             hideCheckbox('radmac_fallback', false);
  1311.             $('.auth_server .vouchers_helptext').addClass('hidden');
  1312.         } else {
  1313.             // if "none" is selected : we hide most of authentication settings
  1314.             hideRadius(true);
  1315.             hideCheckbox('reauthenticate', true);
  1316.             hideClass('auth_server', true);
  1317.             hideInput('radmac_secret', true);
  1318.             hideCheckbox('radmac_fallback', true);
  1319.         }
  1320.  
  1321.  
  1322.         // we try to restore all previous selected servers
  1323.         $.each(saved_values, function(key, value) {
  1324.             $('select[name="auth_server[]"] option').filter(function(i, e) {return $(e).val() == value}).attr("selected", "selected");
  1325.         });
  1326.  
  1327.         triggerChangesAuthServer();
  1328.     }
  1329.  
  1330.     function triggerChangesAuthServer() {
  1331.         if(!$('#enable').prop('checked')) return;
  1332.  
  1333.         let shouldHideLocal = true;
  1334.         let shouldHideRadius = true;
  1335.         let shouldHideLdap = true;
  1336.         let shouldHideSecondAuth = true;
  1337.  
  1338.         if($('#auth_method').val().indexOf("none") !== 0) {
  1339.             $.each($('select[name="auth_server[]"]').val(), function(key,value) {
  1340.                 if(value.indexOf("radius") === 0) {
  1341.                     shouldHideRadius = false;
  1342.                 } else if(value.indexOf("ldap") === 0) {
  1343.                     shouldHideLdap = false;
  1344.                 }
  1345.                 else if(value.indexOf("Local Auth") === 0) {
  1346.                     shouldHideLocal = false;
  1347.                 }
  1348.                 if($('#auth_method').val().indexOf("authserver") === 0) { // There is no second auth possibility when none/radmac are selected
  1349.                     shouldHideSecondAuth = false;
  1350.                 }
  1351.             });
  1352.  
  1353.             $.each($('select[name="auth_server2[]"]').val(), function(key,value) {
  1354.                 if(value.indexOf("radius") === 0) {
  1355.                     shouldHideRadius = false;
  1356.                 } else if(value.indexOf("ldap") === 0) {
  1357.                     shouldHideLdap = false;
  1358.                 }
  1359.                 else if(value.indexOf("Local Auth") === 0) {
  1360.                     shouldHideLocal = false;
  1361.                 }
  1362.             });
  1363.         }
  1364.         hideCheckbox('localauth_priv', shouldHideLocal); // Hide/Show Local Auth options
  1365.         hideClass('auth_server2', shouldHideSecondAuth); // Hide/show second auth context
  1366.         hideRadius(shouldHideRadius); // Hide/Show Radius authentication options
  1367.         hideClass('Accounting', shouldHideRadius);
  1368.         hideRadiusAccounting(shouldHideRadius, !$('input[name="radacct_enable"]').prop('checked'));
  1369.     }
  1370.     // ---------- Click checkbox handlers ---------------------------------------------------------
  1371.     $("#enable").click(function() {
  1372.         hideSections(!this.checked);
  1373.         hidePerUserBandwith(!$("#peruserbw").prop('checked'));
  1374.         hidePassthru(!$("#passthrumacadd").prop('checked'));
  1375.         triggerChangesAuthMethod();
  1376.         triggerChangesAuthServer();
  1377.     });
  1378.  
  1379.     $('select[name="auth_server[]"]').on('change', function() {
  1380.         triggerChangesAuthServer();
  1381.     });
  1382.     $('select[name="auth_server2[]"]').on('change', function() {
  1383.         triggerChangesAuthServer();
  1384.     });
  1385.     $('select[name="auth_method"]').on('change', function() {
  1386.         triggerChangesAuthMethod();
  1387.     });
  1388.     $('input[name="radacct_enable"]').on('change', function() {
  1389.         hideRadiusAccounting(false, !this.checked);
  1390.     });
  1391.  
  1392.  
  1393.     $("#httpslogin_enable").click(function() {
  1394.         hideHTTPS(!this.checked);
  1395.     });
  1396.     $("#nomacfilter").click(function()
  1397.     {
  1398.         let radmac_option = $('select[name="auth_method"] option[value="radmac"]');
  1399.         if(this.checked) {
  1400.             radmac_option.prop('disabled','disabled');
  1401.             if($('select[name="auth_method"]').val() == radmac_option.val() || $('select[name="auth_method"]').val() == null) {
  1402.                 $('select[name="auth_method"]').val($('select[name="auth_method"] option:first').val());
  1403.             }
  1404.         } else {
  1405.             radmac_option.removeAttr('disabled');
  1406.         }
  1407.     });
  1408.  
  1409.  
  1410.     $("#peruserbw").click(function() {
  1411.         hidePerUserBandwith(!this.checked);
  1412.     });
  1413.  
  1414.     $("#passthrumacadd").click(function() {
  1415.         hidePassthru(!this.checked);
  1416.     });
  1417.  
  1418.     $("#customhtml").click(function() {
  1419.         hideClass('Custom-HTML', !this.checked);
  1420.         hideClass('Default-HTML', this.checked);
  1421.     })
  1422.  
  1423.     // ---------- On initial page load ------------------------------------------------------------
  1424.     hideSections(!$('#enable').prop('checked'));
  1425.     hidePerUserBandwith(!$("#peruserbw").prop('checked'));
  1426.     hidePassthru(!$("#passthrumacadd").prop('checked'));
  1427.     triggerChangesAuthMethod();
  1428.     triggerChangesAuthServer();
  1429.  
  1430. });
  1431. //]]>
  1432. </script>
  1433.  
  1434. <?php include("foot.inc");
Add Comment
Please, Sign In to add comment