Project

General

Profile

Download (100 KB) Statistics
| Branch: | Tag: | Revision:
1 5b237745 Scott Ullrich
<?php
2
/*
3 ac24dc24 Renato Botelho
 * captiveportal.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6 38809d47 Renato Botelho do Couto
 * Copyright (c) 2004-2013 BSD Perimeter
7
 * Copyright (c) 2013-2016 Electric Sheep Fencing
8 a68f7a3d Luiz Otavio O Souza
 * Copyright (c) 2014-2024 Rubicon Communications, LLC (Netgate)
9 ac24dc24 Renato Botelho
 * All rights reserved.
10
 *
11
 * originally part of m0n0wall (http://m0n0.ch/wall)
12 c5d81585 Renato Botelho
 * Copyright (c) 2003-2006 Manuel Kasper <mk@neon1.net>.
13 ac24dc24 Renato Botelho
 * All rights reserved.
14
 *
15 b12ea3fb Renato Botelho
 * 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 ac24dc24 Renato Botelho
 *
19 b12ea3fb Renato Botelho
 * http://www.apache.org/licenses/LICENSE-2.0
20 ac24dc24 Renato Botelho
 *
21 b12ea3fb Renato Botelho
 * 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 ac24dc24 Renato Botelho
 */
27 36254e4a Scott Ullrich
28 5b237745 Scott Ullrich
/* include all configuration functions */
29 5f1aaed4 Augustin FL
require_once("auth.inc");
30
require_once("PEAR.php"); // required for bcmath
31
require_once("Auth/RADIUS.php"); // required for radius accounting
32 a55cdcc0 Ermal Lu?i
require_once("config.inc");
33
require_once("functions.inc");
34 71fdaecd Ermal
require_once("filter.inc");
35 156487ed Ermal Lu?i
require_once("voucher.inc");
36 24600471 Augustin-FL
require_once("xmlrpc_client.inc");
37 0bd34ed6 Scott Ullrich
38 5f1aaed4 Augustin FL
/* Captiveportal Radius Accounting */
39
PEAR::loadExtension('bcmath');
40
// The RADIUS Package doesn't have these vars so we create them ourself
41
define("CUSTOM_RADIUS_ACCT_INPUT_GIGAWORDS", "52");
42
define("CUSTOM_RADIUS_ACCT_OUTPUT_GIGAWORDS", "53");
43
define("GIGAWORDS_RIGHT_OPERAND", '4294967296'); // 2^32
44
45 88479de6 jim-p
function get_captive_portal_logo() {
46 63d6bb4f Marcos Mendoza
	global $g, $cpzone;
47 748372bc Stephen Jones
	$logo_src = "captiveportal-default-logo.png";
48
	// Check if customlogo is set and if the element exists
49
	// Check if the image is in the directory
50 65db621a Reid Linnemann
	$cpzone_config = config_get_path("captiveportal/{$cpzone}", []);
51 63d6bb4f Marcos Mendoza
	if (isset($cpzone_config['customlogo']) &&
52
	    is_array($cpzone_config['element']) &&
53
	    !empty($cpzone_config['element'])) {
54
		foreach ($cpzone_config['element'] as $element) {
55 748372bc Stephen Jones
			if (strpos($element['name'], "captiveportal-logo.") !== false) {
56
				if (file_exists("{$g['captiveportal_path']}/{$element['name']}")) {
57
					$logo_src = $element['name'];
58
					break;
59
				}
60
			}
61
		}
62
	}
63 88479de6 jim-p
	return $logo_src;
64
}
65
66
function get_captive_portal_bg() {
67
	$bg_src = "linear-gradient(135deg, #1475CF, #2B40B5, #1C1275)";
68 63d6bb4f Marcos Mendoza
	global $g, $cpzone;
69 748372bc Stephen Jones
	// check if custombg is set and if the element exists
70 65db621a Reid Linnemann
	$cpzone_config = config_get_path("captiveportal/{$cpzone}", []);
71 63d6bb4f Marcos Mendoza
	if (isset($cpzone_config['custombg']) &&
72
	    is_array($cpzone_config['element']) &&
73
	    !empty($cpzone_config['element'])) {
74
		foreach ($cpzone_config['element'] as $element) {
75 748372bc Stephen Jones
			if (strpos($element['name'],"captiveportal-background.") !== false) {
76
				if( file_exists("{$g['captiveportal_path']}/{$element['name']}")) {
77
					$bg_src = "url(" . $element['name'] . ")" . "center center no-repeat fixed";
78
					break;
79
				}
80
			}
81
		}
82
	}
83 88479de6 jim-p
	return $bg_src;
84
}
85
86
function get_default_captive_portal_html() {
87 63d6bb4f Marcos Mendoza
	global $g, $cpzone;
88 88479de6 jim-p
89
	$translated_text1 = gettext("User");
90
	$translated_text2 = gettext("Password");
91
	$translated_text3 = gettext("First Authentication Method ");
92
	$translated_text4 = gettext("Second Authentication Method ");
93
	// default images to use.
94
	$logo_src = get_captive_portal_logo();
95
	$bg_src = get_captive_portal_bg();
96 748372bc Stephen Jones
	// bring in terms and conditions
97 63d6bb4f Marcos Mendoza
	$cpzone_config = config_get_path("captiveportal/{$cpzone}", []);
98
	$termsconditions = base64_decode($cpzone_config['termsconditions']);
99 748372bc Stephen Jones
	// if there is no terms and conditions do not require the checkbox to be selected.
100
	$disabled = "";
101
	if ($termsconditions) {
102
		$disabled = "disabled";
103
	}
104 0e296bce Ermal
	$htmltext = <<<EOD
105 748372bc Stephen Jones
<!DOCTYPE html>
106 1e0b1727 Phil Davis
<html>
107 748372bc Stephen Jones
108
<head>
109
110
  <meta charset="UTF-8">
111
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
112
  <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
113
  <title>Captive Portal Login Page</title>
114
  <style>
115 dee5b2b7 Steve Beaver
	  #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;}
116 748372bc Stephen Jones
  </style>
117
</head>
118
119 1e0b1727 Phil Davis
<body>
120 748372bc Stephen Jones
<div id="content">
121
	<div class="login-card">
122
		<img src="{$logo_src}"/><br>
123
 		<h1></h1>
124 131b297d Stephen Jones
		<div id="error-message">
125
			\$PORTAL_MESSAGE\$
126
		</div>
127 748372bc Stephen Jones
	  <form name="login_form" method="post" action="\$PORTAL_ACTION\$">
128 eb43c5b1 Augustin FL
EOD;
129 63d6bb4f Marcos Mendoza
	if ($cpzone_config['auth_method'] != "none"){
130
		if ($cpzone_config['auth_method'] === 'authserver' && !empty($cpzone_config['auth_server2'])) {
131 15064e4f Stephen Jones
			$htmltext .= <<<EOD
132
			<div class="auth_head_div">
133
				<h6 class="auth_head">{$translated_text3}</h6>
134
			</div>
135
			<div class="auth_source">
136
137
EOD;
138
		}
139 748372bc Stephen Jones
		$htmltext .=<<<EOD
140
		<input type="text" name="auth_user" placeholder="{$translated_text1}" id="auth_user">
141
		<input type="password" name="auth_pass" placeholder="{$translated_text2}" id="auth_pass">
142 eb43c5b1 Augustin FL
EOD;
143 0e296bce Ermal
144 63d6bb4f Marcos Mendoza
		if ($cpzone_config['auth_method'] === 'authserver' && !empty($cpzone_config['auth_server2'])) {
145 15064e4f Stephen Jones
			$htmltext .= <<<EOD
146
			</div>
147
			<div class="auth_head_div">
148
				<h6 class="auth_head">{$translated_text4}</h6>
149
			</div>
150
			<div class="auth_source">
151
152
			<input type="text" name="auth_user2" placeholder="{$translated_text1}" id="auth_user2">
153
			<input type="password" name="auth_pass2" placeholder="{$translated_text2}" id="auth_pass2">
154
			</div>
155
EOD;
156
		}
157
158 36254e4a Scott Ullrich
159 63d6bb4f Marcos Mendoza
		if (config_path_enabled("voucher/{$cpzone}")) {
160 748372bc Stephen Jones
			$translated_text = gettext("Voucher Code");
161
			$htmltext .= <<<EOD
162 15064e4f Stephen Jones
				<br  /><br  />
163 53ea4b8b Viktor G
				<input name="auth_voucher" type="text" placeholder="{$translated_text}" value="#VOUCHER#">
164 023aa1f2 Scott Ullrich
EOD;
165 748372bc Stephen Jones
		}
166 c2056357 Scott Ullrich
	}
167 0bd34ed6 Scott Ullrich
168 748372bc Stephen Jones
if ($termsconditions) {
169 0e296bce Ermal
	$htmltext .= <<<EOD
170 748372bc Stephen Jones
		  <div class="login-help">
171
			<ul class="list">
172
				<li class="list__item">
173
				  <label class="label--checkbox">
174
					<input type="checkbox" class="checkbox" onchange="document.getElementById('login').disabled = !this.checked;">
175 15064e4f Stephen Jones
					<span>I agree with the <a  rel="noopener" href="#terms" onclick="document.getElementById('terms').style.display = 'block';">terms & conditions</a></span>
176 748372bc Stephen Jones
				  </label>
177
				</li>
178
			</ul>
179
		  </div>
180
EOD;
181
}
182
	$htmltext .= <<<EOD
183
184
		<input name="redirurl" type="hidden" value="\$PORTAL_REDIRURL\$">
185
		<input type="submit" name="accept" class="login login-submit" value="Login" id="login" {$disabled}>
186
	  </form>
187
	  <br  />
188
	  <span> <i>Made with &hearts; by</i> <strong>Netgate</strong></span>
189
	</div>
190
	<div id="terms">
191
		<textarea readonly>{$termsconditions}</textarea>
192
	</div>
193
</div>
194 1e0b1727 Phil Davis
</body>
195 5b237745 Scott Ullrich
</html>
196
197 023aa1f2 Scott Ullrich
EOD;
198 0bd34ed6 Scott Ullrich
199 023aa1f2 Scott Ullrich
	return $htmltext;
200
}
201 0bd34ed6 Scott Ullrich
202 023aa1f2 Scott Ullrich
function captiveportal_configure() {
203 63d6bb4f Marcos Mendoza
	global $g, $cpzone, $cpzoneid;
204
	$cp_config = config_get_path('captiveportal');
205
	if (is_array($cp_config)) {
206
		$keep_online_users = false;
207
		foreach ($cp_config as $cpzone) {
208 0b783d30 Viktor G
			if (isset($cpzone['preservedb'])) {
209
				$keep_online_users = true;
210
				break;
211
			}
212
		}
213
		if (!$keep_online_users) {
214
			/* see https://redmine.pfsense.org/issues/12455 */
215
			unlink_if_exists("{$g['vardb_path']}/captiveportal_online_users");
216
		}
217 63d6bb4f Marcos Mendoza
		foreach ($cp_config as $cpkey => $cp) {
218 b4792bf8 Ermal
			$cpzone = $cpkey;
219 baec2b00 Ermal
			$cpzoneid = $cp['zoneid'];
220 b4792bf8 Ermal
			captiveportal_configure_zone($cp);
221
		}
222 4010266a Ermal
	}
223 b4792bf8 Ermal
}
224
225 937b2a59 Viktor G
function captiveportal_configure_zone($cpcfg, $reload_rules = false) {
226 63d6bb4f Marcos Mendoza
	global $g, $cpzone, $cpzoneid;
227 b4792bf8 Ermal
228
	$captiveportallck = lock("captiveportal{$cpzone}", LOCK_EX);
229 1e0b1727 Phil Davis
230 b4792bf8 Ermal
	if (isset($cpcfg['enable'])) {
231 023aa1f2 Scott Ullrich
232 816fef25 Marcos Mendoza
		if (is_platform_booting()) {
233 b4792bf8 Ermal
			echo "Starting captive portal({$cpcfg['zone']})... ";
234 1e0b1727 Phil Davis
		} else {
235 37e67d04 Ermal
			captiveportal_syslog("Reconfiguring captive portal({$cpcfg['zone']}).");
236 1e0b1727 Phil Davis
		}
237 023aa1f2 Scott Ullrich
238 7c2468c5 Viktor G
		/* init captive portal pipes and anchors */
239 937b2a59 Viktor G
		captiveportal_init_rules($reload_rules);
240 023aa1f2 Scott Ullrich
241
		/* kill any running minicron */
242 b4792bf8 Ermal
		killbypid("{$g['varrun_path']}/cp_prunedb_{$cpzone}.pid");
243
244 023aa1f2 Scott Ullrich
		/* initialize minicron interval value */
245 b4792bf8 Ermal
		$croninterval = $cpcfg['croninterval'] ? $cpcfg['croninterval'] : 60;
246 023aa1f2 Scott Ullrich
247
		/* double check if the $croninterval is numeric and at least 10 seconds. If not we set it to 60 to avoid problems */
248 1e0b1727 Phil Davis
		if ((!is_numeric($croninterval)) || ($croninterval < 10)) {
249 eb7aa263 Ermal
			$croninterval = 60;
250 1e0b1727 Phil Davis
		}
251 023aa1f2 Scott Ullrich
252
		/* write portal page */
253 1e0b1727 Phil Davis
		if (is_array($cpcfg['page']) && $cpcfg['page']['htmltext']) {
254 b4792bf8 Ermal
			$htmltext = base64_decode($cpcfg['page']['htmltext']);
255 1e0b1727 Phil Davis
		} else {
256 023aa1f2 Scott Ullrich
			/* example/template page */
257
			$htmltext = get_default_captive_portal_html();
258 5b237745 Scott Ullrich
		}
259
260 b4792bf8 Ermal
		$fd = @fopen("{$g['varetc_path']}/captiveportal_{$cpzone}.html", "w");
261 5b237745 Scott Ullrich
		if ($fd) {
262 7a7e94a7 Scott Ullrich
			// Special case handling.  Convert so that we can pass this page
263
			// through the PHP interpreter later without clobbering the vars.
264 b4792bf8 Ermal
			$htmltext = str_replace("\$PORTAL_ZONE\$", "#PORTAL_ZONE#", $htmltext);
265 7a7e94a7 Scott Ullrich
			$htmltext = str_replace("\$PORTAL_REDIRURL\$", "#PORTAL_REDIRURL#", $htmltext);
266
			$htmltext = str_replace("\$PORTAL_MESSAGE\$", "#PORTAL_MESSAGE#", $htmltext);
267
			$htmltext = str_replace("\$CLIENT_MAC\$", "#CLIENT_MAC#", $htmltext);
268
			$htmltext = str_replace("\$CLIENT_IP\$", "#CLIENT_IP#", $htmltext);
269
			$htmltext = str_replace("\$PORTAL_ACTION\$", "#PORTAL_ACTION#", $htmltext);
270 1e0b1727 Phil Davis
			if ($cpcfg['preauthurl']) {
271 b4792bf8 Ermal
				$htmltext = str_replace("\$PORTAL_REDIRURL\$", "{$cpcfg['preauthurl']}", $htmltext);
272
				$htmltext = str_replace("#PORTAL_REDIRURL#", "{$cpcfg['preauthurl']}", $htmltext);
273 c4e228f3 Scott Ullrich
			}
274 5b237745 Scott Ullrich
			fwrite($fd, $htmltext);
275 36254e4a Scott Ullrich
			fclose($fd);
276 5b237745 Scott Ullrich
		}
277 2e62a7c4 Ermal
		unset($htmltext);
278 36254e4a Scott Ullrich
279 5b237745 Scott Ullrich
		/* write error page */
280 1e0b1727 Phil Davis
		if (is_array($cpcfg['page']) && $cpcfg['page']['errtext']) {
281 b4792bf8 Ermal
			$errtext = base64_decode($cpcfg['page']['errtext']);
282 1e0b1727 Phil Davis
		} else {
283 a34b8b3b Ermal
			/* example page  */
284
			$errtext = get_default_captive_portal_html();
285 5b237745 Scott Ullrich
		}
286
287 b4792bf8 Ermal
		$fd = @fopen("{$g['varetc_path']}/captiveportal-{$cpzone}-error.html", "w");
288 5b237745 Scott Ullrich
		if ($fd) {
289 7a7e94a7 Scott Ullrich
			// Special case handling.  Convert so that we can pass this page
290
			// through the PHP interpreter later without clobbering the vars.
291 b4792bf8 Ermal
			$errtext = str_replace("\$PORTAL_ZONE\$", "#PORTAL_ZONE#", $errtext);
292 7a7e94a7 Scott Ullrich
			$errtext = str_replace("\$PORTAL_REDIRURL\$", "#PORTAL_REDIRURL#", $errtext);
293
			$errtext = str_replace("\$PORTAL_MESSAGE\$", "#PORTAL_MESSAGE#", $errtext);
294
			$errtext = str_replace("\$CLIENT_MAC\$", "#CLIENT_MAC#", $errtext);
295
			$errtext = str_replace("\$CLIENT_IP\$", "#CLIENT_IP#", $errtext);
296
			$errtext = str_replace("\$PORTAL_ACTION\$", "#PORTAL_ACTION#", $errtext);
297 1e0b1727 Phil Davis
			if ($cpcfg['preauthurl']) {
298 b4792bf8 Ermal
				$errtext = str_replace("\$PORTAL_REDIRURL\$", "{$cpcfg['preauthurl']}", $errtext);
299
				$errtext = str_replace("#PORTAL_REDIRURL#", "{$cpcfg['preauthurl']}", $errtext);
300 c4e228f3 Scott Ullrich
			}
301 5b237745 Scott Ullrich
			fwrite($fd, $errtext);
302 36254e4a Scott Ullrich
			fclose($fd);
303 5b237745 Scott Ullrich
		}
304 2e62a7c4 Ermal
		unset($errtext);
305 36254e4a Scott Ullrich
306 b4792bf8 Ermal
		/* write logout page */
307 1e0b1727 Phil Davis
		if (is_array($cpcfg['page']) && $cpcfg['page']['logouttext']) {
308 b4792bf8 Ermal
			$logouttext = base64_decode($cpcfg['page']['logouttext']);
309 1e0b1727 Phil Davis
		} else {
310 5b87b24e Ermal
			/* example page */
311 d18f3f6e Phil Davis
			$translated_text1 = gettext("Redirecting...");
312
			$translated_text2 = gettext("Redirecting to");
313
			$translated_text3 = gettext("Logout");
314
			$translated_text4 = gettext("Click the button below to disconnect");
315 5b87b24e Ermal
			$logouttext = <<<EOD
316 91f026b0 ayvis
<html>
317 d18f3f6e Phil Davis
<head><title>{$translated_text1}</title></head>
318 91f026b0 ayvis
<body>
319
<span style="font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">
320 d18f3f6e Phil Davis
<b>{$translated_text2} <a href="<?=\$my_redirurl;?>"><?=\$my_redirurl;?></a>...</b>
321 91f026b0 ayvis
</span>
322
<script type="text/javascript">
323 1b244d38 Colin Fleming
//<![CDATA[
324 5b87b24e Ermal
LogoutWin = window.open('', 'Logout', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=256,height=64');
325
if (LogoutWin) {
326 91f026b0 ayvis
	LogoutWin.document.write('<html>');
327 d18f3f6e Phil Davis
	LogoutWin.document.write('<head><title>{$translated_text3}</title></head>') ;
328 5f18e743 NOYB
	LogoutWin.document.write('<body style="background-color:#435370">');
329
	LogoutWin.document.write('<div class="text-center" style="color: #ffffff; font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">') ;
330 d18f3f6e Phil Davis
	LogoutWin.document.write('<b>{$translated_text4}</b><p />');
331 91f026b0 ayvis
	LogoutWin.document.write('<form method="POST" action="<?=\$logouturl;?>">');
332
	LogoutWin.document.write('<input name="logout_id" type="hidden" value="<?=\$sessionid;?>" />');
333
	LogoutWin.document.write('<input name="zone" type="hidden" value="<?=\$cpzone;?>" />');
334 d18f3f6e Phil Davis
	LogoutWin.document.write('<input name="logout" type="submit" value="{$translated_text3}" />');
335 91f026b0 ayvis
	LogoutWin.document.write('</form>');
336
	LogoutWin.document.write('</div></body>');
337
	LogoutWin.document.write('</html>');
338 5060dea7 Scott Ullrich
	LogoutWin.document.close();
339 5b87b24e Ermal
}
340
341 6991e1a6 Erik Fonnesbeck
document.location.href="<?=\$my_redirurl;?>";
342 1b244d38 Colin Fleming
//]]>
343 91f026b0 ayvis
</script>
344
</body>
345
</html>
346 5b87b24e Ermal
347
EOD;
348
		}
349
350 b4792bf8 Ermal
		$fd = @fopen("{$g['varetc_path']}/captiveportal-{$cpzone}-logout.html", "w");
351 5b87b24e Ermal
		if ($fd) {
352
			fwrite($fd, $logouttext);
353
			fclose($fd);
354
		}
355 2e62a7c4 Ermal
		unset($logouttext);
356
357 0bd34ed6 Scott Ullrich
		/* write elements */
358
		captiveportal_write_elements();
359 5b237745 Scott Ullrich
360 57cc06af Chris Buechler
		/* kill any running CP nginx instances */
361 4ddcc5ca Augustin-FL
		killbypid("{$g['varrun_path']}/nginx-{$cpzone}-CaptivePortal.pid", 0.1);
362
		killbypid("{$g['varrun_path']}/nginx-{$cpzone}-CaptivePortal-SSL.pid", 0.1);
363 37e67d04 Ermal
364 769e254e Ermal
		/* start up the webserving daemon */
365 e3cf528e bcyrill
		captiveportal_init_webgui_zone($cpcfg);
366 36254e4a Scott Ullrich
367 aa69dbd2 Scott Ullrich
		/* Kill any existing prunecaptiveportal processes */
368 1e0b1727 Phil Davis
		if (file_exists("{$g['varrun_path']}/cp_prunedb_{$cpzone}.pid")) {
369 b4792bf8 Ermal
			killbypid("{$g['varrun_path']}/cp_prunedb_{$cpzone}.pid");
370 1e0b1727 Phil Davis
		}
371 aa69dbd2 Scott Ullrich
372 0bd34ed6 Scott Ullrich
		/* start pruning process (interval defaults to 60 seconds) */
373 b4792bf8 Ermal
		mwexec("/usr/local/bin/minicron $croninterval {$g['varrun_path']}/cp_prunedb_{$cpzone}.pid " .
374
			"/etc/rc.prunecaptiveportal {$cpzone}");
375 36254e4a Scott Ullrich
376 eb43c5b1 Augustin FL
		/* delete outdated radius server database if exist */
377 37e67d04 Ermal
		unlink_if_exists("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db");
378 36254e4a Scott Ullrich
379 816fef25 Marcos Mendoza
		if (is_platform_booting() || $reload_rules) {
380 e35ab948 Michael Newton
			/* send Accounting-On to server */
381 e42ea151 Augustin FL
			captiveportal_send_server_accounting('on');
382 b4792bf8 Ermal
			echo "done\n";
383 7b80e743 Augustin-FL
384 937b2a59 Viktor G
			if (isset($cpcfg['preservedb']) || $reload_rules ||
385 0eae38cd Augustin-FL
			    captiveportal_xmlrpc_sync_get_details($syncip, $syncport, $syncuser, $syncpass)) {
386
387 7b80e743 Augustin-FL
				$connected_users = captiveportal_read_db();
388
				if (!empty($connected_users)) {
389
					echo "Reconnecting users to captive portal {$cpcfg['zone']}... ";
390
					foreach ($connected_users as $user) {
391
						captiveportal_reserve_ruleno($user['pipeno']);
392 65530037 Reid Linnemann
						captiveportal_ether_configure_entry($user, 'auth', true);
393 7b80e743 Augustin-FL
					}
394
					echo "done\n";
395
				}
396 dc883862 Viktor G
			} else {
397
				/* reset database on unclean shutdown, see https://redmine.pfsense.org/issues/12355 */
398
				unlink_if_exists("{$g['vardb_path']}/captiveportal{$cpzone}.db");
399 7b80e743 Augustin-FL
			}
400 e35ab948 Michael Newton
		}
401 5b237745 Scott Ullrich
	} else {
402 4877ae1e Chris Buechler
		killbypid("{$g['varrun_path']}/nginx-{$cpzone}-CaptivePortal.pid");
403
		killbypid("{$g['varrun_path']}/nginx-{$cpzone}-CaptivePortal-SSL.pid");
404 b4792bf8 Ermal
		killbypid("{$g['varrun_path']}/cp_prunedb_{$cpzone}.pid");
405
		@unlink("{$g['varetc_path']}/captiveportal_{$cpzone}.html");
406
		@unlink("{$g['varetc_path']}/captiveportal-{$cpzone}-error.html");
407
		@unlink("{$g['varetc_path']}/captiveportal-{$cpzone}-logout.html");
408 f8cb8685 Ermal
409 3ece6d54 plumbeo
		captiveportal_radius_stop_all(10); // NAS-Request
410 f8cb8685 Ermal
411 19ae6203 Steve Wheeler
		/* Release allocated pipes for this zone */
412
		$pipes_to_remove = captiveportal_free_dnrules();
413
		captiveportal_delete_rules($pipes_to_remove);
414
415 8b34498c Ermal
		/* remove old information */
416 26ee5aaf Ermal
		unlink_if_exists("{$g['vardb_path']}/captiveportal{$cpzone}.db");
417 8b34498c Ermal
		unlink_if_exists("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db");
418 0f50d70d Ermal
		unlink_if_exists("{$g['vardb_path']}/captiveportal_{$cpzone}.rules");
419 3db19cf1 Scott Ullrich
	}
420 36254e4a Scott Ullrich
421 f8b11310 Ermal Lu?i
	unlock($captiveportallck);
422 1e0b1727 Phil Davis
423 5b237745 Scott Ullrich
	return 0;
424
}
425
426 769e254e Ermal
function captiveportal_init_webgui() {
427 63d6bb4f Marcos Mendoza
	global $cpzone;
428 769e254e Ermal
429 63d6bb4f Marcos Mendoza
	foreach (config_get_path('captiveportal', []) as $cpkey => $cp) {
430
		$cpzone = $cpkey;
431
		captiveportal_init_webgui_zone($cp);
432 b4792bf8 Ermal
	}
433
}
434 769e254e Ermal
435 e3cf528e bcyrill
function captiveportal_init_webgui_zonename($zone) {
436 63d6bb4f Marcos Mendoza
	global $cpzone;
437 1e0b1727 Phil Davis
438 63d6bb4f Marcos Mendoza
	$cpzone_config = config_get_path("captiveportal/{$zone}");
439
	if (isset($cpzone_config)) {
440 e3cf528e bcyrill
		$cpzone = $zone;
441 63d6bb4f Marcos Mendoza
		captiveportal_init_webgui_zone($cpzone_config);
442 e3cf528e bcyrill
	}
443
}
444
445
function captiveportal_init_webgui_zone($cpcfg) {
446 63d6bb4f Marcos Mendoza
	global $g, $cpzone;
447 b4792bf8 Ermal
448 1e0b1727 Phil Davis
	if (!isset($cpcfg['enable'])) {
449 b4792bf8 Ermal
		return;
450 1e0b1727 Phil Davis
	}
451 b4792bf8 Ermal
452
	if (isset($cpcfg['httpslogin'])) {
453 36f6ed35 bcyrill
		$cert = lookup_cert($cpcfg['certref']);
454 26308930 Marcos Mendoza
		$cert = $cert['item'];
455 adca02c4 bcyrill
		$crt = base64_decode($cert['crt']);
456
		$key = base64_decode($cert['prv']);
457
		$ca = ca_chain($cert);
458 87e7fdea bcyrill
459 c4a278f2 Chris Buechler
		/* generate nginx configuration */
460 1e0b1727 Phil Davis
		if (!empty($cpcfg['listenporthttps'])) {
461 1c69dbb0 Ermal
			$listenporthttps = $cpcfg['listenporthttps'];
462 1e0b1727 Phil Davis
		} else {
463 1c69dbb0 Ermal
			$listenporthttps = 8001 + $cpcfg['zoneid'];
464 1e0b1727 Phil Davis
		}
465 c4a278f2 Chris Buechler
		system_generate_nginx_config("{$g['varetc_path']}/nginx-{$cpzone}-CaptivePortal-SSL.conf",
466
			$crt, $key, $ca, "nginx-{$cpzone}-CaptivePortal-SSL.pid", $listenporthttps, "/usr/local/captiveportal",
467 3ed3a367 doktornotor
			"cert-{$cpzone}-portal.pem", "ca-{$cpzone}-portal.pem", $cpzone, false);
468 769e254e Ermal
	}
469
470 c4a278f2 Chris Buechler
	/* generate nginx configuration */
471 1e0b1727 Phil Davis
	if (!empty($cpcfg['listenporthttp'])) {
472 1c69dbb0 Ermal
		$listenporthttp = $cpcfg['listenporthttp'];
473 1e0b1727 Phil Davis
	} else {
474 1c69dbb0 Ermal
		$listenporthttp = 8000 + $cpcfg['zoneid'];
475 1e0b1727 Phil Davis
	}
476 c4a278f2 Chris Buechler
	system_generate_nginx_config("{$g['varetc_path']}/nginx-{$cpzone}-CaptivePortal.conf",
477
		"", "", "", "nginx-{$cpzone}-CaptivePortal.pid", $listenporthttp, "/usr/local/captiveportal",
478 3ed3a367 doktornotor
		"", "", $cpzone, false);
479 769e254e Ermal
480 c4a278f2 Chris Buechler
	@unlink("{$g['varrun']}/nginx-{$cpzone}-CaptivePortal.pid");
481
	/* attempt to start nginx */
482 4877ae1e Chris Buechler
	$res = mwexec("/usr/local/sbin/nginx -c {$g['varetc_path']}/nginx-{$cpzone}-CaptivePortal.conf");
483 769e254e Ermal
484
	/* fire up https instance */
485 fe2eb995 Ermal
	if (isset($cpcfg['httpslogin'])) {
486 c4a278f2 Chris Buechler
		@unlink("{$g['varrun']}/nginx-{$cpzone}-CaptivePortal-SSL.pid");
487
		$res = mwexec("/usr/local/sbin/nginx -c {$g['varetc_path']}/nginx-{$cpzone}-CaptivePortal-SSL.conf");
488 fe2eb995 Ermal
	}
489 769e254e Ermal
}
490
491 847e5e82 Scott Ullrich
/* reinit will disconnect all users, be careful! */
492 1d9e9cca Ermal
function captiveportal_init_rules($reinit = false) {
493 63d6bb4f Marcos Mendoza
	global $g, $cpzone;
494 36254e4a Scott Ullrich
495 63d6bb4f Marcos Mendoza
	if (!config_path_enabled("captiveportal/{$cpzone}")) {
496 769e254e Ermal
		return;
497 1e0b1727 Phil Davis
	}
498 769e254e Ermal
499 7c2468c5 Viktor G
	dummynet_load_module('100');
500 f084049d Ermal LUÇI
501
	/* Cleanup so nothing is leaked */
502 a4a21be1 plumbeo
	captiveportal_free_dnrules(2000, 64500, false, $reinit);
503 769e254e Ermal
504 1643d2f4 plumbeo
	$captiveportallck = try_lock("captiveportal{$cpzone}", 0);
505 eade409a Ermal
506 7c2468c5 Viktor G
	/* delete all anchors */
507
	captiveportal_delete_rules(array(), $reinit);
508 769e254e Ermal
509 7c2468c5 Viktor G
	/* load passthru mac anchors */
510
	captiveportal_passthrumac_configure();
511 1e0b1727 Phil Davis
512 7c2468c5 Viktor G
	/* load allowedip anchors */
513
	captiveportal_allowedip_configure();
514 eade409a Ermal
515 7c2468c5 Viktor G
	/* load allowed hostname anchors */
516
	captiveportal_allowedhostname_configure();
517 517b893e Renato Botelho
518 1643d2f4 plumbeo
	if ($captiveportallck) {
519 eade409a Ermal
		unlock($captiveportallck);
520 1e0b1727 Phil Davis
	}
521 3db19cf1 Scott Ullrich
}
522
523 517b893e Renato Botelho
/* Delete all rules related to specific cpzone */
524 7bdf5cd6 plumbeo
function captiveportal_delete_rules($pipes_to_remove = array(), $clear_auth_rules = true) {
525 63d6bb4f Marcos Mendoza
	global $g, $cpzone;
526 78784180 Augustin-FL
527 7c2468c5 Viktor G
	/* delete MAC passthru entries */
528 63d6bb4f Marcos Mendoza
	config_init_path("captiveportal/{$cpzone}/passthrumac");
529 4b9165e5 Marcos Mendoza
	foreach (config_get_path("captiveportal/{$cpzone}/passthrumac", []) as $macent) {
530 7c2468c5 Viktor G
		captiveportal_passthrumac_delete_entry($macent);
531
	}
532 517b893e Renato Botelho
533 7c2468c5 Viktor G
	/* delete Allowed IP entries */
534 63d6bb4f Marcos Mendoza
	config_init_path("captiveportal/{$cpzone}/allowedip");
535 4b9165e5 Marcos Mendoza
	foreach (config_get_path("captiveportal/{$cpzone}/allowedip", []) as $ipent) {
536 63d6bb4f Marcos Mendoza
		captiveportal_ether_delete_entry($ipent, 'allowedhosts');
537 517b893e Renato Botelho
	}
538
539 7c2468c5 Viktor G
	/* delete Allowed Hostnames entries */
540
	captiveportal_allowedhostname_cleanup();
541 517b893e Renato Botelho
542 7c2468c5 Viktor G
	/* delete authenticated clients rules */
543
	$connected_users = captiveportal_read_db();
544
	if (!empty($connected_users) && $clear_auth_rules) {
545
		foreach ($connected_users as $user) {
546 3e49e032 Viktor G
			captiveportal_ether_delete_entry($user, 'auth');
547 7bdf5cd6 plumbeo
		}
548 517b893e Renato Botelho
	}
549
550 7c2468c5 Viktor G
	/* delete pipes */
551 43bd2b88 Reid Linnemann
	captiveportal_pipes_delete($pipes_to_remove);
552 517b893e Renato Botelho
}
553
554 1e0b1727 Phil Davis
/*
555 f1f58a6f Ermal
 * Remove clients that have been around for longer than the specified amount of time
556 eb7aa263 Ermal
 * db file structure:
557 ad2a86ea Viktor G
 * timestamp,ipfw_rule_no(deprecated),clientip,clientmac,username,sessionid,password,session_timeout,idle_timeout,session_terminate_time,interim_interval,traffic_quota,auth_method,context
558 eb7aa263 Ermal
 * (password is in Base64 and only saved when reauthentication is enabled)
559
 */
560 5b237745 Scott Ullrich
function captiveportal_prune_old() {
561 63d6bb4f Marcos Mendoza
	global $g, $cpzone, $cpzoneid;
562 b4792bf8 Ermal
563 1e0b1727 Phil Davis
	if (empty($cpzone)) {
564 b4792bf8 Ermal
		return;
565 1e0b1727 Phil Davis
	}
566 23c4f978 Scott Ullrich
567 65db621a Reid Linnemann
	$cpcfg = config_get_path("captiveportal/{$cpzone}", []);
568
	$vcpcfg = config_get_path("voucher/{$cpzone}", []);
569 f1f58a6f Ermal
570 5060dea7 Scott Ullrich
	/* check for expired entries */
571 f1f58a6f Ermal
	$idletimeout = 0;
572
	$timeout = 0;
573 1e0b1727 Phil Davis
	if (!empty($cpcfg['timeout']) && is_numeric($cpcfg['timeout'])) {
574 f1f58a6f Ermal
		$timeout = $cpcfg['timeout'] * 60;
575 1e0b1727 Phil Davis
	}
576 f1f58a6f Ermal
577 1e0b1727 Phil Davis
	if (!empty($cpcfg['idletimeout']) && is_numeric($cpcfg['idletimeout'])) {
578 f1f58a6f Ermal
		$idletimeout = $cpcfg['idletimeout'] * 60;
579 1e0b1727 Phil Davis
	}
580 f1f58a6f Ermal
581 acbd943d plumbeo
	/* check for entries exceeding their traffic quota */
582
	$trafficquota = 0;
583
	if (!empty($cpcfg['trafficquota']) && is_numeric($cpcfg['trafficquota'])) {
584
		$trafficquota = $cpcfg['trafficquota'] * 1048576;
585
	}
586
587 13164061 Augustin-FL
	/* Is there any job to do? If we are in High Availability sync, are we in backup mode ? */
588
	if ((!$timeout && !$idletimeout && !$trafficquota && !isset($cpcfg['reauthenticate']) &&
589 f3e403d5 plumbeo
	    !isset($cpcfg['radiussession_timeout']) && !isset($cpcfg['radiustraffic_quota']) &&
590 13164061 Augustin-FL
	    !isset($vcpcfg['enable']) && !isset($cpcfg['radacct_enable'])) ||
591
	    captiveportal_ha_is_node_in_backup_mode($cpzone)) {
592 5060dea7 Scott Ullrich
		return;
593 1e0b1727 Phil Davis
	}
594 5060dea7 Scott Ullrich
595 006802ab Ermal
596 26ee5aaf Ermal
	/* Read database */
597
	/* NOTE: while this can be simplified in non radius case keep as is for now */
598 5060dea7 Scott Ullrich
	$cpdb = captiveportal_read_db();
599 0bd34ed6 Scott Ullrich
600 5060dea7 Scott Ullrich
	$unsetindexes = array();
601 5ebe85e9 Ermal
	$voucher_needs_sync = false;
602 1e0b1727 Phil Davis
	/*
603 b09c2d86 Ermal
	 * Snapshot the time here to use for calculation to speed up the process.
604
	 * If something is missed next run will catch it!
605
	 */
606
	$pruning_time = time();
607 3e5c0ab7 Ermal
	foreach ($cpdb as $cpentry) {
608 b5ccfb0b Renato Botelho
		$stop_time = $pruning_time;
609 5060dea7 Scott Ullrich
610
		$timedout = false;
611
		$term_cause = 1;
612 643315be plumbeo
		/* hard timeout or session_timeout from radius if enabled */
613
		if (isset($cpcfg['radiussession_timeout'])) {
614 0369eeb4 plumbeo
			$utimeout = (is_numeric($cpentry[7])) ? $cpentry[7] : $timeout;
615
		} else {
616
			$utimeout = $timeout;
617 643315be plumbeo
		}
618 0369eeb4 plumbeo
		if ($utimeout) {
619
			if (($pruning_time - $cpentry[0]) >= $utimeout) {
620 5060dea7 Scott Ullrich
				$timedout = true;
621 5705c60a Renato Botelho
				$term_cause = 5; // Session-Timeout
622 e4c34f17 plumbeo
				$logout_cause = 'SESSION TIMEOUT';
623 5060dea7 Scott Ullrich
			}
624 eb7aa263 Ermal
		}
625 23c4f978 Scott Ullrich
626 5060dea7 Scott Ullrich
		/* Session-Terminate-Time */
627 5705c60a Renato Botelho
		if (!$timedout && !empty($cpentry[9])) {
628
			if ($pruning_time >= $cpentry[9]) {
629 5060dea7 Scott Ullrich
				$timedout = true;
630 5705c60a Renato Botelho
				$term_cause = 5; // Session-Timeout
631 e4c34f17 plumbeo
				$logout_cause = 'SESSION TIMEOUT';
632 5060dea7 Scott Ullrich
			}
633
		}
634
635 eb43c5b1 Augustin FL
		/* check if an idle_timeout has been set and if its set change the idletimeout to this value */
636 5705c60a Renato Botelho
		$uidletimeout = (is_numeric($cpentry[8])) ? $cpentry[8] : $idletimeout;
637 ad2a86ea Viktor G
		/* if an idle timeout is specified, get last activity timestamp from pf */
638 1a1967d6 Ermal
		if (!$timedout && $uidletimeout > 0) {
639 f4c867e0 Renato Botelho
			$lastact = captiveportal_get_last_activity($cpentry[2]);
640 5060dea7 Scott Ullrich
			/*	If the user has logged on but not sent any traffic they will never be logged out.
641 1e0b1727 Phil Davis
			 *	We "fix" this by setting lastact to the login timestamp.
642 f56a73f1 Scott Ullrich
			 */
643 5705c60a Renato Botelho
			$lastact = $lastact ? $lastact : $cpentry[0];
644 b09c2d86 Ermal
			if ($lastact && (($pruning_time - $lastact) >= $uidletimeout)) {
645 5060dea7 Scott Ullrich
				$timedout = true;
646 5705c60a Renato Botelho
				$term_cause = 4; // Idle-Timeout
647 e4c34f17 plumbeo
				$logout_cause = 'IDLE TIMEOUT';
648 63d6bb4f Marcos Mendoza
				if (!config_path_enabled("captiveportal/{$cpzone}", "includeidletime")) {
649 1878e1c9 plumbeo
					$stop_time = $lastact;
650
				}
651 5060dea7 Scott Ullrich
			}
652 336e3c1c Charlie
		}
653
654 5060dea7 Scott Ullrich
		/* if vouchers are configured, activate session timeouts */
655 5705c60a Renato Botelho
		if (!$timedout && isset($vcpcfg['enable']) && !empty($cpentry[7])) {
656
			if ($pruning_time >= ($cpentry[0] + $cpentry[7])) {
657 5060dea7 Scott Ullrich
				$timedout = true;
658 5705c60a Renato Botelho
				$term_cause = 5; // Session-Timeout
659 e4c34f17 plumbeo
				$logout_cause = 'SESSION TIMEOUT';
660 5ebe85e9 Ermal
				$voucher_needs_sync = true;
661 5060dea7 Scott Ullrich
			}
662
		}
663
664 f3e403d5 plumbeo
		/* traffic quota, value retrieved from the radius attribute if the option is enabled */
665
		if (isset($cpcfg['radiustraffic_quota'])) {
666 76bfc872 plumbeo
			$utrafficquota = (is_numeric($cpentry[11])) ? $cpentry[11] : $trafficquota;
667
		} else {
668
			$utrafficquota = $trafficquota;
669 f3e403d5 plumbeo
		}
670 7c2468c5 Viktor G
671 76bfc872 plumbeo
		if (!$timedout && $utrafficquota > 0) {
672 7c2468c5 Viktor G
			$volume = getVolume($cpentry[2]);
673 76bfc872 plumbeo
			if (($volume['input_bytes'] + $volume['output_bytes']) > $utrafficquota) {
674 acbd943d plumbeo
				$timedout = true;
675
				$term_cause = 10; // NAS-Request
676 e4c34f17 plumbeo
				$logout_cause = 'QUOTA EXCEEDED';
677 acbd943d plumbeo
			}
678
		}
679
680 5060dea7 Scott Ullrich
		if ($timedout) {
681 eb43c5b1 Augustin FL
			captiveportal_disconnect($cpentry, $term_cause, $stop_time);
682 e4c34f17 plumbeo
			captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], $logout_cause);
683 5705c60a Renato Botelho
			$unsetindexes[] = $cpentry[5];
684 5060dea7 Scott Ullrich
		}
685
686 e42ea151 Augustin FL
		/* do periodic reauthentication? For Radius servers, send accounting updates? */
687 eb43c5b1 Augustin FL
		if (!$timedout) {
688 e42ea151 Augustin FL
			//Radius servers : send accounting
689 b0a29e82 Augustin-FL
			if (isset($cpcfg['radacct_enable']) && $cpentry['authmethod'] === 'radius') {
690 e42ea151 Augustin FL
				if (substr($cpcfg['reauthenticateacct'], 0, 9) === "stopstart") {
691 5060dea7 Scott Ullrich
					/* stop and restart accounting */
692 e42ea151 Augustin FL
					if ($cpcfg['reauthenticateacct'] === "stopstartfreeradius") {
693 ab225849 jim-p
						$rastart_time = 0;
694
						$rastop_time = 60;
695
					} else {
696
						$rastart_time = $cpentry[0];
697 e42ea151 Augustin FL
						$rastop_time = time();
698 ab225849 jim-p
					}
699 e42ea151 Augustin FL
					captiveportal_send_server_accounting('stop',
700
						$cpentry[1], // ruleno
701 5705c60a Renato Botelho
						$cpentry[4], // username
702
						$cpentry[2], // clientip
703
						$cpentry[3], // clientmac
704 e42ea151 Augustin FL
						$cpentry[5], // sessionid
705
						$rastart_time, // start time
706
						$rastop_time, // Stop Time
707
						10); // NAS Request
708 7c2468c5 Viktor G
					/* XXX rewrite to C wrapper pfSense_pf_anchor_zerocnt() */
709
					captiveportal_anchor_zerocnt($cpentry[2], 'auth');
710 ab225849 jim-p
					if ($cpcfg['reauthenticateacct'] == "stopstartfreeradius") {
711
						/* Need to pause here or the FreeRADIUS server gets confused about packet ordering. */
712
						sleep(1);
713
					}
714 e42ea151 Augustin FL
					captiveportal_send_server_accounting('start',
715
						$cpentry[1], // ruleno
716 5705c60a Renato Botelho
						$cpentry[4], // username
717
						$cpentry[2], // clientip
718 e42ea151 Augustin FL
						$cpentry[3], // clientmac
719
						$cpentry[5]); // sessionid
720 f1f58a6f Ermal
				} else if ($cpcfg['reauthenticateacct'] == "interimupdate") {
721 5705c60a Renato Botelho
					$session_time = $pruning_time - $cpentry[0];
722 1e0b1727 Phil Davis
					if (!empty($cpentry[10]) && $cpentry[10] > 60) {
723 5705c60a Renato Botelho
						$interval = $cpentry[10];
724 1e0b1727 Phil Davis
					} else {
725 338c0941 Ermal
						$interval = 0;
726 1e0b1727 Phil Davis
					}
727 338c0941 Ermal
					$past_interval_min = ($session_time > $interval);
728 1e0b1727 Phil Davis
					if ($interval != 0) {
729 40a8f669 Renato Botelho
						$within_interval = ($session_time % $interval >= 0 && $session_time % $interval <= 59);
730 1e0b1727 Phil Davis
					}
731 40a8f669 Renato Botelho
					if ($interval === 0 || ($interval > 0 && $past_interval_min && $within_interval)) {
732 e42ea151 Augustin FL
					captiveportal_send_server_accounting('update',
733
						$cpentry[1], // ruleno
734
						$cpentry[4], // username
735
						$cpentry[2], // clientip
736
						$cpentry[3], // clientmac
737
						$cpentry[5], // sessionid
738
						$cpentry[0]); // start time
739 338c0941 Ermal
					}
740 5060dea7 Scott Ullrich
				}
741
			}
742 23c4f978 Scott Ullrich
743 eb43c5b1 Augustin FL
			/* check this user again */
744 b0a29e82 Augustin-FL
			if (isset($cpcfg['reauthenticate']) && $cpentry['context'] !== 'voucher') {
745 eb43c5b1 Augustin FL
				$auth_result = captiveportal_authenticate_user(
746
					$cpentry[4], // username
747 5705c60a Renato Botelho
					base64_decode($cpentry[6]), // password
748
					$cpentry[3], // clientmac
749 748372bc Stephen Jones
					$cpentry[2], // clientip
750 eb43c5b1 Augustin FL
					$cpentry[1], // ruleno
751 b0a29e82 Augustin-FL
					$cpentry['context']); // context
752 eb43c5b1 Augustin FL
				if ($auth_result['result'] === false) {
753
					captiveportal_disconnect($cpentry, 17);
754 1ec82c30 Marcos Mendoza
					captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "DISCONNECT - REAUTHENTICATION FAILED", $auth_result['reply_message']);
755 5705c60a Renato Botelho
					$unsetindexes[] = $cpentry[5];
756 eb43c5b1 Augustin FL
				} else if ($auth_result['result'] === true) {
757 b0a29e82 Augustin-FL
					if ($cpentry['authmethod'] !== $auth_result['auth_method']) {
758 eb43c5b1 Augustin FL
						// if the user got authenticated against another server type:  we update the database
759
						if (!empty($cpentry[5])) {
760 b0a29e82 Augustin-FL
							captiveportal_update_entry($cpentry['sessionid'], $auth_result['auth_method'], 'authmethod');
761 1ec82c30 Marcos Mendoza
							captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CHANGED AUTHENTICATION SERVER", $auth_result['reply_message']);
762 eb43c5b1 Augustin FL
						}
763
						// User was logged on a RADIUS server, but is now logged in by another server type : we send an accounting Stop
764 63d6bb4f Marcos Mendoza
						if (config_path_enabled("captiveportal/{$cpzone}", "radacct_enable") && $cpentry['authmethod'] == 'radius') {
765 eb43c5b1 Augustin FL
							if ($cpcfg['reauthenticateacct'] === "stopstartfreeradius") {
766
								$rastart_time = 0;
767
								$rastop_time = 60;
768
							} else {
769
								$rastart_time = $cpentry[0];
770
								$rastop_time = time();
771
							}
772
							captiveportal_send_server_accounting('stop',
773
								$cpentry[1], // ruleno
774
								$cpentry[4], // username
775
								$cpentry[2], // clientip
776
								$cpentry[3], // clientmac
777
								$cpentry[5], // sessionid
778
								$rastart_time, // start time
779
								$rastop_time, // Stop Time
780
								3); // Lost Service
781
						// User was logged on a non-RADIUS Server but is now logged in by a RADIUS server : we send an accounting Start
782 63d6bb4f Marcos Mendoza
						} else if(config_path_enabled("captiveportal/{$cpzone}", "radacct_enable") && $auth_result['auth_method'] === 'radius') {
783 eb43c5b1 Augustin FL
							captiveportal_send_server_accounting('start',
784
								$cpentry[1], // ruleno
785
								$cpentry[4], // username
786
								$cpentry[2], // clientip
787
								$cpentry[3], // clientmac
788
								$cpentry[5], // sessionid
789
								$cpentry[0]); // start_time
790
						}
791
					}
792
					captiveportal_reapply_attributes($cpentry, $auth_result['attributes']);
793 1e0b1727 Phil Davis
				}
794 5060dea7 Scott Ullrich
			}
795
		}
