Project

General

Profile

Download (60.1 KB) Statistics
| Branch: | Tag: | Revision:
1 5b237745 Scott Ullrich
<?php
2
/*
3
	captiveportal.inc
4 b260c8e0 Scott Ullrich
	part of pfSense (http://www.pfSense.org)
5 5060dea7 Scott Ullrich
	Copyright (C) 2004-2011 Scott Ullrich <sullrich@gmail.com>
6 b260c8e0 Scott Ullrich
	Copyright (C) 2009 Ermal Lu?i <ermal.luci@gmail.com>
7 0bd34ed6 Scott Ullrich
	Copyright (C) 2003-2006 Manuel Kasper <mk@neon1.net>.
8 5060dea7 Scott Ullrich
9
	originally part of m0n0wall (http://m0n0.ch/wall)
10 5b237745 Scott Ullrich
	All rights reserved.
11 36254e4a Scott Ullrich
12 5b237745 Scott Ullrich
	Redistribution and use in source and binary forms, with or without
13
	modification, are permitted provided that the following conditions are met:
14 36254e4a Scott Ullrich
15 5b237745 Scott Ullrich
	1. Redistributions of source code must retain the above copyright notice,
16
	   this list of conditions and the following disclaimer.
17 36254e4a Scott Ullrich
18 5b237745 Scott Ullrich
	2. Redistributions in binary form must reproduce the above copyright
19
	   notice, this list of conditions and the following disclaimer in the
20
	   documentation and/or other materials provided with the distribution.
21 36254e4a Scott Ullrich
22 5b237745 Scott Ullrich
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
23
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
24
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
26
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
27
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
28
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
29
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
30
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
31
	POSSIBILITY OF SUCH DAMAGE.
32 ca83c6ea Scott Ullrich
33 9699028a Scott Ullrich
	This version of captiveportal.inc has been modified by Rob Parker
34
	<rob.parker@keycom.co.uk> to include changes for per-user bandwidth management
35
	via returned RADIUS attributes. This page has been modified to delete any
36
	added rules which may have been created by other per-user code (index.php, etc).
37
	These changes are (c) 2004 Keycom PLC.
38 523855b0 Scott Ullrich
	
39 bf444c34 Ermal
	pfSense_BUILDER_BINARIES:	/sbin/ipfw	/sbin/sysctl	/sbin/kldunload
40 5060dea7 Scott Ullrich
	pfSense_BUILDER_BINARIES:	/usr/local/sbin/lighttpd	/usr/local/bin/minicron /sbin/pfctl
41
	pfSense_BUILDER_BINARIES:	/bin/hostname	/bin/cp 
42
	pfSense_MODULE: captiveportal
43 9699028a Scott Ullrich
*/
44 36254e4a Scott Ullrich
45 5b237745 Scott Ullrich
/* include all configuration functions */
46 a55cdcc0 Ermal Lu?i
require_once("config.inc");
47
require_once("functions.inc");
48 71fdaecd Ermal
require_once("filter.inc");
49 856e58a6 Scott Ullrich
require_once("radius.inc");
50 156487ed Ermal Lu?i
require_once("voucher.inc");
51 0bd34ed6 Scott Ullrich
52 023aa1f2 Scott Ullrich
function get_default_captive_portal_html() {
53 5b237745 Scott Ullrich
	global $config, $g;
54 023aa1f2 Scott Ullrich
	// Detect if vouchers are being used and default to the voucher page
55 10370262 Scott Ullrich
	if(isset($config['voucher']['enable'])) {
56 023aa1f2 Scott Ullrich
			$htmltext = <<<EOD
57
<html> 
58
	<body> 
59
		<form method="post" action="\$PORTAL_ACTION\$"> 
60 3e404e0c Ermal
			<input name="redirurl" type="hidden" value="\$PORTAL_REDIRURL\$"> 
61 023aa1f2 Scott Ullrich
			<center>
62
				<table cellpadding="6" cellspacing="0" width="550" height="380" style="border:1px solid #000000">
63
					<tr height="10" bgcolor="#990000">
64
						<td style="border-bottom:1px solid #000000">
65
							<font color='white'>
66
								<b>
67
									Guest Voucher code required to continue
68
								</b>
69
							</font>
70
						</td>
71
					</tr>
72
					<tr>
73
						<td>
74
							<div id="mainlevel">
75
								<center>
76
									<table width="100%" border="0" cellpadding="5" cellspacing="0">
77 5060dea7 Scott Ullrich
										<tr>
78
											<td>
79 023aa1f2 Scott Ullrich
												<center>
80
													<div id="mainarea">
81
														<center>
82
															<table width="100%" border="0" cellpadding="5" cellspacing="5">
83
																<tr>
84
																	<td>
85
																		<div id="maindivarea">
86
																			<center>
87
																				<div id='statusbox'>
88
																					<font color='red' face='arial' size='+1'>
89
																						<b>
90
																							\$PORTAL_MESSAGE\$
91
																						</b>
92
																					</font>
93
																				</div>
94
																				<p/>
95
																				<div id='loginbox'>
96
																					Enter Voucher Code: 
97
																					<input name="auth_voucher" type="text" style="border:1px dashed;" size="22"> 
98
																					<input name="accept" type="submit" value="Continue"> 
99
																				</div>
100
																			</center>
101
																		</div>
102 5060dea7 Scott Ullrich
																	</td>
103 023aa1f2 Scott Ullrich
																</tr>
104
															</table>
105
														</center>
106
													</div>
107
												</center>
108
											</td>
109
										</tr>
110
									</table>
111
								</center>
112
							</div>
113
						</td>
114
					</tr>
115
				</table>
116
			</center>
117
		</form>
118
	</body> 
119
</html>
120 36254e4a Scott Ullrich
121 023aa1f2 Scott Ullrich
EOD;
122 c2056357 Scott Ullrich
		return $htmltext;
123
	}
124 0bd34ed6 Scott Ullrich
125 023aa1f2 Scott Ullrich
	// Vouchers are not found, return the normal user/pass auth page
126
	$htmltext = <<<EOD
127 b260c8e0 Scott Ullrich
<html> 
128
	<body> 
129 ce7c3bb5 Scott Ullrich
		<form method="post" action="\$PORTAL_ACTION\$"> 
130
		<input name="redirurl" type="hidden" value="\$PORTAL_REDIRURL\$">
131 b260c8e0 Scott Ullrich
			<center>
132
				<table cellpadding="6" cellspacing="0" width="550" height="380" style="border:1px solid #000000">
133
					<tr height="10" bgcolor="#990000">
134
						<td style="border-bottom:1px solid #000000">
135
							<font color='white'>
136
								<b>
137
									{$g['product_name']} captive portal
138
								</b>
139
							</font>
140
						</td>
141
					</tr>
142
					<tr>
143
						<td>
144
							<div id="mainlevel">
145
								<center>
146
									<table width="100%" border="0" cellpadding="5" cellspacing="0">
147 5060dea7 Scott Ullrich
										<tr>
148
											<td>
149 b260c8e0 Scott Ullrich
												<center>
150
													<div id="mainarea">
151
														<center>
152
															<table width="100%" border="0" cellpadding="5" cellspacing="5">
153
																<tr>
154 5060dea7 Scott Ullrich
																	<td>
155 b260c8e0 Scott Ullrich
																		<div id="maindivarea">
156
																			<center>
157
																				<div id='statusbox'>
158
																					<font color='red' face='arial' size='+1'>
159
																						<b>
160 ce7c3bb5 Scott Ullrich
																							\$PORTAL_MESSAGE\$
161 b260c8e0 Scott Ullrich
																						</b>
162
																					</font>
163
																				</div>
164
																				<br/>
165
																				<div id='loginbox'>
166
																					<table>
167
																					   <tr><td colspan="2"><center>Welcome to the {$g['product_name']} Captive Portal!</td></tr>
168
																					   <tr><td>&nbsp;</td></tr>
169
																					   <tr><td align="right">Username:</td><td><input name="auth_user" type="text" style="border: 1px dashed;"></td></tr>
170
																					   <tr><td align="right">Password:</td><td><input name="auth_pass" type="password" style="border: 1px dashed;"></td></tr>
171
																					   <tr><td>&nbsp;</td></tr>
172
																					   <tr>
173 5060dea7 Scott Ullrich
																						 <td colspan="2">
174 b260c8e0 Scott Ullrich
																							<center><input name="accept" type="submit" value="Continue"></center>
175 5060dea7 Scott Ullrich
																						 </td>
176 b260c8e0 Scott Ullrich
																					   </tr>
177
																					</table>
178
																				</div>
179
																			</center>
180
																		</div>
181 5060dea7 Scott Ullrich
																	</td>
182 b260c8e0 Scott Ullrich
																</tr>
183
															</table>
184
														</center>
185
													</div>
186
												</center>
187
											</td>
188
										</tr>
189
									</table>
190
								</center>
191
							</div>
192
						</td>
193
					</tr>
194
				</table>
195
			</center>
196
		</form>
197
	</body> 
198 5b237745 Scott Ullrich
</html>
199
200 023aa1f2 Scott Ullrich
EOD;
201 0bd34ed6 Scott Ullrich
202 023aa1f2 Scott Ullrich
	return $htmltext;
203
}
204 0bd34ed6 Scott Ullrich
205 023aa1f2 Scott Ullrich
function captiveportal_configure() {
206
	global $config, $g;
207
208 d31bc32a Ermal
	$captiveportallck = lock('captiveportal', LOCK_EX);
209 023aa1f2 Scott Ullrich
	
210
	if (isset($config['captiveportal']['enable'])) {
211
212 d12003c9 jim-p
		if ($g['booting'])
213 023aa1f2 Scott Ullrich
			echo "Starting captive portal... ";
214 0e3e825c Chris Buechler
		else
215
			captiveportal_syslog("Restarting captive portal.");
216 023aa1f2 Scott Ullrich
217
		/* kill any running mini_httpd */
218
		killbypid("{$g['varrun_path']}/lighty-CaptivePortal.pid");
219
		killbypid("{$g['varrun_path']}/lighty-CaptivePortal-SSL.pid");
220
221
		/* remove old information */
222
		unlink_if_exists("{$g['vardb_path']}/captiveportal.db");
223
		unlink_if_exists("{$g['vardb_path']}/captiveportal_mac.db");
224
		unlink_if_exists("{$g['vardb_path']}/captiveportal_ip.db");
225
		unlink_if_exists("{$g['vardb_path']}/captiveportal_radius.db");
226
227
		/* setup new database in case someone tries to access the status -> captive portal page */
228
		touch("{$g['vardb_path']}/captiveportal.db");
229
230
		/* kill any running minicron */
231 e8aef0ec Scott Ullrich
		killbypid("{$g['varrun_path']}/cp_prunedb.pid");
232 023aa1f2 Scott Ullrich
233
		/* init ipfw rules */
234
		captiveportal_init_rules(true);
235
236
		/* stop accounting on all clients */
237 d31bc32a Ermal
		captiveportal_radius_stop_all();
238 023aa1f2 Scott Ullrich
239
		/* initialize minicron interval value */
240
		$croninterval = $config['captiveportal']['croninterval'] ? $config['captiveportal']['croninterval'] : 60;
241
242
		/* double check if the $croninterval is numeric and at least 10 seconds. If not we set it to 60 to avoid problems */
243 eb7aa263 Ermal
		if ((!is_numeric($croninterval)) || ($croninterval < 10))
244
			$croninterval = 60;
245 023aa1f2 Scott Ullrich
246
		/* write portal page */
247
		if ($config['captiveportal']['page']['htmltext'])
248
			$htmltext = base64_decode($config['captiveportal']['page']['htmltext']);
249
		else {
250
			/* example/template page */
251
			$htmltext = get_default_captive_portal_html();
252 5b237745 Scott Ullrich
		}
253
254
		$fd = @fopen("{$g['varetc_path']}/captiveportal.html", "w");
255
		if ($fd) {
256 7a7e94a7 Scott Ullrich
			// Special case handling.  Convert so that we can pass this page
257
			// through the PHP interpreter later without clobbering the vars.
258
			$htmltext = str_replace("\$PORTAL_REDIRURL\$", "#PORTAL_REDIRURL#", $htmltext);
259
			$htmltext = str_replace("\$PORTAL_MESSAGE\$", "#PORTAL_MESSAGE#", $htmltext);
260
			$htmltext = str_replace("\$CLIENT_MAC\$", "#CLIENT_MAC#", $htmltext);
261
			$htmltext = str_replace("\$CLIENT_IP\$", "#CLIENT_IP#", $htmltext);
262
			$htmltext = str_replace("\$ORIGINAL_PORTAL_IP\$", "#ORIGINAL_PORTAL_IP#", $htmltext);
263
			$htmltext = str_replace("\$PORTAL_ACTION\$", "#PORTAL_ACTION#", $htmltext);
264 c4e228f3 Scott Ullrich
			if($config['captiveportal']['preauthurl']) {
265
				$htmltext = str_replace("\$PORTAL_REDIRURL\$", "{$config['captiveportal']['preauthurl']}", $htmltext);
266
				$htmltext = str_replace("#PORTAL_REDIRURL#", "{$config['captiveportal']['preauthurl']}", $htmltext);
267
			}
268 5b237745 Scott Ullrich
			fwrite($fd, $htmltext);
269 36254e4a Scott Ullrich
			fclose($fd);
270 5b237745 Scott Ullrich
		}
271 36254e4a Scott Ullrich
272 5b237745 Scott Ullrich
		/* write error page */
273
		if ($config['captiveportal']['page']['errtext'])
274
			$errtext = base64_decode($config['captiveportal']['page']['errtext']);
275
		else {
276 a34b8b3b Ermal
			/* example page  */
277
			$errtext = get_default_captive_portal_html();
278 5b237745 Scott Ullrich
		}
279
280
		$fd = @fopen("{$g['varetc_path']}/captiveportal-error.html", "w");
281
		if ($fd) {
282 7a7e94a7 Scott Ullrich
			// Special case handling.  Convert so that we can pass this page
283
			// through the PHP interpreter later without clobbering the vars.
284
			$errtext = str_replace("\$PORTAL_REDIRURL\$", "#PORTAL_REDIRURL#", $errtext);
285
			$errtext = str_replace("\$PORTAL_MESSAGE\$", "#PORTAL_MESSAGE#", $errtext);
286
			$errtext = str_replace("\$CLIENT_MAC\$", "#CLIENT_MAC#", $errtext);
287
			$errtext = str_replace("\$CLIENT_IP\$", "#CLIENT_IP#", $errtext);
288
			$errtext = str_replace("\$ORIGINAL_PORTAL_IP\$", "#ORIGINAL_PORTAL_IP#", $errtext);
289
			$errtext = str_replace("\$PORTAL_ACTION\$", "#PORTAL_ACTION#", $errtext);
290 c4e228f3 Scott Ullrich
			if($config['captiveportal']['preauthurl']) {
291
				$errtext = str_replace("\$PORTAL_REDIRURL\$", "{$config['captiveportal']['preauthurl']}", $errtext);
292
				$errtext = str_replace("#PORTAL_REDIRURL#", "{$config['captiveportal']['preauthurl']}", $errtext);
293
			}
294 5b237745 Scott Ullrich
			fwrite($fd, $errtext);
295 36254e4a Scott Ullrich
			fclose($fd);
296 5b237745 Scott Ullrich
		}
297 36254e4a Scott Ullrich
298 5b87b24e Ermal
		/* write error page */
299
		if ($config['captiveportal']['page']['logouttext'])
300
			$logouttext = base64_decode($config['captiveportal']['page']['logouttext']);
301
		else {
302
			/* example page */
303
			$logouttext = <<<EOD
304
<HTML>
305
<HEAD><TITLE>Redirecting...</TITLE></HEAD>
306
<BODY>
307
<SPAN STYLE="font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">
308 6991e1a6 Erik Fonnesbeck
<B>Redirecting to <A HREF="<?=\$my_redirurl;?>"><?=\$my_redirurl;?></A>...</B>
309 5b87b24e Ermal
</SPAN>
310
<SCRIPT LANGUAGE="JavaScript">
311
<!--
312
LogoutWin = window.open('', 'Logout', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=256,height=64');
313
if (LogoutWin) {
314 5060dea7 Scott Ullrich
	LogoutWin.document.write('<HTML>');
315
	LogoutWin.document.write('<HEAD><TITLE>Logout</TITLE></HEAD>') ;
316
	LogoutWin.document.write('<BODY BGCOLOR="#435370">');
317
	LogoutWin.document.write('<DIV ALIGN="center" STYLE="color: #ffffff; font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">') ;
318
	LogoutWin.document.write('<B>Click the button below to disconnect</B><P>');
319
	LogoutWin.document.write('<FORM METHOD="POST" ACTION="<?=\$logouturl;?>">');
320
	LogoutWin.document.write('<INPUT NAME="logout_id" TYPE="hidden" VALUE="<?=\$sessionid;?>">');
321
	LogoutWin.document.write('<INPUT NAME="logout" TYPE="submit" VALUE="Logout">');
322
	LogoutWin.document.write('</FORM>');
323
	LogoutWin.document.write('</DIV></BODY>');
324
	LogoutWin.document.write('</HTML>');
325
	LogoutWin.document.close();
326 5b87b24e Ermal
}
327
328 6991e1a6 Erik Fonnesbeck
document.location.href="<?=\$my_redirurl;?>";
329 5b87b24e Ermal
-->
330
</SCRIPT>
331
</BODY>
332
</HTML>
333
334
EOD;
335
		}
336
337
		$fd = @fopen("{$g['varetc_path']}/captiveportal-logout.html", "w");
338
		if ($fd) {
339
			fwrite($fd, $logouttext);
340
			fclose($fd);
341
		}
342 0bd34ed6 Scott Ullrich
		/* write elements */
343
		captiveportal_write_elements();
344 5b237745 Scott Ullrich
345 769e254e Ermal
		/* start up the webserving daemon */
346
		captiveportal_init_webgui();
347 36254e4a Scott Ullrich
348 aa69dbd2 Scott Ullrich
		/* Kill any existing prunecaptiveportal processes */
349 e8aef0ec Scott Ullrich
		if(file_exists("{$g['varrun_path']}/cp_prunedb.pid"))
350
			killbypid("{$g['varrun_path']}/cp_prunedb.pid");
351 aa69dbd2 Scott Ullrich
352 0bd34ed6 Scott Ullrich
		/* start pruning process (interval defaults to 60 seconds) */
353 e8aef0ec Scott Ullrich
		mwexec("/usr/local/bin/minicron $croninterval {$g['varrun_path']}/cp_prunedb.pid " .
354 c6c92abf Scott Ullrich
			"/etc/rc.prunecaptiveportal");
355 36254e4a Scott Ullrich
356 d99f7864 Scott Ullrich
		/* generate radius server database */
357 d31bc32a Ermal
		captiveportal_init_radius_servers();
358 36254e4a Scott Ullrich
359 d99f7864 Scott Ullrich
		if ($g['booting'])
360
			echo "done\n";
361 36254e4a Scott Ullrich
362 5b237745 Scott Ullrich
	} else {
363 23a0c341 Scott Ullrich
		killbypid("{$g['varrun_path']}/lighty-CaptivePortal.pid");
364 699cb4fe Ermal
		killbypid("{$g['varrun_path']}/lighty-CaptivePortal-SSL.pid");
365 e8aef0ec Scott Ullrich
		killbypid("{$g['varrun_path']}/cp_prunedb.pid");
366 12ee8fe4 Scott Ullrich
367 d31bc32a Ermal
		captiveportal_radius_stop_all();
368 3db19cf1 Scott Ullrich
369
		mwexec("/sbin/sysctl net.link.ether.ipfw=0");
370
371 5209079f Ermal Luçi
		/* unload ipfw */
372 faebbab3 Scott Ullrich
		if (is_module_loaded("ipfw.ko"))		
373
			mwexec("/sbin/kldunload ipfw.ko");
374 85250056 Ermal Lu?i
		$listifs = get_configured_interface_list_by_realif();
375 bbc6768b Ermal Lu?i
		foreach ($listifs as $listrealif => $listif) {
376 7c587b9f Ermal Lu?i
			if (!empty($listrealif)) {
377 da9d6701 Ermal
				if (does_interface_exist($listrealif)) {
378 bf444c34 Ermal
					pfSense_interface_flags($listrealif, -IFF_IPFW_FILTER);
379 da9d6701 Ermal
					$carpif = link_ip_to_carp_interface(find_interface_ip($listrealif));
380 5060dea7 Scott Ullrich
					if (!empty($carpif)) {
381 da9d6701 Ermal
						$carpsif = explode(" ", $carpif);
382
						foreach ($carpsif as $cpcarp)
383 bf444c34 Ermal
							pfSense_interface_flags($cpcarp, -IFF_IPFW_FILTER);
384 da9d6701 Ermal
					}
385 2ee45728 Ermal Lu?i
				}
386 bbc6768b Ermal Lu?i
			}
387
		}
388 3db19cf1 Scott Ullrich
	}
389 36254e4a Scott Ullrich
390 f8b11310 Ermal Lu?i
	unlock($captiveportallck);
391 c3214b80 Scott Ullrich
	
392 5b237745 Scott Ullrich
	return 0;
393
}
394
395 769e254e Ermal
function captiveportal_init_webgui() {
396
	global $g, $config;
397
398
	 if (!isset($config['captiveportal']['enable']))
399 5060dea7 Scott Ullrich
				return;
400 769e254e Ermal
401 985070dc Ermal
	if ($config['captiveportal']['maxprocperip'])
402
		$maxproc = $config['captiveportal']['maxprocperip'];
403 769e254e Ermal
	else
404
		$maxproc = 16;
405
406
	$use_fastcgi = true;
407
408
	if (isset($config['captiveportal']['httpslogin'])) {
409
		$cert = base64_decode($config['captiveportal']['certificate']);
410
		if (isset($config['captiveportal']['cacertificate']))
411
			$cacert = base64_decode($config['captiveportal']['cacertificate']);
412
		else
413
			$cacert = "";
414
		$key = base64_decode($config['captiveportal']['private-key']);
415
		/* generate lighttpd configuration */
416
		system_generate_lighty_config("{$g['varetc_path']}/lighty-CaptivePortal-SSL.conf",
417 2c063971 Ermal
			$cert, $key, $cacert, "lighty-CaptivePortal-SSL.pid", "8001", "/usr/local/captiveportal/",
418 769e254e Ermal
			"cert-portal.pem", "ca-portal.pem", "1", $maxproc, $use_fastcgi, true);
419
	}
420
421
	/* generate lighttpd configuration */
422
	system_generate_lighty_config("{$g['varetc_path']}/lighty-CaptivePortal.conf",
423
		"", "", "", "lighty-CaptivePortal.pid", "8000", "/usr/local/captiveportal/",
424
		"cert-portal.pem", "ca-portal.pem", "1", $maxproc, $use_fastcgi, true);
425
426
	/* attempt to start lighttpd */
427
	$res = mwexec("/usr/local/sbin/lighttpd -f {$g['varetc_path']}/lighty-CaptivePortal.conf");
428
429
	/* fire up https instance */
430
	if (isset($config['captiveportal']['httpslogin']))
431
		$res = mwexec("/usr/local/sbin/lighttpd -f {$g['varetc_path']}/lighty-CaptivePortal-SSL.conf");
432
}
433
434 847e5e82 Scott Ullrich
/* reinit will disconnect all users, be careful! */
435 1d9e9cca Ermal
function captiveportal_init_rules($reinit = false) {
436 3db19cf1 Scott Ullrich
	global $config, $g;
437 36254e4a Scott Ullrich
438 769e254e Ermal
	if (!isset($config['captiveportal']['enable']))
439
		return;
440
441
	$cpips = array();
442
	$ifaces = get_configured_interface_list();
443
	foreach ($ifaces as $kiface => $kiface2) {
444
		$tmpif = get_real_interface($kiface);
445
		pfSense_interface_flags($tmpif, -IFF_IPFW_FILTER);
446
	}
447
	$cpinterfaces = explode(",", $config['captiveportal']['interface']);
448
	$firsttime = 0;
449
	foreach ($cpinterfaces as $cpifgrp) {
450
		if (!isset($ifaces[$cpifgrp]))
451
			continue;
452
		$tmpif = get_real_interface($cpifgrp);
453
		if (!empty($tmpif)) {
454
			if ($firsttime > 0)
455
				$cpinterface .= " or ";
456
			$cpinterface .= "via {$tmpif}";
457
			$firsttime = 1;
458
			$cpipm = get_interface_ip($cpifgrp);
459
			if (is_ipaddr($cpipm)) {
460
				$carpif = link_ip_to_carp_interface($cpipm);
461
				if (!empty($carpif)) {
462
					$carpsif = explode(" ", $carpif);
463
					foreach ($carpsif as $cpcarp) {
464
						pfSense_interface_flags($cpcarp, IFF_IPFW_FILTER);
465
						$carpip = find_interface_ip($cpcarp);
466
						if (is_ipaddr($carpip))
467
							$cpips[] = $carpip;
468
					}
469
				}
470
				$cpips[] = $cpipm;
471
				pfSense_interface_flags($tmpif, IFF_IPFW_FILTER);
472
			}
473
		}
474
	}
475
	if (count($cpips) > 0) {
476
		$cpactive = true;
477
		$cpinterface = "{ {$cpinterface} } ";
478 5060dea7 Scott Ullrich
		} else
479 769e254e Ermal
		return false;
480
481 eade409a Ermal
	if ($reinit == false)
482
		$captiveportallck = lock('captiveportal');
483
484 1d9e9cca Ermal
	/* init dummynet/ipfw rules number database */
485
	captiveportal_init_ipfw_ruleno();
486
487 769e254e Ermal
	/* make sure ipfw is loaded */
488
	if (!is_module_loaded("ipfw.ko"))
489
		filter_load_ipfw();
490
	/* Always load dummynet now that even allowed ip and mac passthrough use it. */
491
	if (!is_module_loaded("dummynet.ko"))
492
		mwexec("/sbin/kldload dummynet");
493
494 5060dea7 Scott Ullrich
	$cprules =	"add 65291 set 1 allow pfsync from any to any\n";
495 b01792a0 Ermal
	$cprules .= "add 65292 set 1 allow carp from any to any\n";
496 181a843c Scott Ullrich
497 3db19cf1 Scott Ullrich
	$cprules .= <<<EOD
498 b01792a0 Ermal
# add 65300 set 1 skipto 65534 all from any to any not layer2
499 3db19cf1 Scott Ullrich
# layer 2: pass ARP
500 b01792a0 Ermal
add 65301 set 1 pass layer2 mac-type arp
501 b9d1d810 Scott Ullrich
# pfsense requires for WPA
502 b01792a0 Ermal
add 65302 set 1 pass layer2 mac-type 0x888e
503
add 65303 set 1 pass layer2 mac-type 0x88c7
504 684c787e Scott Ullrich
505 36254e4a Scott Ullrich
# PPP Over Ethernet Discovery Stage
506 b01792a0 Ermal
add 65304 set 1 pass layer2 mac-type 0x8863
507 684c787e Scott Ullrich
# PPP Over Ethernet Session Stage
508 b01792a0 Ermal
add 65305 set 1 pass layer2 mac-type 0x8864
509 55f5c311 Ermal Lu?i
# Allow WPA
510 b01792a0 Ermal
add 65306 set 1 pass layer2 mac-type 0x888e
511 684c787e Scott Ullrich
512 3db19cf1 Scott Ullrich
# layer 2: block anything else non-IP
513 b01792a0 Ermal
add 65307 set 1 deny layer2 not mac-type ip
514 3db19cf1 Scott Ullrich
515
EOD;
516
517 b01792a0 Ermal
	$rulenum = 65310;
518 fb516dda Chris Buechler
	$ipcount = 0;
519 0ba17c67 Ermal
	$ips = "";
520 fb516dda Chris Buechler
	foreach ($cpips as $cpip) {
521
		if($ipcount == 0) {
522
			$ips = "{$cpip} ";
523
		} else {
524
			$ips .= "or {$cpip} ";
525
		}
526
		$ipcount++;
527
	}
528 0ba17c67 Ermal
	$ips = "{ 255.255.255.255 or {$ips} }";
529 746e60c9 Ermal
	$cprules .= "add {$rulenum} set 1 pass ip from any to {$ips} in\n";
530 2f27dffd Ermal
	$rulenum++;
531 746e60c9 Ermal
	$cprules .= "add {$rulenum} set 1 pass ip from {$ips} to any out\n";
532 2f27dffd Ermal
	$rulenum++;
533
	$cprules .= "add {$rulenum} set 1 pass icmp from {$ips} to any out icmptype 0\n";
534
	$rulenum++;
535
	$cprules .= "add {$rulenum} set 1 pass icmp from any to {$ips} in icmptype 8 \n";
536
	$rulenum++;
537 b01792a0 Ermal
	/* Allowed ips */
538
	$cprules .= "add {$rulenum} allow ip from table(3) to any in\n";
539
	$rulenum++;
540
	$cprules .= "add {$rulenum} allow ip from any to table(4) out\n";
541
	$rulenum++;
542
	$cprules .= "add {$rulenum} pipe tablearg ip from table(5) to any in\n";
543
	$rulenum++;
544
	$cprules .= "add {$rulenum} pipe tablearg ip from any to table(6) out\n";
545
	$rulenum++;
546
	$cprules .= "add {$rulenum} allow ip from any to table(7) in\n";
547
	$rulenum++;
548
	$cprules .= "add {$rulenum} allow ip from table(8) to any out\n";
549
	$rulenum++;
550
	$cprules .= "add {$rulenum} pipe tablearg ip from any to table(9) in\n";
551
	$rulenum++;
552
	$cprules .= "add {$rulenum} pipe tablearg ip from table(10) to any out\n";
553
	$rulenum++;
554
555
	/* Authenticated users rules. */
556 f9f71ad3 Ermal Lu?i
	if (isset($config['captiveportal']['peruserbw'])) {
557 6ce61a8f Ermal
		$cprules .= "add {$rulenum} set 1 pipe tablearg ip from table(1) to any in\n";
558 f9f71ad3 Ermal Lu?i
		$rulenum++;
559 6ce61a8f Ermal
		$cprules .= "add {$rulenum} set 1 pipe tablearg ip from any to table(2) out\n";
560 f9f71ad3 Ermal Lu?i
		$rulenum++;
561
	} else {
562 6ce61a8f Ermal
		$cprules .= "add {$rulenum} set 1 allow ip from table(1) to any in\n";
563 5060dea7 Scott Ullrich
		$rulenum++;
564
		$cprules .= "add {$rulenum} set 1 allow ip from any to table(2) out\n";
565
		$rulenum++;
566 f9f71ad3 Ermal Lu?i
	}
567
	
568 5060dea7 Scott Ullrich
	   $cprules .= <<<EOD
569 5480497a Scott Ullrich
570 d44bccc7 Scott Ullrich
# redirect non-authenticated clients to captive portal
571 6ce61a8f Ermal
add 65531 set 1 fwd 127.0.0.1,8000 tcp from any to any in
572 3db19cf1 Scott Ullrich
# let the responses from the captive portal web server back out
573 6ce61a8f Ermal
add 65532 set 1 pass tcp from any to any out
574 3db19cf1 Scott Ullrich
# block everything else
575 6ce61a8f Ermal
add 65533 set 1 deny all from any to any
576 3db19cf1 Scott Ullrich
# pass everything else on layer2
577 6ce61a8f Ermal
add 65534 set 1 pass all from any to any layer2
578 3db19cf1 Scott Ullrich
579
EOD;
580
581 769e254e Ermal
	/* generate passthru mac database */
582
	$cprules .= captiveportal_passthrumac_configure(true);
583
	$cprules .= "\n";
584 55c18b30 Scott Ullrich
585 769e254e Ermal
	/* allowed ipfw rules to make allowed ip work */
586
	$cprules .= captiveportal_allowedip_configure();
587
588 55c18b30 Scott Ullrich
	/* allowed ipfw rules to make allowed hostnames work */
589
	$cprules .= captiveportal_allowedhostname_configure();
590
	
591 769e254e Ermal
	/* load rules */
592 1d9e9cca Ermal
	if ($reinit == true)
593
		$cprules = "table all flush\nflush\n{$cprules}";
594
	else {
595
		$tmprules = "table 3 flush\n";
596
		$tmprules .= "table 4 flush\n";
597
		$tmprules .= "table 5 flush\n";
598
		$tmprules .= "table 6 flush\n";
599
		$tmprules .= "table 7 flush\n";
600
		$tmprules .= "table 8 flush\n";
601
		$tmprules .= "table 9 flush\n";
602
		$tmprules .= "table 10 flush\n";
603
		$tmprules .= "flush\n";
604
		$cprules = "{$tmprules}\n{$cprules}";
605
	}
606 eade409a Ermal
607
	file_put_contents("{$g['tmp_path']}/ipfw.cp.rules", $cprules);
608
	mwexec("/sbin/ipfw -q {$g['tmp_path']}/ipfw.cp.rules", true);
609 0b108eda Scott Ullrich
	//@unlink("{$g['tmp_path']}/ipfw.cp.rules");
610 eade409a Ermal
611
	if ($reinit == false)
612
		unlock($captiveportallck);
613
614 9634e95b Ermal
	/* activate ipfw(4) so CP can work */
615 769e254e Ermal
	mwexec("/sbin/sysctl net.link.ether.ipfw=1");
616
617
	return $cprules;
618 3db19cf1 Scott Ullrich
}
619
620 eb7aa263 Ermal
/* remove clients that have been around for longer than the specified amount of time
621
 * db file structure:
622
 * timestamp,ipfw_rule_no,clientip,clientmac,username,sessionid,password,session_timeout,idle_timeout,session_terminate_time
623
 * (password is in Base64 and only saved when reauthentication is enabled)
624
 */
625 5b237745 Scott Ullrich
function captiveportal_prune_old() {
626 5060dea7 Scott Ullrich
	global $g, $config;
627 23c4f978 Scott Ullrich
628 5060dea7 Scott Ullrich
	/* check for expired entries */
629
	if (empty($config['captiveportal']['timeout']) ||
630 eb7aa263 Ermal
	!is_numeric($config['captiveportal']['timeout']))
631 5060dea7 Scott Ullrich
		$timeout = 0;
632
	else
633
		$timeout = $config['captiveportal']['timeout'] * 60;
634 eb7aa263 Ermal
635 5060dea7 Scott Ullrich
	if (empty($config['captiveportal']['idletimeout']) ||
636 eb7aa263 Ermal
	!is_numeric($config['captiveportal']['idletimeout']))
637 5060dea7 Scott Ullrich
		$idletimeout = 0;
638
	else
639
		$idletimeout = $config['captiveportal']['idletimeout'] * 60;
640 23c4f978 Scott Ullrich
641 5060dea7 Scott Ullrich
	if (!$timeout && !$idletimeout && !isset($config['captiveportal']['reauthenticate']) && 
642 eb7aa263 Ermal
	!isset($config['captiveportal']['radiussession_timeout']) && !isset($config['voucher']['enable']))
643 5060dea7 Scott Ullrich
		return;
644
645 006802ab Ermal
	$radiusservers = captiveportal_get_radius_servers();
646
647 5060dea7 Scott Ullrich
	/* read database */
648
	$cpdb = captiveportal_read_db();
649
650
	/*	To make sure we iterate over ALL accounts on every run the count($cpdb) is moved
651
	 *	outside of the loop. Otherwise the loop would evaluate count() on every iteration
652
	 *	and since $i would increase and count() would decrement they would meet before we
653
	 *	had a chance to iterate over all accounts.
654
	 */
655
	$unsetindexes = array();
656 5ebe85e9 Ermal
	$voucher_needs_sync = false;
657 b09c2d86 Ermal
	/* 
658
	 * Snapshot the time here to use for calculation to speed up the process.
659
	 * If something is missed next run will catch it!
660
	 */
661
	$pruning_time = time();
662
	$stop_time = $pruning_time;
663 3e5c0ab7 Ermal
	foreach ($cpdb as $cpentry) {
664 5060dea7 Scott Ullrich
665
		$timedout = false;
666
		$term_cause = 1;
667
668
		/* hard timeout? */
669
		if ($timeout) {
670 b09c2d86 Ermal
			if (($pruning_time - $cpentry[0]) >= $timeout) {
671 5060dea7 Scott Ullrich
				$timedout = true;
672
				$term_cause = 5; // Session-Timeout
673
			}
674 eb7aa263 Ermal
		}
675 23c4f978 Scott Ullrich
676 5060dea7 Scott Ullrich
		/* Session-Terminate-Time */
677 3e5c0ab7 Ermal
		if (!$timedout && !empty($cpentry[9])) {
678 b09c2d86 Ermal
			if ($pruning_time >= $cpentry[9]) {
679 5060dea7 Scott Ullrich
				$timedout = true;
680
				$term_cause = 5; // Session-Timeout
681
			}
682
		}
683
684
		/* check if the radius idle_timeout attribute has been set and if its set change the idletimeout to this value */
685 3e5c0ab7 Ermal
		$uidletimeout = (is_numeric($cpentry[8])) ? $cpentry[8] : $idletimeout;
686 5060dea7 Scott Ullrich
		/* if an idle timeout is specified, get last activity timestamp from ipfw */
687
		if (!$timedout && $uidletimeout) {
688 3e5c0ab7 Ermal
			$lastact = captiveportal_get_last_activity($cpentry[2]);
689 5060dea7 Scott Ullrich
			/*	If the user has logged on but not sent any traffic they will never be logged out.
690
			 *	We "fix" this by setting lastact to the login timestamp. 
691
			 */
692 3e5c0ab7 Ermal
			$lastact = $lastact ? $lastact : $cpentry[0];
693 b09c2d86 Ermal
			if ($lastact && (($pruning_time - $lastact) >= $uidletimeout)) {
694 5060dea7 Scott Ullrich
				$timedout = true;
695
				$term_cause = 4; // Idle-Timeout
696
				$stop_time = $lastact; // Entry added to comply with WISPr
697
			}
698 336e3c1c Charlie
		}
699
700 5060dea7 Scott Ullrich
		/* if vouchers are configured, activate session timeouts */
701 f45075dd Ermal
		if (!$timedout && isset($config['voucher']['enable']) && !empty($cpentry[7])) {
702 b09c2d86 Ermal
			if ($pruning_time >= ($cpentry[0] + $cpentry[7])) {
703 5060dea7 Scott Ullrich
				$timedout = true;
704
				$term_cause = 5; // Session-Timeout
705 5ebe85e9 Ermal
				$voucher_needs_sync = true;
706 5060dea7 Scott Ullrich
			}
707
		}
708
709
		/* if radius session_timeout is enabled and the session_timeout is not null, then check if the user should be logged out */
710 3e5c0ab7 Ermal
		if (!$timedout && isset($config['captiveportal']['radiussession_timeout']) && !empty($cpentry[7])) {
711 b09c2d86 Ermal
			if ($pruning_time >= ($cpentry[0] + $cpentry[7])) {
712 5060dea7 Scott Ullrich
				$timedout = true;
713
				$term_cause = 5; // Session-Timeout
714
			}
715
		}
716
717
		if ($timedout) {
718 3e5c0ab7 Ermal
			captiveportal_disconnect($cpentry, $radiusservers,$term_cause,$stop_time);
719
			captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "TIMEOUT");
720
			$unsetindexes[] = $cpentry[5];
721 5060dea7 Scott Ullrich
		}
722
723
		/* do periodic RADIUS reauthentication? */
724
		if (!$timedout && !empty($radiusservers)) {
725
			if (isset($config['captiveportal']['radacct_enable'])) {
726
				if ($config['captiveportal']['reauthenticateacct'] == "stopstart") {
727
					/* stop and restart accounting */
728 3e5c0ab7 Ermal
					RADIUS_ACCOUNTING_STOP($cpentry[1], // ruleno
729
						$cpentry[4], // username
730
						$cpentry[5], // sessionid
731
						$cpentry[0], // start time
732 5060dea7 Scott Ullrich
						$radiusservers,
733 3e5c0ab7 Ermal
						$cpentry[2], // clientip
734
						$cpentry[3], // clientmac
735 5060dea7 Scott Ullrich
						10); // NAS Request
736 3e5c0ab7 Ermal
					exec("/sbin/ipfw table 1 entryzerostats {$cpentry[2]}");
737
					exec("/sbin/ipfw table 2 entryzerostats {$cpentry[2]}");
738
					RADIUS_ACCOUNTING_START($cpentry[1], // ruleno
739
						$cpentry[4], // username
740
						$cpentry[5], // sessionid
741 5060dea7 Scott Ullrich
						$radiusservers,
742 3e5c0ab7 Ermal
						$cpentry[2], // clientip
743
						$cpentry[3]); // clientmac
744 5060dea7 Scott Ullrich
				} else if ($config['captiveportal']['reauthenticateacct'] == "interimupdate") {
745 3e5c0ab7 Ermal
					RADIUS_ACCOUNTING_STOP($cpentry[1], // ruleno
746
						$cpentry[4], // username
747
						$cpentry[5], // sessionid
748
						$cpentry[0], // start time
749 5060dea7 Scott Ullrich
						$radiusservers,
750 3e5c0ab7 Ermal
						$cpentry[2], // clientip
751
						$cpentry[3], // clientmac
752 5060dea7 Scott Ullrich
						10, // NAS Request
753
						true); // Interim Updates
754
				}
755
			}
756
757
			/* check this user against RADIUS again */
758
			if (isset($config['captiveportal']['reauthenticate'])) {
759 3e5c0ab7 Ermal
				$auth_list = RADIUS_AUTHENTICATION($cpentry[4], // username
760
					base64_decode($cpentry[6]), // password
761 5060dea7 Scott Ullrich
					$radiusservers,
762 3e5c0ab7 Ermal
					$cpentry[2], // clientip
763
					$cpentry[3], // clientmac
764
					$cpentry[1]); // ruleno
765 5060dea7 Scott Ullrich
				if ($auth_list['auth_val'] == 3) {
766 3e5c0ab7 Ermal
					captiveportal_disconnect($cpentry, $radiusservers, 17);
767
					captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "RADIUS_DISCONNECT", $auth_list['reply_message']);
768
					$unsetindexes[] = $cpentry[5];
769 5060dea7 Scott Ullrich
				}
770
			}
771
		}
772
	}
