diff options
author | Vinicius Coque <vinicius.coque@bluepex.com> | 2011-03-25 08:49:04 -0300 |
---|---|---|
committer | Vinicius Coque <vinicius.coque@bluepex.com> | 2011-03-25 08:49:04 -0300 |
commit | 54bdff758f68e2e1b1ebd42b8b0b629b68ed1a3d (patch) | |
tree | 27b7d45e582e3d84cdf8d0fa0f3bc93b65f3c986 /etc/inc/captiveportal.inc | |
parent | b845290619244e8cfe3bc2aa6271c6629eeb86b5 (diff) | |
parent | 401fb0ad8fa7ad06743435808dac8e913b3c16bb (diff) | |
download | pfsense-54bdff758f68e2e1b1ebd42b8b0b629b68ed1a3d.zip pfsense-54bdff758f68e2e1b1ebd42b8b0b629b68ed1a3d.tar.gz |
Merge remote-tracking branch 'mainline/master' into inc
Conflicts:
etc/inc/auth.inc
etc/inc/config.lib.inc
etc/inc/filter.inc
etc/inc/pfsense-utils.inc
etc/inc/pkg-utils.inc
etc/inc/priv.defs.inc
etc/inc/services.inc
etc/inc/shaper.inc
etc/inc/voucher.inc
etc/inc/vpn.inc
usr/local/www/fbegin.inc
Diffstat (limited to 'etc/inc/captiveportal.inc')
-rw-r--r-- | etc/inc/captiveportal.inc | 611 |
1 files changed, 512 insertions, 99 deletions
diff --git a/etc/inc/captiveportal.inc b/etc/inc/captiveportal.inc index 4a3b80d..6535f54 100644 --- a/etc/inc/captiveportal.inc +++ b/etc/inc/captiveportal.inc @@ -211,6 +211,8 @@ function captiveportal_configure() { if ($g['booting']) echo "Starting captive portal... "; + else + captiveportal_syslog("Restarting captive portal."); /* kill any running mini_httpd */ killbypid("{$g['varrun_path']}/lighty-CaptivePortal.pid"); @@ -226,7 +228,7 @@ function captiveportal_configure() { touch("{$g['vardb_path']}/captiveportal.db"); /* kill any running minicron */ - killbypid("{$g['varrun_path']}/minicron.pid"); + killbypid("{$g['varrun_path']}/cp_prunedb.pid"); /* init ipfw rules */ captiveportal_init_rules(true); @@ -417,8 +419,12 @@ EOD; /* start up the webserving daemon */ captiveportal_init_webgui(); + /* Kill any existing prunecaptiveportal processes */ + if(file_exists("{$g['varrun_path']}/cp_prunedb.pid")) + killbypid("{$g['varrun_path']}/cp_prunedb.pid"); + /* start pruning process (interval defaults to 60 seconds) */ - mwexec("/usr/local/bin/minicron $croninterval {$g['varrun_path']}/minicron.pid " . + mwexec("/usr/local/bin/minicron $croninterval {$g['varrun_path']}/cp_prunedb.pid " . "/etc/rc.prunecaptiveportal"); /* generate radius server database */ @@ -429,7 +435,7 @@ EOD; } else { killbypid("{$g['varrun_path']}/lighty-CaptivePortal.pid"); - killbypid("{$g['varrun_path']}/minicron.pid"); + killbypid("{$g['varrun_path']}/cp_prunedb.pid"); captiveportal_radius_stop_all(); @@ -498,6 +504,7 @@ function captiveportal_init_webgui() { $res = mwexec("/usr/local/sbin/lighttpd -f {$g['varetc_path']}/lighty-CaptivePortal-SSL.conf"); } +/* reinit will disconnect all users, be careful! */ function captiveportal_init_rules($reinit = false) { global $config, $g; @@ -708,48 +715,47 @@ function captiveportal_prune_old() { !isset($config['captiveportal']['radiussession_timeout']) && !isset($config['voucher']['enable'])) return; + $radiusservers = captiveportal_get_radius_servers(); + /* read database */ $cpdb = captiveportal_read_db(); - $radiusservers = captiveportal_get_radius_servers(); - /* To make sure we iterate over ALL accounts on every run the count($cpdb) is moved * outside of the loop. Otherwise the loop would evaluate count() on every iteration * and since $i would increase and count() would decrement they would meet before we * had a chance to iterate over all accounts. */ $unsetindexes = array(); - $no_users = count($cpdb); - for ($i = 0; $i < $no_users; $i++) { + foreach ($cpdb as $cpentry) { $timedout = false; $term_cause = 1; /* hard timeout? */ if ($timeout) { - if ((time() - $cpdb[$i][0]) >= $timeout) { + if ((time() - $cpentry[0]) >= $timeout) { $timedout = true; $term_cause = 5; // Session-Timeout } } /* Session-Terminate-Time */ - if (!$timedout && !empty($cpdb[$i][9])) { - if (time() >= $cpdb[$i][9]) { + if (!$timedout && !empty($cpentry[9])) { + if (time() >= $cpentry[9]) { $timedout = true; $term_cause = 5; // Session-Timeout } } /* check if the radius idle_timeout attribute has been set and if its set change the idletimeout to this value */ - $uidletimeout = (is_numeric($cpdb[$i][8])) ? $cpdb[$i][8] : $idletimeout; + $uidletimeout = (is_numeric($cpentry[8])) ? $cpentry[8] : $idletimeout; /* if an idle timeout is specified, get last activity timestamp from ipfw */ if (!$timedout && $uidletimeout) { - $lastact = captiveportal_get_last_activity($cpdb[$i][2]); + $lastact = captiveportal_get_last_activity($cpentry[2]); /* If the user has logged on but not sent any traffic they will never be logged out. * We "fix" this by setting lastact to the login timestamp. */ - $lastact = $lastact ? $lastact : $cpdb[$i][0]; + $lastact = $lastact ? $lastact : $cpentry[0]; if ($lastact && ((time() - $lastact) >= $uidletimeout)) { $timedout = true; $term_cause = 4; // Idle-Timeout @@ -758,25 +764,25 @@ function captiveportal_prune_old() { } /* if vouchers are configured, activate session timeouts */ - if (!$timedout && isset($config['voucher']['enable']) && !empty($cpdb[$i][7])) { - if (time() >= ($cpdb[$i][0] + $cpdb[$i][7])) { + if (!$timedout && isset($config['voucher']['enable']) && !empty($cpentry[7])) { + if (time() >= ($cpentry[0] + $cpentry[7])) { $timedout = true; $term_cause = 5; // Session-Timeout } } /* if radius session_timeout is enabled and the session_timeout is not null, then check if the user should be logged out */ - if (!$timedout && isset($config['captiveportal']['radiussession_timeout']) && !empty($cpdb[$i][7])) { - if (time() >= ($cpdb[$i][0] + $cpdb[$i][7])) { + if (!$timedout && isset($config['captiveportal']['radiussession_timeout']) && !empty($cpentry[7])) { + if (time() >= ($cpentry[0] + $cpentry[7])) { $timedout = true; $term_cause = 5; // Session-Timeout } } if ($timedout) { - captiveportal_disconnect($cpdb[$i], $radiusservers,$term_cause,$stop_time); - captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "TIMEOUT"); - $unsetindexes[$i] = $i; + captiveportal_disconnect($cpentry, $radiusservers,$term_cause,$stop_time); + captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "TIMEOUT"); + $unsetindexes[] = $cpentry[5]; } /* do periodic RADIUS reauthentication? */ @@ -784,30 +790,30 @@ function captiveportal_prune_old() { if (isset($config['captiveportal']['radacct_enable'])) { if ($config['captiveportal']['reauthenticateacct'] == "stopstart") { /* stop and restart accounting */ - RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno - $cpdb[$i][4], // username - $cpdb[$i][5], // sessionid - $cpdb[$i][0], // start time + RADIUS_ACCOUNTING_STOP($cpentry[1], // ruleno + $cpentry[4], // username + $cpentry[5], // sessionid + $cpentry[0], // start time $radiusservers, - $cpdb[$i][2], // clientip - $cpdb[$i][3], // clientmac + $cpentry[2], // clientip + $cpentry[3], // clientmac 10); // NAS Request - exec("/sbin/ipfw table 1 entryzerostats {$cpdb[$i][2]}"); - exec("/sbin/ipfw table 2 entryzerostats {$cpdb[$i][2]}"); - RADIUS_ACCOUNTING_START($cpdb[$i][1], // ruleno - $cpdb[$i][4], // username - $cpdb[$i][5], // sessionid + exec("/sbin/ipfw table 1 entryzerostats {$cpentry[2]}"); + exec("/sbin/ipfw table 2 entryzerostats {$cpentry[2]}"); + RADIUS_ACCOUNTING_START($cpentry[1], // ruleno + $cpentry[4], // username + $cpentry[5], // sessionid $radiusservers, - $cpdb[$i][2], // clientip - $cpdb[$i][3]); // clientmac + $cpentry[2], // clientip + $cpentry[3]); // clientmac } else if ($config['captiveportal']['reauthenticateacct'] == "interimupdate") { - RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno - $cpdb[$i][4], // username - $cpdb[$i][5], // sessionid - $cpdb[$i][0], // start time + RADIUS_ACCOUNTING_STOP($cpentry[1], // ruleno + $cpentry[4], // username + $cpentry[5], // sessionid + $cpentry[0], // start time $radiusservers, - $cpdb[$i][2], // clientip - $cpdb[$i][3], // clientmac + $cpentry[2], // clientip + $cpentry[3], // clientmac 10, // NAS Request true); // Interim Updates } @@ -815,26 +821,24 @@ function captiveportal_prune_old() { /* check this user against RADIUS again */ if (isset($config['captiveportal']['reauthenticate'])) { - $auth_list = RADIUS_AUTHENTICATION($cpdb[$i][4], // username - base64_decode($cpdb[$i][6]), // password + $auth_list = RADIUS_AUTHENTICATION($cpentry[4], // username + base64_decode($cpentry[6]), // password $radiusservers, - $cpdb[$i][2], // clientip - $cpdb[$i][3], // clientmac - $cpdb[$i][1]); // ruleno + $cpentry[2], // clientip + $cpentry[3], // clientmac + $cpentry[1]); // ruleno if ($auth_list['auth_val'] == 3) { - captiveportal_disconnect($cpdb[$i], $radiusservers, 17); - captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "RADIUS_DISCONNECT", $auth_list['reply_message']); - $unsetindexes[$i] = $i; + captiveportal_disconnect($cpentry, $radiusservers, 17); + captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "RADIUS_DISCONNECT", $auth_list['reply_message']); + $unsetindexes[] = $cpentry[5]; } } } } - /* This is a kludge to overcome some php weirdness */ - foreach($unsetindexes as $unsetindex) - unset($cpdb[$unsetindex]); /* write database */ - captiveportal_write_db($cpdb); + if (!empty($unsetindexes)) + captiveportal_write_db($cpdb, false, $unsetindexes); } /* remove a single client according to the DB entry */ @@ -856,12 +860,15 @@ function captiveportal_disconnect($dbent, $radiusservers,$term_cause = 1,$stop_t false, $stop_time); } - /* Delete client's ip entry from tables 3 and 4. */ - mwexec("/sbin/ipfw table 1 delete {$dbent[2]}"); - mwexec("/sbin/ipfw table 2 delete {$dbent[2]}"); - - /* Release the ruleno so it can be reallocated to new clients. */ - captiveportal_free_ipfw_ruleno($dbent[1]); + + if (is_ipaddr($dbent[2])) { + /* Delete client's ip entry from tables 3 and 4. */ + mwexec("/sbin/ipfw table 1 delete {$dbent[2]}"); + mwexec("/sbin/ipfw table 2 delete {$dbent[2]}"); + /* XXX: Redundant?! Ensure all pf(4) states are killed. */ + mwexec("pfctl -k {$dbent[2]}"); + mwexec("pfctl -K {$dbent[2]}"); + } /* * These are the pipe numbers we use to control traffic shaping for each logged in user via captive portal @@ -872,32 +879,33 @@ function captiveportal_disconnect($dbent, $radiusservers,$term_cause = 1,$stop_t mwexec("/sbin/ipfw pipe " . ($dbent[1]+20001) . " delete"); } - /* XXX: Redundant?! Ensure all pf(4) states are killed. */ - mwexec("pfctl -k {$dbent[2]}"); - mwexec("pfctl -K {$dbent[2]}"); - + /* Release the ruleno so it can be reallocated to new clients. */ + captiveportal_free_ipfw_ruleno($dbent[1]); } -/* remove a single client by ipfw rule number */ -function captiveportal_disconnect_client($id,$term_cause = 1) { +/* remove a single client by sessionid */ +function captiveportal_disconnect_client($sessionid, $term_cause = 1, $logoutReason = "LOGOUT") { global $g, $config; - /* read database */ - $cpdb = captiveportal_read_db(); $radiusservers = captiveportal_get_radius_servers(); + $unsetindex = array(); + + $cpdblck = lock('captiveportaldb', LOCK_EX); + + /* read database */ + $cpdb = captiveportal_read_db(true); /* find entry */ - foreach ($cpdb as $i => $cpentry) { - if ($cpentry[1] == $id) { - captiveportal_disconnect($cpentry, $radiusservers, $term_cause); - captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "DISCONNECT"); - unset($cpdb[$i]); - break; - } - } + if (isset($cpdb[$sessionid])) { + $cpentry = $cpdb[$sessionid]; + /* write database */ + $unsetindex[] = $sessionid; + captiveportal_write_db($cpdb, true, $unsetindex); + unlock($cpdblck); - /* write database */ - captiveportal_write_db($cpdb); + captiveportal_disconnect($cpentry, $radiusservers, $term_cause); + captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "DISCONNECT"); + } } /* send RADIUS acct stop for all current clients */ @@ -1077,10 +1085,10 @@ function setup_dnsfilter_entries() { $cp_filterdns_conf = ""; if (is_array($config['captiveportal']['allowedhostname'])) { foreach ($config['captiveportal']['allowedhostname'] as $hostnameent) { - $cp_filterdns_conf .= "ipfw $hostnameent 3 '/etc/rc.captiveportal_configure'\n"; - $cp_filterdns_conf .= "ipfw $hostnameent 4 '/etc/rc.captiveportal_configure'\n"; - $cp_filterdns_conf .= "ipfw $hostnameent 7 '/etc/rc.captiveportal_configure'\n"; - $cp_filterdns_conf .= "ipfw $hostnameent 8 '/etc/rc.captiveportal_configure'\n"; + $cp_filterdns_conf .= "ipfw {$hostnameent['hostname']} 3\n"; + $cp_filterdns_conf .= "ipfw {$hostnameent['hostname']} 4\n"; + $cp_filterdns_conf .= "ipfw {$hostnameent['hostname']} 7\n"; + $cp_filterdns_conf .= "ipfw {$hostnameent['hostname']} 8\n"; } } file_put_contents($cp_filterdns_filename, $cp_filterdns_conf); @@ -1254,38 +1262,54 @@ function radius($username,$password,$clientip,$clientmac,$type) { } /* read captive portal DB into array */ -function captiveportal_read_db() { - global $g; +function captiveportal_read_db($locked = false) { + global $g; - $cpdb = array(); + $cpdb = array(); + if ($locked == false) $cpdblck = lock('captiveportaldb'); - $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r"); - if ($fd) { - while (!feof($fd)) { - $line = trim(fgets($fd)); - if ($line) - $cpdb[] = explode(",", $line); + $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r"); + if ($fd) { + while (!feof($fd)) { + $line = trim(fgets($fd)); + if ($line) { + $cpe = explode(",", $line); + /* Hash by session id */ + $cpdb[$cpe[5]] = $cpe; } - fclose($fd); } + fclose($fd); + } + if ($locked == false) unlock($cpdblck); - return $cpdb; + return $cpdb; } /* write captive portal DB */ -function captiveportal_write_db($cpdb) { - global $g; +function captiveportal_write_db($cpdb, $locked = false, $remove = false) { + global $g; + if ($locked == false) $cpdblck = lock('captiveportaldb', LOCK_EX); - $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w"); - if ($fd) { - foreach ($cpdb as $cpent) { - fwrite($fd, join(",", $cpent) . "\n"); - } - fclose($fd); + + if (is_array($remove)) { + if (!empty($remove)) { + $cpdb = captiveportal_read_db(true); + foreach ($remove as $key) + unset($cpdb[$key]); + } else + return; //This makes sure no record removal calls + } + $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w"); + if ($fd) { + foreach ($cpdb as $cpent) { + fwrite($fd, join(",", $cpent) . "\n"); } - unlock($cpdblck); + fclose($fd); + } + if ($locked == false) + unlock($cpdblck); } function captiveportal_write_elements() { @@ -1515,4 +1539,393 @@ function portal_ip_from_client_ip($cliip) { return false; } +/* functions move from index.php */ + +function portal_reply_page($redirurl, $type = null, $message = null, $clientmac = null, $clientip = null, $username = null, $password = null) { + global $g, $config; + + /* Get captive portal layout */ + if ($type == "redir") { + header("Location: {$redirurl}"); + return; + } else if ($type == "login") + $htmltext = get_include_contents("{$g['varetc_path']}/captiveportal.html"); + else + $htmltext = get_include_contents("{$g['varetc_path']}/captiveportal-error.html"); + + /* substitute the PORTAL_REDIRURL variable */ + if ($config['captiveportal']['preauthurl']) { + $htmltext = str_replace("\$PORTAL_REDIRURL\$", "{$config['captiveportal']['preauthurl']}", $htmltext); + $htmltext = str_replace("#PORTAL_REDIRURL#", "{$config['captiveportal']['preauthurl']}", $htmltext); + } + + /* substitute other variables */ + if (isset($config['captiveportal']['httpslogin'])) { + $htmltext = str_replace("\$PORTAL_ACTION\$", "https://{$config['captiveportal']['httpsname']}:8001/", $htmltext); + $htmltext = str_replace("#PORTAL_ACTION#", "https://{$config['captiveportal']['httpsname']}:8001/", $htmltext); + } else { + $ifip = portal_ip_from_client_ip($clientip); + if (!$ifip) + $ourhostname = $config['system']['hostname'] . ":8000"; + else + $ourhostname = "{$ifip}:8000"; + $htmltext = str_replace("\$PORTAL_ACTION\$", "http://{$ourhostname}/", $htmltext); + $htmltext = str_replace("#PORTAL_ACTION#", "http://{$ourhostname}/", $htmltext); + } + + $htmltext = str_replace("\$PORTAL_REDIRURL\$", htmlspecialchars($redirurl), $htmltext); + $htmltext = str_replace("\$PORTAL_MESSAGE\$", htmlspecialchars($message), $htmltext); + $htmltext = str_replace("\$CLIENT_MAC\$", htmlspecialchars($clientmac), $htmltext); + $htmltext = str_replace("\$CLIENT_IP\$", htmlspecialchars($clientip), $htmltext); + + // Special handling case for captive portal master page so that it can be ran + // through the PHP interpreter using the include method above. We convert the + // $VARIABLE$ case to #VARIABLE# in /etc/inc/captiveportal.inc before writing out. + $htmltext = str_replace("#PORTAL_REDIRURL#", htmlspecialchars($redirurl), $htmltext); + $htmltext = str_replace("#PORTAL_MESSAGE#", htmlspecialchars($message), $htmltext); + $htmltext = str_replace("#CLIENT_MAC#", htmlspecialchars($clientmac), $htmltext); + $htmltext = str_replace("#CLIENT_IP#", htmlspecialchars($clientip), $htmltext); + $htmltext = str_replace("#USERNAME#", htmlspecialchars($username), $htmltext); + $htmltext = str_replace("#PASSWORD#", htmlspecialchars($password), $htmltext); + + echo $htmltext; +} + +function portal_mac_radius($clientmac,$clientip) { + global $config ; + + $radmac_secret = $config['captiveportal']['radmac_secret']; + + /* authentication against the radius server */ + $username = mac_format($clientmac); + $auth_list = radius($username,$radmac_secret,$clientip,$clientmac,"MACHINE LOGIN"); + if ($auth_list['auth_val'] == 2) + return TRUE; + if (!empty($auth_list['url_redirection'])) + portal_reply_page($auth_list['url_redirection'], "redir"); + + return FALSE; +} + +function portal_allow($clientip,$clientmac,$username,$password = null, $attributes = null, $ruleno = null) { + + global $redirurl, $g, $config, $type, $passthrumac, $_POST; + + /* See if a ruleno is passed, if not start sessions because this means there isn't one atm */ + if ($ruleno == null) + $ruleno = captiveportal_get_next_ipfw_ruleno(); + + /* if the pool is empty, return appropriate message and exit */ + if (is_null($ruleno)) { + portal_reply_page($redirurl, "error", "System reached maximum login capacity"); + log_error("WARNING! Captive portal has reached maximum login capacity"); + exit; + } + + // Ensure we create an array if we are missing attributes + if (!is_array($attributes)) + $attributes = array(); + + $radiusservers = captiveportal_get_radius_servers(); + + /* Do not allow concurrent login execution. */ + $cpdblck = lock('captiveportaldb', LOCK_EX); + + unset($sessionid); + + /* read in client database */ + $cpdb = captiveportal_read_db(true); + + if ($attributes['voucher']) + $remaining_time = $attributes['session_timeout']; + + $writecfg = false; + /* Find an existing session */ + if ((isset($config['captiveportal']['noconcurrentlogins'])) && $passthrumac) { + if (isset($config['captiveportal']['passthrumacadd'])) { + $mac = captiveportal_passthrumac_findbyname($username); + if (!empty($mac)) { + if ($_POST['replacemacpassthru']) { + foreach ($config['captiveportal']['passthrumac'] as $idx => $macent) { + if ($macent['mac'] == $mac['mac']) { + $macrules = ""; + $ruleno = captiveportal_get_ipfw_passthru_ruleno($mac['mac']); + if ($ruleno) { + captiveportal_free_ipfw_ruleno($ruleno, true); + $macrules .= "delete {$ruleno}\n"; + ++$ruleno; + $macrules .= "delete {$ruleno}\n"; + } + unset($config['captiveportal']['passthrumac'][$idx]); + $mac['mac'] = $clientmac; + $config['captiveportal']['passthrumac'][] = $mac; + $macrules .= captiveportal_passthrumac_configure_entry($mac); + file_put_contents("{$g['tmp_path']}/macentry.rules.tmp", $macrules); + mwexec("/sbin/ipfw -q {$g['tmp_path']}/macentry.rules.tmp"); + $writecfg = true; + $sessionid = true; + break; + } + } + } else { + portal_reply_page($redirurl, "error", "Username: {$username} is already authenticated using another MAC address.", + $clientmac, $clientip, $username, $password); + exit; + } + } + } + } + + foreach ($cpdb as $sid => $cpentry) { + /* on the same ip */ + if($cpentry[2] == $clientip) { + captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - REUSING OLD SESSION"); + $sessionid = $sid; + break; + } + elseif (($attributes['voucher']) && ($username != 'unauthenticated') && ($cpentry[4] == $username)) { + // user logged in with an active voucher. Check for how long and calculate + // how much time we can give him (voucher credit - used time) + $remaining_time = $cpentry[0] + $cpentry[7] - time(); + if ($remaining_time < 0) // just in case. + $remaining_time = 0; + + /* This user was already logged in so we disconnect the old one */ + captiveportal_disconnect($cpentry,$radiusservers,13); + captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - TERMINATING OLD SESSION"); + unset($cpdb[$sid]); + break; + } + elseif ((isset($config['captiveportal']['noconcurrentlogins'])) && ($username != 'unauthenticated')) { + /* on the same username */ + if (strcasecmp($cpentry[4], $username) == 0) { + /* This user was already logged in so we disconnect the old one */ + captiveportal_disconnect($cpentry,$radiusservers,13); + captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - TERMINATING OLD SESSION"); + unset($cpdb[$sid]); + break; + } + } + } + + if ($attributes['voucher'] && $remaining_time <= 0) + return 0; // voucher already used and no time left + + if (!isset($sessionid)) { + /* generate unique session ID */ + $tod = gettimeofday(); + $sessionid = substr(md5(mt_rand() . $tod['sec'] . $tod['usec'] . $clientip . $clientmac), 0, 16); + + /* Add rules for traffic shaping + * We don't need to add extra rules since traffic will pass due to the following kernel option + * net.inet.ip.fw.one_pass: 1 + */ + $peruserbw = isset($config['captiveportal']['peruserbw']); + + $bw_up = isset($attributes['bw_up']) ? trim($attributes['bw_up']) : $config['captiveportal']['bwdefaultup']; + $bw_down = isset($attributes['bw_down']) ? trim($attributes['bw_down']) : $config['captiveportal']['bwdefaultdn']; + + if ($passthrumac) { + $mac = array(); + $mac['mac'] = $clientmac; + if (isset($config['captiveportal']['passthrumacaddusername'])) + $mac['username'] = $username; + $mac['descr'] = "Auto added pass-through MAC for user {$username}"; + if (!empty($bw_up)) + $mac['bw_up'] = $bw_up; + if (!empty($bw_down)) + $mac['bw_down'] = $bw_down; + if (!is_array($config['captiveportal']['passthrumac'])) + $config['captiveportal']['passthrumac'] = array(); + $config['captiveportal']['passthrumac'][] = $mac; + unlock($cpdblck); + $macrules = captiveportal_passthrumac_configure_entry($mac); + file_put_contents("{$g['tmp_path']}/macentry.rules.tmp", $macrules); + mwexec("/sbin/ipfw -q {$g['tmp_path']}/macentry.rules.tmp"); + $writecfg = true; + } else { + if ($peruserbw && !empty($bw_up) && is_numeric($bw_up)) { + $bw_up_pipeno = $ruleno + 20000; + //$bw_up /= 1000; // Scale to Kbit/s + mwexec("/sbin/ipfw pipe {$bw_up_pipeno} config bw {$bw_up}Kbit/s queue 100"); + + if (!isset($config['captiveportal']['nomacfilter'])) + mwexec("/sbin/ipfw table 1 add {$clientip} mac {$clientmac} {$bw_up_pipeno}"); + else + mwexec("/sbin/ipfw table 1 add {$clientip} {$bw_up_pipeno}"); + } else { + if (!isset($config['captiveportal']['nomacfilter'])) + mwexec("/sbin/ipfw table 1 add {$clientip} mac {$clientmac}"); + else + mwexec("/sbin/ipfw table 1 add {$clientip}"); + } + if ($peruserbw && !empty($bw_down) && is_numeric($bw_down)) { + $bw_down_pipeno = $ruleno + 20001; + //$bw_down /= 1000; // Scale to Kbit/s + mwexec("/sbin/ipfw pipe {$bw_down_pipeno} config bw {$bw_down}Kbit/s queue 100"); + + if (!isset($config['captiveportal']['nomacfilter'])) + mwexec("/sbin/ipfw table 2 add {$clientip} mac {$clientmac} {$bw_down_pipeno}"); + else + mwexec("/sbin/ipfw table 2 add {$clientip} {$bw_down_pipeno}"); + } else { + if (!isset($config['captiveportal']['nomacfilter'])) + mwexec("/sbin/ipfw table 2 add {$clientip} mac {$clientmac}"); + else + mwexec("/sbin/ipfw table 2 add {$clientip}"); + } + + if ($attributes['voucher']) + $attributes['session_timeout'] = $remaining_time; + + /* encode password in Base64 just in case it contains commas */ + $bpassword = base64_encode($password); + $cpdb[] = array(time(), $ruleno, $clientip, $clientmac, $username, $sessionid, $bpassword, + $attributes['session_timeout'], $attributes['idle_timeout'], $attributes['session_terminate_time']); + + /* rewrite information to database */ + captiveportal_write_db($cpdb, true); + unlock($cpdblck); + + if (isset($config['captiveportal']['radacct_enable']) && !empty($radiusservers)) { + $acct_val = RADIUS_ACCOUNTING_START($ruleno, + $username, $sessionid, $radiusservers, $clientip, $clientmac); + if ($acct_val == 1) + captiveportal_logportalauth($username,$clientmac,$clientip,$type,"RADIUS ACCOUNTING FAILED"); + } + } + } else + unlock($cpdblck); + + if ($writecfg == true) + write_config(); + + /* redirect user to desired destination */ + if (!empty($attributes['url_redirection'])) + $my_redirurl = $attributes['url_redirection']; + else if ($config['captiveportal']['redirurl']) + $my_redirurl = $config['captiveportal']['redirurl']; + else + $my_redirurl = $redirurl; + + if(isset($config['captiveportal']['logoutwin_enable']) && !$passthrumac) { + + if (isset($config['captiveportal']['httpslogin'])) + $logouturl = "https://{$config['captiveportal']['httpsname']}:8001/"; + else { + $ifip = portal_ip_from_client_ip($clientip); + if (!$ifip) + $ourhostname = $config['system']['hostname'] . ":8000"; + else + $ourhostname = "{$ifip}:8000"; + $logouturl = "http://{$ourhostname}/"; + } + + if (isset($attributes['reply_message'])) + $message = $attributes['reply_message']; + else + $message = 0; + + include("{$g['varetc_path']}/captiveportal-logout.html"); + + } else { + header("Location: " . $my_redirurl); + } + + return $sessionid; +} + + +/* + * Used for when pass-through credits are enabled. + * Returns true when there was at least one free login to deduct for the MAC. + * Expired entries are removed as they are seen. + * Active entries are updated according to the configuration. + */ +function portal_consume_passthrough_credit($clientmac) { + global $config; + + if (!empty($config['captiveportal']['freelogins_count']) && is_numeric($config['captiveportal']['freelogins_count'])) + $freeloginscount = $config['captiveportal']['freelogins_count']; + else + return false; + + if (!empty($config['captiveportal']['freelogins_resettimeout']) && is_numeric($config['captiveportal']['freelogins_resettimeout'])) + $resettimeout = $config['captiveportal']['freelogins_resettimeout']; + else + return false; + + if ($freeloginscount < 1 || $resettimeout <= 0 || !$clientmac) + return false; + + $updatetimeouts = isset($config['captiveportal']['freelogins_updatetimeouts']); + + /* + * Read database of used MACs. Lines are a comma-separated list + * of the time, MAC, then the count of pass-through credits remaining. + */ + $usedmacs = captiveportal_read_usedmacs_db(); + + $currenttime = time(); + $found = false; + foreach ($usedmacs as $key => $usedmac) { + $usedmac = explode(",", $usedmac); + + if ($usedmac[1] == $clientmac) { + if ($usedmac[0] + ($resettimeout * 3600) > $currenttime) { + if ($usedmac[2] < 1) { + if ($updatetimeouts) { + $usedmac[0] = $currenttime; + unset($usedmacs[$key]); + $usedmacs[] = implode(",", $usedmac); + captiveportal_write_usedmacs_db($usedmacs); + } + + return false; + } else { + $usedmac[2] -= 1; + $usedmacs[$key] = implode(",", $usedmac); + } + + $found = true; + } else + unset($usedmacs[$key]); + + break; + } else if ($usedmac[0] + ($resettimeout * 3600) <= $currenttime) + unset($usedmacs[$key]); + } + + if (!$found) { + $usedmac = array($currenttime, $clientmac, $freeloginscount - 1); + $usedmacs[] = implode(",", $usedmac); + } + + captiveportal_write_usedmacs_db($usedmacs); + return true; +} + +function captiveportal_read_usedmacs_db() { + global $g; + + $cpumaclck = lock('captiveusedmacs'); + if (file_exists("{$g['vardb_path']}/captiveportal_usedmacs.db")) { + $usedmacs = file("{$g['vardb_path']}/captiveportal_usedmacs.db", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES); + if (!$usedmacs) + $usedmacs = array(); + } else + $usedmacs = array(); + + unlock($cpumaclck); + return $usedmacs; +} + +function captiveportal_write_usedmacs_db($usedmacs) { + global $g; + + $cpumaclck = lock('captiveusedmacs', LOCK_EX); + @file_put_contents("{$g['vardb_path']}/captiveportal_usedmacs.db", implode("\n", $usedmacs)); + unlock($cpumaclck); +} + ?> |