796
	}
797 f32eae2d Ermal
	unset($cpdb);
798 23c4f978 Scott Ullrich
799 522f1cc7 Ermal
	captiveportal_prune_old_automac();
800
801 1e0b1727 Phil Davis
	if ($voucher_needs_sync == true) {
802 4a778ba9 Augustin-FL
		/* perform in-use vouchers expiration using check_reload_status */
803 5ebe85e9 Ermal
		send_event("service sync vouchers");
804 1e0b1727 Phil Davis
	}
805 5ebe85e9 Ermal
806 5060dea7 Scott Ullrich
	/* write database */
807 1e0b1727 Phil Davis
	if (!empty($unsetindexes)) {
808 26ee5aaf Ermal
		captiveportal_remove_entries($unsetindexes);
809 1e0b1727 Phil Davis
	}
810 5b237745 Scott Ullrich
}
811
812 522f1cc7 Ermal
function captiveportal_prune_old_automac() {
813 63d6bb4f Marcos Mendoza
	global $g, $cpzone, $cpzoneid;
814 65db621a Reid Linnemann
	$cpzone_config = config_get_path("captiveportal/{$cpzone}", []);
815 522f1cc7 Ermal
816 63d6bb4f Marcos Mendoza
	if (is_array($cpzone_config['passthrumac']) &&
817
	    isset($cpzone_config['passthrumacadd'])) {
818 522f1cc7 Ermal
		$tmpvoucherdb = array();
819
		$writecfg = false;
820 63d6bb4f Marcos Mendoza
		foreach ($cpzone_config['passthrumac'] as $eid => $emac) {
821 517b893e Renato Botelho
			if ($emac['logintype'] != "voucher") {
822
				continue;
823
			}
824 63d6bb4f Marcos Mendoza
			if (isset($cpzone_config['noconcurrentlogins'])) {
825 517b893e Renato Botelho
				if (isset($tmpvoucherdb[$emac['username']])) {
826 829322b3 Christian McDonald
					$temac = config_get_path("captiveportal/{$cpzone}/passthrumac/{$tmpvoucherdb[$emac['username']]}");
827 8bfe17da Marcos Mendoza
					captiveportal_passthrumac_delete_entry($temac);
828 522f1cc7 Ermal
					$writecfg = true;
829 517b893e Renato Botelho
					captiveportal_logportalauth($temac['username'], $temac['mac'],
830
					    $temac['ip'], "DUPLICATE {$temac['username']} LOGIN - TERMINATING OLD SESSION");
831 7e3ea4a8 Christian McDonald
					config_del_path("captiveportal/{$cpzone}/passthrumac/{$tmpvoucherdb[$emac['username']]}");
832 522f1cc7 Ermal
				}
833 517b893e Renato Botelho
				$tmpvoucherdb[$emac['username']] = $eid;
834
			}
835 522f1cc7 Ermal
		}
836 bae729da Ermal
		unset($tmpvoucherdb);
837 1e0b1727 Phil Davis
		if ($writecfg === true) {
838 522f1cc7 Ermal
			write_config("Prune session for auto-added macs");
839 1e0b1727 Phil Davis
		}
840 522f1cc7 Ermal
	}
841
}
842
843 3db19cf1 Scott Ullrich
/* remove a single client according to the DB entry */
844 4a778ba9 Augustin-FL
function captiveportal_disconnect($dbent, $term_cause = 1, $stop_time = null, $carp_loop = false) {
845 63d6bb4f Marcos Mendoza
	global $g, $cpzone, $cpzoneid;
846 d99f7864 Scott Ullrich
847
	$stop_time = (empty($stop_time)) ? time() : $stop_time;
848
849 7c2468c5 Viktor G
	/* this client needs to be deleted - remove pf anchor */
850 65db621a Reid Linnemann
	$cpzone_config = config_get_path("captiveportal/{$cpzone}", []);
851 63d6bb4f Marcos Mendoza
	if (isset($cpzone_config['radacct_enable']) && $dbent['authmethod'] == 'radius') {
852
		if ($cpzone_config['reauthenticateacct'] == "stopstartfreeradius") {
853 9d706ff8 Renato Botelho
			/*
854
			 * Interim updates are on so the session time must be
855
			 * reported as the elapsed time since the previous
856
			 * interim update.
857
			 */
858 00de7de6 jim-p
			$session_time = ($stop_time - $dbent[0]) % 60;
859
			$start_time = $stop_time - $session_time;
860
		} else {
861
			$start_time = $dbent[0];
862
		}
863 748372bc Stephen Jones
		captiveportal_send_server_accounting('stop',
864 e42ea151 Augustin FL
			$dbent[1], // ruleno
865 5705c60a Renato Botelho
			$dbent[4], // username
866
			$dbent[2], // clientip
867
			$dbent[3], // clientmac
868 e42ea151 Augustin FL
			$dbent[5], // sessionid
869
			$start_time, // start time
870
			$stop_time, // stop time
871
			$term_cause); // Acct-Terminate-Cause
872 d99f7864 Scott Ullrich
	}
873 1e0b1727 Phil Davis
874 7c2468c5 Viktor G
	if (is_ipaddrv4($dbent[2])) {
875 3c4fcd5b Renato Botelho
		/*
876 32661caf Viktor G
		 * Delete client's anchor entry from auth anchor
877 3c4fcd5b Renato Botelho
		 */
878 356f29a0 Renato Botelho
		$cpsession = captiveportal_isip_logged($dbent[2]);
879
		if (!empty($cpsession)) {
880 7c2468c5 Viktor G
			$host = array();
881
			$host['ip'] = $dbent[2];
882 63d6bb4f Marcos Mendoza
			if (!config_path_enabled("captiveportal/{$cpzone}", "nomacfilter")) {
883 7c2468c5 Viktor G
				$host['mac'] = $dbent[3];
884
			}
885
			captiveportal_ether_delete_entry($host, 'auth');
886 356f29a0 Renato Botelho
		}
887 32c392aa Ermal
		/* XXX: Redundant?! Ensure all pf(4) states are killed. */
888 e79a872f Christian McDonald
		$_gb = @pfSense_kill_states($dbent[2]);
889
		$_gb = @pfSense_kill_srcstates($dbent[2]);
890 32c392aa Ermal
	}
891 f9f71ad3 Ermal Lu?i
892 06ef0830 Augustin-FL
	// XMLRPC Call over to the backup node if necessary
893 4a778ba9 Augustin-FL
	if (captiveportal_xmlrpc_sync_get_details($syncip, $syncport,
894
	    $syncuser, $syncpass, $carp_loop)) {
895
		$rpc_client = new pfsense_xmlrpc_client();
896
		$rpc_client->setConnectionData($syncip, $syncport, $syncuser, $syncpass);
897
		$rpc_client->set_noticefile("CaptivePortalUserSync");
898
		$arguments = array(
899
			'sessionid' => $dbent[5],
900
			'term_cause' => $term_cause,
901
			'stop_time' => $stop_time
902
		);
903
904
		$rpc_client->xmlrpc_method('captive_portal_sync',
905
			array(
906
				'op' => 'disconnect_user',
907
				'zone' => $cpzone,
908
				'session' => base64_encode(serialize($arguments))
909
			)
910
		);
911
	}
912
	return true;
913 3db19cf1 Scott Ullrich
}
914 12ee8fe4 Scott Ullrich
915 006802ab Ermal
/* remove a single client by sessionid */
916
function captiveportal_disconnect_client($sessionid, $term_cause = 1, $logoutReason = "LOGOUT") {
917 63d6bb4f Marcos Mendoza
	global $g;
918 36254e4a Scott Ullrich
919 69c97c32 Chris Buechler
	$sessionid = SQLite3::escapeString($sessionid);
920 006802ab Ermal
	/* read database */
921 5038fb53 bcyrill
	$result = captiveportal_read_db("WHERE sessionid = '{$sessionid}'");
922 d99f7864 Scott Ullrich
923
	/* find entry */
924 5038fb53 bcyrill
	if (!empty($result)) {
925
		foreach ($result as $cpentry) {
926 eb43c5b1 Augustin FL
			captiveportal_disconnect($cpentry, $term_cause);
927 5705c60a Renato Botelho
			captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "DISCONNECT");
928 5038fb53 bcyrill
		}
929 eb43c5b1 Augustin FL
		captiveportal_remove_entries(array($sessionid));
930 26ee5aaf Ermal
	}