773 23c4f978 Scott Ullrich
774 5ebe85e9 Ermal
	if ($voucher_needs_sync == true)
775
		/* Triger a sync of the vouchers on config */
776
		send_event("service sync vouchers");
777
778 5060dea7 Scott Ullrich
	/* write database */
779 e92916d6 Ermal
	if (!empty($unsetindexes))
780
		captiveportal_write_db($cpdb, false, $unsetindexes);
781 5b237745 Scott Ullrich
}
782
783 3db19cf1 Scott Ullrich
/* remove a single client according to the DB entry */
784 0bd34ed6 Scott Ullrich
function captiveportal_disconnect($dbent, $radiusservers,$term_cause = 1,$stop_time = null) {
785 d99f7864 Scott Ullrich
	global $g, $config;
786
787
	$stop_time = (empty($stop_time)) ? time() : $stop_time;
788
789
	/* this client needs to be deleted - remove ipfw rules */
790 40b48c6c Ermal Lu?i
	if (isset($config['captiveportal']['radacct_enable']) && !empty($radiusservers)) {
791 d99f7864 Scott Ullrich
		RADIUS_ACCOUNTING_STOP($dbent[1], // ruleno
792 5060dea7 Scott Ullrich
			$dbent[4], // username
793
			$dbent[5], // sessionid
794
			$dbent[0], // start time
795
			$radiusservers,
796
			$dbent[2], // clientip
797
			$dbent[3], // clientmac
798
			$term_cause, // Acct-Terminate-Cause
799
			false,
800
			$stop_time);
801 d99f7864 Scott Ullrich
	}
802 32c392aa Ermal
	
803
	if (is_ipaddr($dbent[2])) {
804
		/* Delete client's ip entry from tables 3 and 4. */
805
		mwexec("/sbin/ipfw table 1 delete {$dbent[2]}");
806
		mwexec("/sbin/ipfw table 2 delete {$dbent[2]}");
807
		/* XXX: Redundant?! Ensure all pf(4) states are killed. */
808
		mwexec("pfctl -k {$dbent[2]}");
809
		mwexec("pfctl -K {$dbent[2]}");
810
	}
811 f9f71ad3 Ermal Lu?i
812
	/* 
813
	* These are the pipe numbers we use to control traffic shaping for each logged in user via captive portal
814
	* We could get an error if the pipe doesn't exist but everything should still be fine
815
	*/
816
	if (isset($config['captiveportal']['peruserbw'])) {
817
		mwexec("/sbin/ipfw pipe " . ($dbent[1]+20000) . " delete");
818
		mwexec("/sbin/ipfw pipe " . ($dbent[1]+20001) . " delete");
819
	}
820 7a7abeba Scott Ullrich
821 32c392aa Ermal
	/* Release the ruleno so it can be reallocated to new clients. */
822
	captiveportal_free_ipfw_ruleno($dbent[1]);
823 d322e3b3 Scott Ullrich
824
	// XMLRPC Call over to the master Voucher node
825 adcf909a Ermal
	if(!empty($config['voucher']['vouchersyncdbip'])) {
826
		$syncip   = $config['voucher']['vouchersyncdbip'];
827
		$syncport = $config['voucher']['vouchersyncport'];
828
		$syncpass = $config['voucher']['vouchersyncpass'];
829
		$vouchersyncusername = $config['voucher']['vouchersyncusername'];
830 f989aa5b Ermal
		$remote_status = xmlrpc_sync_voucher_disconnect($dbent, $syncip, $syncport, $syncpass, $vouchersyncusername, $term_cause, $stop_time);
831 d322e3b3 Scott Ullrich
	}
832
833 3db19cf1 Scott Ullrich
}
834 12ee8fe4 Scott Ullrich
835 006802ab Ermal
/* remove a single client by sessionid */
836
function captiveportal_disconnect_client($sessionid, $term_cause = 1, $logoutReason = "LOGOUT") {
837 d99f7864 Scott Ullrich
	global $g, $config;
838 36254e4a Scott Ullrich
839 d99f7864 Scott Ullrich
	$radiusservers = captiveportal_get_radius_servers();
840 006802ab Ermal
	$unsetindex = array();
841
842
	/* read database */
843 7d6be855 Ermal
	$cpdb = captiveportal_read_db();
844 d99f7864 Scott Ullrich
845
	/* find entry */
846 006802ab Ermal
	if (isset($cpdb[$sessionid])) {
847
		$cpentry = $cpdb[$sessionid];
848
		/* write database */
849
		$unsetindex[] = $sessionid;
850 7d6be855 Ermal
		captiveportal_write_db($cpdb, false, $unsetindex);
851 36254e4a Scott Ullrich
852 006802ab Ermal
		captiveportal_disconnect($cpentry, $radiusservers, $term_cause);
853
		captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "DISCONNECT");
854
	}		
