| 1 | <?php
 | 
  
    | 2 | /*
 | 
  
    | 3 |  * index.php
 | 
  
    | 4 |  *
 | 
  
    | 5 |  * part of pfSense (https://www.pfsense.org)
 | 
  
    | 6 |  * Copyright (c) 2004-2013 BSD Perimeter
 | 
  
    | 7 |  * Copyright (c) 2013-2016 Electric Sheep Fencing
 | 
  
    | 8 |  * Copyright (c) 2014-2024 Rubicon Communications, LLC (Netgate)
 | 
  
    | 9 |  * All rights reserved.
 | 
  
    | 10 |  *
 | 
  
    | 11 |  * Originally part of m0n0wall (http://m0n0.ch/wall)
 | 
  
    | 12 |  * Copyright (c) 2003-2006 Manuel Kasper <mk@neon1.net>.
 | 
  
    | 13 |  * All rights reserved.
 | 
  
    | 14 |  *
 | 
  
    | 15 |  * Licensed under the Apache License, Version 2.0 (the "License");
 | 
  
    | 16 |  * you may not use this file except in compliance with the License.
 | 
  
    | 17 |  * You may obtain a copy of the License at
 | 
  
    | 18 |  *
 | 
  
    | 19 |  * http://www.apache.org/licenses/LICENSE-2.0
 | 
  
    | 20 |  *
 | 
  
    | 21 |  * Unless required by applicable law or agreed to in writing, software
 | 
  
    | 22 |  * distributed under the License is distributed on an "AS IS" BASIS,
 | 
  
    | 23 |  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 | 
  
    | 24 |  * See the License for the specific language governing permissions and
 | 
  
    | 25 |  * limitations under the License.
 | 
  
    | 26 |  */
 | 
  
    | 27 | 
 | 
  
    | 28 | require_once("auth.inc");
 | 
  
    | 29 | require_once("util.inc");
 | 
  
    | 30 | require_once("functions.inc");
 | 
  
    | 31 | require_once("captiveportal.inc");
 | 
  
    | 32 | 
 | 
  
    | 33 | header("Expires: 0");
 | 
  
    | 34 | header("Cache-Control: no-cache, no-store, must-revalidate");
 | 
  
    | 35 | header("Connection: close");
 | 
  
    | 36 | 
 | 
  
    | 37 | global $cpzone, $cpzoneid, $cpzoneprefix;
 | 
  
    | 38 | 
 | 
  
    | 39 | $cpzone = strtolower($_REQUEST['zone']);
 | 
  
    | 40 | $cpcfg = config_get_path("captiveportal/{$cpzone}", []);
 | 
  
    | 41 | 
 | 
  
    | 42 | /* NOTE: IE 8/9 is buggy and that is why this is needed */
 | 
  
    | 43 | $orig_request = trim($_REQUEST['redirurl'], " /");
 | 
  
    | 44 | 
 | 
  
    | 45 | /* If the post-auth redirect is set, always use it. Otherwise take what was supplied in URL. */
 | 
  
    | 46 | if (!empty($cpcfg) && is_URL($cpcfg['redirurl'], true)) {
 | 
  
    | 47 | 	$redirurl = $cpcfg['redirurl'];
 | 
  
    | 48 | } elseif (preg_match("/redirurl=(.*)/", $orig_request, $matches)) {
 | 
  
    | 49 | 	$redirurl = urldecode($matches[1]);
 | 
  
    | 50 | } elseif ($_REQUEST['redirurl']) {
 | 
  
    | 51 | 	$redirurl = $_REQUEST['redirurl'];
 | 
  
    | 52 | }
 | 
  
    | 53 | /* Sanity check: If the redirect target is not a URL, do not attempt to use it like one. */
 | 
  
    | 54 | if (!is_URL(urldecode($redirurl), true)) {
 | 
  
    | 55 | 	$redirurl = "";
 | 
  
    | 56 | }
 | 
  
    | 57 | 
 | 
  
    | 58 | if (empty($cpcfg)) {
 | 
  
    | 59 | 	log_error("Submission to captiveportal with unknown parameter zone: " . htmlspecialchars($cpzone));
 | 
  
    | 60 | 	portal_reply_page($redirurl, "error", gettext("Internal error"));
 | 
  
    | 61 | 	ob_flush();
 | 
  
    | 62 | 	return;
 | 
  
    | 63 | }
 | 
  
    | 64 | 
 | 
  
    | 65 | $cpzoneid = $cpcfg['zoneid'];
 | 
  
    | 66 | $cpzoneprefix = CPPREFIX . $cpzoneid;
 | 
  
    | 67 | $orig_host = $_SERVER['HTTP_HOST'];
 | 
  
    | 68 | $clientip = $_SERVER['REMOTE_ADDR'];
 | 
  
    | 69 | 
 | 
  
    | 70 | if (!$clientip) {
 | 
  
    | 71 | 	/* not good - bail out */
 | 
  
    | 72 | 	log_error("Zone: {$cpzone} - Captive portal could not determine client's IP address.");
 | 
  
    | 73 | 	$errormsg = gettext("An error occurred.  Please check the system logs for more information.");
 | 
  
    | 74 | 	portal_reply_page($redirurl, "error", $errormsg);
 | 
  
    | 75 | 	ob_flush();
 | 
  
    | 76 | 	return;
 | 
  
    | 77 | }
 | 
  
    | 78 | 
 | 
  
    | 79 | $ourhostname = portal_hostname_from_client_ip($clientip);
 | 
  
    | 80 | $protocol = (config_path_enabled("captiveportal/{$cpzone}", "httpslogin")) ? 'https://' : 'http://';
 | 
  
    | 81 | $logouturl = "{$protocol}{$ourhostname}/";
 | 
  
    | 82 | 
 | 
  
    | 83 | $cpsession = captiveportal_isip_logged($clientip);
 | 
  
    | 84 | if (!empty($cpsession)) {
 | 
  
    | 85 |   $sessionid = $cpsession['sessionid'];
 | 
  
    | 86 | }
 | 
  
    | 87 | 
 | 
  
    | 88 | /* ----------------------CUSTOM CODE -------------------------------------------------------------------------
 | 
  
    | 89 |         24.11 Stbl Index.php Custom Code for Kea DHCP, check MAC address, not IP to verify client validation
 | 
  
    | 90 |         Kea flushes the IP quickly after the client disconnects, creating IP/MAC conflicts 
 | 
  
    | 91 |           within the captive portal database of validated clients.  
 | 
  
    | 92 |         It is the MAC that it validated, not the IP as IP can change frequently with Kea Reclamation policies
 | 
  
    | 93 |         This code updates the IP in the database to the current one for this MAC and passes the client through
 | 
  
    | 94 |           the Portal respecting that the client has a validated MAC.   Dale Harron, 17 December 2024. 
 | 
  
    | 95 | ------------------------------------------------------------------------------------------------------------- */
 | 
  
    | 96 | 
 | 
  
    | 97 | $dbchanged = "no";
 | 
  
    | 98 | 
 | 
  
    | 99 | $ARPforIP = shell_exec("arp '$clientip'");
 | 
  
    | 100 | $clientmac = substr($ARPforIP, 17, 24);
 | 
  
    | 101 | if (preg_match('/([a-fA-F0-9]{2}[:|\-]?){6}/', "'{$clientmac}'", $matches)) {
 | 
  
    | 102 |     $clientmac = $matches[0];
 | 
  
    | 103 | }
 | 
  
    | 104 | 
 | 
  
    | 105 | /* query the cp database for this client MAC address to see if it is already authorized */
 | 
  
    | 106 | $query = "WHERE mac = '{$clientmac}'";
 | 
  
    | 107 | $cpdbqry = captiveportal_read_db($query);
 | 
  
    | 108 | foreach ($cpdbqry as $cpentrymac) {
 | 
  
    | 109 |   $cpsessionmac = $cpentrymac;
 | 
  
    | 110 | }
 | 
  
    | 111 | $sessionidmac = $cpentrymac[5];
 | 
  
    | 112 | $cpdbip = $cpentrymac[2];
 | 
  
    | 113 | $cpdbmac = $cpentrymac[3];
 | 
  
    | 114 | unset ($cpdbqry);
 | 
  
    | 115 | 
 | 
  
    | 116 | // If either MACs or IPs don't match, fix the CP DB otherwise do nothing and run original index.php code
 | 
  
    | 117 | if ($cpdbip <> $clientip || $cpdbmac <> $clientmac) {
 | 
  
    | 118 |   //  captiveportal_logportalauth($clientmac, $cpdbmac, $cpdbip, $clientip, $clientmac . "=" . $cpdbmac . "=" . $clientip . "=" . $cpdbip . "=" . $clientmac);
 | 
  
    | 119 | 
 | 
  
    | 120 |   if( ($clientip <> $cpdbip) && ($cpdbmac == $clientmac)) {
 | 
  
    | 121 |     // MACs match so update the old IP in the CP DB to current IP for the authorized MAC of this device
 | 
  
    | 122 |     $updt_field = "ip";
 | 
  
    | 123 |     $new_value = "'{$clientip}'";
 | 
  
    | 124 |     captiveportal_write_db("UPDATE captiveportal SET {$updt_field} = {$new_value} WHERE sessionid = '{$sessionidmac}'");
 | 
  
    | 125 |     $dbchanged = "yes";
 | 
  
    | 126 |   }  
 | 
  
    | 127 | 
 | 
  
    | 128 |   // If this is a new MAC that has an authorized IP but is not yet authorized itself, change the IP in the cp database to OFFLINE.
 | 
  
    | 129 |   // This will retain the old MAC's authorization and free up the IP to be assigned to this device's MAC; present the login page.
 | 
  
    | 130 | 
 | 
  
    | 131 |   /* query the cp database for this client IP address to see if it is already associated with an authorized MAC*/
 | 
  
    | 132 |   $query = "WHERE ip = '{$clientip}'";
 | 
  
    | 133 |   $cpdbqry = captiveportal_read_db($query);
 | 
  
    | 134 |   foreach ($cpdbqry as $cpentryip) {
 | 
  
    | 135 |     $cpsessionip = $cpentryip;
 | 
  
    | 136 |   }
 | 
  
    | 137 |   $sessionidip = $cpentryip[5];
 | 
  
    | 138 |   $cpdbip = $cpentryip[2];
 | 
  
    | 139 | 
 | 
  
    | 140 |   if (!$cpdbmac && $cpdbip == $clientip) {
 | 
  
    | 141 |     $updt_field = 'ip';
 | 
  
    | 142 |     $new_value_tmp = "NOIP-" .  $cpzone;
 | 
  
    | 143 |     $new_value = "'{$new_value_tmp}'";
 | 
  
    | 144 |     captiveportal_write_db("UPDATE captiveportal SET {$updt_field} = {$new_value} WHERE sessionid = '{$sessionid}'");
 | 
  
    | 145 |     $dbchanged = "yes";
 | 
  
    | 146 |   }
 | 
  
    | 147 |   unset($cpdbqry);
 | 
  
    | 148 | 
 | 
  
    | 149 |   // If this MAC/IP pair does not match the MAC/IP in the Captive Portal database, then the IP has changed
 | 
  
    | 150 |   // update the Captive Portal database to associate the current IP with this already authorized MAC
 | 
  
    | 151 |   // Note, this could result in multiple IP/MAC pairs of the same IP in the Captive Portal database. 
 | 
  
    | 152 |   // Set all conflicting IPs to OFFLINE to retain authorization in CP DB
 | 
  
    | 153 | 
 | 
  
    | 154 |   if(($cpidmac <> $clientmac) && ($cpdbip == $clientip)) {  
 | 
  
    | 155 |     //Remove IP conflicts assigned to other MACs in the Captive Portal Database
 | 
  
    | 156 |     $query = "WHERE ip = '{$clientip}'";
 | 
  
    | 157 |     $cpdb_ip = captiveportal_read_db($query);
 | 
  
    | 158 |     foreach ($cpdb_ip as $cpentryip) {
 | 
  
    | 159 |       $cpsessionip = $cpentryip;
 | 
  
    | 160 |       $sessionidip = $cpsessionip['sessionid'];
 | 
  
    | 161 |       $updt_field = "ip";
 | 
  
    | 162 |       $unassignip = "NOIP-" .  $cpzone;
 | 
  
    | 163 |       $new_value = "'{$unassignip}'";
 | 
  
    | 164 |       if($clientmac <> $cpentryip[3]) {
 | 
  
    | 165 |         captiveportal_write_db("UPDATE captiveportal SET {$updt_field} = {$new_value} WHERE sessionid = '{$sessionidip}'");
 | 
  
    | 166 |         $dbchanged = "yes";
 | 
  
    | 167 |       //captiveportal_logportalauth($clientmac, $cpdbmac, $clientip, $cpdbip, $clientmac . "=" . $cpdbmac . "=" . $clientip . "=" . $cpdbip . "=" . $clientmac);
 | 
  
    | 168 |       }
 | 
  
    | 169 |       unset($cpdb_ip);
 | 
  
    | 170 |     }
 | 
  
    | 171 |   }
 | 
  
    | 172 | 
 | 
  
    | 173 |   //If the Database has been updated, reload the database for this zone.
 | 
  
    | 174 |   if( $dbchanged == "yes" ) {
 | 
  
    | 175 |     //Now reload this database with these changes to make them active
 | 
  
    | 176 |     if (isset($cpcfg['preservedb']) || $reload_rules ||
 | 
  
    | 177 |       captiveportal_xmlrpc_sync_get_details($syncip, $syncport, $syncuser, $syncpass)) {
 | 
  
    | 178 |       $connected_users = captiveportal_read_db();
 | 
  
    | 179 |       if (!empty($connected_users)) {
 | 
  
    | 180 |         //echo "Reconnecting users to captive portal {$cpcfg['zone']}... ";
 | 
  
    | 181 |         foreach ($connected_users as $user) {
 | 
  
    | 182 |         captiveportal_reserve_ruleno($user['pipeno']);
 | 
  
    | 183 |         captiveportal_ether_configure_entry($user, 'auth', true);
 | 
  
    | 184 |         }
 | 
  
    | 185 |       //echo "done\n";
 | 
  
    | 186 |       }
 | 
  
    | 187 |       $cpsession = captiveportal_isip_logged($clientip);
 | 
  
    | 188 |       if (!empty($cpsession)) {
 | 
  
    | 189 |         $sessionid = $cpsession['sessionid'];
 | 
  
    | 190 |       }  //ensure the $sessionid matches the now updated IP of this client
 | 
  
    | 191 |     } 
 | 
  
    | 192 |   } //reindex as CP DB has changed
 | 
  
    | 193 | } //if either IPs or MACs are not equal do something
 | 
  
    | 194 | //captiveportal_logportalauth($clientmac, $cpdbmac, $clientip, $cpdbip, $clientmac . "=" . $cpdbmac . "=" . $clientip . "=" . $cpdbip . "=" . $clientmac);
 | 
  
    | 195 | 
 | 
  
    | 196 | /* ----------- End Custom Code  ----------- */
 | 
  
    | 197 | 
 | 
  
    | 198 | /* Automatically switching to the logout page requires a custom logout page to be present. */
 | 
  
    | 199 | if ((!empty($cpsession)) && (! $_POST['logout_id']) && (!empty($cpcfg['page']['logouttext']))) {
 | 
  
    | 200 | 	/* if client already connected and a custom logout page is set : show logout page */
 | 
  
    | 201 | 	$attributes = array();
 | 
  
    | 202 | 	if (!empty($cpsession['session_timeout']))
 | 
  
    | 203 | 		$attributes['session_timeout'] = $cpsession['session_timeout'];
 | 
  
    | 204 | 	if (!empty($cpsession['session_terminate_time']))
 | 
  
    | 205 | 		$attributes['session_terminate_time'] = $cpsession['session_terminate_time'];
 | 
  
    | 206 | 
 | 
  
    | 207 | 	include("{$g['varetc_path']}/captiveportal-{$cpzone}-logout.html");
 | 
  
    | 208 | 	ob_flush();
 | 
  
    | 209 | 	return;
 | 
  
    | 210 | } elseif (!empty($cpsession) && !isset($_POST['logout_id'])) {
 | 
  
    | 211 | 	/* If the client tries to access the captive portal page while already connected,
 | 
  
    | 212 | 		but no custom logout page exists */
 | 
  
    | 213 | 	$logo_src = "{$protocol}{$ourhostname}/" . get_captive_portal_logo();
 | 
  
    | 214 | 	$bg_src = get_captive_portal_bg();
 | 
  
    | 215 | ?>
 | 
  
    | 216 | <!DOCTYPE html>
 | 
  
    | 217 | <html>
 | 
  
    | 218 | <head>
 | 
  
    | 219 |   <meta charset="UTF-8">
 | 
  
    | 220 |   <meta name="viewport" content="width=device-width, initial-scale=1.0">
 | 
  
    | 221 |   <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
 | 
  
    | 222 |   <title>Captive Portal</title>
 | 
  
    | 223 |   <style>
 | 
  
    | 224 | 	  #content,.login,.login-card a,.login-card h1,.login-help{text-align:center}body,html{margin:0;padding:0;width:100%;height:100%;display:table}#content{font-family:'Source Sans Pro',sans-serif;background-color:#1C1275;background:<?= $bg_src ?>;-webkit-background-size:cover;-moz-background-size:cover;-o-background-size:cover;background-size:cover;display:table-cell;vertical-align:middle}.login-card{padding:40px;width:280px;background-color:#F7F7F7;margin:100px auto 10px;border-radius:2px;box-shadow:0 2px 2px rgba(0,0,0,.3);overflow:hidden}.login-card h1{font-weight:400;font-size:2.3em;color:#1383c6}.login-card h1 span{color:#f26721}.login-card img{width:70%;height:70%}.login-card input[type=submit]{width:100%;display:block;margin-bottom:10px;position:relative}.login-card input[type=text],input[type=password]{height:44px;font-size:16px;width:100%;margin-bottom:10px;-webkit-appearance:none;background:#fff;border:1px solid #d9d9d9;border-top:1px solid silver;padding:0 8px;box-sizing:border-box;-moz-box-sizing:border-box}.login-card input[type=text]:hover,input[type=password]:hover{border:1px solid #b9b9b9;border-top:1px solid #a0a0a0;-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.login{font-size:14px;font-family:Arial,sans-serif;font-weight:700;height:36px;padding:0 8px}.login-submit{-webkit-appearance:none;-moz-appearance:none;appearance:none;border:0;color:#fff;text-shadow:0 1px rgba(0,0,0,.1);background-color:#4d90fe}.login-submit:disabled{opacity:.6}.login-submit:hover{border:0;text-shadow:0 1px rgba(0,0,0,.3);background-color:#357ae8}.login-card a{text-decoration:none;color:#222;font-weight:400;display:inline-block;opacity:.6;transition:opacity ease .5s}.login-card a:hover{opacity:1}.login-help{width:100%;font-size:12px}.list{list-style-type:none;padding:0}.list__item{margin:0 0 .7rem;padding:0}label{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;text-align:left;font-size:14px;}input[type=checkbox]{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;margin-right:10px;float:left}@media screen and (max-width:450px){.login-card{width:70%!important}.login-card img{width:30%;height:30%}}textarea{width:66%;margin:auto;height:120px;max-height:120px;background-color:#f7f7f7;padding:20px}#terms{display:none;padding-top:100px;padding-bottom:300px;}.auth_source{border: 1px solid lightgray; padding:20px 8px 0px 8px; margin-top: -2em; border-radius: 2px; }.auth_head{background-color:#f7f7f7;display:inline-block;}.auth_head_div{text-align:left;}#error-message{text-align:left;color:#ff3e3e;font-style:italic;}
 | 
  
    | 225 |   </style>
 | 
  
    | 226 | </head>
 | 
  
    | 227 | 
 | 
  
    | 228 | <body>
 | 
  
    | 229 | <div id="content">
 | 
  
    | 230 | 	<div class="login-card">
 | 
  
    | 231 | 		<img src="<?= $logo_src ?>"/><br>
 | 
  
    | 232 | 		<h1></h1>
 | 
  
    | 233 | 		<div class="login-help">
 | 
  
    | 234 | 			<?= gettext("The portal session is connected.") ?>
 | 
  
    | 235 | <?php if (!empty($redirurl)):
 | 
  
    | 236 | 		$redirurl = htmlspecialchars($redirurl); ?>
 | 
  
    | 237 | 			<br/><br/>
 | 
  
    | 238 | 			<?= gettext("Proceed to: ") ?>
 | 
  
    | 239 | 			<a href="<?=$redirurl?>"><?=$redirurl?></a>
 | 
  
    | 240 | <?php endif; ?>
 | 
  
    | 241 | 		</div>
 | 
  
    | 242 | <br/>
 | 
  
    | 243 | 	<form method="POST" action="<?=$logouturl;?>">
 | 
  
    | 244 | 		<input name="logout_id" type="hidden" value="<?=$sessionid;?>" />
 | 
  
    | 245 | 		<input name="zone" type="hidden" value="<?=$cpzone;?>" />
 | 
  
    | 246 | 		<input name="logout" type="submit" value="<?= gettext("Disconnect") ?>" />
 | 
  
    | 247 | 	</form>
 | 
  
    | 248 | 	<br  />
 | 
  
    | 249 | 	<span> <i>Made with ♥ by</i> <strong>Netgate</strong></span>
 | 
  
    | 250 | 	</div>
 | 
  
    | 251 | </div>
 | 
  
    | 252 | </body>
 | 
  
    | 253 | </html>
 | 
  
    | 254 | <?php
 | 
  
    | 255 | 	ob_flush();
 | 
  
    | 256 | 	return;
 | 
  
    | 257 | } elseif ($orig_host != $ourhostname) {
 | 
  
    | 258 | 	/* the client thinks it's connected to the desired web server, but instead
 | 
  
    | 259 | 	   it's connected to us. Issue a redirect... */
 | 
  
    | 260 | 	$protocol = (isset($cpcfg['httpslogin'])) ? 'https://' : 'http://';
 | 
  
    | 261 | 	header("Location: {$protocol}{$ourhostname}/index.php?zone={$cpzone}&redirurl=" . urlencode("http://{$orig_host}/{$orig_request}"));
 | 
  
    | 262 | 
 | 
  
    | 263 | 	ob_flush();
 | 
  
    | 264 | 	return;
 | 
  
    | 265 | }
 | 
  
    | 266 | 
 | 
  
    | 267 | /* find MAC address for client */
 | 
  
    | 268 | $tmpres = pfSense_ip_to_mac($clientip);
 | 
  
    | 269 | if (!is_array($tmpres)) {
 | 
  
    | 270 | 	if (!isset($cpcfg['nomacfilter']) || isset($cpcfg['passthrumacadd'])) {
 | 
  
    | 271 | 		/* unable to find MAC address - shouldn't happen! - bail out */
 | 
  
    | 272 | 		captiveportal_logportalauth("unauthenticated", "noclientmac", $clientip, "ERROR");
 | 
  
    | 273 | 		echo "An error occurred.  Please check the system logs for more information.";
 | 
  
    | 274 | 		log_error("Zone: {$cpzone} - Captive portal could not determine client's MAC address.  Disable MAC address filtering in captive portal if you do not need this functionality.");
 | 
  
    | 275 | 		ob_flush();
 | 
  
    | 276 | 		return;
 | 
  
    | 277 | 	}
 | 
  
    | 278 | } else {
 | 
  
    | 279 | 	/* always save MAC address in DB to allow macfilter/nomacfilter switching without flushing all clients */
 | 
  
    | 280 | 	$clientmac = $tmpres['macaddr'];
 | 
  
    | 281 | }
 | 
  
    | 282 | unset($tmpres);
 | 
  
    | 283 | 
 | 
  
    | 284 | if ($_POST['logout_id']) {
 | 
  
    | 285 | 	$safe_logout_id = SQLite3::escapeString($_POST['logout_id']);
 | 
  
    | 286 | 	captiveportal_disconnect_client($safe_logout_id);
 | 
  
    | 287 | 	header("Location: index.php?zone=" . $cpzone);
 | 
  
    | 288 | 	ob_flush();
 | 
  
    | 289 | 	return;
 | 
  
    | 290 | } elseif (($_POST['accept'] || $cpcfg['auth_method'] === 'radmac' || !empty($cpcfg['blockedmacsurl'])) && !isset($cpcfg['nomacfilter']) && $clientmac && captiveportal_blocked_mac($clientmac)) {
 | 
  
    | 291 | 	captiveportal_logportalauth($clientmac, $clientmac, $clientip, "Blocked MAC address");
 | 
  
    | 292 | 	if (!empty($cpcfg['blockedmacsurl'])) {
 | 
  
    | 293 | 		portal_reply_page($cpcfg['blockedmacsurl'], "redir");
 | 
  
    | 294 | 	} else {
 | 
  
    | 295 | 		if ($cpcfg['auth_method'] === 'radmac') {
 | 
  
    | 296 | 			echo gettext("This MAC address has been blocked");
 | 
  
    | 297 | 		} else {
 | 
  
    | 298 | 			portal_reply_page($redirurl, "error", "This MAC address has been blocked", $clientmac, $clientip);
 | 
  
    | 299 | 		}
 | 
  
    | 300 | 	}
 | 
  
    | 301 | } elseif (portal_consume_passthrough_credit($clientmac)) {
 | 
  
    | 302 | 	/* allow the client through if it had a pass-through credit for its MAC */
 | 
  
    | 303 | 	captiveportal_logportalauth("unauthenticated", $clientmac, $clientip, "ACCEPT");
 | 
  
    | 304 | 	portal_allow($clientip, $clientmac, "unauthenticated", null, $redirurl);
 | 
  
    | 305 | 
 | 
  
    | 306 | } elseif (config_path_enabled("voucher/{$cpzone}") && ($_POST['accept'] && $_POST['auth_voucher']) || $_GET['voucher']) {
 | 
  
    | 307 | 	if (isset($_POST['auth_voucher'])) {
 | 
  
    | 308 | 		$voucher = trim($_POST['auth_voucher']);
 | 
  
    | 309 | 	} else {
 | 
  
    | 310 | 		/* submit voucher via URL, see https://redmine.pfsense.org/issues/1984 */
 | 
  
    | 311 | 		$voucher = trim($_GET['voucher']);
 | 
  
    | 312 | 		portal_reply_page($redirurl, "login", null, $clientmac, $clientip, null, null, $voucher);
 | 
  
    | 313 | 		return;
 | 
  
    | 314 | 	}
 | 
  
    | 315 | 	$errormsg = gettext("Invalid credentials specified.");
 | 
  
    | 316 | 	$timecredit = voucher_auth($voucher);
 | 
  
    | 317 | 	// $timecredit contains either a credit in minutes or an error message
 | 
  
    | 318 | 	if ($timecredit > 0) {  // voucher is valid. Remaining minutes returned
 | 
  
    | 319 | 		// if multiple vouchers given, use the first as username
 | 
  
    | 320 | 		$a_vouchers = preg_split("/[\t\n\r ]+/s", $voucher);
 | 
  
    | 321 | 		$voucher = $a_vouchers[0];
 | 
  
    | 322 | 		$attr = array(
 | 
  
    | 323 | 			'voucher' => 1,
 | 
  
    | 324 | 			'session_timeout' => $timecredit*60,
 | 
  
    | 325 | 			'session_terminate_time' => 0);
 | 
  
    | 326 | 		if (portal_allow($clientip, $clientmac, $voucher, null, $redirurl, $attr, null, 'voucher', 'voucher') === 2) {
 | 
  
    | 327 | 			portal_reply_page($redirurl, "error", "Reuse of identification not allowed.", $clientmac, $clientip);
 | 
  
    | 328 | 		} elseif (portal_allow($clientip, $clientmac, $voucher, null, $redirurl, $attr, null, 'voucher', 'voucher')) {
 | 
  
    | 329 | 			// YES: user is good for $timecredit minutes.
 | 
  
    | 330 | 			captiveportal_logportalauth($voucher, $clientmac, $clientip, "Voucher login good for $timecredit min.");
 | 
  
    | 331 | 		} else {
 | 
  
    | 332 | 			portal_reply_page($redirurl, "error", config_get_path("voucher/{$cpzone}/descrmsgexpired") ? config_get_path("voucher/{$cpzone}/descrmsgexpired"): $errormsg, $clientmac, $clientip);
 | 
  
    | 333 | 		}
 | 
  
    | 334 | 	} elseif (-1 == $timecredit) {  // valid but expired
 | 
  
    | 335 | 		captiveportal_logportalauth($voucher, $clientmac, $clientip, "FAILURE", "voucher expired");
 | 
  
    | 336 | 		portal_reply_page($redirurl, "error", config_get_path("voucher/{$cpzone}/descrmsgexpired") ? config_get_path("voucher/{$cpzone}/descrmsgexpired"): $errormsg, $clientmac, $clientip);
 | 
  
    | 337 | 	} else {
 | 
  
    | 338 | 		captiveportal_logportalauth($voucher, $clientmac, $clientip, "FAILURE");
 | 
  
    | 339 | 		portal_reply_page($redirurl, "error", config_get_path("voucher/{$cpzone}/descrmsgnoaccess") ? config_get_path("voucher/{$cpzone}/descrmsgnoaccess") : $errormsg, $clientmac, $clientip);
 | 
  
    | 340 | 	}
 | 
  
    | 341 | 
 | 
  
    | 342 | } elseif ($_POST['accept'] || $cpcfg['auth_method'] === 'radmac') {
 | 
  
    | 343 | 	
 | 
  
    | 344 | 		if ($cpcfg['auth_method'] === 'radmac' && !isset($_POST['accept'])) {
 | 
  
    | 345 | 			$user = $clientmac; 
 | 
  
    | 346 | 			$passwd = $cpcfg['radmac_secret'];
 | 
  
    | 347 | 			$context = 'radmac'; // Radius MAC authentication
 | 
  
    | 348 | 		} elseif (!empty(trim($_POST['auth_user2']))) { 
 | 
  
    | 349 | 			$user = trim($_POST['auth_user2']);
 | 
  
    | 350 | 			$passwd = $_POST['auth_pass2'];
 | 
  
    | 351 | 			$context = 'second'; // Assume users to use the first context if auth_user2 is empty/does not exist
 | 
  
    | 352 | 		} else {
 | 
  
    | 353 | 			$user = trim($_POST['auth_user']);
 | 
  
    | 354 | 			$passwd = $_POST['auth_pass'];
 | 
  
    | 355 | 			$context = 'first';
 | 
  
    | 356 | 		}
 | 
  
    | 357 | 	
 | 
  
    | 358 | 	$pipeno = captiveportal_get_next_dn_ruleno('auth', 2000, 64500, true);
 | 
  
    | 359 | 	/* if the pool is empty, return appropriate message and exit */
 | 
  
    | 360 | 	if (is_null($pipeno)) {
 | 
  
    | 361 | 		$replymsg = gettext("System reached maximum login capacity");
 | 
  
    | 362 | 		if ($cpcfg['auth_method'] === 'radmac') {
 | 
  
    | 363 | 			echo $replymsg;
 | 
  
    | 364 | 			ob_flush();
 | 
  
    | 365 | 			return;
 | 
  
    | 366 | 		} else {
 | 
  
    | 367 | 			portal_reply_page($redirurl, "error", $replymsg, $clientmac, $clientip);
 | 
  
    | 368 | 		}
 | 
  
    | 369 | 		log_error("Zone: {$cpzone} - WARNING!  Captive portal has reached maximum login capacity");
 | 
  
    | 370 | 		
 | 
  
    | 371 | 	}
 | 
  
    | 372 | 	
 | 
  
    | 373 | 	$auth_result = captiveportal_authenticate_user($user, $passwd, $clientmac, $clientip, $pipeno, $context);
 | 
  
    | 374 | 	
 | 
  
    | 375 | 	if ($auth_result['result']) {
 | 
  
    | 376 | 		captiveportal_logportalauth($user, $clientmac, $clientip, $auth_result['login_status']);
 | 
  
    | 377 | 		portal_allow($clientip, $clientmac, $user, $passwd, $redirurl, $auth_result['attributes'], null, $auth_result['auth_method'], $context);
 | 
  
    | 378 | 	} else {
 | 
  
    | 379 | 		$type = "error";
 | 
  
    | 380 | 			
 | 
  
    | 381 | 		if (is_URL($auth_result['attributes']['url_redirection'], true)) {
 | 
  
    | 382 | 			$redirurl = $auth_result['attributes']['url_redirection'];
 | 
  
    | 383 | 			$type = "redir";
 | 
  
    | 384 | 		}
 | 
  
    | 385 | 		
 | 
  
    | 386 | 		if ($auth_result['login_message']) {
 | 
  
    | 387 | 			$replymsg = $auth_result['login_message'];
 | 
  
    | 388 | 		} else {
 | 
  
    | 389 | 			$replymsg = gettext("Invalid credentials specified.");
 | 
  
    | 390 | 		}
 | 
  
    | 391 | 		
 | 
  
    | 392 | 		captiveportal_logportalauth($user, $clientmac, $clientip, $auth_result['login_status'], $replymsg);
 | 
  
    | 393 | 
 | 
  
    | 394 | 		/* Radius MAC authentication. */
 | 
  
    | 395 | 		if ($context === 'radmac' && $type !== 'redir' && !isset($cpcfg['radmac_fallback'])) {
 | 
  
    | 396 | 			echo $replymsg;
 | 
  
    | 397 | 		} else {
 | 
  
    | 398 | 			portal_reply_page($redirurl, $type, $replymsg, $clientmac, $clientip);
 | 
  
    | 399 | 		}
 | 
  
    | 400 | 	}
 | 
  
    | 401 | } else {
 | 
  
    | 402 | 	/* display captive portal page */
 | 
  
    | 403 | 	portal_reply_page($redirurl, "login", null, $clientmac, $clientip);
 | 
  
    | 404 | }
 | 
  
    | 405 | 
 | 
  
    | 406 | ob_flush();
 | 
  
    | 407 | 
 | 
  
    | 408 | ?>
 |