931 5b237745 Scott Ullrich
}
932
933 feab4e54 plumbeo
/* remove all clients */
934 78784180 Augustin-FL
function captiveportal_disconnect_all($term_cause = 6, $logoutReason = "DISCONNECT", $carp_loop = false) {
935 63d6bb4f Marcos Mendoza
	global $g, $cpzone, $cpzoneid;
936 feab4e54 plumbeo
937 78784180 Augustin-FL
	if (captiveportal_xmlrpc_sync_get_details($syncip, $syncport, $syncuser, $syncpass, $carp_loop)) {
938
		$rpc_client = new pfsense_xmlrpc_client();
939
		$rpc_client->setConnectionData($syncip, $syncport, $syncuser, $syncpass);
940
		$rpc_client->set_noticefile("CaptivePortalUserSync");
941
		$arguments = array(
942
			'term_cause' => $term_cause,
943
			'logout_reason' => $logoutReason
944
		);
945
946
		$rpc_client->xmlrpc_method('captive_portal_sync',
947
			array(
948
				'op' => 'disconnect_all',
949
				'zone' => $cpzone,
950
				'arguments' => base64_encode(serialize($arguments))
951
			)
952
		);
953
	}
954 d793617e plumbeo
	/* check if we're pruning old entries and eventually wait */
955 47f96785 plumbeo
	$rcprunelock = try_lock("rcprunecaptiveportal{$cpzone}", 15);
956 d793617e plumbeo
957
	/* if we still don't have the lock, unlock forcefully and take it */
958
	if (!$rcprunelock) {
959 134a8703 Marcos Mendoza
		log_error("CP zone {$cpzone}: could not obtain the lock for more than 15 seconds, lock taken forcefully to disconnect all users");
960 d793617e plumbeo
		unlock_force("rcprunecaptiveportal{$cpzone}");
961
		$rcprunelock = lock("rcprunecaptiveportal{$cpzone}", LOCK_EX);
962
	}
963
964 47f96785 plumbeo
	/* take a lock so new users won't be able to log in */
965
	$cpdblck = lock("captiveportaldb{$cpzone}", LOCK_EX);
966
967
	captiveportal_radius_stop_all($term_cause, $logoutReason);
968 feab4e54 plumbeo
969 3e49e032 Viktor G
	/* reinit captiveportal pipes and anchors */
970
	captiveportal_init_rules(true);
971
972 47f96785 plumbeo
	/* remove users from the database */
973
	$cpdb = captiveportal_read_db();
974 025ec94a plumbeo
	$unsetindexes = array_column($cpdb,5);
975
	if (!empty($unsetindexes)) {
976 78784180 Augustin-FL
		// High Availability : do not sync removed entries
977
		captiveportal_remove_entries($unsetindexes, true);
978 025ec94a plumbeo
	}
979 feab4e54 plumbeo
980 47f96785 plumbeo
	unlock($cpdblck);
981 d793617e plumbeo
	unlock($rcprunelock);
982 78784180 Augustin-FL
	return true;
983 feab4e54 plumbeo
}
984
985 e42ea151 Augustin FL
/* send RADIUS acct stop for all current clients connected with RADIUS servers */
986 3ece6d54 plumbeo
function captiveportal_radius_stop_all($term_cause = 6, $logoutReason = "DISCONNECT") {
987 63d6bb4f Marcos Mendoza
	global $g, $cpzone, $cpzoneid;
988 d99f7864 Scott Ullrich
989 3ece6d54 plumbeo
	$cpdb = captiveportal_read_db();
990
991 65db621a Reid Linnemann
	$cpzone_config = config_get_path("captiveportal/{$cpzone}", []);
992 63d6bb4f Marcos Mendoza
	$radacct = isset($cpzone_config['radacct_enable']) ? true : false;
993 3ece6d54 plumbeo
	foreach ($cpdb as $cpentry) {
994 b0a29e82 Augustin-FL
		if ($cpentry['authmethod'] === 'radius' && $radacct) {
995 63d6bb4f Marcos Mendoza
			if ($cpzone_config['reauthenticateacct'] == "stopstartfreeradius") {
996 e42ea151 Augustin FL
				$session_time = (time() - $cpentry[0]) % 60;
997
				$start_time = time() - $session_time;
998
			} else {
999
				$start_time = $cpentry[0];
1000 ebc0e4b6 Ermal
			}
1001 748372bc Stephen Jones
			captiveportal_send_server_accounting('stop',
1002 e42ea151 Augustin FL
				$cpentry[1], // ruleno
1003
				$cpentry[4], // username
1004
				$cpentry[2], // clientip
1005
				$cpentry[3], // clientmac
1006
				$cpentry[5], // sessionid
1007
				$start_time, // start time
1008 1ec82c30 Marcos Mendoza
				time(), // stop time
1009 e42ea151 Augustin FL
				$term_cause); // Acct-Terminate-Cause
1010 d99f7864 Scott Ullrich
		}
1011 3ece6d54 plumbeo
		captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], $logoutReason);
1012 d99f7864 Scott Ullrich
	}
1013 3ece6d54 plumbeo
	unset($cpdb);