855 5b237745 Scott Ullrich
}
856
857
/* send RADIUS acct stop for all current clients */
858 d31bc32a Ermal
function captiveportal_radius_stop_all() {
859
	global $config;
860 d99f7864 Scott Ullrich
861
	if (!isset($config['captiveportal']['radacct_enable']))
862
		return;
863
864
	$radiusservers = captiveportal_get_radius_servers();
865 40b48c6c Ermal Lu?i
	if (!empty($radiusservers)) {
866 d31bc32a Ermal
		$cpdb = captiveportal_read_db();
867
		foreach ($cpdb as $cpentry) {
868
			RADIUS_ACCOUNTING_STOP($cpentry[1], // ruleno
869 5060dea7 Scott Ullrich
				$cpentry[4], // username
870
				$cpentry[5], // sessionid
871
				$cpentry[0], // start time
872
				$radiusservers,
873
				$cpentry[2], // clientip
874
				$cpentry[3], // clientmac
875
				7); // Admin Reboot
876 d99f7864 Scott Ullrich
		}
877
	}
878 5b237745 Scott Ullrich
}
879
880 d5ae560d Ermal
function captiveportal_passthrumac_configure_entry($macent) {
881
	$rules = "";
882 5060dea7 Scott Ullrich
	$enBwup = isset($macent['bw_up']);
883
	$enBwdown = isset($macent['bw_down']);
884 d5ae560d Ermal
	$actionup = "allow";
885
	$actiondown = "allow";
886
887 5060dea7 Scott Ullrich
	if ($enBwup && $enBwdown)
888
		$ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, true);
