Project

General

Profile

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