1014 5b237745 Scott Ullrich
}
1015
1016 43bd2b88 Reid Linnemann
function captiveportal_passthrumac_delete_entry($macent) {
1017 63d6bb4f Marcos Mendoza
	global $g, $cpzone;
1018 d5ae560d Ermal
1019 43bd2b88 Reid Linnemann
	$host = str_replace("/", "_", str_replace(":", "", $macent['mac']));
1020 63d6bb4f Marcos Mendoza
	$cpzoneprefix = CPPREFIX . config_get_path("captiveportal/{$cpzone}/zoneid");
1021 666f88e0 Renato Botelho
1022 a413e743 Renato Botelho
	if ($macent['action'] == 'pass') {
1023 43bd2b88 Reid Linnemann
		$pipes = captiveportal_get_dn_passthru_pipes($macent['mac']);
1024
		if (!empty($pipes)) {
1025
			captiveportal_pipes_delete($pipes);
1026 666f88e0 Renato Botelho
		}
1027 7c2468c5 Viktor G
	} else {
1028
		/* no rules on passthru block */
1029
		return;
1030 666f88e0 Renato Botelho
	}
1031
1032 978ea085 linnemannr
	pfSense_pf_cp_flush("{$cpzoneprefix}_passthrumac/{$host}", "ether");
1033 666f88e0 Renato Botelho
}
1034
1035 7c2468c5 Viktor G
function captiveportal_passthrumac_configure($startindex = 0, $stopindex = 0) {
1036 63d6bb4f Marcos Mendoza
	global $g, $cpzone;
1037 36254e4a Scott Ullrich
1038 63d6bb4f Marcos Mendoza
	$passthrumac_config = config_get_path("captiveportal/{$cpzone}/passthrumac");
1039
	if (is_array($passthrumac_config)) {
1040 41196b69 Ermal LUÇI
		if ($stopindex > 0) {
1041
			for ($idx = $startindex; $idx <= $stopindex; $idx++) {
1042 63d6bb4f Marcos Mendoza
				if (isset($passthrumac_config[$idx])) {
1043
					captiveportal_ether_configure_entry($passthrumac_config[$idx], 'passthrumac');
1044 41196b69 Ermal LUÇI
				}
1045
			}
1046
		} else {
1047 63d6bb4f Marcos Mendoza
			$nentries = count($passthrumac_config);
1048 41196b69 Ermal LUÇI
			if ($nentries > 2000) {
1049
				$nloops = $nentries / 1000;
1050
				$remainder= $nentries % 1000;
1051 1e0b1727 Phil Davis
				for ($i = 0; $i < $nloops; $i++) {
1052 41196b69 Ermal LUÇI
					mwexec_bg("/usr/local/sbin/fcgicli -f /etc/rc.captiveportal_configure_mac -d \"cpzone={$cpzone}&startidx=" . ($i * 1000) . "&stopidx=" . ((($i+1) * 1000) - 1) . "\"");
1053 1e0b1727 Phil Davis
				}
1054
				if ($remainder > 0) {
1055 41196b69 Ermal LUÇI
					mwexec_bg("/usr/local/sbin/fcgicli -f /etc/rc.captiveportal_configure_mac -d \"cpzone={$cpzone}&startidx=" . ($i * 1000) . "&stopidx=" . (($i* 1000) + $remainder) ."\"");
1056 1e0b1727 Phil Davis
				}
1057 41196b69 Ermal LUÇI
			} else {
1058 63d6bb4f Marcos Mendoza
				foreach ($passthrumac_config as $macent) {
1059 7c2468c5 Viktor G
					captiveportal_ether_configure_entry($macent, 'passthrumac');
1060 1e0b1727 Phil Davis
				}
1061 41196b69 Ermal LUÇI
			}
1062 fe9ec12b Ermal LUÇI
		}
1063 621fed0e Ermal
	}
1064 5b237745 Scott Ullrich
}
1065
1066 fac13a5e Ermal
function captiveportal_passthrumac_findbyname($username) {
1067 63d6bb4f Marcos Mendoza
	global $cpzone;
1068 fac13a5e Ermal
1069 63d6bb4f Marcos Mendoza
	$passthrumac_config = config_get_path("captiveportal/{$cpzone}/passthrumac");
1070
	if (is_array($passthrumac_config)) {
1071
		foreach ($passthrumac_config as $macent) {
1072 1e0b1727 Phil Davis
			if ($macent['username'] == $username) {
1073 fac13a5e Ermal
				return $macent;
1074 1e0b1727 Phil Davis
			}
1075 fac13a5e Ermal
		}
1076
	}
1077
	return NULL;
1078
}
1079
1080 7c2468c5 Viktor G
function captiveportal_ether_delete_entry($hostent, $anchor = 'allowedhosts') {
1081 63d6bb4f Marcos Mendoza
	global $g, $cpzone;
1082 b01792a0 Ermal
1083 63d6bb4f Marcos Mendoza
	$cpzoneprefix = CPPREFIX . config_get_path("captiveportal/{$cpzone}/zoneid");
1084 0b108eda Scott Ullrich
1085 7c2468c5 Viktor G
	if (!empty($hostent['sn'])) {
1086
		$host = $hostent['ip'] . '_' . $hostent['sn'];
1087
	} else {
1088
		$host = $hostent['ip'] . '_32';
1089 1e0b1727 Phil Davis
	}
1090 b01792a0 Ermal
1091 43bd2b88 Reid Linnemann
	$pipes = pfSense_pf_cp_get_eth_pipes("{$cpzoneprefix}_{$anchor}/{$host}");
1092
	if (!empty($pipes)) {
1093
		captiveportal_pipes_delete($pipes);
1094 1e0b1727 Phil Davis
	}
1095 3e49e032 Viktor G
	/* flush anchor rules */
1096
	pfSense_pf_cp_flush("{$cpzoneprefix}_{$anchor}/{$host}", "ether");
1097 f23a6091 Scott Ullrich
}
1098
1099
function captiveportal_allowedhostname_configure() {
1100 63d6bb4f Marcos Mendoza
	global $g, $cpzone, $cpzoneid;
1101 f23a6091 Scott Ullrich
1102 63d6bb4f Marcos Mendoza
	$allowedhostname_config = config_get_path("captiveportal/{$cpzone}/allowedhostname");
1103
	if (!is_array($allowedhostname_config)) {
1104 7c2468c5 Viktor G
		return false;
1105 517b893e Renato Botelho
	}
1106
1107
	$cp_filterdns_conf = "";
1108 63d6bb4f Marcos Mendoza
	foreach ($allowedhostname_config as $id => $hostnameent) {
1109 7c2468c5 Viktor G
		$cp_filterdns_conf .= captiveportal_allowedhostname_configure_entry($hostnameent, $id);
1110 517b893e Renato Botelho
	}
1111
	$cp_filterdns_filename = "{$g['varetc_path']}/filterdns-{$cpzone}-captiveportal.conf";
1112 7c2468c5 Viktor G
	if ((!file_exists($cp_filterdns_filename) && !empty($cp_filterdns_conf)) ||
1113
	    (file_exists($cp_filterdns_filename) && ($cp_filterdns_conf != file_get_contents($cp_filterdns_filename)))) {
1114
		@file_put_contents($cp_filterdns_filename, $cp_filterdns_conf);
1115
		filter_configure();
1116
		captiveportal_filterdns_configure();
1117
	}
1118 517b893e Renato Botelho
}
1119
1120
function captiveportal_filterdns_configure() {
1121 63d6bb4f Marcos Mendoza
	global $g, $cpzone, $cpzoneid;
1122 517b893e Renato Botelho
1123 2568e151 Christian McDonald
	$cp_filterdns_filename = g_get('varetc_path') .
1124 517b893e Renato Botelho
	    "/filterdns-{$cpzone}-captiveportal.conf";
1125
1126 65db621a Reid Linnemann
	$cpzone_config = config_get_path("captiveportal/{$cpzone}", []);
1127 63d6bb4f Marcos Mendoza
	if (isset($cpzone_config['enable']) &&
1128
	    is_array($cpzone_config['allowedhostname']) &&
1129 7c2468c5 Viktor G
	    file_exists($cp_filterdns_filename) &&
1130
	    !empty(file_get_contents($cp_filterdns_filename))) {
1131 2568e151 Christian McDonald
		if (isvalidpid(g_get('varrun_path') .
1132 517b893e Renato Botelho
		    "/filterdns-{$cpzone}-cpah.pid")) {
1133 2568e151 Christian McDonald
			sigkillbypid(g_get('varrun_path') .
1134 517b893e Renato Botelho
			    "/filterdns-{$cpzone}-cpah.pid", "HUP");
1135 1e0b1727 Phil Davis
		} else {
1136 517b893e Renato Botelho
			mwexec("/usr/local/sbin/filterdns -p " .
1137
			    "{$g['varrun_path']}/filterdns-{$cpzone}-cpah.pid" .
1138 a4aebf44 Renato Botelho
			    " -i 300 -c {$cp_filterdns_filename} -d 1");
1139 1e0b1727 Phil Davis
		}
1140 7b5eab84 bcyrill
	} else {
1141
		killbypid("{$g['varrun_path']}/filterdns-{$cpzone}-cpah.pid");
1142
		@unlink("{$g['varrun_path']}/filterdns-{$cpzone}-cpah.pid");
1143 55c18b30 Scott Ullrich
	}
1144 f23a6091 Scott Ullrich
}
1145
1146 cb0a2913 Ermal Lu?i
function captiveportal_allowedip_configure() {
1147 63d6bb4f Marcos Mendoza
	global $g, $cpzone;
1148 36254e4a Scott Ullrich
1149 63d6bb4f Marcos Mendoza
	$allowedip_config = config_get_path("captiveportal/{$cpzone}/allowedip");
1150
	if (is_array($allowedip_config)) {
1151
		foreach ($allowedip_config as $ipent) {
1152 7c2468c5 Viktor G
			captiveportal_allowedip_configure_entry($ipent);
1153 1e0b1727 Phil Davis
		}
1154 cb0a2913 Ermal Lu?i
	}
1155 5b237745 Scott Ullrich
}
1156
1157 2d53158f stompro
/* get last activity timestamp given client IP address */
1158 f4c867e0 Renato Botelho
function captiveportal_get_last_activity($ip) {
1159 63d6bb4f Marcos Mendoza
	global $cpzone;
1160 36254e4a Scott Ullrich
1161 63d6bb4f Marcos Mendoza
	$cpzoneprefix = CPPREFIX . config_get_path("captiveportal/{$cpzone}/zoneid");
1162 7c2468c5 Viktor G
	$anchor = $cpzoneprefix . '_auth';
1163
1164 978ea085 linnemannr
	$active_times = pfSense_pf_cp_get_eth_last_active("{$anchor}/{$ip}_32");
1165
	$time = 0;
1166
	if (!empty($active_times)) {
1167
		foreach ($active_times as $active_time) {
1168
			if ( $active_time > $time)
1169
				$time = $active_time;
1170
	   }
1171 d99f7864 Scott Ullrich
	}
1172 36254e4a Scott Ullrich
1173 978ea085 linnemannr
	return $time;
1174 5b237745 Scott Ullrich
}
1175
1176
1177 3db19cf1 Scott Ullrich
/* log successful captive portal authentication to syslog */
1178
/* part of this code from php.net */
1179 086cf944 Phil Davis
function captiveportal_logportalauth($user, $mac, $ip, $status, $message = null) {
1180 d99f7864 Scott Ullrich
	// Log it
1181 1e0b1727 Phil Davis
	if (!$message) {
1182 12feed15 Ermal
		$message = "{$status}: {$user}, {$mac}, {$ip}";
1183 1e0b1727 Phil Davis
	} else {
1184 d31bc32a Ermal
		$message = trim($message);
1185 12feed15 Ermal
		$message = "{$status}: {$user}, {$mac}, {$ip}, {$message}";
1186 d31bc32a Ermal
	}
1187 f56a73f1 Scott Ullrich
	captiveportal_syslog($message);
1188
}
1189
1190
/* log simple messages to syslog */
1191
function captiveportal_syslog($message) {
1192 12feed15 Ermal
	global $cpzone;
1193
1194 f56a73f1 Scott Ullrich
	$message = trim($message);
1195 9aec47b7 Chris Buechler
	$message = "Zone: {$cpzone} - {$message}";
1196 f56a73f1 Scott Ullrich
	openlog("logportalauth", LOG_PID, LOG_LOCAL4);
1197
	// Log it
1198
	syslog(LOG_INFO, $message);
1199 d99f7864 Scott Ullrich
	closelog();
1200 3db19cf1 Scott Ullrich
}
1201
1202 eb43c5b1 Augustin FL
/* Authenticate users using Authentication Backend */
1203 d900b9d4 Marcos Mendoza
function captiveportal_authenticate_user(&$login = '', $password = '', $clientmac = '', $clientip = '', $pipeno = 'null', $context = 'first') {
1204 63d6bb4f Marcos Mendoza
	global $g, $cpzone;
1205 65db621a Reid Linnemann
	$cpcfg = config_get_path("captiveportal/{$cpzone}", []);
1206 748372bc Stephen Jones
1207 eb43c5b1 Augustin FL
	$login_status = 'FAILURE';
1208
	$login_msg = gettext('Invalid credentials specified');
1209
	$reply_attributes = array();
1210
	$auth_method = '';
1211
	$auth_result = null;
1212 748372bc Stephen Jones
1213
	/*
1214
	Management of the reply Message (reason why the authentication failed) :
1215 eb43c5b1 Augustin FL
	multiple authentication servers can be used, so multiple reply messages could theoretically be returned.
1216
	But only one message is returned (the most important one).
1217
	The return value of authenticate_user() define how important messages are :
1218 748372bc Stephen Jones
		- Reply message of a successful auth is more important than reply message of
1219 eb43c5b1 Augustin FL
		a user failed auth(invalid credentials/authorization)
1220 748372bc Stephen Jones
1221
		- Reply message of a user failed auth is more important than reply message of
1222 eb43c5b1 Augustin FL
		a server failed auth (unable to contact server)
1223 748372bc Stephen Jones
1224 eb43c5b1 Augustin FL
		- When multiple user failed auth are encountered, messages returned by remote servers
1225
		(eg. reply in RADIUS Access-Reject) are more important than pfSense error messages.
1226 748372bc Stephen Jones
1227
	The $authlevel variable is a flag indicating the status of authentication
1228 eb43c5b1 Augustin FL
	0 = failed server auth
1229
	1 = failed user auth
1230 4864d7f6 Josh Soref
	2 = failed user auth with custom server reply received
1231 eb43c5b1 Augustin FL
	3 = successful auth
1232
	*/
1233
	$authlevel = 0;
1234
1235
	/* Getting authentication servers from captiveportal configuration */
1236
	$auth_servers = array();
1237 748372bc Stephen Jones
1238 eb43c5b1 Augustin FL
	if ($cpcfg['auth_method'] === 'none') {
1239
		$auth_servers[] = array('type' => 'none');
1240
	} else {
1241
		if ($context === 'second') {
1242
			$fullauthservers = explode(",", $cpcfg['auth_server2']);
1243
		} else {
1244
			$fullauthservers = explode(",", $cpcfg['auth_server']);
1245
		}
1246 d44bccc7 Scott Ullrich
1247 eb43c5b1 Augustin FL
		foreach ($fullauthservers as $authserver) {
1248
			if (strpos($authserver, ' - ') !== false) {
1249
				$authserver = explode(' - ', $authserver);
1250
				array_shift($authserver);
1251
				$authserver = implode(' - ', $authserver);
1252 748372bc Stephen Jones
1253 eb43c5b1 Augustin FL
				if (auth_get_authserver($authserver) !== null) {
1254
					$auth_servers[] = auth_get_authserver($authserver);
1255
				} else {
1256
					log_error("Zone: {$cpzone} - Captive portal was unable to find the settings of the server '{$authserver}' used for authentication !");
1257
				}
1258
			}
1259
		}
1260 1e0b1727 Phil Davis
	}
1261 ebc0e4b6 Ermal
1262 eb43c5b1 Augustin FL
	/* Unable to find the any authentication server config - shouldn't happen! - bail out */
1263
	if (count($auth_servers) === 0) {
1264
		log_error("Zone: {$cpzone} - No valid server could be used for authentication.");
1265
		$login_msg = gettext("Internal Error");
1266 d2c98878 falbertopl
	} else {
1267 eb43c5b1 Augustin FL
		foreach ($auth_servers as $authcfg) {
1268
			if ($authlevel < 3) {
1269
				$radmac_error = false;
1270 b1cc8f31 jim-p
				$attributes = array("nas_identifier" => empty($cpcfg["radiusnasid"]) ? "CaptivePortal-{$cpzone}" : $cpcfg["radiusnasid"],
1271 eb43c5b1 Augustin FL
					"nas_port_type" => RADIUS_ETHERNET,
1272
					"nas_port" => $pipeno,
1273
					"framed_ip" => $clientip);
1274
				if (mac_format($clientmac) !== null) {
1275
					$attributes["calling_station_id"] = mac_format($clientmac);
1276
				}
1277 748372bc Stephen Jones
1278 eb43c5b1 Augustin FL
				$result = null;
1279
				$status = null;
1280
				$msg = null;
1281 748372bc Stephen Jones
1282 eb43c5b1 Augustin FL
				/* Radius MAC authentication */
1283 774ff51b Augustin-FL
				if ($context === 'radmac' && $clientmac) {
1284 eb43c5b1 Augustin FL
					if ($authcfg['type'] === 'radius') {
1285
						$login = mac_format($clientmac);
1286
						$status = "MACHINE LOGIN";
1287
					} else {
1288
						/* Trying to perform a Radius MAC authentication on a non-radius server - shouldn't happen! - bail out */
1289
						$msg = gettext("Internal Error");
1290
						log_error("Zone: {$cpzone} - Trying to perform RADIUS MAC authentication on a non-RADIUS server !");
1291
						$radmac_error = true;
1292
						$result = null;
1293
					}
1294
				}
1295 748372bc Stephen Jones
1296 eb43c5b1 Augustin FL
				if (!$radmac_error) {
1297
					if ($authcfg['type'] === 'none') {
1298
						$result = true;
1299
					} else {
1300
						$result = authenticate_user($login, $password, $authcfg, $attributes);
1301
					}
1302 748372bc Stephen Jones
1303 eb43c5b1 Augustin FL
					if (!empty($attributes['error_message'])) {
1304
						$msg = $attributes['error_message'];
1305
					}
1306 748372bc Stephen Jones
1307 eb43c5b1 Augustin FL
					if ($authcfg['type'] == 'Local Auth' && $result && isset($cpcfg['localauth_priv'])) {
1308 1bb9c407 Marcos Mendoza
						$tmp_user_item_config = getUserEntry($login);
1309
						if (!userHasPrivilege($tmp_user_item_config['item'], "user-services-captiveportal-login")) {
1310 eb43c5b1 Augustin FL
							$result = false;
1311
							$msg = gettext("Access Denied");
1312
						}
1313
					}
1314 774ff51b Augustin-FL
					if ($context === 'radmac' && $result === null && empty($attributes['reply_message'])) {
1315
						$msg = gettext("RADIUS MAC Authentication Failed.");
1316
					}
1317 748372bc Stephen Jones
1318 eb43c5b1 Augustin FL
					if (empty($status)) {
1319
						if ($result === true) {
1320
							$status = "ACCEPT";
1321
						} elseif ($result === null) {
1322
							$status = "ERROR";
1323
						} else {
1324
							$status = "FAILURE";
1325
						}
1326
					}
1327 748372bc Stephen Jones
1328 774ff51b Augustin-FL
					if ($context === 'radmac' && $login == mac_format($clientmac) || $authcfg['type'] === 'none' && empty($login)) {
1329 eb43c5b1 Augustin FL
						$login = "unauthenticated";
1330 748372bc Stephen Jones
					}
1331 22e32874 Augustin-FL
				}
1332
				// We determine a flag
1333
				if ($result === true) {
1334
					$val = 3;
1335
				} elseif ($result === false && !empty($attributes['reply_message'])) {
1336
					$val = 2;
1337
					$msg = $attributes['reply_message'];
1338
				} elseif ($result === false) {
1339
					$val = 1;
1340
				} elseif ($result === null) {
1341
					$val = 0;
1342 eb43c5b1 Augustin FL
				}
1343 748372bc Stephen Jones
1344 665cbe2a Augustin-FL
				if ($val >= $authlevel) {
1345
					$authlevel = $val;
1346 eb43c5b1 Augustin FL
					$auth_method = $authcfg['type'];
1347
					$login_status = $status;
1348
					$login_msg = $msg;
1349
					$reply_attributes = $attributes;
1350
					$auth_result = $result;
1351
				}
1352
			}
1353
		}
1354 1e0b1727 Phil Davis
	}
1355 0bd34ed6 Scott Ullrich
1356 eb43c5b1 Augustin FL
	return array('result'=>$auth_result, 'attributes'=>$reply_attributes, 'auth_method' =>$auth_method, 'login_status'=> $login_status, 'login_message' => $login_msg);
1357 0bd34ed6 Scott Ullrich
}
1358
1359 26ee5aaf Ermal
function captiveportal_opendb() {
1360 63d6bb4f Marcos Mendoza
	global $g, $cpzone, $cpzoneid;
1361 5060dea7 Scott Ullrich
1362 79e46ebd jim-p
	$db_path = "{$g['vardb_path']}/captiveportal{$cpzone}.db";
1363
	$createquery = "CREATE TABLE IF NOT EXISTS captiveportal (" .
1364
				"allow_time INTEGER, pipeno INTEGER, ip TEXT, mac TEXT, username TEXT, " .
1365
				"sessionid TEXT, bpassword TEXT, session_timeout INTEGER, idle_timeout INTEGER, " .
1366 f3e403d5 plumbeo
				"session_terminate_time INTEGER, interim_interval INTEGER, traffic_quota INTEGER, " .
1367 b0a29e82 Augustin-FL
				"bw_up INTEGER, bw_down INTEGER, authmethod TEXT, context TEXT); " .
1368 79e46ebd jim-p
			"CREATE UNIQUE INDEX IF NOT EXISTS idx_active ON captiveportal (sessionid, username); " .
1369
			"CREATE INDEX IF NOT EXISTS user ON captiveportal (username); " .
1370
			"CREATE INDEX IF NOT EXISTS ip ON captiveportal (ip); " .
1371
			"CREATE INDEX IF NOT EXISTS starttime ON captiveportal (allow_time)";
1372
1373 6b8ad2da jim-p
	try {
1374
		$DB = new SQLite3($db_path);
1375 18ca572b Chris Buechler
		$DB->busyTimeout(60000);
1376 6b8ad2da jim-p
	} catch (Exception $e) {
1377
		captiveportal_syslog("Could not open {$db_path} as an sqlite database for {$cpzone}. Error message: " . $e->getMessage() . " -- Trying again.");
1378
		unlink_if_exists($db_path);
1379
		try {
1380
			$DB = new SQLite3($db_path);
1381 18ca572b Chris Buechler
			$DB->busyTimeout(60000);
1382 6b8ad2da jim-p
		} catch (Exception $e) {
1383
			captiveportal_syslog("Still could not open {$db_path} as an sqlite database for {$cpzone}. Error message: " . $e->getMessage() . " -- Remove the database file manually and ensure there is enough free space.");
1384
			return;
1385
		}
1386
	}
1387
1388 7c38032f jim-p
	if (!$DB) {
1389
		captiveportal_syslog("Could not open {$db_path} as an sqlite database for {$cpzone}. Error message: {$DB->lastErrorMsg()}. Trying again.");
1390
		unlink_if_exists($db_path);
1391
		$DB = new SQLite3($db_path);
1392 18ca572b Chris Buechler
		$DB->busyTimeout(60000);
1393 7c38032f jim-p
		if (!$DB) {
1394
			captiveportal_syslog("Still could not open {$db_path} as an sqlite database for {$cpzone}. Error message: {$DB->lastErrorMsg()}. Remove the database file manually and ensure there is enough free space.");
1395
			return;
1396
		}
1397
	}
1398
1399 79e46ebd jim-p
	if (! $DB->exec($createquery)) {
1400
		captiveportal_syslog("Error during table {$cpzone} creation. Error message: {$DB->lastErrorMsg()}. Resetting and trying again.");
1401
1402
		/* If unable to initialize the database, reset and try again. */
1403
		$DB->close();
1404
		unset($DB);
1405
		unlink_if_exists($db_path);
1406
		$DB = new SQLite3($db_path);
1407 18ca572b Chris Buechler
		$DB->busyTimeout(60000);
1408 79e46ebd jim-p
		if ($DB->exec($createquery)) {
1409
			captiveportal_syslog("Successfully reinitialized tables for {$cpzone} -- database has been reset.");
1410 5e71234e Chris Buechler
			if (!is_numericint($cpzoneid)) {
1411 63d6bb4f Marcos Mendoza
				$cp_config = config_get_path('captiveportal');
1412
				if (is_array($cp_config)) {
1413
					foreach ($cp_config as $cpkey => $cp) {
1414 0c388fef Renato Botelho
						if ($cpzone == $cpkey) {
1415 5e71234e Chris Buechler
							$cpzoneid = $cp['zoneid'];
1416
						}
1417
					}
1418
				}
1419
			}
1420
			if (is_numericint($cpzoneid)) {
1421 7c2468c5 Viktor G
				captiveportal_delete_rules(array(), true);
1422
				filter_configure();
1423 5e71234e Chris Buechler
				captiveportal_syslog("Flushed tables for {$cpzone} after database reset.");
1424
			}
1425 79e46ebd jim-p
		} else {
1426
			captiveportal_syslog("Still unable to create tables for {$cpzone}. Error message: {$DB->lastErrorMsg()}. Remove the database file manually and try again.");
1427
		}
1428 1e0b1727 Phil Davis
	}
1429 26ee5aaf Ermal
1430
	return $DB;
1431 0bd34ed6 Scott Ullrich
}
1432
1433 26ee5aaf Ermal
/* read captive portal DB into array */
1434
function captiveportal_read_db($query = "") {
1435 5cf91315 Renato Botelho
	$cpdb = array();
1436 5060dea7 Scott Ullrich
1437 26ee5aaf Ermal
	$DB = captiveportal_opendb();
1438
	if ($DB) {
1439 5cf91315 Renato Botelho
		$response = $DB->query("SELECT * FROM captiveportal {$query}");
1440 d338018f Ermal
		if ($response != FALSE) {
1441 1e0b1727 Phil Davis
			while ($row = $response->fetchArray()) {
1442 d338018f Ermal
				$cpdb[] = $row;
1443 1e0b1727 Phil Davis
			}
1444 d338018f Ermal
		}
1445 5cf91315 Renato Botelho
		$DB->close();
1446 006802ab Ermal
	}
1447 26ee5aaf Ermal
1448
	return $cpdb;
1449
}
1450
1451 4a778ba9 Augustin-FL
function captiveportal_remove_entries($remove, $carp_loop = false) {
1452
	global $cpzone;
1453 26ee5aaf Ermal
1454 1e0b1727 Phil Davis
	if (!is_array($remove) || empty($remove)) {
1455 26ee5aaf Ermal
		return;
1456 1e0b1727 Phil Davis
	}
1457 26ee5aaf Ermal
1458 1974c2d6 bcyrill
	$query = "DELETE FROM captiveportal WHERE sessionid in (";
1459 1e0b1727 Phil Davis
	foreach ($remove as $idx => $unindex) {
1460 26ee5aaf Ermal
		$query .= "'{$unindex}'";
1461 1e0b1727 Phil Davis
		if ($idx < (count($remove) - 1)) {
1462 26ee5aaf Ermal
			$query .= ",";
1463 1e0b1727 Phil Davis
		}
1464 006802ab Ermal
	}
1465 26ee5aaf Ermal
	$query .= ")";
1466
	captiveportal_write_db($query);
1467 4a778ba9 Augustin-FL
1468
	if (captiveportal_xmlrpc_sync_get_details($syncip, $syncport, $syncuser, $syncpass, $carp_loop)) {
1469
		$rpc_client = new pfsense_xmlrpc_client();
1470
		$rpc_client->setConnectionData($syncip, $syncport, $syncuser, $syncpass);
1471
		$rpc_client->set_noticefile("CaptivePortalUserSync");
1472
		$rpc_client->xmlrpc_method('captive_portal_sync',
1473
			array(
1474
				'op' => 'remove_entries',
1475
				'zone' => $cpzone,
1476
				'entries' => base64_encode(serialize($remove))
1477
			)
1478
		);
1479
	}
1480
	return true;
1481 26ee5aaf Ermal
}
1482
1483
/* write captive portal DB */
1484
function captiveportal_write_db($queries) {
1485
	global $g;
1486
1487 1e0b1727 Phil Davis
	if (is_array($queries)) {
1488 26ee5aaf Ermal
		$query = implode(";", $queries);
1489 1e0b1727 Phil Davis
	} else {
1490 26ee5aaf Ermal
		$query = $queries;
1491 1e0b1727 Phil Davis
	}
1492 26ee5aaf Ermal
1493
	$DB = captiveportal_opendb();
1494
	if ($DB) {
1495 5cf91315 Renato Botelho
		$DB->exec("BEGIN TRANSACTION");
1496
		$result = $DB->exec($query);
1497 1e0b1727 Phil Davis
		if (!$result) {
1498 5cf91315 Renato Botelho
			captiveportal_syslog("Trying to modify DB returned error: {$DB->lastErrorMsg()}");
1499 1e0b1727 Phil Davis
		} else {
1500 5cf91315 Renato Botelho
			$DB->exec("END TRANSACTION");
1501 1e0b1727 Phil Davis
		}
1502 5cf91315 Renato Botelho
		$DB->close();
1503 26ee5aaf Ermal
		return $result;
1504 1e0b1727 Phil Davis
	} else {
1505 26ee5aaf Ermal
		return true;
1506 1e0b1727 Phil Davis
	}
1507 0bd34ed6 Scott Ullrich
}
1508
1509
function captiveportal_write_elements() {
1510 63d6bb4f Marcos Mendoza
	global $g, $cpzone;
1511 1e0b1727 Phil Davis
1512 65db621a Reid Linnemann
	$cpcfg = config_get_path("captiveportal/{$cpzone}", []);
1513 b4792bf8 Ermal
1514 2568e151 Christian McDonald
	if (!is_dir(g_get('captiveportal_element_path'))) {
1515
		@mkdir(g_get('captiveportal_element_path'));
1516 1e0b1727 Phil Davis
	}
1517 769e254e Ermal
1518 b4792bf8 Ermal
	if (is_array($cpcfg['element'])) {
1519
		foreach ($cpcfg['element'] as $data) {
1520 84b128b6 jim-p
			/* Do not attempt to decode or write out empty files. */
1521 748372bc Stephen Jones
			if (isset($data['nocontent'])) {
1522
					continue;
1523
			}
1524 84b128b6 jim-p
			if (empty($data['content']) || empty(base64_decode($data['content']))) {
1525
				unlink_if_exists("{$g['captiveportal_element_path']}/{$data['name']}");
1526
				touch("{$g['captiveportal_element_path']}/{$data['name']}");
1527
			} elseif (!@file_put_contents("{$g['captiveportal_element_path']}/{$data['name']}", base64_decode($data['content']))) {
1528 1579e70f Phil Davis
				printf(gettext('Error: cannot open \'%1$s\' in captiveportal_write_elements()%2$s'), $data['name'], "\n");
1529 1fadb31d Scott Ullrich
				return 1;
1530
			}
1531 1e0b1727 Phil Davis
			if (!file_exists("{$g['captiveportal_path']}/{$data['name']}")) {
1532 bdba4fa7 Ermal
				@symlink("{$g['captiveportal_element_path']}/{$data['name']}", "{$g['captiveportal_path']}/{$data['name']}");
1533 1e0b1727 Phil Davis
			}
1534 1fadb31d Scott Ullrich
		}
1535
	}
1536 1e0b1727 Phil Davis
1537 769e254e Ermal
	return 0;
1538 0bd34ed6 Scott Ullrich
}
1539
1540 b27df7cf Renato Botelho
function captiveportal_free_dnrules($rulenos_start = 2000,
1541 7bdf5cd6 plumbeo
    $rulenos_range_max = 64500, $dry_run = false, $clear_auth_pipes = true) {
1542 fbfbc6bd Renato Botelho
	global $g, $cpzone;
1543 7fb23399 Ermal
1544 b2c92623 Renato Botelho
	$removed_pipes = array();
1545
1546 ff8b4019 Renato Botelho
	if (!file_exists("{$g['vardb_path']}/captiveportaldn.rules")) {
1547
		return $removed_pipes;
1548
	}
1549
1550 b27df7cf Renato Botelho
	if (!$dry_run) {
1551
		$cpruleslck = lock("captiveportalrulesdn", LOCK_EX);
1552
	}
1553 ff8b4019 Renato Botelho
1554 82e22457 Marcos Mendoza
	$rules = unserialize_data(file_get_contents(
1555
	    "{$g['vardb_path']}/captiveportaldn.rules"), []);
1556 ff8b4019 Renato Botelho
	$ridx = $rulenos_start;
1557
	while ($ridx < $rulenos_range_max) {
1558 7bdf5cd6 plumbeo
		if (substr($rules[$ridx], 0, strlen($cpzone . '_')) == $cpzone . '_') {
1559
			if (!$clear_auth_pipes && substr($rules[$ridx], 0, strlen($cpzone . '_auth')) == $cpzone . '_auth') {
1560
				$ridx += 2;
1561
			} else {
1562
				if (!$dry_run) {
1563
					$rules[$ridx] = false;
1564
				}
1565
				$removed_pipes[] = $ridx;
1566
				$ridx++;
1567
				if (!$dry_run) {
1568
					$rules[$ridx] = false;
1569
				}
1570
				$removed_pipes[] = $ridx;
1571
				$ridx++;
1572 ff8b4019 Renato Botelho
			}
1573
		} else {
1574
			$ridx += 2;
1575 7fb23399 Ermal
		}
1576
	}
1577 ff8b4019 Renato Botelho
1578 b27df7cf Renato Botelho
	if (!$dry_run) {
1579 ff8b4019 Renato Botelho
		file_put_contents("{$g['vardb_path']}/captiveportaldn.rules",
1580
		    serialize($rules));
1581 b27df7cf Renato Botelho
		unlock($cpruleslck);
1582
	}
1583 b2c92623 Renato Botelho
1584 ff8b4019 Renato Botelho
	unset($rules);
1585
1586 b2c92623 Renato Botelho
	return $removed_pipes;
1587 7fb23399 Ermal
}
1588
1589 7b80e743 Augustin-FL
function captiveportal_reserve_ruleno($ruleno) {
1590
	global $g, $cpzone;
1591
1592
	$cpruleslck = lock("captiveportalrulesdn", LOCK_EX);
1593
	if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) {
1594 82e22457 Marcos Mendoza
		$rules = unserialize_data(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules"), array_pad(array(), 64500, false));
1595 7b80e743 Augustin-FL
	} else {
1596
		$rules = array_pad(array(), 64500, false);
1597
	}
1598 7bdf5cd6 plumbeo
	$rules[$ruleno] = $cpzone . '_auth';
1599 7b80e743 Augustin-FL
	$ruleno++;
1600 7bdf5cd6 plumbeo
	$rules[$ruleno] = $cpzone . '_auth';
1601 7b80e743 Augustin-FL
1602
	file_put_contents("{$g['vardb_path']}/captiveportaldn.rules", serialize($rules));
1603
	unlock($cpruleslck);
1604
	unset($rules);
1605
1606
	return $ruleno;
1607
}
1608
1609 32661caf Viktor G
function captiveportal_get_next_dn_ruleno($rule_type = 'default', $rulenos_start = 2000, $rulenos_range_max = 64500, $check_only = false) {
1610 63d6bb4f Marcos Mendoza
	global $g, $cpzone;
1611 6ce61a8f Ermal
1612 aea56408 Ermal
	$cpruleslck = lock("captiveportalrulesdn", LOCK_EX);
1613
	$ruleno = 0;
1614
	if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) {
1615 82e22457 Marcos Mendoza
		$rules = unserialize_data(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules"), []);
1616 1f965b69 Ermal
		$ridx = $rulenos_start;
1617
		while ($ridx < $rulenos_range_max) {
1618 f38b383b Ermal
			if (empty($rules[$ridx])) {
1619 1f965b69 Ermal
				$ruleno = $ridx;
1620 7bdf5cd6 plumbeo
				$rules[$ridx] = $cpzone . '_' . $rule_type;
1621 aea56408 Ermal
				$ridx++;
1622 7bdf5cd6 plumbeo
				$rules[$ridx] = $cpzone . '_' . $rule_type;
1623 1f965b69 Ermal
				break;
1624 21f82ab6 Ermal
			} else {
1625
				$ridx += 2;
1626 aea56408 Ermal
			}
1627
		}
1628
	} else {
1629 fe3693cb Ermal
		$rules = array_pad(array(), $rulenos_range_max, false);
1630 d2c98878 falbertopl
		$ruleno = $rulenos_start;
1631 7bdf5cd6 plumbeo
		$rules[$rulenos_start] = $cpzone . '_' . $rule_type;
1632 1f965b69 Ermal
		$rulenos_start++;
1633 7bdf5cd6 plumbeo
		$rules[$rulenos_start] = $cpzone . '_' . $rule_type;
1634 aea56408 Ermal
	}
1635 32661caf Viktor G
	if (!$check_only) {
1636
		file_put_contents("{$g['vardb_path']}/captiveportaldn.rules", serialize($rules));
1637
	}
1638 aea56408 Ermal
	unlock($cpruleslck);
1639 1f965b69 Ermal
	unset($rules);
1640 aea56408 Ermal
1641
	return $ruleno;
1642
}
1643
1644 43bd2b88 Reid Linnemann
function captiveportal_free_dn_rulenos($rulenos) {
1645 63d6bb4f Marcos Mendoza
	global $g;
1646 43bd2b88 Reid Linnemann
1647
	$cpruleslck = lock("captiveportalrulesdn", LOCK_EX);
1648
	if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) {
1649 82e22457 Marcos Mendoza
		$rules = unserialize_data(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules"), []);
1650 43bd2b88 Reid Linnemann
		foreach ($rulenos as $ruleno) {
1651 32661caf Viktor G
			$rules[$ruleno] = false;
1652 43bd2b88 Reid Linnemann
		}
1653
		file_put_contents("{$g['vardb_path']}/captiveportaldn.rules", serialize($rules));
1654
		unset($rules);
1655
	}
1656
	unlock($cpruleslck);
1657
}
1658
1659
function captiveportal_get_dn_passthru_pipes($mac, $anchor = 'passthrumac') {
1660 63d6bb4f Marcos Mendoza
	global $g, $cpzone, $cpzoneid;
1661 b273dd26 Ermal
1662 65db621a Reid Linnemann
	$cpcfg = config_get_path("captiveportal/{$cpzone}", []);
1663 7c2468c5 Viktor G
	$cpzoneprefix = CPPREFIX . $cpcfg['zoneid'];
1664 1e0b1727 Phil Davis
	if (!isset($cpcfg['enable'])) {
1665 b273dd26 Ermal
		return NULL;
1666 1e0b1727 Phil Davis
	}
1667 aea56408 Ermal
1668 43bd2b88 Reid Linnemann
	$host = str_replace(":", "", $mac);
1669 63d6bb4f Marcos Mendoza
	$pipes = pfSense_pf_cp_get_eth_pipes("{$cpzoneprefix}_{$anchor}/{$host}");
1670 1f965b69 Ermal
1671 43bd2b88 Reid Linnemann
	return $pipes;
1672 920cafaf Scott Ullrich
}
1673
1674 360d815d Scott Ullrich
/**
1675
 * This function will calculate the traffic produced by a client
1676
 * based on its firewall rule
1677
 *
1678
 * Point of view: NAS
1679
 *
1680
 * Input means: from the client
1681
 * Output means: to the client
1682
 *
1683
 */