889
	else
890
		$ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, false);
891 d5ae560d Ermal
892
	if ($enBwup) {
893 5060dea7 Scott Ullrich
		$bw_up = $ruleno + 20000;
894
		$rules .= "pipe {$bw_up} config bw {$macent['bw_up']}Kbit/s queue 100\n";
895 d5ae560d Ermal
		$actionup = "pipe {$bw_up}";
896 5060dea7 Scott Ullrich
	}
897
	if ($enBwdown) {
898 d5ae560d Ermal
		$bw_down = $ruleno + 20001;
899
		$rules .= "pipe {$bw_down} config bw {$macent['bw_down']}Kbit/s queue 100\n";
900
		$actiondown = "pipe {$bw_down}";
901 5060dea7 Scott Ullrich
	}
902 ffcf81bb Chris Buechler
	$rules .= "add {$ruleno} {$actiondown} ip from any to any MAC {$macent['mac']} any\n";
903 d5ae560d Ermal
	$ruleno++;
904 ffcf81bb Chris Buechler
	$rules .= "add {$ruleno} {$actionup} ip from any to any MAC any {$macent['mac']}\n";
905 d5ae560d Ermal
906
	return $rules;
907
}
908
909 dedf51a2 Ermal Lu?i
function captiveportal_passthrumac_configure($lock = false) {
910 5b237745 Scott Ullrich
	global $config, $g;
911 36254e4a Scott Ullrich
912 d5ae560d Ermal
	$rules = "";
913 36254e4a Scott Ullrich
914 5b237745 Scott Ullrich
	if (is_array($config['captiveportal']['passthrumac'])) {
915 1dbe445a Ermal
		$macdb = array();
916 5b237745 Scott Ullrich
		foreach ($config['captiveportal']['passthrumac'] as $macent) {
917 d5ae560d Ermal
			$rules .= captiveportal_passthrumac_configure_entry($macent);
918 1dbe445a Ermal
			$macdb[$macent['mac']]['active']  = true;
919
920 5b237745 Scott Ullrich
		}
921
	}
922 0bd34ed6 Scott Ullrich
923 d5ae560d Ermal
	return $rules;
924 5b237745 Scott Ullrich
}
925
926 fac13a5e Ermal
function captiveportal_passthrumac_findbyname($username) {
927
	global $config;
928
929
	if (is_array($config['captiveportal']['passthrumac'])) {
930
		foreach ($config['captiveportal']['passthrumac'] as $macent) {
931
			if ($macent['username'] == $username)
932
				return $macent;
933
		}
934
	}
935
	return NULL;
936
}
937
938 b01792a0 Ermal
/* 
939
 * table (3=IN)/(4=OUT) hold allowed ip's without bw limits
940
 * table (5=IN)/(6=OUT) hold allowed ip's with bw limit.
941
 */
