Project

General

Profile

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