1684
1685 f4c867e0 Renato Botelho
function getVolume($ip) {
1686 63d6bb4f Marcos Mendoza
	global $g, $cpzone;
1687 360d815d Scott Ullrich
1688 65db621a Reid Linnemann
	$cpzone_config = config_get_path("captiveportal/{$cpzone}", []);
1689 63d6bb4f Marcos Mendoza
	$reverse = isset($cpzone_config['reverseacct']) ? true : false;
1690 5060dea7 Scott Ullrich
	$volume = array();
1691
	// Initialize vars properly, since we don't want NULL vars
1692 b40b4a3e Renato Botelho
	$volume['input_pkts'] = $volume['input_bytes'] = 0;
1693
	$volume['output_pkts'] = $volume['output_bytes'] = 0;
1694 360d815d Scott Ullrich
1695 7c2468c5 Viktor G
	/* no needs to check allowedip */
1696 63d6bb4f Marcos Mendoza
	$cpzoneprefix = CPPREFIX . $cpzone_config['zoneid'];
1697 7c2468c5 Viktor G
	$anchor = $cpzoneprefix . '_auth';
1698 f9f71ad3 Ermal Lu?i
1699 1880e201 Marcos Mendoza
	// It is presumed that a list of arrays is returned, each containing rule direction, and packet and bytes counters.
1700 af044b67 Marcos Mendoza
	$result = pfSense_pf_cp_get_eth_rule_counters("{$anchor}/{$ip}_32");
1701 1880e201 Marcos Mendoza
	if (!empty($result) && is_array($result)) {
1702
		$input_pkts = 0;
1703
		$input_bytes = 0;
1704
		$output_pkts = 0;
1705
		$output_bytes = 0;
1706
1707
		foreach ($result as $rule_counters) {
1708
			switch ($rule_counters[0]) {
1709
				case 1: // rule direction 'PF_IN'
1710 c1bc55a9 Reid Linnemann
					$input_pkts += $rule_counters['input_pkts'];
1711
					$input_bytes += $rule_counters['input_bytes'];
1712 1880e201 Marcos Mendoza
					break;
1713
				case 2: // rule direction 'PF_OUT'
1714 c1bc55a9 Reid Linnemann
					$output_pkts += $rule_counters['output_pkts'];
1715
					$output_bytes += $rule_counters['output_bytes'];
1716 1880e201 Marcos Mendoza
					break;
1717
				case 0: // rule direction 'PF_INOUT'
1718 c1bc55a9 Reid Linnemann
					$input_pkts += $rule_counters['input_pkts'];
1719
					$input_bytes += $rule_counters['input_bytes'];
1720
					$output_pkts += $rule_counters['output_pkts'];
1721
					$output_bytes += $rule_counters['output_bytes'];
1722 1880e201 Marcos Mendoza
					break;
1723
				default:
1724
					break;
1725
			}
1726
		}
1727 978ea085 linnemannr
1728 f48abba2 Michael Newton
		if ($reverse) {
1729 978ea085 linnemannr
			$volume['output_pkts'] = $input_pkts;
1730
			$volume['output_bytes'] = $input_bytes;
1731
			$volume['input_pkts'] = $output_pkts;
1732
			$volume['input_bytes'] = $output_bytes;
1733 b40b4a3e Renato Botelho
		} else {
1734 978ea085 linnemannr
			$volume['output_pkts'] = $output_pkts;
1735
			$volume['output_bytes'] = $output_bytes;
1736
			$volume['input_pkts'] = $input_pkts;
1737
			$volume['input_bytes'] = $input_bytes;
1738 f48abba2 Michael Newton
		}
1739 5060dea7 Scott Ullrich
	}
1740 360d815d Scott Ullrich
1741 5060dea7 Scott Ullrich
	return $volume;
1742 360d815d Scott Ullrich
}
1743
1744 f8b11310 Ermal Lu?i
function portal_ip_from_client_ip($cliip) {
1745 63d6bb4f Marcos Mendoza
	global $cpzone;
1746 f8b11310 Ermal Lu?i
1747 45bef774 bcyrill
	$isipv6 = is_ipaddrv6($cliip);
1748 63d6bb4f Marcos Mendoza
	$interfaces = explode(",", config_get_path("captiveportal/{$cpzone}/interface", []));
1749 f8b11310 Ermal Lu?i
	foreach ($interfaces as $cpif) {
1750 45bef774 bcyrill
		if ($isipv6) {
1751
			$ip = get_interface_ipv6($cpif);
1752
			$sn = get_interface_subnetv6($cpif);
1753
		} else {
1754
			$ip = get_interface_ip($cpif);
1755
			$sn = get_interface_subnet($cpif);
1756
		}
1757 1e0b1727 Phil Davis
		if (ip_in_subnet($cliip, "{$ip}/{$sn}")) {
1758 f8b11310 Ermal Lu?i
			return $ip;
1759 1e0b1727 Phil Davis
		}
1760 f8b11310 Ermal Lu?i
	}
1761
1762 07b780c8 Viktor G
	$route = route_get($cliip, 'inet', true);
1763 c428cdf4 Renato Botelho do Couto
	if (empty($route)) {
1764
		return false;
1765
	}
1766
1767
	$iface = $route[0]['interface-name'];
1768 f86fa91c jim-p
	if (!empty($iface)) {
1769 c428cdf4 Renato Botelho do Couto
		$ip = ($isipv6) ? find_interface_ipv6($iface)
1770
		    : find_interface_ip($iface);
1771 1e0b1727 Phil Davis
		if (is_ipaddr($ip)) {
1772 f86fa91c jim-p
			return $ip;
1773 1e0b1727 Phil Davis
		}
1774 f86fa91c jim-p
	}
1775
1776 cc125e13 Chris Buechler
	// doesn't match up to any particular interface
1777 1e0b1727 Phil Davis
	// so let's set the portal IP to what PHP says
1778
	// the server IP issuing the request is.
1779
	// allows same behavior as 1.2.x where IP isn't
1780 cc125e13 Chris Buechler
	// in the subnet of any CP interface (static routes, etc.)
1781
	// rather than forcing to DNS hostname resolution
1782
	$ip = $_SERVER['SERVER_ADDR'];
1783 1e0b1727 Phil Davis
	if (is_ipaddr($ip)) {
1784 cc125e13 Chris Buechler
		return $ip;
1785 1e0b1727 Phil Davis
	}
1786 cc125e13 Chris Buechler
1787 f8b11310 Ermal Lu?i
	return false;
1788
}
1789
1790 de132ae3 bcyrill
function portal_hostname_from_client_ip($cliip) {
1791 63d6bb4f Marcos Mendoza
	global $cpzone;
1792 de132ae3 bcyrill
1793 65db621a Reid Linnemann
	$cpcfg = config_get_path("captiveportal/{$cpzone}", []);
1794 de132ae3 bcyrill
1795
	if (isset($cpcfg['httpslogin'])) {
1796 4320083f Renato Botelho
		$listenporthttps = $cpcfg['listenporthttps'] ? $cpcfg['listenporthttps'] : ($cpcfg['zoneid'] + 8001);
1797 de132ae3 bcyrill
		$ourhostname = $cpcfg['httpsname'];
1798 1e0b1727 Phil Davis
1799
		if ($listenporthttps != 443) {
1800 de132ae3 bcyrill
			$ourhostname .= ":" . $listenporthttps;
1801 1e0b1727 Phil Davis
		}
1802 de132ae3 bcyrill
	} else {
1803 6c07db48 Phil Davis
		$listenporthttp = $cpcfg['listenporthttp'] ? $cpcfg['listenporthttp'] : ($cpcfg['zoneid'] + 8000);
1804 de132ae3 bcyrill
		$ifip = portal_ip_from_client_ip($cliip);
1805 1e0b1727 Phil Davis
		if (!$ifip) {
1806 63d6bb4f Marcos Mendoza
			$ourhostname = config_get_path('system/hostname') . config_get_path('system/domain');
1807 1e0b1727 Phil Davis
		} else {
1808 de132ae3 bcyrill
			$ourhostname = (is_ipaddrv6($ifip)) ? "[{$ifip}]" : "{$ifip}";
1809 1e0b1727 Phil Davis
		}
1810
1811
		if ($listenporthttp != 80) {
1812 de132ae3 bcyrill
			$ourhostname .= ":" . $listenporthttp;
1813 1e0b1727 Phil Davis
		}
1814 de132ae3 bcyrill
	}
1815 1e0b1727 Phil Davis
1816 d5ac388b bcyrill
	return $ourhostname;
1817 de132ae3 bcyrill
}
1818
1819 ac631bba lgcosta
/* functions move from index.php */
1820
1821 53ea4b8b Viktor G
function portal_reply_page($redirurl, $type = null, $message = null, $clientmac = null, $clientip = null, $username = null, $password = null, $voucher = null) {
1822 63d6bb4f Marcos Mendoza
	global $g, $cpzone;
1823 ac631bba lgcosta
1824 65db621a Reid Linnemann
	$cpcfg = config_get_path("captiveportal/{$cpzone}", []);
1825 de9ba32b jim-p
	$ourhostname = portal_hostname_from_client_ip($clientip);
1826
	$protocol = (isset($cpcfg['httpslogin'])) ? 'https://' : 'http://';
1827
	$portal_url = "{$protocol}{$ourhostname}/index.php?zone={$cpzone}";
1828
1829 ac631bba lgcosta
	/* Get captive portal layout */
1830
	if ($type == "redir") {
1831 9fc1648e jim-p
		$redirurl = is_URL($redirurl, true) ? $redirurl : $portal_url;
1832 ac631bba lgcosta
		header("Location: {$redirurl}");
1833
		return;
1834 1e0b1727 Phil Davis
	} else if ($type == "login") {
1835 b4792bf8 Ermal
		$htmltext = get_include_contents("{$g['varetc_path']}/captiveportal_{$cpzone}.html");
1836 1e0b1727 Phil Davis
	} else {
1837 b4792bf8 Ermal
		$htmltext = get_include_contents("{$g['varetc_path']}/captiveportal-{$cpzone}-error.html");
1838 1e0b1727 Phil Davis
	}
1839 b4792bf8 Ermal
1840 ac631bba lgcosta
	/* substitute the PORTAL_REDIRURL variable */
1841 de132ae3 bcyrill
	if ($cpcfg['preauthurl']) {
1842
		$htmltext = str_replace("\$PORTAL_REDIRURL\$", "{$cpcfg['preauthurl']}", $htmltext);
1843
		$htmltext = str_replace("#PORTAL_REDIRURL#", "{$cpcfg['preauthurl']}", $htmltext);
1844 ac631bba lgcosta
	}
1845
1846
	/* substitute other variables */
1847 de9ba32b jim-p
	$htmltext = str_replace("\$PORTAL_ACTION\$", $portal_url, $htmltext);
1848
	$htmltext = str_replace("#PORTAL_ACTION#", $portal_url, $htmltext);
1849 ac631bba lgcosta
1850 b4792bf8 Ermal
	$htmltext = str_replace("\$PORTAL_ZONE\$", htmlspecialchars($cpzone), $htmltext);
1851 ac631bba lgcosta
	$htmltext = str_replace("\$PORTAL_REDIRURL\$", htmlspecialchars($redirurl), $htmltext);
1852
	$htmltext = str_replace("\$PORTAL_MESSAGE\$", htmlspecialchars($message), $htmltext);
1853
	$htmltext = str_replace("\$CLIENT_MAC\$", htmlspecialchars($clientmac), $htmltext);
1854
	$htmltext = str_replace("\$CLIENT_IP\$", htmlspecialchars($clientip), $htmltext);
1855
1856 1e0b1727 Phil Davis
	// Special handling case for captive portal master page so that it can be ran
1857 ac631bba lgcosta
	// through the PHP interpreter using the include method above.  We convert the
1858
	// $VARIABLE$ case to #VARIABLE# in /etc/inc/captiveportal.inc before writing out.
1859 b4792bf8 Ermal
	$htmltext = str_replace("#PORTAL_ZONE#", htmlspecialchars($cpzone), $htmltext);
1860 ac631bba lgcosta
	$htmltext = str_replace("#PORTAL_REDIRURL#", htmlspecialchars($redirurl), $htmltext);
1861
	$htmltext = str_replace("#PORTAL_MESSAGE#", htmlspecialchars($message), $htmltext);
1862
	$htmltext = str_replace("#CLIENT_MAC#", htmlspecialchars($clientmac), $htmltext);
1863
	$htmltext = str_replace("#CLIENT_IP#", htmlspecialchars($clientip), $htmltext);
1864
	$htmltext = str_replace("#USERNAME#", htmlspecialchars($username), $htmltext);
1865
	$htmltext = str_replace("#PASSWORD#", htmlspecialchars($password), $htmltext);
1866 53ea4b8b Viktor G
	$htmltext = str_replace("#VOUCHER#", htmlspecialchars($voucher), $htmltext);
1867 ac631bba lgcosta
1868 87e7fdea bcyrill
	echo $htmltext;
1869 ac631bba lgcosta
}
1870
1871 aec0f2fd Ermal
function captiveportal_reapply_attributes($cpentry, $attributes) {
1872 63d6bb4f Marcos Mendoza
	global $cpzone, $g;
1873 87e7fdea bcyrill
1874 65db621a Reid Linnemann
	$cpzone_config = config_get_path("captiveportal/{$cpzone}", []);
1875 63d6bb4f Marcos Mendoza
	if (isset($cpzone_config['peruserbw'])) {
1876
		$dwfaultbw_up = !empty($cpzone_config['bwdefaultup']) ? $cpzone_config['bwdefaultup'] : 0;
1877
		$dwfaultbw_down = !empty($cpzone_config['bwdefaultdn']) ? $cpzone_config['bwdefaultdn'] : 0;
1878 1e0b1727 Phil Davis
	} else {
1879 ce90c89a Ermal LUÇI
		$dwfaultbw_up = $dwfaultbw_down = 0;
1880 1e0b1727 Phil Davis
	}
1881 7c4e07c6 jim-p
	/* pipe throughputs must always be an integer, enforce that restriction again here. */
1882 63d6bb4f Marcos Mendoza
	if (isset($cpzone_config['radiusperuserbw'])) {
1883 f87ddb3b plumbeo
		$bw_up = round(!empty($attributes['bw_up']) ? intval($attributes['bw_up'])/1000 : $dwfaultbw_up, 0);
1884
		$bw_down = round(!empty($attributes['bw_down']) ? intval($attributes['bw_down'])/1000 : $dwfaultbw_down, 0);
1885
	} else {
1886
		$bw_up = round($dwfaultbw_up,0);
1887
		$bw_down = round($dwfaultbw_down,0);
1888
	}
1889
1890 5705c60a Renato Botelho
	$bw_up_pipeno = $cpentry[1];
1891
	$bw_down_pipeno = $cpentry[1]+1;
1892 aec0f2fd Ermal
1893 b0a29e82 Augustin-FL
	if ($cpentry['bw_up'] !== $bw_up) {
1894 7c2468c5 Viktor G
		$_gb = mwexec("/sbin/dnctl pipe {$bw_up_pipeno} config bw {$bw_up}Kbit/s queue 100 buckets 16");
1895 b0a29e82 Augustin-FL
		captiveportal_update_entry($cpentry['sessionid'], $bw_up, 'bw_up');
1896
	}
1897
	if ($cpentry['bw_down'] !== $bw_down) {
1898 7c2468c5 Viktor G
		$_gb = mwexec("/sbin/dnctl pipe {$bw_down_pipeno} config bw {$bw_down}Kbit/s queue 100 buckets 16");
1899 b0a29e82 Augustin-FL
		captiveportal_update_entry($cpentry['sessionid'], $bw_down, 'bw_down');
1900
	}
1901 87e7fdea bcyrill
	unset($bw_up_pipeno, $bw_down_pipeno, $bw_up, $bw_down);
1902 aec0f2fd Ermal
}
1903
1904 b0a29e82 Augustin-FL
function captiveportal_update_entry($sessionid, $new_value, $field_to_update) {
1905
	global $cpzone;
1906
1907
	if (!intval($new_value)) {
1908
		$new_value = "'{$new_value}'";
1909
	}
1910
	captiveportal_write_db("UPDATE captiveportal SET {$field_to_update} = {$new_value} WHERE sessionid = '{$sessionid}'");
1911
}
1912
1913 24600471 Augustin-FL
function portal_allow($clientip, $clientmac, $username, $password = null, $redirurl = null,
1914
    $attributes = null, $pipeno = null, $authmethod = null, $context = 'first', $existing_sessionid = null) {
1915 63d6bb4f Marcos Mendoza
	global $g, $cpzone;
1916 ac631bba lgcosta
1917
	// Ensure we create an array if we are missing attributes
1918 1e0b1727 Phil Davis
	if (!is_array($attributes)) {
1919 ac631bba lgcosta
		$attributes = array();
1920 1e0b1727 Phil Davis
	}
1921 ac631bba lgcosta
1922 26ee5aaf Ermal
	unset($sessionid);
1923 ac631bba lgcosta
1924 006802ab Ermal
	/* Do not allow concurrent login execution. */
1925 b4792bf8 Ermal
	$cpdblck = lock("captiveportaldb{$cpzone}", LOCK_EX);
1926 006802ab Ermal
1927 1e0b1727 Phil Davis
	if ($attributes['voucher']) {
1928 ac631bba lgcosta
		$remaining_time = $attributes['session_timeout'];
1929 f3f98e97 Phil Davis
		$authmethod = "voucher"; // Set RADIUS-Attribute to Voucher to prevent ReAuth-Request for Vouchers Bug: #2155
1930 eb43c5b1 Augustin FL
		$context = "voucher";
1931 1e0b1727 Phil Davis
	}
1932 ac631bba lgcosta
1933
	$writecfg = false;
1934 f3f98e97 Phil Davis
	/* If both "Add MAC addresses of connected users as pass-through MAC" and "Disable concurrent logins" are checked,
1935
	then we need to check if the user was already authenticated using another MAC Address, and if so remove the previous Pass-Through MAC. */
1936 65db621a Reid Linnemann
	$cpzone_config = config_get_path("captiveportal/{$cpzone}", []);
1937 63d6bb4f Marcos Mendoza
	if ((isset($cpzone_config['noconcurrentlogins'])) && ($cpzone_config['noconcurrentlogins'] == 'last') && ($username != 'unauthenticated') && isset($cpzone_config['passthrumacadd'])) {
1938 517b893e Renato Botelho
		$mac = captiveportal_passthrumac_findbyname($username);
1939 a8cf8a30 Augustin-FL
		if (!empty($mac)) {
1940 4b9165e5 Marcos Mendoza
			foreach (config_get_path("captiveportal/{$cpzone}/passthrumac", []) as $idx => $macent) {
1941 517b893e Renato Botelho
				if ($macent['mac'] != $mac['mac']) {
1942
					continue;
1943 ac631bba lgcosta
				}
1944 517b893e Renato Botelho
1945 7c2468c5 Viktor G
				captiveportal_passthrumac_delete_entry($macent);
1946 7e3ea4a8 Christian McDonald
				config_del_path("captiveportal/{$cpzone}/passthrumac/{$idx}");
1947 ac631bba lgcosta
			}
1948
		}
1949
	}
1950
1951 26ee5aaf Ermal
	/* read in client database */
1952
	$query = "WHERE ip = '{$clientip}'";
1953 0a02fc5e Phil Davis
	$tmpusername = SQLite3::escapeString(strtolower($username));
1954 63d6bb4f Marcos Mendoza
	if (config_path_enabled("captiveportal/{$cpzone}/noconcurrentlogins")) {
1955 26ee5aaf Ermal
		$query .= " OR (username != 'unauthenticated' AND lower(username) = '{$tmpusername}')";
1956 1e0b1727 Phil Davis
	}
1957 26ee5aaf Ermal
	$cpdb = captiveportal_read_db($query);
1958
1959 ebc0e4b6 Ermal
	/* Snapshot the timestamp */
1960 b09c2d86 Ermal
	$allow_time = time();
1961 24600471 Augustin-FL
1962
	if ($existing_sessionid !== null) {
1963 4864d7f6 Josh Soref
		// If we received this connection through XMLRPC sync :
1964 24600471 Augustin-FL
		// we fetch allow_time from the info given by the other node
1965
		$allow_time = $attributes['allow_time'];
1966
	}
1967 26ee5aaf Ermal
	$unsetindexes = array();
1968
1969 65db621a Reid Linnemann
	$cpzone_config = config_get_path("captiveportal/{$cpzone}", []);
1970 26ee5aaf Ermal
	foreach ($cpdb as $cpentry) {
1971 ac631bba lgcosta
		/* on the same ip */
1972 5705c60a Renato Botelho
		if ($cpentry[2] == $clientip) {
1973 63d6bb4f Marcos Mendoza
			if (isset($cpzone_config['nomacfilter']) || $cpentry[3] == $clientmac) {
1974 086cf944 Phil Davis
				captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CONCURRENT LOGIN - REUSING OLD SESSION");
1975 1e0b1727 Phil Davis
			} else {
1976 086cf944 Phil Davis
				captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CONCURRENT LOGIN - REUSING IP {$cpentry[2]} WITH DIFFERENT MAC ADDRESS {$cpentry[3]}");
1977 1e0b1727 Phil Davis
			}
1978 5705c60a Renato Botelho
			$sessionid = $cpentry[5];
1979 ac631bba lgcosta
			break;
1980 1e0b1727 Phil Davis
		} elseif (($attributes['voucher']) && ($username != 'unauthenticated') && ($cpentry[4] == $username)) {
1981
			// user logged in with an active voucher. Check for how long and calculate
1982 ac631bba lgcosta
			// how much time we can give him (voucher credit - used time)
1983 5705c60a Renato Botelho
			$remaining_time = $cpentry[0] + $cpentry[7] - $allow_time;
1984 1e0b1727 Phil Davis
			if ($remaining_time < 0) { // just in case.
1985 ac631bba lgcosta
				$remaining_time = 0;
1986 1e0b1727 Phil Davis
			}
1987 ac631bba lgcosta
1988 e79a872f Christian McDonald
			/* This user was already logged in so we disconnect the old one, or
1989 5ed493d8 Viktor G
			keep the old one, refusing the new login, or
1990
			allow the login */
1991
1992 63d6bb4f Marcos Mendoza
			if (!isset($cpzone_config['noconcurrentlogins'])) {
1993 e79a872f Christian McDonald
				captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "config['captiveportal'][$cpzone]['noconcurrentlogins'] 2 does not exists = NOT set");
1994 5ed493d8 Viktor G
			} else {
1995 e79a872f Christian McDonald
				captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "config['captiveportal'][$cpzone]['noconcurrentlogins'] 2 exists = set");
1996 5ed493d8 Viktor G
			}
1997
1998 63d6bb4f Marcos Mendoza
			if ($cpzone_config['noconcurrentlogins'] == "last") {
1999 5ed493d8 Viktor G
				captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "Found last");
2000
			} else {
2001
				captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "Found NOT last");