942
function captiveportal_allowedip_configure_entry($ipent) {
943
944 8b73cc7e Scott Ullrich
	/* This function can deal with hostname or ipaddress */
945 0b108eda Scott Ullrich
	if($ipent['ip']) 	
946
		$ipaddress = $ipent['ip'];
947
948 8b73cc7e Scott Ullrich
	/*  Instead of copying this entire function for something
949
	 *  easy such as hostname vs ip address add this check
950
	 */
951 0b108eda Scott Ullrich
	if($ipent['hostname']) {
952
		$ipaddress = gethostbyname($ipent['hostname']);
953
		if(!is_ipaddr($ipaddress)) 
954
			return;
955
	}
956
957 b01792a0 Ermal
	$rules = "";
958 0b108eda Scott Ullrich
	$enBwup = intval($ipent['bw_up']);
959
	$enBwdown = intval($ipent['bw_down']);
960 b01792a0 Ermal
	$bw_up = "";
961 5060dea7 Scott Ullrich
	$bw_down = "";
962
	$tablein = array();
963
	$tableout = array();
964 b01792a0 Ermal
965 8b73cc7e Scott Ullrich
	if (intval($enBwup) > 0 or intval($enBwdown) > 0)
966 b01792a0 Ermal
		$ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, true);
967
	else
968
		$ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, false);
969
970 5060dea7 Scott Ullrich
	if ($ipent['dir'] == "from") {
971
		if ($enBwup)
972
			$tablein[] = 5;
973
		else
974
			$tablein[] = 3;
975
		if ($enBwdown)
976
			$tableout[] = 6;
977
		else
978
			$tableout[] = 4;
979
	} else if ($ipent['dir'] == "to") {
980
		if ($enBwup)
981
			$tablein[] = 9;
982
		else
983
			$tablein[] = 7;
984
		if ($enBwdown)
985
			$tableout[] = 10;
986
		else
987
			$tableout[] = 8;
988
	} else if ($ipent['dir'] == "both") {
989
		if ($enBwup) {
990
			$tablein[] = 5;
991
			$tablein[] = 9;
992
		} else {
993
			$tablein[] = 3;
994
			$tablein[] = 7;
995
		}
996
		if ($enBwdown) {
997
			$tableout[] = 6;
998
			$tableout[] = 10;
999
		} else {
1000
			$tableout[] = 4;
1001
			$tableout[] = 8;
1002
		}
1003
	}
1004
	if ($enBwup) {
1005
		$bw_up = $ruleno + 20000;
1006
		$rules .= "pipe {$bw_up} config bw {$ipent['bw_up']}Kbit/s queue 100\n";
1007
	}
1008 d6a0379d Ermal
	$subnet = "";
1009
	if (!empty($ipent['sn']))
1010
		$subnet = "/{$ipent['sn']}";
1011 b01792a0 Ermal
	foreach ($tablein as $table)
1012 0b108eda Scott Ullrich
		$rules .= "table {$table} add {$ipaddress}{$subnet} {$bw_up}\n";
1013 5060dea7 Scott Ullrich
	if ($enBwdown) {
1014
		$bw_down = $ruleno + 20001;
1015
		$rules .= "pipe {$bw_down} config bw {$ipent['bw_down']}Kbit/s queue 100\n";
1016
	}
1017
	foreach ($tableout as $table)
1018 0b108eda Scott Ullrich
		$rules .= "table {$table} add {$ipaddress}{$subnet} {$bw_down}\n";
1019 b01792a0 Ermal
1020
	return $rules;
1021
}
1022
1023 f23a6091 Scott Ullrich
/* 
1024
	Adds a dnsfilter entry and watches for hostname changes.
1025
	A change results in reloading the ruleset.
1026
*/
1027 0b108eda Scott Ullrich
function setup_dnsfilter_entries() {
1028 55c18b30 Scott Ullrich
	global $g, $config;
1029 422b8b4e Ermal
1030 f23a6091 Scott Ullrich
	$cp_filterdns_filename = "{$g['varetc_path']}/filterdns-captiveportal.conf";
1031 422b8b4e Ermal
	$cp_filterdns_conf = "";
1032
	if (is_array($config['captiveportal']['allowedhostname'])) {
1033
		foreach ($config['captiveportal']['allowedhostname'] as $hostnameent)  {
1034 e35d6cda Ermal
			$cp_filterdns_conf .= "ipfw {$hostnameent['hostname']} 3\n";
1035
			$cp_filterdns_conf .= "ipfw {$hostnameent['hostname']} 4\n";
1036
			$cp_filterdns_conf .= "ipfw {$hostnameent['hostname']} 7\n";
1037
			$cp_filterdns_conf .= "ipfw {$hostnameent['hostname']} 8\n";
1038 422b8b4e Ermal
		}
1039
	}
1040
	file_put_contents($cp_filterdns_filename, $cp_filterdns_conf);
1041 f7f22750 Ermal
	killbypid("{$g['tmp_path']}/filterdns-cpah.pid");
1042
	mwexec("/usr/local/sbin/filterdns -p {$g['tmp_path']}/filterdns-cpah.pid -i 300 -c {$cp_filterdns_filename} -d 1");
1043 f23a6091 Scott Ullrich
}
1044
1045
function captiveportal_allowedhostname_configure() {
1046
	global $config, $g;
1047
1048 0b108eda Scott Ullrich
	$rules = "\n# captiveportal_allowedhostname_configure()\n";
1049 f23a6091 Scott Ullrich
	setup_dnsfilter_entries();
1050 55c18b30 Scott Ullrich
	if (is_array($config['captiveportal']['allowedhostname'])) {
1051 0b108eda Scott Ullrich
		foreach ($config['captiveportal']['allowedhostname'] as $hostnameent) 
1052
			$rules .= captiveportal_allowedip_configure_entry($hostnameent);
1053 55c18b30 Scott Ullrich
	}
1054 f23a6091 Scott Ullrich
	return $rules;
1055
}
1056
1057 cb0a2913 Ermal Lu?i
function captiveportal_allowedip_configure() {
1058 5b237745 Scott Ullrich
	global $config, $g;
1059 36254e4a Scott Ullrich
1060 6ce61a8f Ermal
	$rules = "";
1061 5b237745 Scott Ullrich
	if (is_array($config['captiveportal']['allowedip'])) {
1062 55c18b30 Scott Ullrich
		foreach ($config['captiveportal']['allowedip'] as $ipent) 
1063 b01792a0 Ermal
			$rules .= captiveportal_allowedip_configure_entry($ipent);
1064 cb0a2913 Ermal Lu?i
	}
1065 36254e4a Scott Ullrich
1066 6ce61a8f Ermal
	return $rules;
1067 5b237745 Scott Ullrich
}
1068
1069 2d53158f stompro
/* get last activity timestamp given client IP address */
1070 f9f71ad3 Ermal Lu?i
function captiveportal_get_last_activity($ip) {
1071 36254e4a Scott Ullrich
1072 d99f7864 Scott Ullrich
	$ipfwoutput = "";
1073 36254e4a Scott Ullrich
1074 6ce61a8f Ermal
	exec("/sbin/ipfw table 1 entrystats {$ip} 2>/dev/null", $ipfwoutput);
1075 f9f71ad3 Ermal Lu?i
	/* Reading only from one of the tables is enough of approximation. */
1076 d99f7864 Scott Ullrich
	if ($ipfwoutput[0]) {
1077
		$ri = explode(" ", $ipfwoutput[0]);
1078 f9f71ad3 Ermal Lu?i
		if ($ri[4])
1079
			return $ri[4];
1080 d99f7864 Scott Ullrich
	}
1081 36254e4a Scott Ullrich
1082 d99f7864 Scott Ullrich
	return 0;
1083 5b237745 Scott Ullrich
}
1084
1085 d31bc32a Ermal
function captiveportal_init_radius_servers() {
1086
	global $config, $g;
1087
1088
	/* generate radius server database */
1089
	if ($config['captiveportal']['radiusip'] && (!isset($config['captiveportal']['auth_method']) ||
1090 5060dea7 Scott Ullrich
		($config['captiveportal']['auth_method'] == "radius"))) {
1091 d31bc32a Ermal
		$radiusip = $config['captiveportal']['radiusip'];
1092
		$radiusip2 = ($config['captiveportal']['radiusip2']) ? $config['captiveportal']['radiusip2'] : null;
1093
1094
		if ($config['captiveportal']['radiusport'])
1095
			$radiusport = $config['captiveportal']['radiusport'];
1096
		else
1097
			$radiusport = 1812;
1098
		if ($config['captiveportal']['radiusacctport'])
1099
			$radiusacctport = $config['captiveportal']['radiusacctport'];
1100
		else
1101
			$radiusacctport = 1813;
1102
		if ($config['captiveportal']['radiusport2'])
1103
			$radiusport2 = $config['captiveportal']['radiusport2'];
1104
		else
1105
			$radiusport2 = 1812;
1106
		$radiuskey = $config['captiveportal']['radiuskey'];
1107
		$radiuskey2 = ($config['captiveportal']['radiuskey2']) ? $config['captiveportal']['radiuskey2'] : null;
1108
1109
		$cprdsrvlck = lock('captiveportalradius', LOCK_EX);
1110
		$fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db", "w");
1111
		if (!$fd) {
1112
			captiveportal_syslog("Error: cannot open radius DB file in captiveportal_configure().\n");
1113
			unlock($cprdsrvlck);
1114
			return 1;
1115
		} else if (isset($radiusip2, $radiuskey2))
1116
			fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey . "\n"
1117
			. $radiusip2 . "," . $radiusport2 . "," . $radiusacctport . "," . $radiuskey2);
1118
		else
1119
			fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey);
1120
		fclose($fd);
1121
		unlock($cprdsrvlck);
1122
	}
