From 856081f09c83477cc6a734c725b8d7cc5b521c62 Mon Sep 17 00:00:00 2001 From: Augustin-FL Date: Sat, 5 Jan 2019 15:49:38 +0100 Subject: [PATCH 1/5] Add allowed bandwith settings to sqlite DB fix #8616 --- src/etc/inc/captiveportal.inc | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/etc/inc/captiveportal.inc b/src/etc/inc/captiveportal.inc index 9b4856f7749..155e7aae833 100644 --- a/src/etc/inc/captiveportal.inc +++ b/src/etc/inc/captiveportal.inc @@ -900,7 +900,7 @@ function captiveportal_prune_old() { /* do periodic reauthentication? For Radius servers, send accounting updates? */ if (!$timedout) { //Radius servers : send accounting - if (isset($cpcfg['radacct_enable']) && $cpentry[12] === 'radius') { + if (isset($cpcfg['radacct_enable']) && $cpentry['authmethod'] === 'radius') { if (substr($cpcfg['reauthenticateacct'], 0, 9) === "stopstart") { /* stop and restart accounting */ if ($cpcfg['reauthenticateacct'] === "stopstartfreeradius") { @@ -956,27 +956,27 @@ function captiveportal_prune_old() { } /* check this user again */ - if (isset($cpcfg['reauthenticate']) && $cpentry[13] !== 'voucher') { + if (isset($cpcfg['reauthenticate']) && $cpentry['context'] !== 'voucher') { $auth_result = captiveportal_authenticate_user( $cpentry[4], // username base64_decode($cpentry[6]), // password $cpentry[3], // clientmac $cpentry[2], // clientip $cpentry[1], // ruleno - $cpentry[13]); // context + $cpentry['context']); // context if ($auth_result['result'] === false) { captiveportal_disconnect($cpentry, 17); captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "DISCONNECT - REAUTHENTICATION FAILED", $auth_list['reply_message']); $unsetindexes[] = $cpentry[5]; } else if ($auth_result['result'] === true) { - if ($cpentry[12] !== $auth_result['auth_method']) { + if ($cpentry['authmethod'] !== $auth_result['auth_method']) { // if the user got authenticated against another server type: we update the database if (!empty($cpentry[5])) { captiveportal_write_db("UPDATE captiveportal SET authmethod = '{$auth_result['auth_method']}' WHERE sessionid = '{$cpentry[5]}'"); captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CHANGED AUTHENTICATION SERVER", $auth_list['reply_message']); } // User was logged on a RADIUS server, but is now logged in by another server type : we send an accounting Stop - if (isset($config['captiveportal'][$cpzone]['radacct_enable']) && $cpentry[12] =='radius') { + if (isset($config['captiveportal'][$cpzone]['radacct_enable']) && $cpentry['authmethod'] == 'radius') { if ($cpcfg['reauthenticateacct'] === "stopstartfreeradius") { $rastart_time = 0; $rastop_time = 60; @@ -1090,7 +1090,7 @@ function captiveportal_disconnect($dbent, $term_cause = 1, $stop_time = null) { $stop_time = (empty($stop_time)) ? time() : $stop_time; /* this client needs to be deleted - remove ipfw rules */ - if (isset($config['captiveportal'][$cpzone]['radacct_enable']) && $dbent[12] =='radius') { + if (isset($config['captiveportal'][$cpzone]['radacct_enable']) && $dbent['authmethod'] == 'radius') { if ($config['captiveportal'][$cpzone]['reauthenticateacct'] == "stopstartfreeradius") { /* * Interim updates are on so the session time must be @@ -1230,7 +1230,7 @@ function captiveportal_radius_stop_all($term_cause = 6, $logoutReason = "DISCONN $radacct = isset($config['captiveportal'][$cpzone]['radacct_enable']) ? true : false; foreach ($cpdb as $cpentry) { - if ($cpentry[12] === 'radius' && $radacct) { + if ($cpentry['authmethod'] === 'radius' && $radacct) { if ($config['captiveportal'][$cpzone]['reauthenticateacct'] == "stopstartfreeradius") { $session_time = (time() - $cpentry[0]) % 60; $start_time = time() - $session_time; @@ -1702,7 +1702,7 @@ function captiveportal_opendb() { "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, traffic_quota INTEGER, " . - "authmethod TEXT, context TEXT); " . + "bw_up INTEGER, bw_down INTEGER, authmethod TEXT, context TEXT); " . "CREATE UNIQUE INDEX IF NOT EXISTS idx_active ON captiveportal (sessionid, username); " . "CREATE INDEX IF NOT EXISTS user ON captiveportal (username); " . "CREATE INDEX IF NOT EXISTS ip ON captiveportal (ip); " . @@ -2414,9 +2414,9 @@ function portal_allow($clientip, $clientmac, $username, $password = null, $attri /* encode password in Base64 just in case it contains commas */ $bpassword = (isset($config['captiveportal'][$cpzone]['reauthenticate'])) ? base64_encode($password) : ''; - $insertquery = "INSERT INTO captiveportal (allow_time, pipeno, ip, mac, username, sessionid, bpassword, session_timeout, idle_timeout, session_terminate_time, interim_interval, traffic_quota, authmethod, context) "; + $insertquery = "INSERT INTO captiveportal (allow_time, pipeno, ip, mac, username, sessionid, bpassword, session_timeout, idle_timeout, session_terminate_time, interim_interval, traffic_quota, bw_up, bw_down, authmethod, context) "; $insertquery .= "VALUES ({$allow_time}, {$pipeno}, '{$clientip}', '{$clientmac}', '{$safe_username}', '{$sessionid}', '{$bpassword}', "; - $insertquery .= "{$session_timeout}, {$idle_timeout}, {$session_terminate_time}, {$interim_interval}, {$traffic_quota}, '{$authmethod}', '{$context}')"; + $insertquery .= "{$session_timeout}, {$idle_timeout}, {$session_terminate_time}, {$interim_interval}, {$traffic_quota}, {$bw_up}, {$bw_down}, '{$authmethod}', '{$context}')"; /* store information to database */ captiveportal_write_db($insertquery); From 3564f4028095f9e77aa7547bce24b097516c7934 Mon Sep 17 00:00:00 2001 From: Augustin-FL Date: Sat, 5 Jan 2019 19:51:28 +0100 Subject: [PATCH 2/5] Update users database when bandwith is updated fix #8616 --- src/etc/inc/captiveportal.inc | 23 +++++++++++++++++----- src/etc/inc/globals.inc | 2 +- src/etc/inc/upgrade_config.inc | 10 ++++++++++ src/usr/local/www/status_captiveportal.php | 2 +- 4 files changed, 30 insertions(+), 7 deletions(-) diff --git a/src/etc/inc/captiveportal.inc b/src/etc/inc/captiveportal.inc index 155e7aae833..dba664fa5ac 100644 --- a/src/etc/inc/captiveportal.inc +++ b/src/etc/inc/captiveportal.inc @@ -972,7 +972,7 @@ function captiveportal_prune_old() { if ($cpentry['authmethod'] !== $auth_result['auth_method']) { // if the user got authenticated against another server type: we update the database if (!empty($cpentry[5])) { - captiveportal_write_db("UPDATE captiveportal SET authmethod = '{$auth_result['auth_method']}' WHERE sessionid = '{$cpentry[5]}'"); + captiveportal_update_entry($cpentry['sessionid'], $auth_result['auth_method'], 'authmethod'); captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CHANGED AUTHENTICATION SERVER", $auth_list['reply_message']); } // User was logged on a RADIUS server, but is now logged in by another server type : we send an accounting Stop @@ -2204,13 +2204,26 @@ function captiveportal_reapply_attributes($cpentry, $attributes) { $bw_up_pipeno = $cpentry[1]; $bw_down_pipeno = $cpentry[1]+1; - $_gb = @pfSense_ipfw_pipe("pipe {$bw_up_pipeno} config bw {$bw_up}Kbit/s queue 100 buckets 16"); - $_gb = @pfSense_ipfw_pipe("pipe {$bw_down_pipeno} config bw {$bw_down}Kbit/s queue 100 buckets 16"); - //captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "RADIUS_BANDWIDTH_REAPPLY", "{$bw_up}/{$bw_down}"); - + if ($cpentry['bw_up'] !== $bw_up) { + $_gb = @pfSense_ipfw_pipe("pipe {$bw_up_pipeno} config bw {$bw_up}Kbit/s queue 100 buckets 16"); + captiveportal_update_entry($cpentry['sessionid'], $bw_up, 'bw_up'); + } + if ($cpentry['bw_down'] !== $bw_down) { + $_gb = @pfSense_ipfw_pipe("pipe {$bw_down_pipeno} config bw {$bw_down}Kbit/s queue 100 buckets 16"); + captiveportal_update_entry($cpentry['sessionid'], $bw_down, 'bw_down'); + } unset($bw_up_pipeno, $bw_down_pipeno, $bw_up, $bw_down); } +function captiveportal_update_entry($sessionid, $new_value, $field_to_update) { + global $config, $cpzone, $g; + + if (!intval($new_value)) { + $new_value = "'{$new_value}'"; + } + captiveportal_write_db("UPDATE captiveportal SET {$field_to_update} = {$new_value} WHERE sessionid = '{$sessionid}'"); +} + function portal_allow($clientip, $clientmac, $username, $password = null, $attributes = null, $pipeno = null, $authmethod = null, $context = 'first') { global $redirurl, $g, $config, $type, $_POST, $cpzone, $cpzoneid; diff --git a/src/etc/inc/globals.inc b/src/etc/inc/globals.inc index 6d082a01d70..f1cc340192b 100644 --- a/src/etc/inc/globals.inc +++ b/src/etc/inc/globals.inc @@ -69,7 +69,7 @@ $g = array( "disablecrashreporter" => false, "crashreporterurl" => "https://crashreporter.pfsense.org/crash_reporter.php", "debug" => false, - "latest_config" => "19.0", + "latest_config" => "19.1", "minimum_ram_warning" => "101", "minimum_ram_warning_text" => "128 MB", "wan_interface_name" => "wan", diff --git a/src/etc/inc/upgrade_config.inc b/src/etc/inc/upgrade_config.inc index 97fb3d6a3e7..51398ca6cb3 100644 --- a/src/etc/inc/upgrade_config.inc +++ b/src/etc/inc/upgrade_config.inc @@ -5934,6 +5934,16 @@ function upgrade_189_to_190() { } } +function upgrade_190_to_191() { + global $config, $g; + + if (is_array($config['captiveportal'])) { + foreach ($config['captiveportal'] as $cpzone => $cp) { + unlink_if_exists("{$g['vardb_path']}/captiveportal{$cpzone}.db"); + } + } +} + /* * Special function that is called independent of current config version. It's * a workaround to have config_upgrade running on older versions after next diff --git a/src/usr/local/www/status_captiveportal.php b/src/usr/local/www/status_captiveportal.php index bdfd441f5a8..3963fd2ed53 100644 --- a/src/usr/local/www/status_captiveportal.php +++ b/src/usr/local/www/status_captiveportal.php @@ -268,7 +268,7 @@ function print_details($cpent) { - + From 64a0a53a920ae5496d3d93d6960e0611cf336418 Mon Sep 17 00:00:00 2001 From: Augustin-FL Date: Sat, 5 Jan 2019 20:19:43 +0100 Subject: [PATCH 3/5] do not look for other servers when an auth is successful fix #9255 --- src/etc/inc/captiveportal.inc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/etc/inc/captiveportal.inc b/src/etc/inc/captiveportal.inc index dba664fa5ac..550b9747181 100644 --- a/src/etc/inc/captiveportal.inc +++ b/src/etc/inc/captiveportal.inc @@ -1679,8 +1679,8 @@ function captiveportal_authenticate_user(&$login = '', &$password = '', $clientm $val = 0; } - if ($val >= $auth_val) { - $auth_val = $val; + if ($val >= $authlevel) { + $authlevel = $val; $auth_method = $authcfg['type']; $login_status = $status; $login_msg = $msg; From 8e0b30067d693e38f020ea954c0f000b9e6de50e Mon Sep 17 00:00:00 2001 From: Augustin-FL Date: Fri, 4 Jan 2019 20:36:19 +0100 Subject: [PATCH 4/5] Create ipfw rules for connected users after configuring captive portal fix #8616 --- src/etc/inc/captiveportal.inc | 64 +++++++++++++++++++---------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/src/etc/inc/captiveportal.inc b/src/etc/inc/captiveportal.inc index 550b9747181..b08bd350e0f 100644 --- a/src/etc/inc/captiveportal.inc +++ b/src/etc/inc/captiveportal.inc @@ -225,8 +225,6 @@ function captiveportal_configure() { function captiveportal_configure_zone($cpcfg) { global $config, $g, $cpzone, $cpzoneid; - $captiveportallck = lock("captiveportal{$cpzone}", LOCK_EX); - if (isset($cpcfg['enable'])) { if (platform_booting()) { @@ -235,8 +233,8 @@ function captiveportal_configure_zone($cpcfg) { captiveportal_syslog("Reconfiguring captive portal({$cpcfg['zone']})."); } - /* (re)init ipfw rules. Cause all users to disconnect */ - captiveportal_init_rules(true); + /* (re)init ipfw rules */ + captiveportal_init_rules(); /* kill any running minicron */ killbypid("{$g['varrun_path']}/cp_prunedb_{$cpzone}.pid"); @@ -373,9 +371,6 @@ EOD; mwexec("/usr/local/bin/minicron $croninterval {$g['varrun_path']}/cp_prunedb_{$cpzone}.pid " . "/etc/rc.prunecaptiveportal {$cpzone}"); - /* delete outdated radius server database if exist */ - unlink_if_exists("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db"); - if (platform_booting()) { /* send Accounting-On to server */ captiveportal_send_server_accounting('on'); @@ -396,8 +391,6 @@ EOD; /* remove old information */ unlink_if_exists("{$g['vardb_path']}/captiveportal{$cpzone}.db"); - unlink_if_exists("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db"); - unlink_if_exists("{$g['vardb_path']}/captiveportal_{$cpzone}.rules"); /* Release allocated pipes for this zone */ $pipes_to_remove = captiveportal_free_dnrules(); @@ -422,8 +415,6 @@ EOD; } } - unlock($captiveportallck); - return 0; } @@ -572,10 +563,6 @@ function captiveportal_init_rules($reinit = false) { captiveportal_load_modules(); captiveportal_init_general_rules(); - /* Cleanup so nothing is leaked */ - captiveportal_free_dnrules(); - unlink_if_exists("{$g['vardb_path']}/captiveportal_{$cpzone}.rules"); - $skipto = captiveportal_ipfw_ruleno($cpzoneid); $cprules = ''; @@ -618,10 +605,6 @@ function captiveportal_init_rules($reinit = false) { return false; } - if ($reinit == false) { - $captiveportallck = lock("captiveportal{$cpzone}"); - } - $rulenum = $skipto; $cprules .= "table {$cpzone}_pipe_mac create type mac valtype pipe\n"; $cprules .= captiveportal_create_ipfw_rule("add", $rulenum, @@ -715,17 +698,45 @@ function captiveportal_init_rules($reinit = false) { /* allowed ipfw rules to make allowed hostnames work */ $cprules .= captiveportal_allowedhostname_configure(); + /* if reinit : flush pipes, so nothing is leaked. if not reinit, just destroy tables without flushing pipes */ + if ($reinit) { + $pipes_to_remove = captiveportal_free_dnrules(); + captiveportal_delete_rules($pipes_to_remove); + } else { + captiveportal_delete_rules(); + } + /* load rules */ - captiveportal_delete_rules(); + $captiveportallck = lock("captiveportal{$cpzone}"); file_put_contents("{$g['tmp_path']}/ipfw_{$cpzone}.cp.rules", $cprules); mwexec("/sbin/ipfw -q {$g['tmp_path']}/ipfw_{$cpzone}.cp.rules", true); @unlink("{$g['tmp_path']}/ipfw_{$cpzone}.cp.rules"); unset($cprules); captiveportal_filterdns_configure(); + unlock($captiveportallck); + + if ($reinit) { + unlink_if_exists("{$g['vardb_path']}/captiveportal{$cpzone}.db"); + } else { + foreach (captiveportal_read_db() as $user) { + $bw_up = intval($user['bw_up']); + $bw_down = intval($user['bw_down']); + $clientip = $user['ip']; + $clientmac = $user['mac']; + $bw_up_pipeno = $user['pipeno']; + $bw_down_pipeno = $user['pipeno'] + 1; - if ($reinit == false) { - unlock($captiveportallck); + $rule_entry = "{$clientip}/" . (is_ipaddrv6($clientip) ? "128" : "32"); + if (!isset($config['captiveportal'][$cpzone]['nomacfilter'])) { + $rule_entry .= ",{$clientmac}"; + } + $_gb = @pfSense_ipfw_pipe("pipe {$bw_up_pipeno} config bw {$bw_up}Kbit/s queue 100 buckets 16"); + $_gb = @pfSense_ipfw_pipe("pipe {$bw_down_pipeno} config bw {$bw_down}Kbit/s queue 100 buckets 16"); + + $_gb = @pfSense_ipfw_table("{$cpzone}_auth_up", IP_FW_TABLE_XADD, "{$rule_entry}", $bw_up_pipeno); + $_gb = @pfSense_ipfw_table("{$cpzone}_auth_down", IP_FW_TABLE_XADD, "{$rule_entry}", $bw_down_pipeno); + } } } @@ -1208,14 +1219,7 @@ function captiveportal_disconnect_all($term_cause = 6, $logoutReason = "DISCONNE captiveportal_radius_stop_all($term_cause, $logoutReason); - /* remove users from the database */ - $cpdb = captiveportal_read_db(); - $unsetindexes = array_column($cpdb,5); - if (!empty($unsetindexes)) { - captiveportal_remove_entries($unsetindexes); - } - - /* reinit ipfw rules */ + /* reinit ipfw rules, flush user database */ captiveportal_init_rules(true); unlock($cpdblck); From 83bcda578ca7b5ca004a99d4435d8b275d4ef981 Mon Sep 17 00:00:00 2001 From: Augustin-FL Date: Sat, 12 Jan 2019 03:21:59 +0100 Subject: [PATCH 5/5] Create an option for saving connected users across reboot Implement redmine #5644 --- src/etc/inc/system.inc | 10 ++++++---- src/usr/local/www/services_captiveportal.php | 10 ++++++++++ 2 files changed, 16 insertions(+), 4 deletions(-) diff --git a/src/etc/inc/system.inc b/src/etc/inc/system.inc index 73b1acbcac3..d55adeea072 100644 --- a/src/etc/inc/system.inc +++ b/src/etc/inc/system.inc @@ -2113,15 +2113,17 @@ function system_reboot_sync($reroot=false) { } function system_reboot_cleanup() { - global $config, $cpzone, $cpzoneid; + global $g, $config, $cpzone; mwexec("/usr/local/bin/beep.sh stop"); require_once("captiveportal.inc"); if (is_array($config['captiveportal'])) { foreach ($config['captiveportal'] as $cpzone=>$cp) { - /* send Accounting-Stop packet for all clients, termination cause 'Admin-Reboot' */ - $cpzoneid = $cp['zoneid']; - captiveportal_radius_stop_all(7); // Admin-Reboot + if (!isset($cp['preservedb'])) { + /* send Accounting-Stop packet for all clients, termination cause 'Admin-Reboot' */ + captiveportal_radius_stop_all(7); // Admin-Reboot + unlink_if_exists("{$g['vardb_path']}/captiveportal{$cpzone}.db"); + } /* Send Accounting-Off packet to the RADIUS server */ captiveportal_send_server_accounting('off'); } diff --git a/src/usr/local/www/services_captiveportal.php b/src/usr/local/www/services_captiveportal.php index ccdc6152af1..69875d12a31 100644 --- a/src/usr/local/www/services_captiveportal.php +++ b/src/usr/local/www/services_captiveportal.php @@ -151,6 +151,7 @@ $pconfig['radmac_secret'] = $a_cp[$cpzone]['radmac_secret']; $pconfig['radmac_fallback'] = isset($a_cp[$cpzone]['radmac_fallback']); $pconfig['reauthenticate'] = isset($a_cp[$cpzone]['reauthenticate']); + $pconfig['preservedb'] = isset($a_cp[$cpzone]['preservedb']); $pconfig['reauthenticateacct'] = $a_cp[$cpzone]['reauthenticateacct']; $pconfig['httpslogin_enable'] = isset($a_cp[$cpzone]['httpslogin']); $pconfig['httpsname'] = $a_cp[$cpzone]['httpsname']; @@ -353,6 +354,7 @@ $newcp['localauth_priv'] = isset($_POST['localauth_priv']); $newcp['radacct_enable'] = $_POST['radacct_enable'] ? true : false; $newcp['reauthenticate'] = $_POST['reauthenticate'] ? true : false; + $newcp['preservedb'] = $_POST['preservedb'] ? true : false; $newcp['radmac_secret'] = $_POST['radmac_secret'] ? $_POST['radmac_secret'] : false; $newcp['radmac_fallback'] = $_POST['radmac_fallback'] ? true : false; $newcp['reauthenticateacct'] = $_POST['reauthenticateacct']; @@ -633,6 +635,13 @@ function build_authserver_list() { $pconfig['blockedmacsurl'] ))->setHelp('Blocked MAC addresses will be redirected to this URL when attempting access.'); +$section->addInput(new Form_Checkbox( + 'preservedb', + 'Preserve users database', + 'Preserve connected users across reboot', + $pconfig['preservedb'] +))->setHelp('If enabled, connected users won\'t be disconnected during a pfSense reboot.'); + $section->addInput(new Form_Checkbox( 'noconcurrentlogins', 'Concurrent user logins', @@ -1178,6 +1187,7 @@ function hideGeneral(hide) { hideInput('preauthurl', hide); hideInput('redirurl', hide); hideInput('blockedmacsurl', hide); + hideCheckbox('preservedb', hide); hideCheckbox('noconcurrentlogins', hide); hideCheckbox('nomacfilter', hide); hideCheckbox('passthrumacadd', hide);