2002
			}
2003 e79a872f Christian McDonald
2004 63d6bb4f Marcos Mendoza
			if (!isset($cpzone_config['noconcurrentlogins'])) {
2005 5ed493d8 Viktor G
				/* 'noconcurrentlogins' not set : accept login 'username' creating multiple sessions. */
2006
				captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "config['captiveportal'][$cpzone]['noconcurrentlogins'] 3 does not exists = NOT set");
2007 e79a872f Christian McDonald
				captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CONCURRENT LOGIN - NOT TERMINATING EXISTING SESSION(S)");
2008 63d6bb4f Marcos Mendoza
			} elseif ($cpzone_config['noconcurrentlogins'] == "last") {
2009 5ed493d8 Viktor G
				/* Classic situation : accept the new login, disconnect the old - present - connection */
2010 63d6bb4f Marcos Mendoza
				if (isset($cpzone_config['noconcurrentlogins'])) {
2011 e79a872f Christian McDonald
					captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "config['captiveportal'][$cpzone]['noconcurrentlogins'] 4 exists = set");
2012 5ed493d8 Viktor G
				} else {
2013 e79a872f Christian McDonald
					captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "config['captiveportal'][$cpzone]['noconcurrentlogins'] 4 does not exists = NOT set");
2014 5ed493d8 Viktor G
				}
2015 e79a872f Christian McDonald
2016 eb43c5b1 Augustin FL
				captiveportal_disconnect($cpentry, 13);
2017 086cf944 Phil Davis
				captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CONCURRENT LOGIN - TERMINATING OLD SESSION");
2018 5705c60a Renato Botelho
				$unsetindexes[] = $cpentry[5];
2019 ac631bba lgcosta
				break;
2020 5ed493d8 Viktor G
			} else {
2021
				/* Implicit 'first' : refuse the new login - 'username' is already logged in */
2022
				captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CONCURRENT VOUCHER LOGIN - NOT ALLOWED KEEPING OLD SESSION ");
2023
				unlock($cpdblck);
2024
				return 2;
2025
			}
2026 63d6bb4f Marcos Mendoza
		} elseif ((isset($cpzone_config['noconcurrentlogins'])) && ($username != 'unauthenticated')) {
2027
			if ($cpzone_config['noconcurrentlogins'] == "last") {
2028 5ed493d8 Viktor G
				/* on the same username */
2029
				if (strcasecmp($cpentry[4], $username) == 0) {
2030
					/* This user was already logged in so we disconnect the old one */
2031
					captiveportal_disconnect($cpentry, 13);
2032
					captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CONCURRENT USER LOGIN - TERMINATING OLD SESSION");
2033
					$unsetindexes[] = $cpentry[5];
2034
					break;
2035
				}
2036
			} else {
2037
				/* Implicit 'first' : refuse the new login - 'username' is already logged in */
2038
				captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CONCURRENT USER LOGIN - NOT ALLOWED KEEPING OLD SESSION ");
2039
				unlock($cpdblck);
2040 e79a872f Christian McDonald
				return 2;
2041 ac631bba lgcosta
			}
2042
		}
2043
	}
2044 f32eae2d Ermal
	unset($cpdb);
2045 ac631bba lgcosta
2046 1e0b1727 Phil Davis
	if (!empty($unsetindexes)) {
2047 26ee5aaf Ermal
		captiveportal_remove_entries($unsetindexes);
2048 1e0b1727 Phil Davis
	}
2049 26ee5aaf Ermal
2050 1e0b1727 Phil Davis
	if ($attributes['voucher'] && $remaining_time <= 0) {
2051 ac631bba lgcosta
		return 0;       // voucher already used and no time left
2052 1e0b1727 Phil Davis
	}
2053 ac631bba lgcosta
2054
	if (!isset($sessionid)) {
2055 24600471 Augustin-FL
		if ($existing_sessionid != null) { // existing_sessionid should only be set during XMLRPC sync
2056
			$sessionid = $existing_sessionid;
2057
		} else {
2058
			/* generate unique session ID */
2059
			$tod = gettimeofday();
2060
			$sessionid = substr(md5(mt_rand() . $tod['sec'] . $tod['usec'] . $clientip . $clientmac), 0, 16);
2061
		}
2062 ac631bba lgcosta
2063 65db621a Reid Linnemann
		$cpzone_config = config_get_path("captiveportal/{$cpzone}", []);
2064 63d6bb4f Marcos Mendoza
		if (isset($cpzone_config['peruserbw'])) {
2065
			$dwfaultbw_up = !empty($cpzone_config['bwdefaultup']) ? $cpzone_config['bwdefaultup'] : 0;
2066
			$dwfaultbw_down = !empty($cpzone_config['bwdefaultdn']) ? $cpzone_config['bwdefaultdn'] : 0;
2067 aa1c6774 Jonatan Ramos
		} else {
2068
			$dwfaultbw_up = $dwfaultbw_down = 0;
2069
		}
2070 7c4e07c6 jim-p
		/* pipe throughputs must always be an integer, enforce that restriction again here. */
2071 63d6bb4f Marcos Mendoza
		if (isset($cpzone_config['radiusperuserbw'])) {
2072 f87ddb3b plumbeo
			$bw_up = round(!empty($attributes['bw_up']) ? intval($attributes['bw_up'])/1000 : $dwfaultbw_up, 0);
2073
			$bw_down = round(!empty($attributes['bw_down']) ? intval($attributes['bw_down'])/1000 : $dwfaultbw_down, 0);
2074
		} else {
2075
			$bw_up = round($dwfaultbw_up,0);
2076
			$bw_down = round($dwfaultbw_down,0);
2077
		}
2078 aa1c6774 Jonatan Ramos
2079 7c2468c5 Viktor G
		$mac = array();
2080
		$mac['action'] = 'pass';
2081
		$mac['ip'] = $clientip;
2082
		$mac['username'] = $username;
2083 f9f3523e Reid Linnemann
		if (!empty($bw_up)) {
2084
			$mac['bw_up'] = $bw_up;
2085
		}
2086
		if (!empty($bw_down)) {
2087
			$mac['bw_down'] = $bw_down;
2088
		}
2089 63d6bb4f Marcos Mendoza
		if (isset($cpzone_config['passthrumacadd'])) {
2090 ac631bba lgcosta
			$mac['mac'] = $clientmac;
2091 20588aac Augustin-FL
			if ($attributes['voucher']) {
2092
				$mac['logintype'] = "voucher";
2093 522f1cc7 Ermal
			}
2094 cc229ee9 Phil Davis
			if ($username == "unauthenticated") {
2095 6c07db48 Phil Davis
				$mac['descr'] = "Auto-added";
2096 20588aac Augustin-FL
			} else if ($authmethod == "voucher") {
2097
				$mac['descr'] = "Auto-added for voucher {$username}";
2098 1e0b1727 Phil Davis
			} else {
2099 6c07db48 Phil Davis
				$mac['descr'] = "Auto-added for user {$username}";
2100 1e0b1727 Phil Davis
			}
2101 63d6bb4f Marcos Mendoza
			config_init_path("captiveportal/{$cpzone}/passthrumac");
2102 a464eaf7 Stephen Jones
			//check for mac duplicates before adding it to config.
2103
			$mac_duplicate = false;
2104 4b9165e5 Marcos Mendoza
			foreach(config_get_path("captiveportal/{$cpzone}/passthrumac", []) as $mac_check){
2105 a464eaf7 Stephen Jones
				if($mac_check['mac'] == $mac['mac']){
2106
					$mac_duplicate = true;
2107
				}
2108
			}
2109
			if(!$mac_duplicate){
2110 63d6bb4f Marcos Mendoza
				config_set_path("captiveportal/{$cpzone}/passthrumac/", $mac);
2111 a464eaf7 Stephen Jones
			}
2112 006802ab Ermal
			unlock($cpdblck);
2113 65530037 Reid Linnemann
			captiveportal_ether_configure_entry($mac, 'passthrumac', true);
2114 ac631bba lgcosta
			$writecfg = true;
2115
		} else {
2116 aea56408 Ermal
			/* See if a pipeno is passed, if not start sessions because this means there isn't one atm */
2117 8ba70cfc jim-p
			if (is_null($pipeno)) {
2118
				$pipeno = captiveportal_get_next_dn_ruleno('auth');
2119 1e0b1727 Phil Davis
			}
2120 aea56408 Ermal
			/* if the pool is empty, return appropriate message and exit */
2121 8ba70cfc jim-p
			if (is_null($pipeno)) {
2122 24600471 Augustin-FL
				captiveportal_syslog("Zone: {$cpzone} - WARNING!  Captive portal has reached maximum login capacity");
2123 aea56408 Ermal
				unlock($cpdblck);
2124 24600471 Augustin-FL
				return false;
2125 aea56408 Ermal
			}
2126
2127 8ba70cfc jim-p
			$mac['pipeno'] = $pipeno;
2128 7c2468c5 Viktor G
			$mac['ip'] = $clientip;
2129 63d6bb4f Marcos Mendoza
			if (!isset($cpzone_config['nomacfilter'])) {
2130 7c2468c5 Viktor G
				$mac['mac'] = $clientmac;
2131 3c4fcd5b Renato Botelho
			}
2132 65530037 Reid Linnemann
			captiveportal_ether_configure_entry($mac, 'auth', true);
2133 ac631bba lgcosta
2134 1e0b1727 Phil Davis
			if ($attributes['voucher']) {
2135 ac631bba lgcosta
				$attributes['session_timeout'] = $remaining_time;
2136 1e0b1727 Phil Davis
			}
2137
2138 1974c2d6 bcyrill
			/* handle empty attributes */
2139
			$session_timeout = (!empty($attributes['session_timeout'])) ? $attributes['session_timeout'] : 'NULL';
2140
			$idle_timeout = (!empty($attributes['idle_timeout'])) ? $attributes['idle_timeout'] : 'NULL';
2141
			$session_terminate_time = (!empty($attributes['session_terminate_time'])) ? $attributes['session_terminate_time'] : 'NULL';
2142 338c0941 Ermal
			$interim_interval = (!empty($attributes['interim_interval'])) ? $attributes['interim_interval'] : 'NULL';
2143 f3e403d5 plumbeo
			$traffic_quota = (!empty($attributes['maxbytes'])) ? $attributes['maxbytes'] : 'NULL';
2144 1974c2d6 bcyrill
2145
			/* escape username */
2146 5cf91315 Renato Botelho
			$safe_username = SQLite3::escapeString($username);
2147 ac631bba lgcosta
2148
			/* encode password in Base64 just in case it contains commas */
2149 63d6bb4f Marcos Mendoza
			$bpassword = (isset($cpzone_config['reauthenticate'])) ? base64_encode($password) : '';
2150 b0a29e82 Augustin-FL
			$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) ";
2151 1974c2d6 bcyrill
			$insertquery .= "VALUES ({$allow_time}, {$pipeno}, '{$clientip}', '{$clientmac}', '{$safe_username}', '{$sessionid}', '{$bpassword}', ";
2152 b0a29e82 Augustin-FL
			$insertquery .= "{$session_timeout}, {$idle_timeout}, {$session_terminate_time}, {$interim_interval}, {$traffic_quota}, {$bw_up}, {$bw_down}, '{$authmethod}', '{$context}')";
2153 ac631bba lgcosta
2154 26ee5aaf Ermal
			/* store information to database */
2155
			captiveportal_write_db($insertquery);
2156 006802ab Ermal
			unlock($cpdblck);
2157 1f965b69 Ermal
			unset($insertquery, $bpassword);
2158 006802ab Ermal
2159 63d6bb4f Marcos Mendoza
			$radacct = isset($cpzone_config['radacct_enable']) ? true : false;
2160 e42ea151 Augustin FL
			if ($authmethod === 'radius' && $radacct) {
2161
				captiveportal_send_server_accounting('start',
2162
					$pipeno, // ruleno
2163
					$username, // username
2164
					$clientip, // clientip
2165
					$clientmac, // clientmac
2166
					$sessionid, // sessionid
2167
					time());  // start time
2168 ac631bba lgcosta
			}
2169 24600471 Augustin-FL
			if (captiveportal_xmlrpc_sync_get_details($syncip, $syncport, $syncuser, $syncpass, isset($existing_sessionid))) {
2170
				// $existing_sessionid prevent carp loop : only forward
2171
				// the connection to the other node if we generated the sessionid by ourselves
2172
				$rpc_client = new pfsense_xmlrpc_client();
2173
				$rpc_client->setConnectionData($syncip, $syncport, $syncuser, $syncpass);
2174
				$rpc_client->set_noticefile("CaptivePortalUserSync");
2175
				$arguments = array(
2176
					'clientip' => $clientip,
2177
					'clientmac' => $clientmac,
2178
					'username' => $username,
2179
					'password' => $password,
2180
					'attributes' => $attributes,
2181
					'allow_time' => $allow_time,
2182
					'authmethod' => $authmethod,
2183
					'context' => $context,
2184
					'sessionid' => $sessionid
2185
				);
2186
2187
				$rpc_client->xmlrpc_method('captive_portal_sync',
2188
					array(
2189
						'op' => 'connect_user',
2190
						'zone' => $cpzone,
2191
						'user' => base64_encode(serialize($arguments))
2192
					)
2193
				);
2194
			}
2195 ac631bba lgcosta
		}