1123
}
1124
1125 5b237745 Scott Ullrich
/* read RADIUS servers into array */
1126
function captiveportal_get_radius_servers() {
1127 ac07425a Ermal
	global $g;
1128
1129
	$cprdsrvlck = lock('captiveportalradius');
1130
	if (file_exists("{$g['vardb_path']}/captiveportal_radius.db")) {
1131
		$radiusservers = array();
1132
		$cpradiusdb = file("{$g['vardb_path']}/captiveportal_radius.db", 
1133
		FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
1134
		if ($cpradiusdb) {
1135
			foreach($cpradiusdb as $cpradiusentry) {
1136
				$line = trim($cpradiusentry);
1137
				if ($line) {
1138
					$radsrv = array();
1139
					list($radsrv['ipaddr'],$radsrv['port'],$radsrv['acctport'],$radsrv['key']) = explode(",",$line);
1140
					$radiusservers[] = $radsrv;
1141 5060dea7 Scott Ullrich
				}
1142
			}
1143 2f70eac7 Ermal Lu?i
		}
1144 60b66b60 Ermal
		unlock($cprdsrvlck);
1145 ac07425a Ermal
		return $radiusservers;
1146
	}
1147
1148
	unlock($cprdsrvlck);
1149
	return false;
1150 5b237745 Scott Ullrich
}
1151
1152 3db19cf1 Scott Ullrich
/* log successful captive portal authentication to syslog */
1153
/* part of this code from php.net */
1154 0bd34ed6 Scott Ullrich
function captiveportal_logportalauth($user,$mac,$ip,$status, $message = null) {
1155 d99f7864 Scott Ullrich
	// Log it
1156
	if (!$message)
1157 f56a73f1 Scott Ullrich
		$message = "$status: $user, $mac, $ip";
1158 d31bc32a Ermal
	else {
1159
		$message = trim($message);
1160 f56a73f1 Scott Ullrich
		$message = "$status: $user, $mac, $ip, $message";
1161 d31bc32a Ermal
	}
1162 f56a73f1 Scott Ullrich
	captiveportal_syslog($message);
1163
}
1164
1165
/* log simple messages to syslog */
1166
function captiveportal_syslog($message) {
1167
	define_syslog_variables();
1168
	$message = trim($message);
1169
	openlog("logportalauth", LOG_PID, LOG_LOCAL4);
1170
	// Log it
1171
	syslog(LOG_INFO, $message);
1172 d99f7864 Scott Ullrich
	closelog();
1173 3db19cf1 Scott Ullrich
}
1174
1175 0bd34ed6 Scott Ullrich
function radius($username,$password,$clientip,$clientmac,$type) {
1176 5060dea7 Scott Ullrich
	global $g, $config;
1177
1178
	$ruleno = captiveportal_get_next_ipfw_ruleno();
1179
1180
	/* If the pool is empty, return appropriate message and fail authentication */
1181
	if (is_null($ruleno)) {
1182
		$auth_list = array();
1183
		$auth_list['auth_val'] = 1;
1184
		$auth_list['error'] = "System reached maximum login capacity";
1185
		return $auth_list;
1186
	}
1187
1188
	$radiusservers = captiveportal_get_radius_servers();
1189
1190
	$auth_list = RADIUS_AUTHENTICATION($username,
1191
		$password,
1192
		$radiusservers,
1193
		$clientip,
1194
		$clientmac,
1195
		$ruleno);
1196
1197
	if ($auth_list['auth_val'] == 2) {
1198
		captiveportal_logportalauth($username,$clientmac,$clientip,$type);
1199
		$sessionid = portal_allow($clientip,
1200
			$clientmac,
1201
			$username,
1202
			$password,
1203
			$auth_list,
1204
			$ruleno);
1205
	}
1206
1207
	return $auth_list;
1208 0bd34ed6 Scott Ullrich
}
1209
1210
/* read captive portal DB into array */
1211 e64c894f Ermal
function captiveportal_read_db($locked = false, $index = 5 /* sessionid by default */) {
1212 006802ab Ermal
	global $g;
1213 5060dea7 Scott Ullrich
1214 006802ab Ermal
	$cpdb = array();
1215 5060dea7 Scott Ullrich
1216 006802ab Ermal
	if ($locked == false)
1217 5060dea7 Scott Ullrich
		$cpdblck = lock('captiveportaldb');
1218 006802ab Ermal
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r");
1219
	if ($fd) {
1220
		while (!feof($fd)) {
1221
			$line = trim(fgets($fd));
1222
			if ($line) {
1223
				$cpe = explode(",", $line);
1224
				/* Hash by session id */
1225 e64c894f Ermal
				$cpdb[$cpe[$index]] = $cpe;
1226 5060dea7 Scott Ullrich
			}
1227
		}
1228 006802ab Ermal
		fclose($fd);
1229
	}
1230
	if ($locked == false)
1231 5060dea7 Scott Ullrich
		unlock($cpdblck);
1232 006802ab Ermal
	return $cpdb;
1233 0bd34ed6 Scott Ullrich
}
1234
1235
/* write captive portal DB */
1236 e92916d6 Ermal
function captiveportal_write_db($cpdb, $locked = false, $remove = false) {
1237 006802ab Ermal
	global $g;
1238 5060dea7 Scott Ullrich
1239 006802ab Ermal
	if ($locked == false)
1240 5060dea7 Scott Ullrich
		$cpdblck = lock('captiveportaldb', LOCK_EX);
1241 006802ab Ermal
1242 e92916d6 Ermal
	if (is_array($remove)) {
1243
		if (!empty($remove)) {
1244
			$cpdb = captiveportal_read_db(true);
1245 1540194f Ermal
			foreach ($remove as $key) {
1246
				if (is_array($key))
1247
					log_error("Captive portal Array passed as unset index: " . print_r($key, true));
1248
				else
1249
					unset($cpdb[$key]);
1250
			}
1251 e92916d6 Ermal
		} else
1252
			return; //This makes sure no record removal calls
1253 006802ab Ermal
	}
1254
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w");
1255
	if ($fd) {
1256
		foreach ($cpdb as $cpent) {
1257
			fwrite($fd, join(",", $cpent) . "\n");
1258 5060dea7 Scott Ullrich
		}
1259 006802ab Ermal
		fclose($fd);
1260
	}
1261
	if ($locked == false)
1262
		unlock($cpdblck);
1263 0bd34ed6 Scott Ullrich
}
1264
1265
function captiveportal_write_elements() {
1266 769e254e Ermal
	global $g, $config;
1267 5060dea7 Scott Ullrich
	
1268 769e254e Ermal
	/* delete any existing elements */
1269
	if (is_dir($g['captiveportal_element_path'])) {
1270
		$dh = opendir($g['captiveportal_element_path']);
1271
		while (($file = readdir($dh)) !== false) {
1272
			if ($file != "." && $file != "..")
1273
				unlink($g['captiveportal_element_path'] . "/" . $file);
1274
		}
1275
		closedir($dh);
1276 5060dea7 Scott Ullrich
	} else {
1277 769e254e Ermal
		@mkdir($g['captiveportal_element_path']);
1278 5060dea7 Scott Ullrich
	}
1279 769e254e Ermal
1280 1fadb31d Scott Ullrich
	if (is_array($config['captiveportal']['element'])) {
1281
		conf_mount_rw();
1282
		foreach ($config['captiveportal']['element'] as $data) {
1283
			$fd = @fopen($g['captiveportal_element_path'] . '/' . $data['name'], "wb");
1284
			if (!$fd) {
1285
				printf("Error: cannot open '{$data['name']}' in captiveportal_write_elements().\n");
1286
				return 1;
1287
			}
1288
			$decoded = base64_decode($data['content']);
1289
			fwrite($fd,$decoded);
1290
			fclose($fd);
1291
			unlink_if_exists("{$g['captiveportal_path']}/{$data['name']}");
1292
			unlink_if_exists("{$g['captiveportal_path']}/{$data['name']}");
1293
			mwexec("cd {$g['captiveportal_path']}/ && ln -s {$g['captiveportal_element_path']}/{$data['name']} {$data['name']}");
1294
		}
1295
		conf_mount_ro();
1296
	}
1297 5060dea7 Scott Ullrich
	
1298 769e254e Ermal
	return 0;
1299 0bd34ed6 Scott Ullrich
}
1300
1301 6ce61a8f Ermal
function captiveportal_init_ipfw_ruleno($rulenos_start = 2000, $rulenos_range_max = 49899) {
1302
	global $g;
1303
1304
	@unlink("{$g['vardb_path']}/captiveportal.rules");
1305
	$rules = array_pad(array(), $rulenos_range_max - $rulenos_start, false);
1306
	file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules));
1307
}
1308
1309 920cafaf Scott Ullrich
/*
1310
 * This function will calculate the lowest free firewall ruleno
1311 f9f71ad3 Ermal Lu?i
 * within the range specified based on the actual logged on users
1312 920cafaf Scott Ullrich
 *
1313
 */
1314 b01792a0 Ermal
function captiveportal_get_next_ipfw_ruleno($rulenos_start = 2000, $rulenos_range_max = 49899, $usebw = false) {
1315 f9f71ad3 Ermal Lu?i
	global $config, $g;
1316 6ce61a8f Ermal
1317 de752609 Scott Ullrich
	if(!isset($config['captiveportal']['enable']))
1318 01d57b8c Scott Ullrich
		return NULL;
1319 6ce61a8f Ermal
1320 d31bc32a Ermal
	$cpruleslck = lock('captiveportalrules', LOCK_EX);
1321 f9f71ad3 Ermal Lu?i
	$ruleno = 0;
1322 6ce61a8f Ermal
	if (file_exists("{$g['vardb_path']}/captiveportal.rules")) {
1323
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules"));
1324
		for ($ridx = 2; $ridx < ($rulenos_range_max - $rulenos_start); $ridx++) {
1325
			if ($rules[$ridx]) {
1326
				/* 
1327 5060dea7 Scott Ullrich
				 * This allows our traffic shaping pipes to be the in pipe the same as ruleno 
1328
				 * and the out pipe ruleno + 1. This removes limitation that where present in 
1329
				 * previous version of the peruserbw.
1330
				 */
1331 2279a587 Ermal
				if (isset($config['captiveportal']['peruserbw']) || $usebw == true)
1332 6ce61a8f Ermal
					$ridx++;
1333
				continue;
1334
			}
1335
			$ruleno = $ridx;
1336
			$rules[$ridx] = "used";
1337 b01792a0 Ermal
			if (isset($config['captiveportal']['peruserbw']) || $usebw == true)
1338 6ce61a8f Ermal
				$rules[++$ridx] = "used";
1339
			break;
1340
		}
1341
	} else {
1342
		$rules = array_pad(array(), $rulenos_range_max - $rulenos_start, false);
1343
		$rules[2] = "used";
1344
		$ruleno = 2;
1345
	}
1346
	file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules));
1347 d31bc32a Ermal
	unlock($cpruleslck);
1348 6ce61a8f Ermal
	return $ruleno;
1349
}
1350
1351 b01792a0 Ermal
function captiveportal_free_ipfw_ruleno($ruleno, $usedbw = false) {
1352 6ce61a8f Ermal
	global $config, $g;
1353
1354
	if(!isset($config['captiveportal']['enable']))
1355
		return NULL;
1356
1357 d31bc32a Ermal
	$cpruleslck = lock('captiveportalrules', LOCK_EX);
1358 6ce61a8f Ermal
	if (file_exists("{$g['vardb_path']}/captiveportal.rules")) {
1359
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules"));
1360
		$rules[$ruleno] = false;
1361 b01792a0 Ermal
		if (isset($config['captiveportal']['peruserbw']) || $usedbw == true)
1362 6ce61a8f Ermal
			$rules[++$ruleno] = false;
1363
		file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules));
1364 f9f71ad3 Ermal Lu?i
	}
1365 d31bc32a Ermal
	unlock($cpruleslck);
1366 6ce61a8f Ermal
}
1367
1368 d5ae560d Ermal
function captiveportal_get_ipfw_passthru_ruleno($value) {
1369 6ce61a8f Ermal
	global $config, $g;
1370
1371
	if(!isset($config['captiveportal']['enable']))
1372 5060dea7 Scott Ullrich
				return NULL;
1373 6ce61a8f Ermal
1374 d31bc32a Ermal
	$cpruleslck = lock('captiveportalrules', LOCK_EX);
1375 5060dea7 Scott Ullrich
	if (file_exists("{$g['vardb_path']}/captiveportal.rules")) {
1376
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules"));
1377 d5ae560d Ermal
		$ruleno = intval(`/sbin/ipfw show | /usr/bin/grep {$value} |  /usr/bin/grep -v grep | /usr/bin/cut -d " " -f 1 | /usr/bin/head -n 1`);
1378 d31bc32a Ermal
		if ($rules[$ruleno]) {
1379
			unlock($cpruleslck);
1380 6ce61a8f Ermal
			return $ruleno;
1381 d31bc32a Ermal
		}
1382 5060dea7 Scott Ullrich
	}
1383 6ce61a8f Ermal
1384 d31bc32a Ermal
	unlock($cpruleslck);
1385 f9f71ad3 Ermal Lu?i
	return NULL;
1386 920cafaf Scott Ullrich
}
1387
1388 360d815d Scott Ullrich
/**
1389
 * This function will calculate the traffic produced by a client
1390
 * based on its firewall rule
1391
 *
1392
 * Point of view: NAS
1393
 *
1394
 * Input means: from the client
1395
 * Output means: to the client
1396
 *
1397
 */