2196 a7ee038b Ermal
	} else {
2197 eb43c5b1 Augustin FL
		/* NOTE: #3062-11 If the pipeno has been allocated free it to not DoS the CP */
2198 1e0b1727 Phil Davis
		if (!is_null($pipeno)) {
2199 32661caf Viktor G
			captiveportal_free_dn_rulenos(array($pipeno, $pipeno+1));
2200 1e0b1727 Phil Davis
		}
2201 a7ee038b Ermal
2202 006802ab Ermal
		unlock($cpdblck);
2203 a7ee038b Ermal
	}
2204 ac631bba lgcosta
2205 1e0b1727 Phil Davis
	if ($writecfg == true) {
2206 b05c860c doktornotor
		write_config(gettext("Captive Portal allowed users configuration changed"));
2207 1e0b1727 Phil Davis
	}
2208 ac631bba lgcosta
2209 24600471 Augustin-FL
	if ($existing_sessionid !== null) {
2210
		if (!empty($sessionid)) {
2211
			return $sessionid;
2212
		} else {
2213
			return false;
2214
		}
2215
	}
2216 65db621a Reid Linnemann
	$cpzone_config = config_get_path("captiveportal/{$cpzone}", []);
2217 ac631bba lgcosta
	/* redirect user to desired destination */
2218 9fc1648e jim-p
	if (is_URL($attributes['url_redirection'], true)) {
2219 ac631bba lgcosta
		$my_redirurl = $attributes['url_redirection'];
2220 63d6bb4f Marcos Mendoza
	} else if (is_URL($cpzone_config['redirurl'], true)) {
2221
		$my_redirurl = $cpzone_config['redirurl'];
2222 9fc1648e jim-p
	} else if (is_URL($redirurl, true)) {
2223 de9ba32b jim-p
		$my_redirurl = $redirurl;
2224 1e0b1727 Phil Davis
	}
2225 ac631bba lgcosta
2226 63d6bb4f Marcos Mendoza
	if (isset($cpzone_config['logoutwin_enable']) && !isset($cpzone_config['passthrumacadd'])) {
2227 de132ae3 bcyrill
		$ourhostname = portal_hostname_from_client_ip($clientip);
2228 63d6bb4f Marcos Mendoza
		$protocol = (isset($cpzone_config['httpslogin'])) ? 'https://' : 'http://';
2229 de132ae3 bcyrill
		$logouturl = "{$protocol}{$ourhostname}/";
2230 ac631bba lgcosta
2231 1e0b1727 Phil Davis
		if (isset($attributes['reply_message'])) {
2232 ac631bba lgcosta
			$message = $attributes['reply_message'];
2233 1e0b1727 Phil Davis
		} else {
2234 ac631bba lgcosta
			$message = 0;
2235 1e0b1727 Phil Davis
		}
2236 ac631bba lgcosta
2237 86573bb9 Phil Davis
		include_once("{$g['varetc_path']}/captiveportal-{$cpzone}-logout.html");
2238 ac631bba lgcosta
2239
	} else {
2240 fb0c2bd6 Ermal
		portal_reply_page($my_redirurl, "redir", "Just redirect the user.");
2241 ac631bba lgcosta
	}
2242
2243
	return $sessionid;
2244
}
2245
2246
2247
/*
2248
 * Used for when pass-through credits are enabled.
2249
 * Returns true when there was at least one free login to deduct for the MAC.
2250
 * Expired entries are removed as they are seen.
2251
 * Active entries are updated according to the configuration.
2252
 */