1398
1399 f9f71ad3 Ermal Lu?i
function getVolume($ip) {
1400 360d815d Scott Ullrich
1401 5060dea7 Scott Ullrich
	$volume = array();
1402 360d815d Scott Ullrich
1403 5060dea7 Scott Ullrich
	// Initialize vars properly, since we don't want NULL vars
1404
	$volume['input_pkts'] = $volume['input_bytes'] = $volume['output_pkts'] = $volume['output_bytes'] = 0 ;
1405 360d815d Scott Ullrich
1406 5060dea7 Scott Ullrich
	// Ingress
1407
	$ipfwin = "";
1408
	$ipfwout = "";
1409
	$matchesin = "";
1410
	$matchesout = "";
1411
	exec("/sbin/ipfw table 1 entrystats {$ip}", $ipfwin);
1412
	if ($ipfwin[0]) {
1413 523855b0 Scott Ullrich
		$ipfwin = split(" ", $ipfwin[0]);
1414
		$volume['input_pkts'] = $ipfwin[2];
1415 c8950140 Evgeny Yurchenko
		$volume['input_bytes'] = $ipfwin[3];
1416 5060dea7 Scott Ullrich
	}
1417 f9f71ad3 Ermal Lu?i
1418 5060dea7 Scott Ullrich
	exec("/sbin/ipfw table 2 entrystats {$ip}", $ipfwout);
1419
	if ($ipfwout[0]) {
1420
		$ipfwout = split(" ", $ipfwout[0]);
1421
		$volume['output_pkts'] = $ipfwout[2];
1422 c8950140 Evgeny Yurchenko
		$volume['output_bytes'] = $ipfwout[3];
1423 5060dea7 Scott Ullrich
	}
1424 360d815d Scott Ullrich
1425 5060dea7 Scott Ullrich
	return $volume;
1426 360d815d Scott Ullrich
}
1427
1428 856e58a6 Scott Ullrich
/**
1429
 * Get the NAS-Identifier
1430
 *
1431
 * We will use our local hostname to make up the nas_id
1432
 */
1433
function getNasID()
1434
{
1435 5060dea7 Scott Ullrich
	$nasId = "";
1436
	exec("/bin/hostname", $nasId);
1437
	if(!$nasId[0])
1438
		$nasId[0] = "{$g['product_name']}";
1439
	return $nasId[0];
1440 856e58a6 Scott Ullrich
}
1441
1442
/**
1443
 * Get the NAS-IP-Address based on the current wan address
1444
 *
1445
 * Use functions in interfaces.inc to find this out
1446
 *
1447
 */
1448
1449
function getNasIP()
1450
{
1451 64c0462b Ermal
	global $config;
1452
1453 5060dea7 Scott Ullrich
	if (empty($config['captiveportal']['radiussrcip_attribute'])) {
1454
			$nasIp = get_interface_ip();
1455
	} else {
1456 4a756e9b Ermal
		if (is_ipaddr($config['captiveportal']['radiussrcip_attribute']))
1457 5060dea7 Scott Ullrich
			$nasIp = $config['captiveportal']['radiussrcip_attribute'];
1458
		else
1459
			$nasIp = get_interface_ip($config['captiveportal']['radiussrcip_attribute']);
1460 36ff7f81 Ermal
	}
1461 64c0462b Ermal
		
1462 5060dea7 Scott Ullrich
	if(!is_ipaddr($nasIp))
1463
		$nasIp = "0.0.0.0";
1464 64c0462b Ermal
1465
	return $nasIp;
1466 856e58a6 Scott Ullrich
}
1467
1468 f8b11310 Ermal Lu?i
function portal_ip_from_client_ip($cliip) {
1469
	global $config;
1470
1471
	$interfaces = explode(",", $config['captiveportal']['interface']);
1472
	foreach ($interfaces as $cpif) {
1473
		$ip = get_interface_ip($cpif);
1474
		$sn = get_interface_subnet($cpif);
1475
		if (ip_in_subnet($cliip, "{$ip}/{$sn}"))
1476
			return $ip;
1477
	}
1478
1479 cc125e13 Chris Buechler
	// doesn't match up to any particular interface
1480
	// so let's set the portal IP to what PHP says 
1481
	// the server IP issuing the request is. 
1482
	// allows same behavior as 1.2.x where IP isn't 
1483
	// in the subnet of any CP interface (static routes, etc.)
1484
	// rather than forcing to DNS hostname resolution
1485
	$ip = $_SERVER['SERVER_ADDR'];
1486
	if (is_ipaddr($ip))
1487
		return $ip;
1488
1489 f8b11310 Ermal Lu?i
	return false;
1490
}
1491
1492 ac631bba lgcosta
/* functions move from index.php */
1493
1494
function portal_reply_page($redirurl, $type = null, $message = null, $clientmac = null, $clientip = null, $username = null, $password = null) {
1495
	global $g, $config;
1496
1497
	/* Get captive portal layout */
1498
	if ($type == "redir") {
1499
		header("Location: {$redirurl}");
1500
		return;
1501
	} else if ($type == "login")
1502
		$htmltext = get_include_contents("{$g['varetc_path']}/captiveportal.html");
1503
	else
1504
		$htmltext = get_include_contents("{$g['varetc_path']}/captiveportal-error.html");
1505
1506
	/* substitute the PORTAL_REDIRURL variable */
1507
	if ($config['captiveportal']['preauthurl']) {
1508
		$htmltext = str_replace("\$PORTAL_REDIRURL\$", "{$config['captiveportal']['preauthurl']}", $htmltext);
1509
		$htmltext = str_replace("#PORTAL_REDIRURL#", "{$config['captiveportal']['preauthurl']}", $htmltext);
1510
	}
1511
1512
	/* substitute other variables */
1513
	if (isset($config['captiveportal']['httpslogin'])) {
1514
		$htmltext = str_replace("\$PORTAL_ACTION\$", "https://{$config['captiveportal']['httpsname']}:8001/", $htmltext);
1515
		$htmltext = str_replace("#PORTAL_ACTION#", "https://{$config['captiveportal']['httpsname']}:8001/", $htmltext);
1516
	} else {
1517
		$ifip = portal_ip_from_client_ip($clientip);
1518
		if (!$ifip)
1519
			$ourhostname = $config['system']['hostname'] . ":8000";
1520
		else
1521
			$ourhostname = "{$ifip}:8000";
1522
		$htmltext = str_replace("\$PORTAL_ACTION\$", "http://{$ourhostname}/", $htmltext);
1523
		$htmltext = str_replace("#PORTAL_ACTION#", "http://{$ourhostname}/", $htmltext);
1524
	}
1525
1526
	$htmltext = str_replace("\$PORTAL_REDIRURL\$", htmlspecialchars($redirurl), $htmltext);
1527
	$htmltext = str_replace("\$PORTAL_MESSAGE\$", htmlspecialchars($message), $htmltext);
1528
	$htmltext = str_replace("\$CLIENT_MAC\$", htmlspecialchars($clientmac), $htmltext);
1529
	$htmltext = str_replace("\$CLIENT_IP\$", htmlspecialchars($clientip), $htmltext);
1530
1531
	// Special handling case for captive portal master page so that it can be ran 
1532
	// through the PHP interpreter using the include method above.  We convert the
1533
	// $VARIABLE$ case to #VARIABLE# in /etc/inc/captiveportal.inc before writing out.
1534
	$htmltext = str_replace("#PORTAL_REDIRURL#", htmlspecialchars($redirurl), $htmltext);
1535
	$htmltext = str_replace("#PORTAL_MESSAGE#", htmlspecialchars($message), $htmltext);
1536
	$htmltext = str_replace("#CLIENT_MAC#", htmlspecialchars($clientmac), $htmltext);
1537
	$htmltext = str_replace("#CLIENT_IP#", htmlspecialchars($clientip), $htmltext);
1538
	$htmltext = str_replace("#USERNAME#", htmlspecialchars($username), $htmltext);
1539
	$htmltext = str_replace("#PASSWORD#", htmlspecialchars($password), $htmltext);
1540
1541
    echo $htmltext;
1542
}
1543
1544
function portal_mac_radius($clientmac,$clientip) {
1545
    global $config ;
1546
1547
    $radmac_secret = $config['captiveportal']['radmac_secret'];
1548
1549
    /* authentication against the radius server */
1550
    $username = mac_format($clientmac);
1551
    $auth_list = radius($username,$radmac_secret,$clientip,$clientmac,"MACHINE LOGIN");
1552
    if ($auth_list['auth_val'] == 2)
1553
        return TRUE;
1554
    if (!empty($auth_list['url_redirection']))
1555
	portal_reply_page($auth_list['url_redirection'], "redir");
1556
1557
    return FALSE;
1558
}
1559
1560
function portal_allow($clientip,$clientmac,$username,$password = null, $attributes = null, $ruleno = null)  {
1561
1562
	global $redirurl, $g, $config, $type, $passthrumac, $_POST;
1563
1564
	/* See if a ruleno is passed, if not start sessions because this means there isn't one atm */
1565
	if ($ruleno == null)
1566
		$ruleno = captiveportal_get_next_ipfw_ruleno();
1567
1568
	/* if the pool is empty, return appropriate message and exit */
1569
	if (is_null($ruleno)) {
1570
		portal_reply_page($redirurl, "error", "System reached maximum login capacity");
1571
		log_error("WARNING!  Captive portal has reached maximum login capacity");
1572
		exit;
1573
	}
1574
1575
	// Ensure we create an array if we are missing attributes
1576
	if (!is_array($attributes))
1577
		$attributes = array();
1578
1579
	$radiusservers = captiveportal_get_radius_servers();
1580
1581 006802ab Ermal
	/* Do not allow concurrent login execution. */
1582
	$cpdblck = lock('captiveportaldb', LOCK_EX);
1583
1584
	unset($sessionid);
1585
1586
	/* read in client database */
1587
	$cpdb = captiveportal_read_db(true);
1588
1589 ac631bba lgcosta
	if ($attributes['voucher'])
1590
		$remaining_time = $attributes['session_timeout'];
1591
1592
	$writecfg = false;
1593
	/* Find an existing session */
1594
	if ((isset($config['captiveportal']['noconcurrentlogins'])) && $passthrumac) {
1595
		if (isset($config['captiveportal']['passthrumacadd'])) {
1596
			$mac = captiveportal_passthrumac_findbyname($username);
1597
			if (!empty($mac)) {
1598
				if ($_POST['replacemacpassthru']) {
1599
					foreach ($config['captiveportal']['passthrumac'] as $idx => $macent) {
1600
						if ($macent['mac'] == $mac['mac']) {
1601
							$macrules = "";
1602
							$ruleno = captiveportal_get_ipfw_passthru_ruleno($mac['mac']);
1603
                                			if ($ruleno) {
1604
								captiveportal_free_ipfw_ruleno($ruleno, true);
1605
                                        			$macrules .= "delete {$ruleno}\n";
1606
								++$ruleno;
1607
                                        			$macrules .= "delete {$ruleno}\n";
1608
                                			}
1609
							unset($config['captiveportal']['passthrumac'][$idx]);
1610
							$mac['mac'] = $clientmac;
1611
							$config['captiveportal']['passthrumac'][] = $mac;
1612
							$macrules .= captiveportal_passthrumac_configure_entry($mac);
1613
							file_put_contents("{$g['tmp_path']}/macentry.rules.tmp", $macrules);
1614
							mwexec("/sbin/ipfw -q {$g['tmp_path']}/macentry.rules.tmp");
1615
							$writecfg = true;
1616
							$sessionid = true;
1617
							break;
1618
						}
1619
					}
1620
                                } else {
1621
					portal_reply_page($redirurl, "error", "Username: {$username} is already authenticated using another MAC address.",
1622
						$clientmac, $clientip, $username, $password);
1623
					exit;
1624
				}
1625
			}
1626
		}
1627
	}
1628
1629 b09c2d86 Ermal
	/* Snaphost the timestamp */
1630
	$allow_time = time();
1631
1632 006802ab Ermal
	foreach ($cpdb as $sid => $cpentry) {
1633 ac631bba lgcosta
		/* on the same ip */
1634 006802ab Ermal
		if($cpentry[2] == $clientip) {
1635
			captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - REUSING OLD SESSION");
1636
			$sessionid = $sid;
1637 ac631bba lgcosta
			break;
1638
		}
1639 006802ab Ermal
		elseif (($attributes['voucher']) && ($username != 'unauthenticated') && ($cpentry[4] == $username)) {
1640 ac631bba lgcosta
			// user logged in with an active voucher. Check for how long and calculate 
1641
			// how much time we can give him (voucher credit - used time)
1642 b09c2d86 Ermal
			$remaining_time = $cpentry[0] + $cpentry[7] - $allow_time;
1643 ac631bba lgcosta
			if ($remaining_time < 0)    // just in case. 
1644
				$remaining_time = 0;
1645
1646
			/* This user was already logged in so we disconnect the old one */
1647 006802ab Ermal
			captiveportal_disconnect($cpentry,$radiusservers,13);
1648
			captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - TERMINATING OLD SESSION");
1649 ce1942d6 Ermal
			unset($cpdb[$sid]);
1650 ac631bba lgcosta
			break;
1651
		}
1652
		elseif ((isset($config['captiveportal']['noconcurrentlogins'])) && ($username != 'unauthenticated')) {
1653
			/* on the same username */
1654 006802ab Ermal
			if (strcasecmp($cpentry[4], $username) == 0) {
1655 ac631bba lgcosta
				/* This user was already logged in so we disconnect the old one */
1656 006802ab Ermal
				captiveportal_disconnect($cpentry,$radiusservers,13);
1657
				captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - TERMINATING OLD SESSION");
1658 ce1942d6 Ermal
				unset($cpdb[$sid]);
1659 ac631bba lgcosta
				break;
1660
			}
1661
		}
1662
	}
1663
1664
	if ($attributes['voucher'] && $remaining_time <= 0)
1665
		return 0;       // voucher already used and no time left
1666
1667
	if (!isset($sessionid)) {
1668
		/* generate unique session ID */
1669
		$tod = gettimeofday();
1670
		$sessionid = substr(md5(mt_rand() . $tod['sec'] . $tod['usec'] . $clientip . $clientmac), 0, 16);
1671
1672
		/* Add rules for traffic shaping
1673
		 * We don't need to add extra rules since traffic will pass due to the following kernel option
1674
		 * net.inet.ip.fw.one_pass: 1
1675
		 */
1676
		$peruserbw = isset($config['captiveportal']['peruserbw']);
1677
1678 2d4003aa Ermal
		$bw_up = isset($attributes['bw_up']) ? round(intval($attributes['bw_up'])/1000, 2) : $config['captiveportal']['bwdefaultup'];
1679
		$bw_down = isset($attributes['bw_down']) ? round(intval($attributes['bw_down'])/1000, 2) : $config['captiveportal']['bwdefaultdn'];
1680 ac631bba lgcosta
1681
		if ($passthrumac) {
1682
			$mac = array();
1683
			$mac['mac'] = $clientmac;
1684
			if (isset($config['captiveportal']['passthrumacaddusername']))
1685
				$mac['username'] = $username;
1686
			$mac['descr'] =  "Auto added pass-through MAC for user {$username}";
1687
			if (!empty($bw_up))
1688
				$mac['bw_up'] = $bw_up;
1689
			if (!empty($bw_down))
1690
				$mac['bw_down'] = $bw_down;
1691
			if (!is_array($config['captiveportal']['passthrumac']))
1692
				$config['captiveportal']['passthrumac'] = array();
1693
			$config['captiveportal']['passthrumac'][] = $mac;
1694 006802ab Ermal
			unlock($cpdblck);
1695 ac631bba lgcosta
			$macrules = captiveportal_passthrumac_configure_entry($mac);
1696
			file_put_contents("{$g['tmp_path']}/macentry.rules.tmp", $macrules);
1697
			mwexec("/sbin/ipfw -q {$g['tmp_path']}/macentry.rules.tmp");
1698
			$writecfg = true;
1699
		} else {
1700
			if ($peruserbw && !empty($bw_up) && is_numeric($bw_up)) {
1701
				$bw_up_pipeno = $ruleno + 20000;
1702
				//$bw_up /= 1000; // Scale to Kbit/s
1703
				mwexec("/sbin/ipfw pipe {$bw_up_pipeno} config bw {$bw_up}Kbit/s queue 100");
1704
1705
				if (!isset($config['captiveportal']['nomacfilter']))
1706
					mwexec("/sbin/ipfw table 1 add {$clientip} mac {$clientmac} {$bw_up_pipeno}");
1707
				else
1708
					mwexec("/sbin/ipfw table 1 add {$clientip} {$bw_up_pipeno}");
1709
			} else {
1710
				if (!isset($config['captiveportal']['nomacfilter']))
1711
					mwexec("/sbin/ipfw table 1 add {$clientip} mac {$clientmac}");
1712
				else
1713
					mwexec("/sbin/ipfw table 1 add {$clientip}");
1714
			}
1715
			if ($peruserbw && !empty($bw_down) && is_numeric($bw_down)) {
1716
				$bw_down_pipeno = $ruleno + 20001;
1717
				//$bw_down /= 1000; // Scale to Kbit/s
1718
				mwexec("/sbin/ipfw pipe {$bw_down_pipeno} config bw {$bw_down}Kbit/s queue 100");
1719
1720
				if (!isset($config['captiveportal']['nomacfilter']))
1721
					mwexec("/sbin/ipfw table 2 add {$clientip} mac {$clientmac} {$bw_down_pipeno}");
1722
				else
1723
					mwexec("/sbin/ipfw table 2 add {$clientip} {$bw_down_pipeno}");
1724
			} else {
1725
				if (!isset($config['captiveportal']['nomacfilter']))
1726
					mwexec("/sbin/ipfw table 2 add {$clientip} mac {$clientmac}");
1727
				else
1728
					mwexec("/sbin/ipfw table 2 add {$clientip}");
1729
			}
1730
1731
			if ($attributes['voucher'])
1732
				$attributes['session_timeout'] = $remaining_time;
1733
1734
			/* encode password in Base64 just in case it contains commas */
1735
			$bpassword = base64_encode($password);
1736 b09c2d86 Ermal
			$cpdb[] = array($allow_time, $ruleno, $clientip, $clientmac, $username, $sessionid, $bpassword,
1737 ac631bba lgcosta
				$attributes['session_timeout'], $attributes['idle_timeout'], $attributes['session_terminate_time']);
1738
1739 006802ab Ermal
			/* rewrite information to database */
1740
			captiveportal_write_db($cpdb, true);
1741
			unlock($cpdblck);
1742
1743 ac631bba lgcosta
			if (isset($config['captiveportal']['radacct_enable']) && !empty($radiusservers)) {
1744
				$acct_val = RADIUS_ACCOUNTING_START($ruleno,
1745
                                		$username, $sessionid, $radiusservers, $clientip, $clientmac);
1746
				if ($acct_val == 1)
1747
					captiveportal_logportalauth($username,$clientmac,$clientip,$type,"RADIUS ACCOUNTING FAILED");
1748
			}
1749
		}
1750 006802ab Ermal
	} else
1751
		unlock($cpdblck);
1752 ac631bba lgcosta
1753
	if ($writecfg == true)
1754
		write_config();
1755
1756
	/* redirect user to desired destination */
1757
	if (!empty($attributes['url_redirection']))
1758
		$my_redirurl = $attributes['url_redirection'];
1759 cd2c71fc Ermal
	else if (!empty($config['captiveportal']['redirurl']))
1760 ac631bba lgcosta
		$my_redirurl = $config['captiveportal']['redirurl'];
1761
	else
1762
		$my_redirurl = $redirurl;
1763
1764
	if(isset($config['captiveportal']['logoutwin_enable']) && !$passthrumac) {
1765
1766
		if (isset($config['captiveportal']['httpslogin']))
1767
			$logouturl = "https://{$config['captiveportal']['httpsname']}:8001/";
1768
		else {
1769
			$ifip = portal_ip_from_client_ip($clientip);
1770
			if (!$ifip)
1771
				$ourhostname = $config['system']['hostname'] . ":8000";
1772
			else
1773
				$ourhostname = "{$ifip}:8000";
1774
			$logouturl = "http://{$ourhostname}/";
1775
		}
1776
1777
		if (isset($attributes['reply_message']))
1778
			$message = $attributes['reply_message'];
1779
		else
1780
			$message = 0;
1781
1782
		include("{$g['varetc_path']}/captiveportal-logout.html");
1783
1784
	} else {
1785
		header("Location: " . $my_redirurl);
1786
	}
1787
1788
	return $sessionid;
1789
}
1790
1791
1792
/*
1793
 * Used for when pass-through credits are enabled.
1794
 * Returns true when there was at least one free login to deduct for the MAC.
1795
 * Expired entries are removed as they are seen.
1796
 * Active entries are updated according to the configuration.
1797
 */