2253
function portal_consume_passthrough_credit($clientmac) {
2254 63d6bb4f Marcos Mendoza
	global $cpzone;
2255 ac631bba lgcosta
2256 65db621a Reid Linnemann
	$cpzone_config = config_get_path("captiveportal/{$cpzone}", []);
2257 63d6bb4f Marcos Mendoza
	if (!empty($cpzone_config['freelogins_count']) && is_numeric($cpzone_config['freelogins_count'])) {
2258
		$freeloginscount = $cpzone_config['freelogins_count'];
2259 1e0b1727 Phil Davis
	} else {
2260 ac631bba lgcosta
		return false;
2261 1e0b1727 Phil Davis
	}
2262 ac631bba lgcosta
2263 63d6bb4f Marcos Mendoza
	if (!empty($cpzone_config['freelogins_resettimeout']) && is_numeric($cpzone_config['freelogins_resettimeout'])) {
2264
		$resettimeout = $cpzone_config['freelogins_resettimeout'];
2265 1e0b1727 Phil Davis
	} else {
2266 ac631bba lgcosta
		return false;
2267 1e0b1727 Phil Davis
	}
2268 ac631bba lgcosta
2269 1e0b1727 Phil Davis
	if ($freeloginscount < 1 || $resettimeout <= 0 || !$clientmac) {
2270 ac631bba lgcosta
		return false;
2271 1e0b1727 Phil Davis
	}
2272 ac631bba lgcosta
2273 63d6bb4f Marcos Mendoza
	$updatetimeouts = isset($cpzone_config['freelogins_updatetimeouts']);
2274 ac631bba lgcosta
2275
	/*
2276
	 * Read database of used MACs.  Lines are a comma-separated list
2277
	 * of the time, MAC, then the count of pass-through credits remaining.
2278
	 */
2279
	$usedmacs = captiveportal_read_usedmacs_db();
2280
2281
	$currenttime = time();
2282
	$found = false;
2283
	foreach ($usedmacs as $key => $usedmac) {
2284
		$usedmac = explode(",", $usedmac);
2285
2286
		if ($usedmac[1] == $clientmac) {
2287
			if ($usedmac[0] + ($resettimeout * 3600) > $currenttime) {
2288
				if ($usedmac[2] < 1) {
2289
					if ($updatetimeouts) {
2290
						$usedmac[0] = $currenttime;
2291
						unset($usedmacs[$key]);
2292
						$usedmacs[] = implode(",", $usedmac);
2293
						captiveportal_write_usedmacs_db($usedmacs);
2294 a81a6edc Viktor G
						xmlrpc_sync_usedmacs($usedmacs);
2295 ac631bba lgcosta
					}
2296
2297
					return false;
2298
				} else {
2299
					$usedmac[2] -= 1;
2300
					$usedmacs[$key] = implode(",", $usedmac);
2301
				}
2302
2303
				$found = true;
2304 1e0b1727 Phil Davis
			} else {
2305 ac631bba lgcosta
				unset($usedmacs[$key]);
2306 1e0b1727 Phil Davis
			}
2307 ac631bba lgcosta
2308
			break;
2309 1e0b1727 Phil Davis
		} else if ($usedmac[0] + ($resettimeout * 3600) <= $currenttime) {
2310
			unset($usedmacs[$key]);
2311
		}
2312 ac631bba lgcosta
	}
2313
2314
	if (!$found) {
2315
		$usedmac = array($currenttime, $clientmac, $freeloginscount - 1);
2316
		$usedmacs[] = implode(",", $usedmac);
2317
	}
2318
2319
	captiveportal_write_usedmacs_db($usedmacs);
2320 a81a6edc Viktor G
	xmlrpc_sync_usedmacs($usedmacs);
2321 ac631bba lgcosta
	return true;
2322
}
2323
2324 e79a872f Christian McDonald
function xmlrpc_sync_usedmacs($usedmacs) {
2325 63d6bb4f Marcos Mendoza
	global $cpzone;
2326 a81a6edc Viktor G
2327
	// XMLRPC Call over to the other node
2328
	if (captiveportal_xmlrpc_sync_get_details($syncip, $syncport,
2329 d900b9d4 Marcos Mendoza
	    $syncuser, $syncpass)) {
2330 a81a6edc Viktor G
		$rpc_client = new pfsense_xmlrpc_client();
2331
		$rpc_client->setConnectionData($syncip, $syncport, $syncuser, $syncpass);
2332
		$rpc_client->set_noticefile("CaptivePortalUsedmacsSync");
2333
		$arguments = array(
2334
			'usedmacs' => $usedmacs
2335
		);
2336
2337
		$rpc_client->xmlrpc_method('captive_portal_sync',
2338
			array(
2339
				'op' => 'write_usedmacs',
2340
				'zone' => $cpzone,
2341
				'arguments' => base64_encode(serialize($arguments))
2342
			)
2343
		);
2344
	}
2345
}
2346
2347 ac631bba lgcosta
function captiveportal_read_usedmacs_db() {
2348 b4792bf8 Ermal
	global $g, $cpzone;
2349 ac631bba lgcosta
2350 b4792bf8 Ermal
	$cpumaclck = lock("captiveusedmacs{$cpzone}");
2351
	if (file_exists("{$g['vardb_path']}/captiveportal_usedmacs_{$cpzone}.db")) {
2352
		$usedmacs = file("{$g['vardb_path']}/captiveportal_usedmacs_{$cpzone}.db", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2353 1e0b1727 Phil Davis
		if (!$usedmacs) {
2354 ac631bba lgcosta
			$usedmacs = array();
2355 1e0b1727 Phil Davis
		}
2356
	} else {
2357 ac631bba lgcosta
		$usedmacs = array();
2358 1e0b1727 Phil Davis
	}
2359 ac631bba lgcosta
2360
	unlock($cpumaclck);
2361
	return $usedmacs;
2362
}
2363
2364
function captiveportal_write_usedmacs_db($usedmacs) {
2365 b4792bf8 Ermal
	global $g, $cpzone;
2366 ac631bba lgcosta
2367 87d2b746 Christopher
	if (!is_array($usedmacs)) {
2368 3dfbc1fa jim-p
		$usedmacs = [];
2369
	}
2370 b4792bf8 Ermal
	$cpumaclck = lock("captiveusedmacs{$cpzone}", LOCK_EX);
2371
	@file_put_contents("{$g['vardb_path']}/captiveportal_usedmacs_{$cpzone}.db", implode("\n", $usedmacs));
2372 ac631bba lgcosta
	unlock($cpumaclck);
2373
}
2374
2375 81ce28d8 Renato Botelho
function captiveportal_blocked_mac($mac) {
2376 22fe3bd7 Marcos Mendoza
	global $cpzone;
2377 0d33f1fc Renato Botelho
2378 1e0b1727 Phil Davis
	if (empty($mac) || !is_macaddr($mac)) {
2379 81ce28d8 Renato Botelho
		return false;
2380 1e0b1727 Phil Davis
	}
2381 0d33f1fc Renato Botelho
2382 22fe3bd7 Marcos Mendoza
	$mac = strtolower($mac);
2383
	$action = '';
2384
	$matched = false;
2385
	foreach (config_get_path("captiveportal/{$cpzone}/passthrumac", []) as $passthrumac) {
2386
		// assume the config entry contains a valid lowercase MAC address
2387
		list($mac_entry, $mac_entry_mask) = explode('/', $passthrumac['mac']);
2388
		if ($mac_entry_mask === null) {
2389
			$mac_entry_mask = 48;
2390
		}
2391
2392
		// Pad config MAC parts with 0 if needed
2393
		$mac_parts = [];
2394
		foreach (explode(':', $mac_entry) as $macpart) {
2395
			$mac_parts[] = str_pad($macpart, 2, '0', STR_PAD_LEFT);
2396
		}
2397
		$mac_entry_long = hexdec(implode($mac_parts));
2398
2399
		// Pad client MAC parts with 0 if needed
2400
		$mac_parts = [];
2401
		foreach (explode(':', $mac) as $macpart) {
2402
			$mac_parts[] = str_pad($macpart, 2, '0', STR_PAD_LEFT);
2403
		}
2404
		$mac_long = hexdec(implode($mac_parts));
2405
2406
		// check against the masked MAC address
2407
		if (($mac_long & (-1 << (48 - $mac_entry_mask))) == ($mac_entry_long & (-1 << (48 - $mac_entry_mask)))) {
2408
			$action = $passthrumac['action'];
2409
			$matched = true;
2410
		}
2411 0d33f1fc Renato Botelho
2412 22fe3bd7 Marcos Mendoza
		// a specific match takes precedence over a partial match
2413
		if ($mac_entry_mask == 48) {
2414
			break;
2415 1e0b1727 Phil Davis
		}
2416
	}
2417 0d33f1fc Renato Botelho
2418 22fe3bd7 Marcos Mendoza
	if ($matched && $action == 'block') {
2419
		return true;
2420
	}
2421 3b2769be Renato Botelho
2422 22fe3bd7 Marcos Mendoza
	return false;
2423 3b2769be Renato Botelho
}
2424
2425 e42ea151 Augustin FL
/* Captiveportal Radius Accounting */
2426
2427
function gigawords($bytes) {
2428
2429
	/*
2430
	 * RFC2866 Specifies a 32bit unsigned integer, which is a max of 4294967295
2431
	 * Currently there is a fault in the PECL radius_put_int function which can handle only 32bit signed integer.
2432
	 */
2433
2434
	// We use BCMath functions since normal integers don't work with so large numbers
2435
	$gigawords = bcdiv( bcsub( $bytes, remainder($bytes) ) , GIGAWORDS_RIGHT_OPERAND) ;
2436
2437
	// We need to manually set this to a zero instead of NULL for put_int() safety
2438
	if (is_null($gigawords)) {
2439
		$gigawords = 0;
2440
	}
2441
2442
	return $gigawords;
2443
}
2444
2445
function remainder($bytes) {
2446
	// Calculate the bytes we are going to send to the radius
2447
	$bytes = bcmod($bytes, GIGAWORDS_RIGHT_OPERAND);
2448 748372bc Stephen Jones
2449 e42ea151 Augustin FL
	if (is_null($bytes)) {
2450
		$bytes = 0;
2451
	}
2452
2453
    return $bytes;
2454
}
2455
2456 5f1aaed4 Augustin FL
function captiveportal_send_server_accounting($type = 'on', $ruleno = null, $username = null, $clientip = null, $clientmac = null, $sessionid = null, $start_time = null, $stop_time = null, $term_cause = null) {
2457 63d6bb4f Marcos Mendoza
	global $cpzone;
2458 62f20eab Michael Newton
2459 65db621a Reid Linnemann
	$cpcfg = config_get_path("captiveportal/{$cpzone}", []);
2460 5f1aaed4 Augustin FL
	$acctcfg = auth_get_authserver($cpcfg['radacct_server']);
2461 748372bc Stephen Jones
2462 13164061 Augustin-FL
	if (!isset($cpcfg['radacct_enable']) || empty($acctcfg) ||
2463
	    captiveportal_ha_is_node_in_backup_mode($cpzone)) {
2464 5f1aaed4 Augustin FL
		return null;
2465 62f20eab Michael Newton
	}
2466 748372bc Stephen Jones
2467 5f1aaed4 Augustin FL
	if ($type === 'on') {
2468
		$racct = new Auth_RADIUS_Acct_On;
2469
	} elseif ($type === 'off') {
2470 62f20eab Michael Newton
		$racct = new Auth_RADIUS_Acct_Off;
2471 5f1aaed4 Augustin FL
	} elseif ($type === 'start') {
2472
		$racct = new Auth_RADIUS_Acct_Start;
2473
		if (!is_int($start_time)) {
2474
			$start_time = time();
2475
		}
2476
	} elseif ($type === 'stop') {
2477
		$racct = new Auth_RADIUS_Acct_Stop;
2478
		if (!is_int($stop_time)) {
2479
			$stop_time = time();
2480
		}
2481
	} elseif ($type === 'update') {
2482
        $racct = new Auth_RADIUS_Acct_Update;
2483
		if (!is_int($stop_time)) {
2484 85a8f9b0 Augustin-FL
			$stop_time = time(); // "top time" here will be used only for calculating session time.
2485 5f1aaed4 Augustin FL
		}
2486 62f20eab Michael Newton
	} else {
2487 5f1aaed4 Augustin FL
		return null;
2488 62f20eab Michael Newton
	}
2489 5f1aaed4 Augustin FL
2490
	$racct->addServer($acctcfg['host'], $acctcfg['radius_acct_port'],
2491
		$acctcfg['radius_secret'], $acctcfg['radius_timeout']);
2492 748372bc Stephen Jones
2493 5f1aaed4 Augustin FL
	$racct->authentic = RADIUS_AUTH_RADIUS;
2494 f790565a Augustin-FL
	if ($cpcfg['auth_method'] === 'radmac' && $username === "unauthenticated" && !empty($clientmac)) {
2495
		$racct->username = mac_format($clientmac);
2496
	} elseif (!empty($username)) {
2497 5f1aaed4 Augustin FL
		$racct->username = $username;
2498 62f20eab Michael Newton
	}
2499
2500 5f1aaed4 Augustin FL
	if (PEAR::isError($racct->start())) {
2501
		captiveportal_syslog('RADIUS ACCOUNTING FAILED : '.$racct->getError());
2502 62f20eab Michael Newton
		$racct->close();
2503 5f1aaed4 Augustin FL
		return null;
2504
	}
2505 748372bc Stephen Jones
2506 7c2468c5 Viktor G
	$nasip = nasip_fallback($acctcfg['radius_nasip_attribute']);
2507 5f1aaed4 Augustin FL
	$nasmac = get_interface_mac(find_ip_interface($nasip));
2508
	$racct->putAttribute(RADIUS_NAS_IP_ADDRESS, $nasip, "addr");
2509 b1cc8f31 jim-p
2510
	$racct->putAttribute(RADIUS_NAS_IDENTIFIER, empty($cpcfg["radiusnasid"]) ? "CaptivePortal-{$cpzone}" : $cpcfg["radiusnasid"] );
2511 748372bc Stephen Jones
2512 5f1aaed4 Augustin FL
	if (is_int($ruleno)) {
2513
		$racct->putAttribute(RADIUS_NAS_PORT_TYPE, RADIUS_ETHERNET);
2514
		$racct->putAttribute(RADIUS_NAS_PORT, intval($ruleno), 'integer');
2515
	}
2516 748372bc Stephen Jones
2517 5f1aaed4 Augustin FL
	if (!empty($sessionid)) {
2518
		$racct->putAttribute(RADIUS_ACCT_SESSION_ID, $sessionid);
2519
	}
2520
2521
	if (!empty($clientip) && is_ipaddr($clientip)) {
2522
		$racct->putAttribute(RADIUS_FRAMED_IP_ADDRESS, $clientip, "addr");
2523
	}
2524
	if (!empty($clientmac)) {
2525
		$racct->putAttribute(RADIUS_CALLING_STATION_ID, mac_format($clientmac));
2526
	}
2527
	if (!empty($nasmac)) {
2528
		$racct->putAttribute(RADIUS_CALLED_STATION_ID, mac_format($nasmac).':'.gethostname());
2529
	}
2530 748372bc Stephen Jones
2531 5f1aaed4 Augustin FL
	// Accounting request Stop and Update : send the current data volume
2532
	if (($type === 'stop' || $type === 'update') && is_int($start_time)) {
2533
		$volume = getVolume($clientip);
2534
		$session_time = $stop_time - $start_time;
2535
		$volume['input_bytes_radius'] = remainder($volume['input_bytes']);
2536
		$volume['input_gigawords'] = gigawords($volume['input_bytes']);
2537
		$volume['output_bytes_radius'] = remainder($volume['output_bytes']);
2538
		$volume['output_gigawords'] = gigawords($volume['output_bytes']);
2539
2540
		// Volume stuff: Ingress
2541
		$racct->putAttribute(RADIUS_ACCT_INPUT_PACKETS, intval($volume['input_pkts']), "integer");
2542
		$racct->putAttribute(RADIUS_ACCT_INPUT_OCTETS, intval($volume['input_bytes_radius']), "integer");
2543
		// Volume stuff: Outgress
2544
		$racct->putAttribute(RADIUS_ACCT_OUTPUT_PACKETS, intval($volume['output_pkts']), "integer");
2545
		$racct->putAttribute(RADIUS_ACCT_OUTPUT_OCTETS, intval($volume['output_bytes_radius']), "integer");
2546
		$racct->putAttribute(RADIUS_ACCT_SESSION_TIME, intval($session_time), "integer");
2547 748372bc Stephen Jones
2548 5f1aaed4 Augustin FL
		$racct->putAttribute(CUSTOM_RADIUS_ACCT_OUTPUT_GIGAWORDS, intval($volume['output_gigawords']), "integer");
2549
		$racct->putAttribute(CUSTOM_RADIUS_ACCT_INPUT_GIGAWORDS, intval($volume['input_gigawords']), "integer");
2550
		// Set session_time
2551
		$racct->session_time = $session_time;
2552
	}
2553 748372bc Stephen Jones
2554 5f1aaed4 Augustin FL
	if ($type === 'stop') {
2555
		if (empty($term_cause)) {
2556
			$term_cause = 1;
2557
		}
2558
		$racct->putAttribute(RADIUS_ACCT_TERMINATE_CAUSE, $term_cause);
2559
	}
2560 748372bc Stephen Jones
2561 62f20eab Michael Newton
	// Send request
2562
	$result = $racct->send();
2563 5f1aaed4 Augustin FL
2564 62f20eab Michael Newton
	if (PEAR::isError($result)) {
2565 5f1aaed4 Augustin FL
		 captiveportal_syslog('RADIUS ACCOUNTING FAILED : '.$racct->getError());
2566
		 $result = null;
2567
	} elseif ($result !== true) {
2568
		$result = false;
2569 62f20eab Michael Newton
	}
2570
2571
	$racct->close();
2572 5f1aaed4 Augustin FL
	return $result;
2573 62f20eab Michael Newton
}
2574 d2ecbddc jim-p
2575
function captiveportal_isip_logged($clientip) {
2576
	global $g, $cpzone;
2577
2578
	/* read in client database */
2579
	$query = "WHERE ip = '{$clientip}'";
2580
	$cpdb = captiveportal_read_db($query);
2581
	foreach ($cpdb as $cpentry) {
2582
		return $cpentry;
2583
	}
2584
}
2585 65a51647 Augustin-FL
2586 7c2468c5 Viktor G
function captiveportal_allowedhostname_cleanup() {
2587 63d6bb4f Marcos Mendoza
	global $g, $cpzone;
2588 7c2468c5 Viktor G
2589 63d6bb4f Marcos Mendoza
	config_init_path("captiveportal/{$cpzone}/allowedhostname");
2590 65db621a Reid Linnemann
	$cpzone_config = config_get_path("captiveportal/{$cpzone}", []);
2591 63d6bb4f Marcos Mendoza
	$cpzoneprefix = CPPREFIX . $cpzone_config['zoneid'];
2592 7c2468c5 Viktor G
2593 63d6bb4f Marcos Mendoza
	foreach ($cpzone_config['allowedhostname'] as $id => $hostnameent) {
2594 3d1a553e Viktor G
		$pipes = pfSense_pf_cp_get_eth_pipes("{$cpzoneprefix}_allowedhosts/hostname_{$id}");
2595 978ea085 linnemannr
		pfSense_pf_cp_flush("{$cpzoneprefix}_allowedhosts/hostname_{$id}", "ether");
2596 43bd2b88 Reid Linnemann
		if (!empty($pipes)) {
2597
			captiveportal_pipes_delete($pipes);
2598 7c2468c5 Viktor G
		}
2599
	}
2600
}
2601
2602
function filter_captiveportal_aliases() {
2603 e79a872f Christian McDonald
	/* return all aliases used in captive portal zones,
2604 7c2468c5 Viktor G
	 * to prevent it from deletion in filter_configure_sync() as unused aliases */
2605 63d6bb4f Marcos Mendoza
	global $g;
2606 7c2468c5 Viktor G
2607
	$aliasesnames = array();
2608
2609 63d6bb4f Marcos Mendoza
	config_init_path('captiveportal');
2610 ac0a027f Christian McDonald
	foreach (config_get_path('captiveportal', []) as $cpzone => $cpcfg) {
2611 7c2468c5 Viktor G
		if (isset($cpcfg['enable'])) {
2612 63d6bb4f Marcos Mendoza
			$cpzoneprefix = CPPREFIX . $cpcfg['zoneid'];
2613 7c2468c5 Viktor G
			$aliasesnames[] = $cpzoneprefix . '_cpips';
2614 63d6bb4f Marcos Mendoza
			config_init_path("captiveportal/{$cpzone}/allowedhostname");
2615
			foreach ($cpcfg['allowedhostname'] as $id => $hostnameent) {
2616 7c2468c5 Viktor G
				$aliasesnames[] = $cpzoneprefix . '_hostname_' . $id;
2617
			}
2618
		}
2619
	}
2620
2621
	return $aliasesnames;
2622
}
2623
2624
function filter_captiveportal_tables() {
2625
	/* return pf rules which defines tables used in captive portal zones */
2626 63d6bb4f Marcos Mendoza
	global $FilterIflist;
2627 7c2468c5 Viktor G
2628
	$rules = '';
2629 63d6bb4f Marcos Mendoza
	config_init_path('captiveportal');
2630 ac0a027f Christian McDonald
	foreach (config_get_path('captiveportal', []) as $cpzone => $cpcfg) {
2631 7c2468c5 Viktor G
		if (!isset($cpcfg['enable'])) {
2632
			continue;
2633
		}
2634
2635 63d6bb4f Marcos Mendoza
		$cpzoneprefix = CPPREFIX . $cpcfg['zoneid'];
2636 7c2468c5 Viktor G
		$cpips = $cpzoneprefix . '_cpips';
2637 b37f3f5d Reid Linnemann
		$cpiplist = array();
2638 7c2468c5 Viktor G
2639
		foreach (explode(",", $cpcfg['interface']) as $cpifgrp) {
2640
			if (isset($FilterIflist[$cpifgrp])) {
2641
				$realif = get_real_interface($cpifgrp);
2642
				if (!empty($realif)) {
2643
					$cpip = get_interface_ip($cpifgrp);
2644
					if (is_ipaddrv4($cpip)) {
2645 b37f3f5d Reid Linnemann
						$cpipliststring = $cpip . ' ' . get_interface_vip_ips($cpifgrp);
2646
						$cpiplist = array_filter(array_merge($cpiplist, explode(' ', $cpipliststring)),
2647
												 function ($val) {
2648
													 return (trim($val) != "");
2649
												 });
2650 7c2468c5 Viktor G
					}
2651
				}
2652
			}
2653
		}
2654
		if (!empty($cpiplist)) {
2655
			/* captive portal web server IP addresses */
2656 b37f3f5d Reid Linnemann
			$rules .= "table <{$cpips}> { " . join(' ', $cpiplist)  . "}\n";
2657 7c2468c5 Viktor G
		}
2658
	}
2659
2660
	if (!empty($rules)) {
2661
		$rules = "\n# Captive Portal\n" . $rules . "\n";
2662
	}
2663
2664
	return $rules;
2665
}
2666
2667
function filter_captiveportal_ether() {
2668 63d6bb4f Marcos Mendoza
	global $g;
2669 7c2468c5 Viktor G
2670
	$rules = '';
2671 63d6bb4f Marcos Mendoza
	config_init_path('captiveportal');
2672 ac0a027f Christian McDonald
	foreach (config_get_path('captiveportal', []) as $cpzone => $cpcfg) {
2673 7c2468c5 Viktor G
		if (!isset($cpcfg['enable'])) {
2674
			continue;
2675
		}
2676
2677 63d6bb4f Marcos Mendoza
		$cpzoneprefix = CPPREFIX . $cpcfg['zoneid'];
2678 7c2468c5 Viktor G
		$rdrtag = $cpzoneprefix . '_rdr';
2679
		$interfaces = captiveportal_zone_interfaces($cpcfg);
2680
2681
		if (!empty($interfaces)) {
2682 4864d7f6 Josh Soref
			/* set 'rdr' tag for further captive portal web portal redirection */
2683 7c2468c5 Viktor G
			$rules .= "ether pass on { {$interfaces} } tag \"{$rdrtag}\"\n";
2684 ce541827 Viktor G
			/* anchor to set the PASS tag for authenticated clients */
2685
			$rules .= "ether anchor \"{$cpzoneprefix}_auth/*\" on { {$interfaces} }\n";
2686 7c2468c5 Viktor G
			/* anchor for Services / Captive Portal / CPZONE / MACs */
2687
			$rules .= "ether anchor \"{$cpzoneprefix}_passthrumac/*\" on { {$interfaces} }\n";
2688
			/* anchor to set the PASSTHRU tag for Allowed IP/Hostnames */
2689
			$rules .= "ether anchor \"{$cpzoneprefix}_allowedhosts/*\" on { {$interfaces} }\n";
2690
		}
2691
	}
2692
2693
	if (!empty($rules)) {
2694
		$rules = "\n# Captive Portal\n" . $rules . "\n";
2695
	}
2696
2697
	return $rules;
2698
}
2699
2700
function filter_captiveportal_rdr() {
2701 63d6bb4f Marcos Mendoza
	global $g, $FilterIflist;
2702 7c2468c5 Viktor G
2703
	$rules = '';
2704 ac0a027f Christian McDonald
	foreach (config_get_path('captiveportal', []) as $cpzone => $cpcfg) {
2705 7c2468c5 Viktor G
		if (!isset($cpcfg['enable'])) {
2706
			continue;
2707
		}
2708
2709 63d6bb4f Marcos Mendoza
		$cpzoneprefix = CPPREFIX . $cpcfg['zoneid'];
2710 7c2468c5 Viktor G
		$rdrtag = $cpzoneprefix . '_rdr';
2711
		$cpips = $cpzoneprefix . '_cpips';
2712 fee50323 Reid Linnemann
		$rdr_ports = captiveportal_zone_portalports($cpcfg);
2713 7c2468c5 Viktor G
		foreach (explode(",", $cpcfg['interface']) as $cpifgrp) {
2714
			if (isset($FilterIflist[$cpifgrp])) {
2715
				$realif = get_real_interface($cpifgrp);
2716
				if (!empty($realif)) {
2717
					$cpip = get_interface_ip($cpifgrp);
2718
					if (is_ipaddrv4($cpip)) {
2719 fee50323 Reid Linnemann
						foreach ($rdr_ports as list($portalias, $cprdrport)) {
2720 ce541827 Viktor G
							$rules .= "rdr on {$realif} inet proto tcp from any to ! <{$cpips}> port {$cprdrport} tagged {$rdrtag} -> {$cpip} port {$portalias}\n";
2721 fee50323 Reid Linnemann
						}
2722 7c2468c5 Viktor G
					}
2723
				}
2724
			}
2725
		}
2726
	}
2727
2728
	if (!empty($rules)) {
2729
		$rules = "\n# Captive Portal\n" . $rules . "\n";
2730
	}
2731
2732
	return $rules;
2733
}
2734
2735
function filter_captiveportal_pass() {
2736 63d6bb4f Marcos Mendoza
	global $g, $FilterIflist;
2737 7c2468c5 Viktor G
2738
	$captiveportal_increment = 'filter_captiveportal_tracker';
2739
2740
	$rules = '';
2741 ac0a027f Christian McDonald
	foreach (config_get_path('captiveportal', []) as $cpzone => $cpcfg) {
2742 7c2468c5 Viktor G
		if (!isset($cpcfg['enable'])) {
2743
			continue;
2744
		}
2745
2746 63d6bb4f Marcos Mendoza
		$cpzoneprefix = CPPREFIX . $cpcfg['zoneid'];
2747 7c2468c5 Viktor G
		$cpips = $cpzoneprefix . '_cpips';
2748
		$authtag = $cpzoneprefix . '_auth';
2749 fee50323 Reid Linnemann
		$rdr_ports = captiveportal_zone_portalports($cpcfg);
2750 7c2468c5 Viktor G
2751
		foreach (explode(",", $cpcfg['interface']) as $cpifgrp) {
2752
			if (!isset($FilterIflist[$cpifgrp])) {
2753
				continue;
2754
			}
2755
			$realif = get_real_interface($cpifgrp);
2756
			if (!empty($realif)) {
2757
				$cpip = get_interface_ip($cpifgrp);
2758
				if (is_ipaddrv4($cpip)) {
2759 fee50323 Reid Linnemann
					foreach ($rdr_ports as list($portalias, $cprdrport)) {						/* pass non-authenticated clients to captive portal */
2760
						$rules .= "pass in quick on {$realif} proto tcp from any to <{$cpips}> port {$portalias} ridentifier {$captiveportal_increment()} keep state(sloppy)\n";
2761
						/* without this rule captive portal doesn't show login page after manual disconnect */
2762
						$rules .= "pass out quick on {$realif} proto tcp from {$cpip} port {$portalias} to any flags any ridentifier {$captiveportal_increment()} keep state(sloppy)\n";
2763
					}
2764 6c055aaf Kristof Provost
					/* block non-authenticated clients access to internet */
2765 7c2468c5 Viktor G
					$rules .= "block in quick on {$realif} from any to ! <{$cpips}> ! tagged {$authtag} ridentifier {$captiveportal_increment()}\n";
2766
				}
2767
			}
2768
		}
2769
	}
2770
2771
	if (!empty($rules)) {
2772
		$rules = "\n# Captive Portal\n" . $rules . "\n";
2773
	}
2774
2775
	return $rules;
2776
}
2777
2778
function captiveportal_zone_interfaces($cpcfg) {
2779
	/* return a list of captive portal zone interfaces */
2780
	global $FilterIflist;
2781
2782
	$interfaces = '';
2783
	foreach (explode(",", $cpcfg['interface']) as $cpifgrp) {
2784
		if (isset($FilterIflist[$cpifgrp])) {
2785
			$realif = get_real_interface($cpifgrp);
2786
			if (!empty($realif) && get_interface_ip($realif)) {
2787 e79a872f Christian McDonald
				$interfaces .= $realif . ' ';
2788 7c2468c5 Viktor G
			}
2789
		}
2790
	}
2791
	return $interfaces;
2792
}
2793
2794 fee50323 Reid Linnemann
/*
2795
 * Returns an array of (alias, rdrport) pairs describing ports to be forwarded for the captive portal
2796
 */
2797 7c2468c5 Viktor G
function captiveportal_zone_portalports($cpcfg) {
2798 fee50323 Reid Linnemann
	$rdr_ports = array();
2799 7c2468c5 Viktor G
	if (isset($cpcfg['httpslogin']) && !isset($cpcfg['nohttpsforwards'])) {
2800
		$portalias = $cpcfg['listenporthttps'] ? $cpcfg['listenporthttps'] : 8001 + $cpcfg['zoneid'];
2801
		$cprdrport = '443';
2802 fee50323 Reid Linnemann
		array_push($rdr_ports, array($portalias, $cprdrport));
2803 7c2468c5 Viktor G
	}
2804 fee50323 Reid Linnemann
	$portalias = $cpcfg['listenporthttp'] ? $cpcfg['listenporthttp'] : 8000 + $cpcfg['zoneid'];
2805
	$cprdrport = '80';
2806
	array_push($rdr_ports, array($portalias, $cprdrport));
2807
2808
	return $rdr_ports;
2809 7c2468c5 Viktor G
}
2810
2811 43bd2b88 Reid Linnemann
function captiveportal_pipes_delete($pipes) {
2812
	if (!empty($pipes)) {
2813
		foreach ($pipes as $pipe) {
2814
			mwexec("/sbin/dnctl pipe delete {$pipe}");
2815
		}
2816
		captiveportal_free_dn_rulenos($pipes);
2817 7c2468c5 Viktor G
	}
2818
}
2819
2820 65530037 Reid Linnemann
function captiveportal_ether_configure_entry($hostent, $anchor, $user_auth = false) {
2821 63d6bb4f Marcos Mendoza
	global $g, $cpzone;
2822 7c2468c5 Viktor G
2823
	if (($hostent['action'] == 'block') && ($anchor == 'passthrumac')) {
2824
		return;
2825
	}
2826
2827 63d6bb4f Marcos Mendoza
	$cpzoneprefix = CPPREFIX . config_get_path("captiveportal/{$cpzone}/zoneid");
2828 7c2468c5 Viktor G
	if ($anchor == 'passthrumac') {
2829 add6447b Kristof Provost
		$tag = $cpzoneprefix . '_auth';
2830 7c2468c5 Viktor G
	} else {
2831
		$tag = $cpzoneprefix . '_' . $anchor;
2832
	}
2833
2834
	if ($anchor == 'passthrumac') {
2835 32661caf Viktor G
		list($pipeup, $pipedown) = captiveportal_pipe_configure($hostent, 'pipe_mac', $user_auth);
2836 7c2468c5 Viktor G
		$host = str_replace("/", "_", str_replace(":", "", $hostent['mac']));
2837
		$l3from = '';
2838
		$l3to = '';
2839
		$macfrom = "from {$hostent['mac']}";
2840
		$macto = "to {$hostent['mac']}";
2841
	} else {
2842 32661caf Viktor G
		list($pipeup, $pipedown) = captiveportal_pipe_configure($hostent, 'auth', $user_auth);
2843 7c2468c5 Viktor G
		$host = $hostent['ip'] . '_32';
2844
		$l3from = "l3 from {$hostent['ip']}";
2845
		$l3to = "l3 to {$hostent['ip']}";
2846 63d6bb4f Marcos Mendoza
		if (!config_path_enabled("captiveportal/{$cpzone}/nomacfilter")) {
2847 7c2468c5 Viktor G
			if (!empty($hostent['mac'])) {
2848
				$macfrom = "from {$hostent['mac']}";
2849
				$macto = "to {$hostent['mac']}";
2850
			} else {
2851
				return;
2852
			}
2853
		} else {
2854
			$macfrom = '';
2855
			$macto = '';
2856
		}
2857
	}
2858
2859 ce541827 Viktor G
	$rules = "ether pass in quick {$macfrom} {$l3from} tag {$tag} dnpipe {$pipeup}\n";
2860
	$rules .= "ether pass out quick {$macto} {$l3to} tag {$tag} dnpipe {$pipedown}\n";
2861 7c2468c5 Viktor G
2862
	captiveportal_load_pfctl("{$cpzoneprefix}_{$anchor}", $host, $rules);
2863
}
2864
2865 32661caf Viktor G
function captiveportal_pipe_configure($host, $type, $user_auth = true) {
2866 63d6bb4f Marcos Mendoza
	global $cpzone;
2867 7c2468c5 Viktor G
2868 65db621a Reid Linnemann
	$cpzone_config = config_get_path("captiveportal/{$cpzone}", []);
2869 7c2468c5 Viktor G
	$bwUp = 0;
2870
	if (!empty($host['bw_up'])) {
2871
		$bwUp = $host['bw_up'];
2872 65530037 Reid Linnemann
	} elseif ($user_auth &&
2873 63d6bb4f Marcos Mendoza
		isset($cpzone_config['peruserbw']) &&
2874
	    !empty($cpzone_config['bwdefaultup'])) {
2875
		$bwUp = $cpzone_config['bwdefaultup'];
2876 7c2468c5 Viktor G
	}
2877
	$bwDown = 0;
2878
	if (!empty($host['bw_down'])) {
2879
		$bwDown = $host['bw_down'];
2880 65530037 Reid Linnemann
	} elseif ($user_auth &&
2881 63d6bb4f Marcos Mendoza
		isset($cpzone_config['peruserbw']) &&
2882
	    !empty($cpzone_config['bwdefaultdn'])) {
2883
		$bwDown = $cpzone_config['bwdefaultdn'];
2884 7c2468c5 Viktor G
	}
2885
2886
	if (isset($host['pipeno']) && !empty($host['pipeno'])) {
2887
		$pipeup = $host['pipeno'];
2888
	} else {
2889 32661caf Viktor G
		$pipeup = captiveportal_get_next_dn_ruleno($type);
2890 7c2468c5 Viktor G
	}
2891
2892 0db2b0ff Viktor G
	mwexec("/sbin/dnctl pipe {$pipeup} config bw {$bwUp}Kbit/s queue 100 buckets 16");
2893 7c2468c5 Viktor G
	$pipedown = $pipeup + 1;
2894 0db2b0ff Viktor G
	mwexec("/sbin/dnctl pipe {$pipedown} config bw {$bwDown}Kbit/s queue 100 buckets 16");
2895 7c2468c5 Viktor G
2896
	return array($pipeup, $pipedown);
2897
}
2898
2899
function captiveportal_allowedip_configure_entry($ipent) {
2900 63d6bb4f Marcos Mendoza
	global $g, $cpzone;
2901 7c2468c5 Viktor G
2902 63d6bb4f Marcos Mendoza
	$cpzoneprefix = CPPREFIX . config_get_path("captiveportal/{$cpzone}/zoneid");
2903 add6447b Kristof Provost
	$tag = $cpzoneprefix . '_auth';
2904 7c2468c5 Viktor G
2905
	if (empty($ipent['sn'])) {
2906
		$ipent['sn'] = '32';
2907
	}
2908
2909
	$host = $ipent['ip'] . '_' . $ipent['sn'];
2910 32661caf Viktor G
	list($pipeup, $pipedown) = captiveportal_pipe_configure($ipent, 'allowed', false);
2911 7c2468c5 Viktor G
2912
	$rules = '';
2913
	if (($ipent['dir'] == 'to') || ($ipent['dir'] == 'both')) {
2914 ce541827 Viktor G
		$rules = "ether pass in quick l3 to {$ipent['ip']}/{$ipent['sn']} tag {$tag} dnpipe {$pipeup}\n";
2915 7c2468c5 Viktor G
	}
2916
	if (($ipent['dir'] == 'from') || ($ipent['dir'] == 'both')) {
2917 ce541827 Viktor G
		$rules .= "ether pass in quick l3 from {$ipent['ip']}/{$ipent['sn']} tag {$tag} dnpipe {$pipedown}\n";
2918 7c2468c5 Viktor G
	}
2919
2920
	captiveportal_load_pfctl("{$cpzoneprefix}_allowedhosts", $host, $rules);
2921
}
2922
2923
function captiveportal_allowedhostname_configure_entry($ipent, $hostnameid = 1) {
2924 63d6bb4f Marcos Mendoza
	global $cpzone;
2925 7c2468c5 Viktor G
2926
	if (!isset($ipent['hostname'])) {
2927
		return;
2928
	}
2929
2930 63d6bb4f Marcos Mendoza
	$cpzoneprefix = CPPREFIX . config_get_path("captiveportal/{$cpzone}/zoneid");
2931 add6447b Kristof Provost
	$tag = $cpzoneprefix . '_auth';
2932 7c2468c5 Viktor G
	$table = $cpzoneprefix . '_hostname_' . $hostnameid;
2933
	$host = 'hostname_' . $hostnameid;
2934 32661caf Viktor G
	list($pipeup, $pipedown) = captiveportal_pipe_configure($ipent, 'allowed', false);
2935 7c2468c5 Viktor G
2936
	$rules = "table <{$table}> persist\n";
2937
	if (($ipent['dir'] == 'to') || ($ipent['dir'] == 'both')) {
2938 ce541827 Viktor G
		$rules .= "ether pass in quick l3 to <{$table}> tag {$tag} dnpipe {$pipeup}\n";
2939 7c2468c5 Viktor G
	}
2940
	if (($ipent['dir'] == 'from') || ($ipent['dir'] == 'both')) {
2941 ce541827 Viktor G
		$rules .= "ether pass in quick l3 from <{$table}> tag {$tag} dnpipe {$pipedown}\n";
2942 7c2468c5 Viktor G
	}
2943
2944
	captiveportal_load_pfctl("{$cpzoneprefix}_allowedhosts", $host, $rules);
2945
2946
	/* return filterdns entry */
2947
	return "pf {$ipent['hostname']} {$table} {$cpzoneprefix}_allowedhosts/{$host}\n";
2948
}
2949
2950
function captiveportal_load_pfctl($anchor, $host, $rules) {
2951
	global $g, $cpzone;
2952
2953
	if (!empty($rules)) {
2954
		mwexec("/usr/bin/printf \"{$rules}\" | /sbin/pfctl -a {$anchor}/{$host} -f-");
2955
	} else {
2956 134a8703 Marcos Mendoza
		log_error("CP zone {$cpzone}: {$anchor} rules are empty for {$host}");
2957 7c2468c5 Viktor G
	}
2958
}
2959
2960
function captiveportal_anchor_zerocnt($ip, $anchor = 'auth') {
2961 63d6bb4f Marcos Mendoza
	global $cpzone;
2962
	$cpzoneprefix = CPPREFIX . config_get_path("captiveportal/{$cpzone}/zoneid");
2963 7c2468c5 Viktor G
2964 af044b67 Marcos Mendoza
	pfSense_pf_cp_zerocnt("{$cpzoneprefix}_{$anchor}/{$ip}_32");
2965 7c2468c5 Viktor G
}
2966
2967 64c0462b Ermal
?>