1798
function portal_consume_passthrough_credit($clientmac) {
1799
	global $config;
1800
1801
	if (!empty($config['captiveportal']['freelogins_count']) && is_numeric($config['captiveportal']['freelogins_count']))
1802
		$freeloginscount = $config['captiveportal']['freelogins_count'];
1803
	else
1804
		return false;
1805
1806
	if (!empty($config['captiveportal']['freelogins_resettimeout']) && is_numeric($config['captiveportal']['freelogins_resettimeout']))
1807
		$resettimeout = $config['captiveportal']['freelogins_resettimeout'];
1808
	else
1809
		return false;
1810
1811 17e7a243 Scott Ullrich
	if ($freeloginscount < 1 || $resettimeout <= 0 || !$clientmac)
1812 ac631bba lgcosta
		return false;
1813
1814
	$updatetimeouts = isset($config['captiveportal']['freelogins_updatetimeouts']);
1815
1816
	/*
1817
	 * Read database of used MACs.  Lines are a comma-separated list
1818
	 * of the time, MAC, then the count of pass-through credits remaining.
1819
	 */
1820
	$usedmacs = captiveportal_read_usedmacs_db();
1821
1822
	$currenttime = time();
1823
	$found = false;
1824
	foreach ($usedmacs as $key => $usedmac) {
1825
		$usedmac = explode(",", $usedmac);
1826
1827
		if ($usedmac[1] == $clientmac) {
1828
			if ($usedmac[0] + ($resettimeout * 3600) > $currenttime) {
1829
				if ($usedmac[2] < 1) {
1830
					if ($updatetimeouts) {
1831
						$usedmac[0] = $currenttime;
1832
						unset($usedmacs[$key]);
1833
						$usedmacs[] = implode(",", $usedmac);
1834
						captiveportal_write_usedmacs_db($usedmacs);
1835
					}
1836
1837
					return false;
1838
				} else {
1839
					$usedmac[2] -= 1;
1840
					$usedmacs[$key] = implode(",", $usedmac);
1841
				}
1842
1843
				$found = true;
1844
			} else
1845
				unset($usedmacs[$key]);
1846
1847
			break;
1848
		} else if ($usedmac[0] + ($resettimeout * 3600) <= $currenttime)
1849
				unset($usedmacs[$key]);
1850
	}
1851
1852
	if (!$found) {
1853
		$usedmac = array($currenttime, $clientmac, $freeloginscount - 1);
1854
		$usedmacs[] = implode(",", $usedmac);
1855
	}
1856
1857
	captiveportal_write_usedmacs_db($usedmacs);
1858
	return true;
1859
}
1860
1861
function captiveportal_read_usedmacs_db() {
1862
	global $g;
1863
1864
	$cpumaclck = lock('captiveusedmacs');
1865
	if (file_exists("{$g['vardb_path']}/captiveportal_usedmacs.db")) {
1866
		$usedmacs = file("{$g['vardb_path']}/captiveportal_usedmacs.db", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
1867 da666ca8 Scott Ullrich
		if (!$usedmacs)
1868 ac631bba lgcosta
			$usedmacs = array();
1869
	} else
1870
		$usedmacs = array();
1871
1872
	unlock($cpumaclck);
1873
	return $usedmacs;
1874
}
1875
1876
function captiveportal_write_usedmacs_db($usedmacs) {
1877
	global $g;
1878
1879
	$cpumaclck = lock('captiveusedmacs', LOCK_EX);
1880
	@file_put_contents("{$g['vardb_path']}/captiveportal_usedmacs.db", implode("\n", $usedmacs));
1881
	unlock($cpumaclck);
1882
}
1883
1884 006802ab Ermal
?>