Project

General

Profile

Download (60 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 e8aef0ec Scott Ullrich
		killbypid("{$g['varrun_path']}/cp_prunedb.pid");
365 12ee8fe4 Scott Ullrich
366 d31bc32a Ermal
		captiveportal_radius_stop_all();
367 3db19cf1 Scott Ullrich
368
		mwexec("/sbin/sysctl net.link.ether.ipfw=0");
369
370 5209079f Ermal Luçi
		/* unload ipfw */
371 faebbab3 Scott Ullrich
		if (is_module_loaded("ipfw.ko"))		
372
			mwexec("/sbin/kldunload ipfw.ko");
373 85250056 Ermal Lu?i
		$listifs = get_configured_interface_list_by_realif();
374 bbc6768b Ermal Lu?i
		foreach ($listifs as $listrealif => $listif) {
375 7c587b9f Ermal Lu?i
			if (!empty($listrealif)) {
376 da9d6701 Ermal
				if (does_interface_exist($listrealif)) {
377 bf444c34 Ermal
					pfSense_interface_flags($listrealif, -IFF_IPFW_FILTER);
378 da9d6701 Ermal
					$carpif = link_ip_to_carp_interface(find_interface_ip($listrealif));
379 5060dea7 Scott Ullrich
					if (!empty($carpif)) {
380 da9d6701 Ermal
						$carpsif = explode(" ", $carpif);
381
						foreach ($carpsif as $cpcarp)
382 bf444c34 Ermal
							pfSense_interface_flags($cpcarp, -IFF_IPFW_FILTER);
383 da9d6701 Ermal
					}
384 2ee45728 Ermal Lu?i
				}
385 bbc6768b Ermal Lu?i
			}
386
		}
387 3db19cf1 Scott Ullrich
	}
388 36254e4a Scott Ullrich
389 f8b11310 Ermal Lu?i
	unlock($captiveportallck);
390 c3214b80 Scott Ullrich
	
391 5b237745 Scott Ullrich
	return 0;
392
}
393
394 769e254e Ermal
function captiveportal_init_webgui() {
395
	global $g, $config;
396
397
	 if (!isset($config['captiveportal']['enable']))
398 5060dea7 Scott Ullrich
				return;
399 769e254e Ermal
400 985070dc Ermal
	if ($config['captiveportal']['maxprocperip'])
401
		$maxproc = $config['captiveportal']['maxprocperip'];
402 769e254e Ermal
	else
403
		$maxproc = 16;
404
405
	$use_fastcgi = true;
406
407
	if (isset($config['captiveportal']['httpslogin'])) {
408
		$cert = base64_decode($config['captiveportal']['certificate']);
409
		if (isset($config['captiveportal']['cacertificate']))
410
			$cacert = base64_decode($config['captiveportal']['cacertificate']);
411
		else
412
			$cacert = "";
413
		$key = base64_decode($config['captiveportal']['private-key']);
414
		/* generate lighttpd configuration */
415
		system_generate_lighty_config("{$g['varetc_path']}/lighty-CaptivePortal-SSL.conf",
416 2c063971 Ermal
			$cert, $key, $cacert, "lighty-CaptivePortal-SSL.pid", "8001", "/usr/local/captiveportal/",
417 769e254e Ermal
			"cert-portal.pem", "ca-portal.pem", "1", $maxproc, $use_fastcgi, true);
418
	}
419
420
	/* generate lighttpd configuration */
421
	system_generate_lighty_config("{$g['varetc_path']}/lighty-CaptivePortal.conf",
422
		"", "", "", "lighty-CaptivePortal.pid", "8000", "/usr/local/captiveportal/",
423
		"cert-portal.pem", "ca-portal.pem", "1", $maxproc, $use_fastcgi, true);
424
425
	/* attempt to start lighttpd */
426
	$res = mwexec("/usr/local/sbin/lighttpd -f {$g['varetc_path']}/lighty-CaptivePortal.conf");
427
428
	/* fire up https instance */
429
	if (isset($config['captiveportal']['httpslogin']))
430
		$res = mwexec("/usr/local/sbin/lighttpd -f {$g['varetc_path']}/lighty-CaptivePortal-SSL.conf");
431
}
432
433 847e5e82 Scott Ullrich
/* reinit will disconnect all users, be careful! */
434 1d9e9cca Ermal
function captiveportal_init_rules($reinit = false) {
435 3db19cf1 Scott Ullrich
	global $config, $g;
436 36254e4a Scott Ullrich
437 769e254e Ermal
	if (!isset($config['captiveportal']['enable']))
438
		return;
439
440
	$cpips = array();
441
	$ifaces = get_configured_interface_list();
442
	foreach ($ifaces as $kiface => $kiface2) {
443
		$tmpif = get_real_interface($kiface);
444
		pfSense_interface_flags($tmpif, -IFF_IPFW_FILTER);
445
	}
446
	$cpinterfaces = explode(",", $config['captiveportal']['interface']);
447
	$firsttime = 0;
448
	foreach ($cpinterfaces as $cpifgrp) {
449
		if (!isset($ifaces[$cpifgrp]))
450
			continue;
451
		$tmpif = get_real_interface($cpifgrp);
452
		if (!empty($tmpif)) {
453
			if ($firsttime > 0)
454
				$cpinterface .= " or ";
455
			$cpinterface .= "via {$tmpif}";
456
			$firsttime = 1;
457
			$cpipm = get_interface_ip($cpifgrp);
458
			if (is_ipaddr($cpipm)) {
459
				$carpif = link_ip_to_carp_interface($cpipm);
460
				if (!empty($carpif)) {
461
					$carpsif = explode(" ", $carpif);
462
					foreach ($carpsif as $cpcarp) {
463
						pfSense_interface_flags($cpcarp, IFF_IPFW_FILTER);
464
						$carpip = find_interface_ip($cpcarp);
465
						if (is_ipaddr($carpip))
466
							$cpips[] = $carpip;
467
					}
468
				}
469
				$cpips[] = $cpipm;
470
				pfSense_interface_flags($tmpif, IFF_IPFW_FILTER);
471
			}
472
		}
473
	}
474
	if (count($cpips) > 0) {
475
		$cpactive = true;
476
		$cpinterface = "{ {$cpinterface} } ";
477 5060dea7 Scott Ullrich
		} else
478 769e254e Ermal
		return false;
479
480 eade409a Ermal
	if ($reinit == false)
481
		$captiveportallck = lock('captiveportal');
482
483 1d9e9cca Ermal
	/* init dummynet/ipfw rules number database */
484
	captiveportal_init_ipfw_ruleno();
485
486 769e254e Ermal
	/* make sure ipfw is loaded */
487
	if (!is_module_loaded("ipfw.ko"))
488
		filter_load_ipfw();
489
	/* Always load dummynet now that even allowed ip and mac passthrough use it. */
490
	if (!is_module_loaded("dummynet.ko"))
491
		mwexec("/sbin/kldload dummynet");
492
493 5060dea7 Scott Ullrich
	$cprules =	"add 65291 set 1 allow pfsync from any to any\n";
494 b01792a0 Ermal
	$cprules .= "add 65292 set 1 allow carp from any to any\n";
495 181a843c Scott Ullrich
496 3db19cf1 Scott Ullrich
	$cprules .= <<<EOD
497 b01792a0 Ermal
# add 65300 set 1 skipto 65534 all from any to any not layer2
498 3db19cf1 Scott Ullrich
# layer 2: pass ARP
499 b01792a0 Ermal
add 65301 set 1 pass layer2 mac-type arp
500 b9d1d810 Scott Ullrich
# pfsense requires for WPA
501 b01792a0 Ermal
add 65302 set 1 pass layer2 mac-type 0x888e
502
add 65303 set 1 pass layer2 mac-type 0x88c7
503 684c787e Scott Ullrich
504 36254e4a Scott Ullrich
# PPP Over Ethernet Discovery Stage
505 b01792a0 Ermal
add 65304 set 1 pass layer2 mac-type 0x8863
506 684c787e Scott Ullrich
# PPP Over Ethernet Session Stage
507 b01792a0 Ermal
add 65305 set 1 pass layer2 mac-type 0x8864
508 55f5c311 Ermal Lu?i
# Allow WPA
509 b01792a0 Ermal
add 65306 set 1 pass layer2 mac-type 0x888e
510 684c787e Scott Ullrich
511 3db19cf1 Scott Ullrich
# layer 2: block anything else non-IP
512 b01792a0 Ermal
add 65307 set 1 deny layer2 not mac-type ip
513 3db19cf1 Scott Ullrich
514
EOD;
515
516 b01792a0 Ermal
	$rulenum = 65310;
517 fb516dda Chris Buechler
	$ipcount = 0;
518 0ba17c67 Ermal
	$ips = "";
519 fb516dda Chris Buechler
	foreach ($cpips as $cpip) {
520
		if($ipcount == 0) {
521
			$ips = "{$cpip} ";
522
		} else {
523
			$ips .= "or {$cpip} ";
524
		}
525
		$ipcount++;
526
	}
527 0ba17c67 Ermal
	$ips = "{ 255.255.255.255 or {$ips} }";
528 746e60c9 Ermal
	$cprules .= "add {$rulenum} set 1 pass ip from any to {$ips} in\n";
529 2f27dffd Ermal
	$rulenum++;
530 746e60c9 Ermal
	$cprules .= "add {$rulenum} set 1 pass ip from {$ips} to any out\n";
531 2f27dffd Ermal
	$rulenum++;
532
	$cprules .= "add {$rulenum} set 1 pass icmp from {$ips} to any out icmptype 0\n";
533
	$rulenum++;
534
	$cprules .= "add {$rulenum} set 1 pass icmp from any to {$ips} in icmptype 8 \n";
535
	$rulenum++;
536 b01792a0 Ermal
	/* Allowed ips */
537
	$cprules .= "add {$rulenum} allow ip from table(3) to any in\n";
538
	$rulenum++;
539
	$cprules .= "add {$rulenum} allow ip from any to table(4) out\n";
540
	$rulenum++;
541
	$cprules .= "add {$rulenum} pipe tablearg ip from table(5) to any in\n";
542
	$rulenum++;
543
	$cprules .= "add {$rulenum} pipe tablearg ip from any to table(6) out\n";
544
	$rulenum++;
545
	$cprules .= "add {$rulenum} allow ip from any to table(7) in\n";
546
	$rulenum++;
547
	$cprules .= "add {$rulenum} allow ip from table(8) to any out\n";
548
	$rulenum++;
549
	$cprules .= "add {$rulenum} pipe tablearg ip from any to table(9) in\n";
550
	$rulenum++;
551
	$cprules .= "add {$rulenum} pipe tablearg ip from table(10) to any out\n";
552
	$rulenum++;
553
554
	/* Authenticated users rules. */
555 f9f71ad3 Ermal Lu?i
	if (isset($config['captiveportal']['peruserbw'])) {
556 6ce61a8f Ermal
		$cprules .= "add {$rulenum} set 1 pipe tablearg ip from table(1) to any in\n";
557 f9f71ad3 Ermal Lu?i
		$rulenum++;
558 6ce61a8f Ermal
		$cprules .= "add {$rulenum} set 1 pipe tablearg ip from any to table(2) out\n";
559 f9f71ad3 Ermal Lu?i
		$rulenum++;
560
	} else {
561 6ce61a8f Ermal
		$cprules .= "add {$rulenum} set 1 allow ip from table(1) to any in\n";
562 5060dea7 Scott Ullrich
		$rulenum++;
563
		$cprules .= "add {$rulenum} set 1 allow ip from any to table(2) out\n";
564
		$rulenum++;
565 f9f71ad3 Ermal Lu?i
	}
566
	
567 5060dea7 Scott Ullrich
	   $cprules .= <<<EOD
568 5480497a Scott Ullrich
569 d44bccc7 Scott Ullrich
# redirect non-authenticated clients to captive portal
570 6ce61a8f Ermal
add 65531 set 1 fwd 127.0.0.1,8000 tcp from any to any in
571 3db19cf1 Scott Ullrich
# let the responses from the captive portal web server back out
572 6ce61a8f Ermal
add 65532 set 1 pass tcp from any to any out
573 3db19cf1 Scott Ullrich
# block everything else
574 6ce61a8f Ermal
add 65533 set 1 deny all from any to any
575 3db19cf1 Scott Ullrich
# pass everything else on layer2
576 6ce61a8f Ermal
add 65534 set 1 pass all from any to any layer2
577 3db19cf1 Scott Ullrich
578
EOD;
579
580 769e254e Ermal
	/* generate passthru mac database */
581
	$cprules .= captiveportal_passthrumac_configure(true);
582
	$cprules .= "\n";
583 55c18b30 Scott Ullrich
584 769e254e Ermal
	/* allowed ipfw rules to make allowed ip work */
585
	$cprules .= captiveportal_allowedip_configure();
586
587 55c18b30 Scott Ullrich
	/* allowed ipfw rules to make allowed hostnames work */
588
	$cprules .= captiveportal_allowedhostname_configure();
589
	
590 769e254e Ermal
	/* load rules */
591 1d9e9cca Ermal
	if ($reinit == true)
592
		$cprules = "table all flush\nflush\n{$cprules}";
593
	else {
594
		$tmprules = "table 3 flush\n";
595
		$tmprules .= "table 4 flush\n";
596
		$tmprules .= "table 5 flush\n";
597
		$tmprules .= "table 6 flush\n";
598
		$tmprules .= "table 7 flush\n";
599
		$tmprules .= "table 8 flush\n";
600
		$tmprules .= "table 9 flush\n";
601
		$tmprules .= "table 10 flush\n";
602
		$tmprules .= "flush\n";
603
		$cprules = "{$tmprules}\n{$cprules}";
604
	}
605 eade409a Ermal
606
	file_put_contents("{$g['tmp_path']}/ipfw.cp.rules", $cprules);
607
	mwexec("/sbin/ipfw -q {$g['tmp_path']}/ipfw.cp.rules", true);
608 0b108eda Scott Ullrich
	//@unlink("{$g['tmp_path']}/ipfw.cp.rules");
609 eade409a Ermal
610
	if ($reinit == false)
611
		unlock($captiveportallck);
612
613 9634e95b Ermal
	/* activate ipfw(4) so CP can work */
614 769e254e Ermal
	mwexec("/sbin/sysctl net.link.ether.ipfw=1");
615
616
	return $cprules;
617 3db19cf1 Scott Ullrich
}
618
619 eb7aa263 Ermal
/* remove clients that have been around for longer than the specified amount of time
620
 * db file structure:
621
 * timestamp,ipfw_rule_no,clientip,clientmac,username,sessionid,password,session_timeout,idle_timeout,session_terminate_time
622
 * (password is in Base64 and only saved when reauthentication is enabled)
623
 */
624 5b237745 Scott Ullrich
function captiveportal_prune_old() {
625 5060dea7 Scott Ullrich
	global $g, $config;
626 23c4f978 Scott Ullrich
627 5060dea7 Scott Ullrich
	/* check for expired entries */
628
	if (empty($config['captiveportal']['timeout']) ||
629 eb7aa263 Ermal
	!is_numeric($config['captiveportal']['timeout']))
630 5060dea7 Scott Ullrich
		$timeout = 0;
631
	else
632
		$timeout = $config['captiveportal']['timeout'] * 60;
633 eb7aa263 Ermal
634 5060dea7 Scott Ullrich
	if (empty($config['captiveportal']['idletimeout']) ||
635 eb7aa263 Ermal
	!is_numeric($config['captiveportal']['idletimeout']))
636 5060dea7 Scott Ullrich
		$idletimeout = 0;
637
	else
638
		$idletimeout = $config['captiveportal']['idletimeout'] * 60;
639 23c4f978 Scott Ullrich
640 5060dea7 Scott Ullrich
	if (!$timeout && !$idletimeout && !isset($config['captiveportal']['reauthenticate']) && 
641 eb7aa263 Ermal
	!isset($config['captiveportal']['radiussession_timeout']) && !isset($config['voucher']['enable']))
642 5060dea7 Scott Ullrich
		return;
643
644 006802ab Ermal
	$radiusservers = captiveportal_get_radius_servers();
645
646 5060dea7 Scott Ullrich
	/* read database */
647
	$cpdb = captiveportal_read_db();
648
649
	/*	To make sure we iterate over ALL accounts on every run the count($cpdb) is moved
650
	 *	outside of the loop. Otherwise the loop would evaluate count() on every iteration
651
	 *	and since $i would increase and count() would decrement they would meet before we
652
	 *	had a chance to iterate over all accounts.
653
	 */
654
	$unsetindexes = array();
655 5ebe85e9 Ermal
	$voucher_needs_sync = false;
656 b09c2d86 Ermal
	/* 
657
	 * Snapshot the time here to use for calculation to speed up the process.
658
	 * If something is missed next run will catch it!
659
	 */
660
	$pruning_time = time();
661
	$stop_time = $pruning_time;
662 3e5c0ab7 Ermal
	foreach ($cpdb as $cpentry) {
663 5060dea7 Scott Ullrich
664
		$timedout = false;
665
		$term_cause = 1;
666
667
		/* hard timeout? */
668
		if ($timeout) {
669 b09c2d86 Ermal
			if (($pruning_time - $cpentry[0]) >= $timeout) {
670 5060dea7 Scott Ullrich
				$timedout = true;
671
				$term_cause = 5; // Session-Timeout
672
			}
673 eb7aa263 Ermal
		}
674 23c4f978 Scott Ullrich
675 5060dea7 Scott Ullrich
		/* Session-Terminate-Time */
676 3e5c0ab7 Ermal
		if (!$timedout && !empty($cpentry[9])) {
677 b09c2d86 Ermal
			if ($pruning_time >= $cpentry[9]) {
678 5060dea7 Scott Ullrich
				$timedout = true;
679
				$term_cause = 5; // Session-Timeout
680
			}
681
		}
682
683
		/* check if the radius idle_timeout attribute has been set and if its set change the idletimeout to this value */
684 3e5c0ab7 Ermal
		$uidletimeout = (is_numeric($cpentry[8])) ? $cpentry[8] : $idletimeout;
685 5060dea7 Scott Ullrich
		/* if an idle timeout is specified, get last activity timestamp from ipfw */
686
		if (!$timedout && $uidletimeout) {
687 3e5c0ab7 Ermal
			$lastact = captiveportal_get_last_activity($cpentry[2]);
688 5060dea7 Scott Ullrich
			/*	If the user has logged on but not sent any traffic they will never be logged out.
689
			 *	We "fix" this by setting lastact to the login timestamp. 
690
			 */
691 3e5c0ab7 Ermal
			$lastact = $lastact ? $lastact : $cpentry[0];
692 b09c2d86 Ermal
			if ($lastact && (($pruning_time - $lastact) >= $uidletimeout)) {
693 5060dea7 Scott Ullrich
				$timedout = true;
694
				$term_cause = 4; // Idle-Timeout
695
				$stop_time = $lastact; // Entry added to comply with WISPr
696
			}
697 336e3c1c Charlie
		}
698
699 5060dea7 Scott Ullrich
		/* if vouchers are configured, activate session timeouts */
700 f45075dd Ermal
		if (!$timedout && isset($config['voucher']['enable']) && !empty($cpentry[7])) {
701 b09c2d86 Ermal
			if ($pruning_time >= ($cpentry[0] + $cpentry[7])) {
702 5060dea7 Scott Ullrich
				$timedout = true;
703
				$term_cause = 5; // Session-Timeout
704 5ebe85e9 Ermal
				$voucher_needs_sync = true;
705 5060dea7 Scott Ullrich
			}
706
		}
707
708
		/* if radius session_timeout is enabled and the session_timeout is not null, then check if the user should be logged out */
709 3e5c0ab7 Ermal
		if (!$timedout && isset($config['captiveportal']['radiussession_timeout']) && !empty($cpentry[7])) {
710 b09c2d86 Ermal
			if ($pruning_time >= ($cpentry[0] + $cpentry[7])) {
711 5060dea7 Scott Ullrich
				$timedout = true;
712
				$term_cause = 5; // Session-Timeout
713
			}
714
		}
715
716
		if ($timedout) {
717 3e5c0ab7 Ermal
			captiveportal_disconnect($cpentry, $radiusservers,$term_cause,$stop_time);
718
			captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "TIMEOUT");
719
			$unsetindexes[] = $cpentry[5];
720 5060dea7 Scott Ullrich
		}
721
722
		/* do periodic RADIUS reauthentication? */
723
		if (!$timedout && !empty($radiusservers)) {
724
			if (isset($config['captiveportal']['radacct_enable'])) {
725
				if ($config['captiveportal']['reauthenticateacct'] == "stopstart") {
726
					/* stop and restart accounting */
727 3e5c0ab7 Ermal
					RADIUS_ACCOUNTING_STOP($cpentry[1], // ruleno
728
						$cpentry[4], // username
729
						$cpentry[5], // sessionid
730
						$cpentry[0], // start time
731 5060dea7 Scott Ullrich
						$radiusservers,
732 3e5c0ab7 Ermal
						$cpentry[2], // clientip
733
						$cpentry[3], // clientmac
734 5060dea7 Scott Ullrich
						10); // NAS Request
735 3e5c0ab7 Ermal
					exec("/sbin/ipfw table 1 entryzerostats {$cpentry[2]}");
736
					exec("/sbin/ipfw table 2 entryzerostats {$cpentry[2]}");
737
					RADIUS_ACCOUNTING_START($cpentry[1], // ruleno
738
						$cpentry[4], // username
739
						$cpentry[5], // sessionid
740 5060dea7 Scott Ullrich
						$radiusservers,
741 3e5c0ab7 Ermal
						$cpentry[2], // clientip
742
						$cpentry[3]); // clientmac
743 5060dea7 Scott Ullrich
				} else if ($config['captiveportal']['reauthenticateacct'] == "interimupdate") {
744 3e5c0ab7 Ermal
					RADIUS_ACCOUNTING_STOP($cpentry[1], // ruleno
745
						$cpentry[4], // username
746
						$cpentry[5], // sessionid
747
						$cpentry[0], // start time
748 5060dea7 Scott Ullrich
						$radiusservers,
749 3e5c0ab7 Ermal
						$cpentry[2], // clientip
750
						$cpentry[3], // clientmac
751 5060dea7 Scott Ullrich
						10, // NAS Request
752
						true); // Interim Updates
753
				}
754
			}
755
756
			/* check this user against RADIUS again */
757
			if (isset($config['captiveportal']['reauthenticate'])) {
758 3e5c0ab7 Ermal
				$auth_list = RADIUS_AUTHENTICATION($cpentry[4], // username
759
					base64_decode($cpentry[6]), // password
760 5060dea7 Scott Ullrich
					$radiusservers,
761 3e5c0ab7 Ermal
					$cpentry[2], // clientip
762
					$cpentry[3], // clientmac
763
					$cpentry[1]); // ruleno
764 5060dea7 Scott Ullrich
				if ($auth_list['auth_val'] == 3) {
765 3e5c0ab7 Ermal
					captiveportal_disconnect($cpentry, $radiusservers, 17);
766
					captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "RADIUS_DISCONNECT", $auth_list['reply_message']);
767
					$unsetindexes[] = $cpentry[5];
768 5060dea7 Scott Ullrich
				}
769
			}
770
		}
771
	}
772 23c4f978 Scott Ullrich
773 5ebe85e9 Ermal
	if ($voucher_needs_sync == true)
774
		/* Triger a sync of the vouchers on config */
775
		send_event("service sync vouchers");
776
777 5060dea7 Scott Ullrich
	/* write database */
778 e92916d6 Ermal
	if (!empty($unsetindexes))
779
		captiveportal_write_db($cpdb, false, $unsetindexes);
780 5b237745 Scott Ullrich
}
781
782 3db19cf1 Scott Ullrich
/* remove a single client according to the DB entry */
783 0bd34ed6 Scott Ullrich
function captiveportal_disconnect($dbent, $radiusservers,$term_cause = 1,$stop_time = null) {
784 d99f7864 Scott Ullrich
	global $g, $config;
785
786
	$stop_time = (empty($stop_time)) ? time() : $stop_time;
787
788
	/* this client needs to be deleted - remove ipfw rules */
789 40b48c6c Ermal Lu?i
	if (isset($config['captiveportal']['radacct_enable']) && !empty($radiusservers)) {
790 d99f7864 Scott Ullrich
		RADIUS_ACCOUNTING_STOP($dbent[1], // ruleno
791 5060dea7 Scott Ullrich
			$dbent[4], // username
792
			$dbent[5], // sessionid
793
			$dbent[0], // start time
794
			$radiusservers,
795
			$dbent[2], // clientip
796
			$dbent[3], // clientmac
797
			$term_cause, // Acct-Terminate-Cause
798
			false,
799
			$stop_time);
800 d99f7864 Scott Ullrich
	}
801 32c392aa Ermal
	
802
	if (is_ipaddr($dbent[2])) {
803
		/* Delete client's ip entry from tables 3 and 4. */
804
		mwexec("/sbin/ipfw table 1 delete {$dbent[2]}");
805
		mwexec("/sbin/ipfw table 2 delete {$dbent[2]}");
806
		/* XXX: Redundant?! Ensure all pf(4) states are killed. */
807
		mwexec("pfctl -k {$dbent[2]}");
808
		mwexec("pfctl -K {$dbent[2]}");
809
	}
810 f9f71ad3 Ermal Lu?i
811
	/* 
812
	* These are the pipe numbers we use to control traffic shaping for each logged in user via captive portal
813
	* We could get an error if the pipe doesn't exist but everything should still be fine
814
	*/
815
	if (isset($config['captiveportal']['peruserbw'])) {
816
		mwexec("/sbin/ipfw pipe " . ($dbent[1]+20000) . " delete");
817
		mwexec("/sbin/ipfw pipe " . ($dbent[1]+20001) . " delete");
818
	}
819 7a7abeba Scott Ullrich
820 32c392aa Ermal
	/* Release the ruleno so it can be reallocated to new clients. */
821
	captiveportal_free_ipfw_ruleno($dbent[1]);
822 d322e3b3 Scott Ullrich
823
	// XMLRPC Call over to the master Voucher node
824 adcf909a Ermal
	if(!empty($config['voucher']['vouchersyncdbip'])) {
825
		$syncip   = $config['voucher']['vouchersyncdbip'];
826
		$syncport = $config['voucher']['vouchersyncport'];
827
		$syncpass = $config['voucher']['vouchersyncpass'];
828
		$vouchersyncusername = $config['voucher']['vouchersyncusername'];
829 f989aa5b Ermal
		$remote_status = xmlrpc_sync_voucher_disconnect($dbent, $syncip, $syncport, $syncpass, $vouchersyncusername, $term_cause, $stop_time);
830 d322e3b3 Scott Ullrich
	}
831
832 3db19cf1 Scott Ullrich
}
833 12ee8fe4 Scott Ullrich
834 006802ab Ermal
/* remove a single client by sessionid */
835
function captiveportal_disconnect_client($sessionid, $term_cause = 1, $logoutReason = "LOGOUT") {
836 d99f7864 Scott Ullrich
	global $g, $config;
837 36254e4a Scott Ullrich
838 d99f7864 Scott Ullrich
	$radiusservers = captiveportal_get_radius_servers();
839 006802ab Ermal
	$unsetindex = array();
840
841
	/* read database */
842 7d6be855 Ermal
	$cpdb = captiveportal_read_db();
843 d99f7864 Scott Ullrich
844
	/* find entry */
845 006802ab Ermal
	if (isset($cpdb[$sessionid])) {
846
		$cpentry = $cpdb[$sessionid];
847
		/* write database */
848
		$unsetindex[] = $sessionid;
849 7d6be855 Ermal
		captiveportal_write_db($cpdb, false, $unsetindex);
850 36254e4a Scott Ullrich
851 006802ab Ermal
		captiveportal_disconnect($cpentry, $radiusservers, $term_cause);
852
		captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "DISCONNECT");
853
	}		
854 5b237745 Scott Ullrich
}
855
856
/* send RADIUS acct stop for all current clients */
857 d31bc32a Ermal
function captiveportal_radius_stop_all() {
858
	global $config;
859 d99f7864 Scott Ullrich
860
	if (!isset($config['captiveportal']['radacct_enable']))
861
		return;
862
863
	$radiusservers = captiveportal_get_radius_servers();
864 40b48c6c Ermal Lu?i
	if (!empty($radiusservers)) {
865 d31bc32a Ermal
		$cpdb = captiveportal_read_db();
866
		foreach ($cpdb as $cpentry) {
867
			RADIUS_ACCOUNTING_STOP($cpentry[1], // ruleno
868 5060dea7 Scott Ullrich
				$cpentry[4], // username
869
				$cpentry[5], // sessionid
870
				$cpentry[0], // start time
871
				$radiusservers,
872
				$cpentry[2], // clientip
873
				$cpentry[3], // clientmac
874
				7); // Admin Reboot
875 d99f7864 Scott Ullrich
		}
876
	}
877 5b237745 Scott Ullrich
}
878
879 d5ae560d Ermal
function captiveportal_passthrumac_configure_entry($macent) {
880
	$rules = "";
881 5060dea7 Scott Ullrich
	$enBwup = isset($macent['bw_up']);
882
	$enBwdown = isset($macent['bw_down']);
883 d5ae560d Ermal
	$actionup = "allow";
884
	$actiondown = "allow";
885
886 5060dea7 Scott Ullrich
	if ($enBwup && $enBwdown)
887
		$ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, true);
888
	else
889
		$ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, false);
890 d5ae560d Ermal
891
	if ($enBwup) {
892 5060dea7 Scott Ullrich
		$bw_up = $ruleno + 20000;
893
		$rules .= "pipe {$bw_up} config bw {$macent['bw_up']}Kbit/s queue 100\n";
894 d5ae560d Ermal
		$actionup = "pipe {$bw_up}";
895 5060dea7 Scott Ullrich
	}
896
	if ($enBwdown) {
897 d5ae560d Ermal
		$bw_down = $ruleno + 20001;
898
		$rules .= "pipe {$bw_down} config bw {$macent['bw_down']}Kbit/s queue 100\n";
899
		$actiondown = "pipe {$bw_down}";
900 5060dea7 Scott Ullrich
	}
901 ffcf81bb Chris Buechler
	$rules .= "add {$ruleno} {$actiondown} ip from any to any MAC {$macent['mac']} any\n";
902 d5ae560d Ermal
	$ruleno++;
903 ffcf81bb Chris Buechler
	$rules .= "add {$ruleno} {$actionup} ip from any to any MAC any {$macent['mac']}\n";
904 d5ae560d Ermal
905
	return $rules;
906
}
907
908 dedf51a2 Ermal Lu?i
function captiveportal_passthrumac_configure($lock = false) {
909 5b237745 Scott Ullrich
	global $config, $g;
910 36254e4a Scott Ullrich
911 d5ae560d Ermal
	$rules = "";
912 36254e4a Scott Ullrich
913 5b237745 Scott Ullrich
	if (is_array($config['captiveportal']['passthrumac'])) {
914 1dbe445a Ermal
		$macdb = array();
915 5b237745 Scott Ullrich
		foreach ($config['captiveportal']['passthrumac'] as $macent) {
916 d5ae560d Ermal
			$rules .= captiveportal_passthrumac_configure_entry($macent);
917 1dbe445a Ermal
			$macdb[$macent['mac']]['active']  = true;
918
919 5b237745 Scott Ullrich
		}
920
	}
921 0bd34ed6 Scott Ullrich
922 d5ae560d Ermal
	return $rules;
923 5b237745 Scott Ullrich
}
924
925 fac13a5e Ermal
function captiveportal_passthrumac_findbyname($username) {
926
	global $config;
927
928
	if (is_array($config['captiveportal']['passthrumac'])) {
929
		foreach ($config['captiveportal']['passthrumac'] as $macent) {
930
			if ($macent['username'] == $username)
931
				return $macent;
932
		}
933
	}
934
	return NULL;
935
}
936
937 b01792a0 Ermal
/* 
938
 * table (3=IN)/(4=OUT) hold allowed ip's without bw limits
939
 * table (5=IN)/(6=OUT) hold allowed ip's with bw limit.
940
 */
941
function captiveportal_allowedip_configure_entry($ipent) {
942
943 8b73cc7e Scott Ullrich
	/* This function can deal with hostname or ipaddress */
944 0b108eda Scott Ullrich
	if($ipent['ip']) 	
945
		$ipaddress = $ipent['ip'];
946
947 8b73cc7e Scott Ullrich
	/*  Instead of copying this entire function for something
948
	 *  easy such as hostname vs ip address add this check
949
	 */
950 0b108eda Scott Ullrich
	if($ipent['hostname']) {
951
		$ipaddress = gethostbyname($ipent['hostname']);
952
		if(!is_ipaddr($ipaddress)) 
953
			return;
954
	}
955
956 b01792a0 Ermal
	$rules = "";
957 0b108eda Scott Ullrich
	$enBwup = intval($ipent['bw_up']);
958
	$enBwdown = intval($ipent['bw_down']);
959 b01792a0 Ermal
	$bw_up = "";
960 5060dea7 Scott Ullrich
	$bw_down = "";
961
	$tablein = array();
962
	$tableout = array();
963 b01792a0 Ermal
964 8b73cc7e Scott Ullrich
	if (intval($enBwup) > 0 or intval($enBwdown) > 0)
965 b01792a0 Ermal
		$ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, true);
966
	else
967
		$ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, false);
968
969 5060dea7 Scott Ullrich
	if ($ipent['dir'] == "from") {
970
		if ($enBwup)
971
			$tablein[] = 5;
972
		else
973
			$tablein[] = 3;
974
		if ($enBwdown)
975
			$tableout[] = 6;
976
		else
977
			$tableout[] = 4;
978
	} else if ($ipent['dir'] == "to") {
979
		if ($enBwup)
980
			$tablein[] = 9;
981
		else
982
			$tablein[] = 7;
983
		if ($enBwdown)
984
			$tableout[] = 10;
985
		else
986
			$tableout[] = 8;
987
	} else if ($ipent['dir'] == "both") {
988
		if ($enBwup) {
989
			$tablein[] = 5;
990
			$tablein[] = 9;
991
		} else {
992
			$tablein[] = 3;
993
			$tablein[] = 7;
994
		}
995
		if ($enBwdown) {
996
			$tableout[] = 6;
997
			$tableout[] = 10;
998
		} else {
999
			$tableout[] = 4;
1000
			$tableout[] = 8;
1001
		}
1002
	}
1003
	if ($enBwup) {
1004
		$bw_up = $ruleno + 20000;
1005
		$rules .= "pipe {$bw_up} config bw {$ipent['bw_up']}Kbit/s queue 100\n";
1006
	}
1007 d6a0379d Ermal
	$subnet = "";
1008
	if (!empty($ipent['sn']))
1009
		$subnet = "/{$ipent['sn']}";
1010 b01792a0 Ermal
	foreach ($tablein as $table)
1011 0b108eda Scott Ullrich
		$rules .= "table {$table} add {$ipaddress}{$subnet} {$bw_up}\n";
1012 5060dea7 Scott Ullrich
	if ($enBwdown) {
1013
		$bw_down = $ruleno + 20001;
1014
		$rules .= "pipe {$bw_down} config bw {$ipent['bw_down']}Kbit/s queue 100\n";
1015
	}
1016
	foreach ($tableout as $table)
1017 0b108eda Scott Ullrich
		$rules .= "table {$table} add {$ipaddress}{$subnet} {$bw_down}\n";
1018 b01792a0 Ermal
1019
	return $rules;
1020
}
1021
1022 f23a6091 Scott Ullrich
/* 
1023
	Adds a dnsfilter entry and watches for hostname changes.
1024
	A change results in reloading the ruleset.
1025
*/
1026 0b108eda Scott Ullrich
function setup_dnsfilter_entries() {
1027 55c18b30 Scott Ullrich
	global $g, $config;
1028 422b8b4e Ermal
1029 f23a6091 Scott Ullrich
	$cp_filterdns_filename = "{$g['varetc_path']}/filterdns-captiveportal.conf";
1030 422b8b4e Ermal
	$cp_filterdns_conf = "";
1031
	if (is_array($config['captiveportal']['allowedhostname'])) {
1032
		foreach ($config['captiveportal']['allowedhostname'] as $hostnameent)  {
1033 e35d6cda Ermal
			$cp_filterdns_conf .= "ipfw {$hostnameent['hostname']} 3\n";
1034
			$cp_filterdns_conf .= "ipfw {$hostnameent['hostname']} 4\n";
1035
			$cp_filterdns_conf .= "ipfw {$hostnameent['hostname']} 7\n";
1036
			$cp_filterdns_conf .= "ipfw {$hostnameent['hostname']} 8\n";
1037 422b8b4e Ermal
		}
1038
	}
1039
	file_put_contents($cp_filterdns_filename, $cp_filterdns_conf);
1040 f7f22750 Ermal
	killbypid("{$g['tmp_path']}/filterdns-cpah.pid");
1041
	mwexec("/usr/local/sbin/filterdns -p {$g['tmp_path']}/filterdns-cpah.pid -i 300 -c {$cp_filterdns_filename} -d 1");
1042 f23a6091 Scott Ullrich
}
1043
1044
function captiveportal_allowedhostname_configure() {
1045
	global $config, $g;
1046
1047 0b108eda Scott Ullrich
	$rules = "\n# captiveportal_allowedhostname_configure()\n";
1048 f23a6091 Scott Ullrich
	setup_dnsfilter_entries();
1049 55c18b30 Scott Ullrich
	if (is_array($config['captiveportal']['allowedhostname'])) {
1050 0b108eda Scott Ullrich
		foreach ($config['captiveportal']['allowedhostname'] as $hostnameent) 
1051
			$rules .= captiveportal_allowedip_configure_entry($hostnameent);
1052 55c18b30 Scott Ullrich
	}
1053 f23a6091 Scott Ullrich
	return $rules;
1054
}
1055
1056 cb0a2913 Ermal Lu?i
function captiveportal_allowedip_configure() {
1057 5b237745 Scott Ullrich
	global $config, $g;
1058 36254e4a Scott Ullrich
1059 6ce61a8f Ermal
	$rules = "";
1060 5b237745 Scott Ullrich
	if (is_array($config['captiveportal']['allowedip'])) {
1061 55c18b30 Scott Ullrich
		foreach ($config['captiveportal']['allowedip'] as $ipent) 
1062 b01792a0 Ermal
			$rules .= captiveportal_allowedip_configure_entry($ipent);
1063 cb0a2913 Ermal Lu?i
	}
1064 36254e4a Scott Ullrich
1065 6ce61a8f Ermal
	return $rules;
1066 5b237745 Scott Ullrich
}
1067
1068 2d53158f stompro
/* get last activity timestamp given client IP address */
1069 f9f71ad3 Ermal Lu?i
function captiveportal_get_last_activity($ip) {
1070 36254e4a Scott Ullrich
1071 d99f7864 Scott Ullrich
	$ipfwoutput = "";
1072 36254e4a Scott Ullrich
1073 6ce61a8f Ermal
	exec("/sbin/ipfw table 1 entrystats {$ip} 2>/dev/null", $ipfwoutput);
1074 f9f71ad3 Ermal Lu?i
	/* Reading only from one of the tables is enough of approximation. */
1075 d99f7864 Scott Ullrich
	if ($ipfwoutput[0]) {
1076
		$ri = explode(" ", $ipfwoutput[0]);
1077 f9f71ad3 Ermal Lu?i
		if ($ri[4])
1078
			return $ri[4];
1079 d99f7864 Scott Ullrich
	}
1080 36254e4a Scott Ullrich
1081 d99f7864 Scott Ullrich
	return 0;
1082 5b237745 Scott Ullrich
}
1083
1084 d31bc32a Ermal
function captiveportal_init_radius_servers() {
1085
	global $config, $g;
1086
1087
	/* generate radius server database */
1088
	if ($config['captiveportal']['radiusip'] && (!isset($config['captiveportal']['auth_method']) ||
1089 5060dea7 Scott Ullrich
		($config['captiveportal']['auth_method'] == "radius"))) {
1090 d31bc32a Ermal
		$radiusip = $config['captiveportal']['radiusip'];
1091
		$radiusip2 = ($config['captiveportal']['radiusip2']) ? $config['captiveportal']['radiusip2'] : null;
1092
1093
		if ($config['captiveportal']['radiusport'])
1094
			$radiusport = $config['captiveportal']['radiusport'];
1095
		else
1096
			$radiusport = 1812;
1097
		if ($config['captiveportal']['radiusacctport'])
1098
			$radiusacctport = $config['captiveportal']['radiusacctport'];
1099
		else
1100
			$radiusacctport = 1813;
1101
		if ($config['captiveportal']['radiusport2'])
1102
			$radiusport2 = $config['captiveportal']['radiusport2'];
1103
		else
1104
			$radiusport2 = 1812;
1105
		$radiuskey = $config['captiveportal']['radiuskey'];
1106
		$radiuskey2 = ($config['captiveportal']['radiuskey2']) ? $config['captiveportal']['radiuskey2'] : null;
1107
1108
		$cprdsrvlck = lock('captiveportalradius', LOCK_EX);
1109
		$fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db", "w");
1110
		if (!$fd) {
1111
			captiveportal_syslog("Error: cannot open radius DB file in captiveportal_configure().\n");
1112
			unlock($cprdsrvlck);
1113
			return 1;
1114
		} else if (isset($radiusip2, $radiuskey2))
1115
			fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey . "\n"
1116
			. $radiusip2 . "," . $radiusport2 . "," . $radiusacctport . "," . $radiuskey2);
1117
		else
1118
			fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey);
1119
		fclose($fd);
1120
		unlock($cprdsrvlck);
1121
	}
1122
}
1123
1124 5b237745 Scott Ullrich
/* read RADIUS servers into array */
1125
function captiveportal_get_radius_servers() {
1126 ac07425a Ermal
	global $g;
1127
1128
	$cprdsrvlck = lock('captiveportalradius');
1129
	if (file_exists("{$g['vardb_path']}/captiveportal_radius.db")) {
1130
		$radiusservers = array();
1131
		$cpradiusdb = file("{$g['vardb_path']}/captiveportal_radius.db", 
1132
		FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
1133
		if ($cpradiusdb) {
1134
			foreach($cpradiusdb as $cpradiusentry) {
1135
				$line = trim($cpradiusentry);
1136
				if ($line) {
1137
					$radsrv = array();
1138
					list($radsrv['ipaddr'],$radsrv['port'],$radsrv['acctport'],$radsrv['key']) = explode(",",$line);
1139
					$radiusservers[] = $radsrv;
1140 5060dea7 Scott Ullrich
				}
1141
			}
1142 2f70eac7 Ermal Lu?i
		}
1143 60b66b60 Ermal
		unlock($cprdsrvlck);
1144 ac07425a Ermal
		return $radiusservers;
1145
	}
1146
1147
	unlock($cprdsrvlck);
1148
	return false;
1149 5b237745 Scott Ullrich
}
1150
1151 3db19cf1 Scott Ullrich
/* log successful captive portal authentication to syslog */
1152
/* part of this code from php.net */
1153 0bd34ed6 Scott Ullrich
function captiveportal_logportalauth($user,$mac,$ip,$status, $message = null) {
1154 d99f7864 Scott Ullrich
	// Log it
1155
	if (!$message)
1156 f56a73f1 Scott Ullrich
		$message = "$status: $user, $mac, $ip";
1157 d31bc32a Ermal
	else {
1158
		$message = trim($message);
1159 f56a73f1 Scott Ullrich
		$message = "$status: $user, $mac, $ip, $message";
1160 d31bc32a Ermal
	}
1161 f56a73f1 Scott Ullrich
	captiveportal_syslog($message);
1162
}
1163
1164
/* log simple messages to syslog */
1165
function captiveportal_syslog($message) {
1166
	define_syslog_variables();
1167
	$message = trim($message);
1168
	openlog("logportalauth", LOG_PID, LOG_LOCAL4);
1169
	// Log it
1170
	syslog(LOG_INFO, $message);
1171 d99f7864 Scott Ullrich
	closelog();
1172 3db19cf1 Scott Ullrich
}
1173
1174 0bd34ed6 Scott Ullrich
function radius($username,$password,$clientip,$clientmac,$type) {
1175 5060dea7 Scott Ullrich
	global $g, $config;
1176
1177
	$ruleno = captiveportal_get_next_ipfw_ruleno();
1178
1179
	/* If the pool is empty, return appropriate message and fail authentication */
1180
	if (is_null($ruleno)) {
1181
		$auth_list = array();
1182
		$auth_list['auth_val'] = 1;
1183
		$auth_list['error'] = "System reached maximum login capacity";
1184
		return $auth_list;
1185
	}
1186
1187
	$radiusservers = captiveportal_get_radius_servers();
1188
1189
	$auth_list = RADIUS_AUTHENTICATION($username,
1190
		$password,
1191
		$radiusservers,
1192
		$clientip,
1193
		$clientmac,
1194
		$ruleno);
1195
1196
	if ($auth_list['auth_val'] == 2) {
1197
		captiveportal_logportalauth($username,$clientmac,$clientip,$type);
1198
		$sessionid = portal_allow($clientip,
1199
			$clientmac,
1200
			$username,
1201
			$password,
1202
			$auth_list,
1203
			$ruleno);
1204
	}
1205
1206
	return $auth_list;
1207 0bd34ed6 Scott Ullrich
}
1208
1209
/* read captive portal DB into array */
1210 e64c894f Ermal
function captiveportal_read_db($locked = false, $index = 5 /* sessionid by default */) {
1211 006802ab Ermal
	global $g;
1212 5060dea7 Scott Ullrich
1213 006802ab Ermal
	$cpdb = array();
1214 5060dea7 Scott Ullrich
1215 006802ab Ermal
	if ($locked == false)
1216 5060dea7 Scott Ullrich
		$cpdblck = lock('captiveportaldb');
1217 006802ab Ermal
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r");
1218
	if ($fd) {
1219
		while (!feof($fd)) {
1220
			$line = trim(fgets($fd));
1221
			if ($line) {
1222
				$cpe = explode(",", $line);
1223
				/* Hash by session id */
1224 e64c894f Ermal
				$cpdb[$cpe[$index]] = $cpe;
1225 5060dea7 Scott Ullrich
			}
1226
		}
1227 006802ab Ermal
		fclose($fd);
1228
	}
1229
	if ($locked == false)
1230 5060dea7 Scott Ullrich
		unlock($cpdblck);
1231 006802ab Ermal
	return $cpdb;
1232 0bd34ed6 Scott Ullrich
}
1233
1234
/* write captive portal DB */
1235 e92916d6 Ermal
function captiveportal_write_db($cpdb, $locked = false, $remove = false) {
1236 006802ab Ermal
	global $g;
1237 5060dea7 Scott Ullrich
1238 006802ab Ermal
	if ($locked == false)
1239 5060dea7 Scott Ullrich
		$cpdblck = lock('captiveportaldb', LOCK_EX);
1240 006802ab Ermal
1241 e92916d6 Ermal
	if (is_array($remove)) {
1242
		if (!empty($remove)) {
1243
			$cpdb = captiveportal_read_db(true);
1244 1540194f Ermal
			foreach ($remove as $key) {
1245
				if (is_array($key))
1246
					log_error("Captive portal Array passed as unset index: " . print_r($key, true));
1247
				else
1248
					unset($cpdb[$key]);
1249
			}
1250 e92916d6 Ermal
		} else
1251
			return; //This makes sure no record removal calls
1252 006802ab Ermal
	}
1253
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w");
1254
	if ($fd) {
1255
		foreach ($cpdb as $cpent) {
1256
			fwrite($fd, join(",", $cpent) . "\n");
1257 5060dea7 Scott Ullrich
		}
1258 006802ab Ermal
		fclose($fd);
1259
	}
1260
	if ($locked == false)
1261
		unlock($cpdblck);
1262 0bd34ed6 Scott Ullrich
}
1263
1264
function captiveportal_write_elements() {
1265 769e254e Ermal
	global $g, $config;
1266 5060dea7 Scott Ullrich
	
1267 769e254e Ermal
	/* delete any existing elements */
1268
	if (is_dir($g['captiveportal_element_path'])) {
1269
		$dh = opendir($g['captiveportal_element_path']);
1270
		while (($file = readdir($dh)) !== false) {
1271
			if ($file != "." && $file != "..")
1272
				unlink($g['captiveportal_element_path'] . "/" . $file);
1273
		}
1274
		closedir($dh);
1275 5060dea7 Scott Ullrich
	} else {
1276 769e254e Ermal
		@mkdir($g['captiveportal_element_path']);
1277 5060dea7 Scott Ullrich
	}
1278 769e254e Ermal
1279 1fadb31d Scott Ullrich
	if (is_array($config['captiveportal']['element'])) {
1280
		conf_mount_rw();
1281
		foreach ($config['captiveportal']['element'] as $data) {
1282
			$fd = @fopen($g['captiveportal_element_path'] . '/' . $data['name'], "wb");
1283
			if (!$fd) {
1284
				printf("Error: cannot open '{$data['name']}' in captiveportal_write_elements().\n");
1285
				return 1;
1286
			}
1287
			$decoded = base64_decode($data['content']);
1288
			fwrite($fd,$decoded);
1289
			fclose($fd);
1290
			unlink_if_exists("{$g['captiveportal_path']}/{$data['name']}");
1291
			unlink_if_exists("{$g['captiveportal_path']}/{$data['name']}");
1292
			mwexec("cd {$g['captiveportal_path']}/ && ln -s {$g['captiveportal_element_path']}/{$data['name']} {$data['name']}");
1293
		}
1294
		conf_mount_ro();
1295
	}
1296 5060dea7 Scott Ullrich
	
1297 769e254e Ermal
	return 0;
1298 0bd34ed6 Scott Ullrich
}
1299
1300 6ce61a8f Ermal
function captiveportal_init_ipfw_ruleno($rulenos_start = 2000, $rulenos_range_max = 49899) {
1301
	global $g;
1302
1303
	@unlink("{$g['vardb_path']}/captiveportal.rules");
1304
	$rules = array_pad(array(), $rulenos_range_max - $rulenos_start, false);
1305
	file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules));
1306
}
1307
1308 920cafaf Scott Ullrich
/*
1309
 * This function will calculate the lowest free firewall ruleno
1310 f9f71ad3 Ermal Lu?i
 * within the range specified based on the actual logged on users
1311 920cafaf Scott Ullrich
 *
1312
 */
1313 b01792a0 Ermal
function captiveportal_get_next_ipfw_ruleno($rulenos_start = 2000, $rulenos_range_max = 49899, $usebw = false) {
1314 f9f71ad3 Ermal Lu?i
	global $config, $g;
1315 6ce61a8f Ermal
1316 de752609 Scott Ullrich
	if(!isset($config['captiveportal']['enable']))
1317 01d57b8c Scott Ullrich
		return NULL;
1318 6ce61a8f Ermal
1319 d31bc32a Ermal
	$cpruleslck = lock('captiveportalrules', LOCK_EX);
1320 f9f71ad3 Ermal Lu?i
	$ruleno = 0;
1321 6ce61a8f Ermal
	if (file_exists("{$g['vardb_path']}/captiveportal.rules")) {
1322
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules"));
1323
		for ($ridx = 2; $ridx < ($rulenos_range_max - $rulenos_start); $ridx++) {
1324
			if ($rules[$ridx]) {
1325
				/* 
1326 5060dea7 Scott Ullrich
				 * This allows our traffic shaping pipes to be the in pipe the same as ruleno 
1327
				 * and the out pipe ruleno + 1. This removes limitation that where present in 
1328
				 * previous version of the peruserbw.
1329
				 */
1330 2279a587 Ermal
				if (isset($config['captiveportal']['peruserbw']) || $usebw == true)
1331 6ce61a8f Ermal
					$ridx++;
1332
				continue;
1333
			}
1334
			$ruleno = $ridx;
1335
			$rules[$ridx] = "used";
1336 b01792a0 Ermal
			if (isset($config['captiveportal']['peruserbw']) || $usebw == true)
1337 6ce61a8f Ermal
				$rules[++$ridx] = "used";
1338
			break;
1339
		}
1340
	} else {
1341
		$rules = array_pad(array(), $rulenos_range_max - $rulenos_start, false);
1342
		$rules[2] = "used";
1343
		$ruleno = 2;
1344
	}
1345
	file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules));
1346 d31bc32a Ermal
	unlock($cpruleslck);
1347 6ce61a8f Ermal
	return $ruleno;
1348
}
1349
1350 b01792a0 Ermal
function captiveportal_free_ipfw_ruleno($ruleno, $usedbw = false) {
1351 6ce61a8f Ermal
	global $config, $g;
1352
1353
	if(!isset($config['captiveportal']['enable']))
1354
		return NULL;
1355
1356 d31bc32a Ermal
	$cpruleslck = lock('captiveportalrules', LOCK_EX);
1357 6ce61a8f Ermal
	if (file_exists("{$g['vardb_path']}/captiveportal.rules")) {
1358
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules"));
1359
		$rules[$ruleno] = false;
1360 b01792a0 Ermal
		if (isset($config['captiveportal']['peruserbw']) || $usedbw == true)
1361 6ce61a8f Ermal
			$rules[++$ruleno] = false;
1362
		file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules));
1363 f9f71ad3 Ermal Lu?i
	}
1364 d31bc32a Ermal
	unlock($cpruleslck);
1365 6ce61a8f Ermal
}
1366
1367 d5ae560d Ermal
function captiveportal_get_ipfw_passthru_ruleno($value) {
1368 6ce61a8f Ermal
	global $config, $g;
1369
1370
	if(!isset($config['captiveportal']['enable']))
1371 5060dea7 Scott Ullrich
				return NULL;
1372 6ce61a8f Ermal
1373 d31bc32a Ermal
	$cpruleslck = lock('captiveportalrules', LOCK_EX);
1374 5060dea7 Scott Ullrich
	if (file_exists("{$g['vardb_path']}/captiveportal.rules")) {
1375
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules"));
1376 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`);
1377 d31bc32a Ermal
		if ($rules[$ruleno]) {
1378
			unlock($cpruleslck);
1379 6ce61a8f Ermal
			return $ruleno;
1380 d31bc32a Ermal
		}
1381 5060dea7 Scott Ullrich
	}
1382 6ce61a8f Ermal
1383 d31bc32a Ermal
	unlock($cpruleslck);
1384 f9f71ad3 Ermal Lu?i
	return NULL;
1385 920cafaf Scott Ullrich
}
1386
1387 360d815d Scott Ullrich
/**
1388
 * This function will calculate the traffic produced by a client
1389
 * based on its firewall rule
1390
 *
1391
 * Point of view: NAS
1392
 *
1393
 * Input means: from the client
1394
 * Output means: to the client
1395
 *
1396
 */
1397
1398 f9f71ad3 Ermal Lu?i
function getVolume($ip) {
1399 360d815d Scott Ullrich
1400 5060dea7 Scott Ullrich
	$volume = array();
1401 360d815d Scott Ullrich
1402 5060dea7 Scott Ullrich
	// Initialize vars properly, since we don't want NULL vars
1403
	$volume['input_pkts'] = $volume['input_bytes'] = $volume['output_pkts'] = $volume['output_bytes'] = 0 ;
1404 360d815d Scott Ullrich
1405 5060dea7 Scott Ullrich
	// Ingress
1406
	$ipfwin = "";
1407
	$ipfwout = "";
1408
	$matchesin = "";
1409
	$matchesout = "";
1410
	exec("/sbin/ipfw table 1 entrystats {$ip}", $ipfwin);
1411
	if ($ipfwin[0]) {
1412 523855b0 Scott Ullrich
		$ipfwin = split(" ", $ipfwin[0]);
1413
		$volume['input_pkts'] = $ipfwin[2];
1414
		$volume['input_bytes'] = $ipfwin[3];
1415 5060dea7 Scott Ullrich
	}
1416 f9f71ad3 Ermal Lu?i
1417 5060dea7 Scott Ullrich
	exec("/sbin/ipfw table 2 entrystats {$ip}", $ipfwout);
1418
	if ($ipfwout[0]) {
1419
		$ipfwout = split(" ", $ipfwout[0]);
1420
		$volume['output_pkts'] = $ipfwout[2];
1421
		$volume['output_bytes'] = $ipfwout[3];
1422
	}
1423 360d815d Scott Ullrich
1424 5060dea7 Scott Ullrich
	return $volume;
1425 360d815d Scott Ullrich
}
1426
1427 856e58a6 Scott Ullrich
/**
1428
 * Get the NAS-Identifier
1429
 *
1430
 * We will use our local hostname to make up the nas_id
1431
 */
1432
function getNasID()
1433
{
1434 5060dea7 Scott Ullrich
	$nasId = "";
1435
	exec("/bin/hostname", $nasId);
1436
	if(!$nasId[0])
1437
		$nasId[0] = "{$g['product_name']}";
1438
	return $nasId[0];
1439 856e58a6 Scott Ullrich
}
1440
1441
/**
1442
 * Get the NAS-IP-Address based on the current wan address
1443
 *
1444
 * Use functions in interfaces.inc to find this out
1445
 *
1446
 */
1447
1448
function getNasIP()
1449
{
1450 64c0462b Ermal
	global $config;
1451
1452 5060dea7 Scott Ullrich
	if (empty($config['captiveportal']['radiussrcip_attribute'])) {
1453
			$nasIp = get_interface_ip();
1454
	} else {
1455 4a756e9b Ermal
		if (is_ipaddr($config['captiveportal']['radiussrcip_attribute']))
1456 5060dea7 Scott Ullrich
			$nasIp = $config['captiveportal']['radiussrcip_attribute'];
1457
		else
1458
			$nasIp = get_interface_ip($config['captiveportal']['radiussrcip_attribute']);
1459 36ff7f81 Ermal
	}
1460 64c0462b Ermal
		
1461 5060dea7 Scott Ullrich
	if(!is_ipaddr($nasIp))
1462
		$nasIp = "0.0.0.0";
1463 64c0462b Ermal
1464
	return $nasIp;
1465 856e58a6 Scott Ullrich
}
1466
1467 f8b11310 Ermal Lu?i
function portal_ip_from_client_ip($cliip) {
1468
	global $config;
1469
1470
	$interfaces = explode(",", $config['captiveportal']['interface']);
1471
	foreach ($interfaces as $cpif) {
1472
		$ip = get_interface_ip($cpif);
1473
		$sn = get_interface_subnet($cpif);
1474
		if (ip_in_subnet($cliip, "{$ip}/{$sn}"))
1475
			return $ip;
1476
	}
1477
1478 cc125e13 Chris Buechler
	// doesn't match up to any particular interface
1479
	// so let's set the portal IP to what PHP says 
1480
	// the server IP issuing the request is. 
1481
	// allows same behavior as 1.2.x where IP isn't 
1482
	// in the subnet of any CP interface (static routes, etc.)
1483
	// rather than forcing to DNS hostname resolution
1484
	$ip = $_SERVER['SERVER_ADDR'];
1485
	if (is_ipaddr($ip))
1486
		return $ip;
1487
1488 f8b11310 Ermal Lu?i
	return false;
1489
}
1490
1491 ac631bba lgcosta
/* functions move from index.php */
1492
1493
function portal_reply_page($redirurl, $type = null, $message = null, $clientmac = null, $clientip = null, $username = null, $password = null) {
1494
	global $g, $config;
1495
1496
	/* Get captive portal layout */
1497
	if ($type == "redir") {
1498
		header("Location: {$redirurl}");
1499
		return;
1500
	} else if ($type == "login")
1501
		$htmltext = get_include_contents("{$g['varetc_path']}/captiveportal.html");
1502
	else
1503
		$htmltext = get_include_contents("{$g['varetc_path']}/captiveportal-error.html");
1504
1505
	/* substitute the PORTAL_REDIRURL variable */
1506
	if ($config['captiveportal']['preauthurl']) {
1507
		$htmltext = str_replace("\$PORTAL_REDIRURL\$", "{$config['captiveportal']['preauthurl']}", $htmltext);
1508
		$htmltext = str_replace("#PORTAL_REDIRURL#", "{$config['captiveportal']['preauthurl']}", $htmltext);
1509
	}
1510
1511
	/* substitute other variables */
1512
	if (isset($config['captiveportal']['httpslogin'])) {
1513
		$htmltext = str_replace("\$PORTAL_ACTION\$", "https://{$config['captiveportal']['httpsname']}:8001/", $htmltext);
1514
		$htmltext = str_replace("#PORTAL_ACTION#", "https://{$config['captiveportal']['httpsname']}:8001/", $htmltext);
1515
	} else {
1516
		$ifip = portal_ip_from_client_ip($clientip);
1517
		if (!$ifip)
1518
			$ourhostname = $config['system']['hostname'] . ":8000";
1519
		else
1520
			$ourhostname = "{$ifip}:8000";
1521
		$htmltext = str_replace("\$PORTAL_ACTION\$", "http://{$ourhostname}/", $htmltext);
1522
		$htmltext = str_replace("#PORTAL_ACTION#", "http://{$ourhostname}/", $htmltext);
1523
	}
1524
1525
	$htmltext = str_replace("\$PORTAL_REDIRURL\$", htmlspecialchars($redirurl), $htmltext);
1526
	$htmltext = str_replace("\$PORTAL_MESSAGE\$", htmlspecialchars($message), $htmltext);
1527
	$htmltext = str_replace("\$CLIENT_MAC\$", htmlspecialchars($clientmac), $htmltext);
1528
	$htmltext = str_replace("\$CLIENT_IP\$", htmlspecialchars($clientip), $htmltext);
1529
1530
	// Special handling case for captive portal master page so that it can be ran 
1531
	// through the PHP interpreter using the include method above.  We convert the
1532
	// $VARIABLE$ case to #VARIABLE# in /etc/inc/captiveportal.inc before writing out.
1533
	$htmltext = str_replace("#PORTAL_REDIRURL#", htmlspecialchars($redirurl), $htmltext);
1534
	$htmltext = str_replace("#PORTAL_MESSAGE#", htmlspecialchars($message), $htmltext);
1535
	$htmltext = str_replace("#CLIENT_MAC#", htmlspecialchars($clientmac), $htmltext);
1536
	$htmltext = str_replace("#CLIENT_IP#", htmlspecialchars($clientip), $htmltext);
1537
	$htmltext = str_replace("#USERNAME#", htmlspecialchars($username), $htmltext);
1538
	$htmltext = str_replace("#PASSWORD#", htmlspecialchars($password), $htmltext);
1539
1540
    echo $htmltext;
1541
}
1542
1543
function portal_mac_radius($clientmac,$clientip) {
1544
    global $config ;
1545
1546
    $radmac_secret = $config['captiveportal']['radmac_secret'];
1547
1548
    /* authentication against the radius server */
1549
    $username = mac_format($clientmac);
1550
    $auth_list = radius($username,$radmac_secret,$clientip,$clientmac,"MACHINE LOGIN");
1551
    if ($auth_list['auth_val'] == 2)
1552
        return TRUE;
1553
    if (!empty($auth_list['url_redirection']))
1554
	portal_reply_page($auth_list['url_redirection'], "redir");
1555
1556
    return FALSE;
1557
}
1558
1559
function portal_allow($clientip,$clientmac,$username,$password = null, $attributes = null, $ruleno = null)  {
1560
1561
	global $redirurl, $g, $config, $type, $passthrumac, $_POST;
1562
1563
	/* See if a ruleno is passed, if not start sessions because this means there isn't one atm */
1564
	if ($ruleno == null)
1565
		$ruleno = captiveportal_get_next_ipfw_ruleno();
1566
1567
	/* if the pool is empty, return appropriate message and exit */
1568
	if (is_null($ruleno)) {
1569
		portal_reply_page($redirurl, "error", "System reached maximum login capacity");
1570
		log_error("WARNING!  Captive portal has reached maximum login capacity");
1571
		exit;
1572
	}
1573
1574
	// Ensure we create an array if we are missing attributes
1575
	if (!is_array($attributes))
1576
		$attributes = array();
1577
1578
	$radiusservers = captiveportal_get_radius_servers();
1579
1580 006802ab Ermal
	/* Do not allow concurrent login execution. */
1581
	$cpdblck = lock('captiveportaldb', LOCK_EX);
1582
1583
	unset($sessionid);
1584
1585
	/* read in client database */
1586
	$cpdb = captiveportal_read_db(true);
1587
1588 ac631bba lgcosta
	if ($attributes['voucher'])
1589
		$remaining_time = $attributes['session_timeout'];
1590
1591
	$writecfg = false;
1592
	/* Find an existing session */
1593
	if ((isset($config['captiveportal']['noconcurrentlogins'])) && $passthrumac) {
1594
		if (isset($config['captiveportal']['passthrumacadd'])) {
1595
			$mac = captiveportal_passthrumac_findbyname($username);
1596
			if (!empty($mac)) {
1597
				if ($_POST['replacemacpassthru']) {
1598
					foreach ($config['captiveportal']['passthrumac'] as $idx => $macent) {
1599
						if ($macent['mac'] == $mac['mac']) {
1600
							$macrules = "";
1601
							$ruleno = captiveportal_get_ipfw_passthru_ruleno($mac['mac']);
1602
                                			if ($ruleno) {
1603
								captiveportal_free_ipfw_ruleno($ruleno, true);
1604
                                        			$macrules .= "delete {$ruleno}\n";
1605
								++$ruleno;
1606
                                        			$macrules .= "delete {$ruleno}\n";
1607
                                			}
1608
							unset($config['captiveportal']['passthrumac'][$idx]);
1609
							$mac['mac'] = $clientmac;
1610
							$config['captiveportal']['passthrumac'][] = $mac;
1611
							$macrules .= captiveportal_passthrumac_configure_entry($mac);
1612
							file_put_contents("{$g['tmp_path']}/macentry.rules.tmp", $macrules);
1613
							mwexec("/sbin/ipfw -q {$g['tmp_path']}/macentry.rules.tmp");
1614
							$writecfg = true;
1615
							$sessionid = true;
1616
							break;
1617
						}
1618
					}
1619
                                } else {
1620
					portal_reply_page($redirurl, "error", "Username: {$username} is already authenticated using another MAC address.",
1621
						$clientmac, $clientip, $username, $password);
1622
					exit;
1623
				}
1624
			}
1625
		}
1626
	}
1627
1628 b09c2d86 Ermal
	/* Snaphost the timestamp */
1629
	$allow_time = time();
1630
1631 006802ab Ermal
	foreach ($cpdb as $sid => $cpentry) {
1632 ac631bba lgcosta
		/* on the same ip */
1633 006802ab Ermal
		if($cpentry[2] == $clientip) {
1634
			captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - REUSING OLD SESSION");
1635
			$sessionid = $sid;
1636 ac631bba lgcosta
			break;
1637
		}
1638 006802ab Ermal
		elseif (($attributes['voucher']) && ($username != 'unauthenticated') && ($cpentry[4] == $username)) {
1639 ac631bba lgcosta
			// user logged in with an active voucher. Check for how long and calculate 
1640
			// how much time we can give him (voucher credit - used time)
1641 b09c2d86 Ermal
			$remaining_time = $cpentry[0] + $cpentry[7] - $allow_time;
1642 ac631bba lgcosta
			if ($remaining_time < 0)    // just in case. 
1643
				$remaining_time = 0;
1644
1645
			/* This user was already logged in so we disconnect the old one */
1646 006802ab Ermal
			captiveportal_disconnect($cpentry,$radiusservers,13);
1647
			captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - TERMINATING OLD SESSION");
1648 ce1942d6 Ermal
			unset($cpdb[$sid]);
1649 ac631bba lgcosta
			break;
1650
		}
1651
		elseif ((isset($config['captiveportal']['noconcurrentlogins'])) && ($username != 'unauthenticated')) {
1652
			/* on the same username */
1653 006802ab Ermal
			if (strcasecmp($cpentry[4], $username) == 0) {
1654 ac631bba lgcosta
				/* This user was already logged in so we disconnect the old one */
1655 006802ab Ermal
				captiveportal_disconnect($cpentry,$radiusservers,13);
1656
				captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - TERMINATING OLD SESSION");
1657 ce1942d6 Ermal
				unset($cpdb[$sid]);
1658 ac631bba lgcosta
				break;
1659
			}
1660
		}
1661
	}
1662
1663
	if ($attributes['voucher'] && $remaining_time <= 0)
1664
		return 0;       // voucher already used and no time left
1665
1666
	if (!isset($sessionid)) {
1667
		/* generate unique session ID */
1668
		$tod = gettimeofday();
1669
		$sessionid = substr(md5(mt_rand() . $tod['sec'] . $tod['usec'] . $clientip . $clientmac), 0, 16);
1670
1671
		/* Add rules for traffic shaping
1672
		 * We don't need to add extra rules since traffic will pass due to the following kernel option
1673
		 * net.inet.ip.fw.one_pass: 1
1674
		 */
1675
		$peruserbw = isset($config['captiveportal']['peruserbw']);
1676
1677 2d4003aa Ermal
		$bw_up = isset($attributes['bw_up']) ? round(intval($attributes['bw_up'])/1000, 2) : $config['captiveportal']['bwdefaultup'];
1678
		$bw_down = isset($attributes['bw_down']) ? round(intval($attributes['bw_down'])/1000, 2) : $config['captiveportal']['bwdefaultdn'];
1679 ac631bba lgcosta
1680
		if ($passthrumac) {
1681
			$mac = array();
1682
			$mac['mac'] = $clientmac;
1683
			if (isset($config['captiveportal']['passthrumacaddusername']))
1684
				$mac['username'] = $username;
1685
			$mac['descr'] =  "Auto added pass-through MAC for user {$username}";
1686
			if (!empty($bw_up))
1687
				$mac['bw_up'] = $bw_up;
1688
			if (!empty($bw_down))
1689
				$mac['bw_down'] = $bw_down;
1690
			if (!is_array($config['captiveportal']['passthrumac']))
1691
				$config['captiveportal']['passthrumac'] = array();
1692
			$config['captiveportal']['passthrumac'][] = $mac;
1693 006802ab Ermal
			unlock($cpdblck);
1694 ac631bba lgcosta
			$macrules = captiveportal_passthrumac_configure_entry($mac);
1695
			file_put_contents("{$g['tmp_path']}/macentry.rules.tmp", $macrules);
1696
			mwexec("/sbin/ipfw -q {$g['tmp_path']}/macentry.rules.tmp");
1697
			$writecfg = true;
1698
		} else {
1699
			if ($peruserbw && !empty($bw_up) && is_numeric($bw_up)) {
1700
				$bw_up_pipeno = $ruleno + 20000;
1701
				//$bw_up /= 1000; // Scale to Kbit/s
1702
				mwexec("/sbin/ipfw pipe {$bw_up_pipeno} config bw {$bw_up}Kbit/s queue 100");
1703
1704
				if (!isset($config['captiveportal']['nomacfilter']))
1705
					mwexec("/sbin/ipfw table 1 add {$clientip} mac {$clientmac} {$bw_up_pipeno}");
1706
				else
1707
					mwexec("/sbin/ipfw table 1 add {$clientip} {$bw_up_pipeno}");
1708
			} else {
1709
				if (!isset($config['captiveportal']['nomacfilter']))
1710
					mwexec("/sbin/ipfw table 1 add {$clientip} mac {$clientmac}");
1711
				else
1712
					mwexec("/sbin/ipfw table 1 add {$clientip}");
1713
			}
1714
			if ($peruserbw && !empty($bw_down) && is_numeric($bw_down)) {
1715
				$bw_down_pipeno = $ruleno + 20001;
1716
				//$bw_down /= 1000; // Scale to Kbit/s
1717
				mwexec("/sbin/ipfw pipe {$bw_down_pipeno} config bw {$bw_down}Kbit/s queue 100");
1718
1719
				if (!isset($config['captiveportal']['nomacfilter']))
1720
					mwexec("/sbin/ipfw table 2 add {$clientip} mac {$clientmac} {$bw_down_pipeno}");
1721
				else
1722
					mwexec("/sbin/ipfw table 2 add {$clientip} {$bw_down_pipeno}");
1723
			} else {
1724
				if (!isset($config['captiveportal']['nomacfilter']))
1725
					mwexec("/sbin/ipfw table 2 add {$clientip} mac {$clientmac}");
1726
				else
1727
					mwexec("/sbin/ipfw table 2 add {$clientip}");
1728
			}
1729
1730
			if ($attributes['voucher'])
1731
				$attributes['session_timeout'] = $remaining_time;
1732
1733
			/* encode password in Base64 just in case it contains commas */
1734
			$bpassword = base64_encode($password);
1735 b09c2d86 Ermal
			$cpdb[] = array($allow_time, $ruleno, $clientip, $clientmac, $username, $sessionid, $bpassword,
1736 ac631bba lgcosta
				$attributes['session_timeout'], $attributes['idle_timeout'], $attributes['session_terminate_time']);
1737
1738 006802ab Ermal
			/* rewrite information to database */
1739
			captiveportal_write_db($cpdb, true);
1740
			unlock($cpdblck);
1741
1742 ac631bba lgcosta
			if (isset($config['captiveportal']['radacct_enable']) && !empty($radiusservers)) {
1743
				$acct_val = RADIUS_ACCOUNTING_START($ruleno,
1744
                                		$username, $sessionid, $radiusservers, $clientip, $clientmac);
1745
				if ($acct_val == 1)
1746
					captiveportal_logportalauth($username,$clientmac,$clientip,$type,"RADIUS ACCOUNTING FAILED");
1747
			}
1748
		}
1749 006802ab Ermal
	} else
1750
		unlock($cpdblck);
1751 ac631bba lgcosta
1752
	if ($writecfg == true)
1753
		write_config();
1754
1755
	/* redirect user to desired destination */
1756
	if (!empty($attributes['url_redirection']))
1757
		$my_redirurl = $attributes['url_redirection'];
1758 cd2c71fc Ermal
	else if (!empty($config['captiveportal']['redirurl']))
1759 ac631bba lgcosta
		$my_redirurl = $config['captiveportal']['redirurl'];
1760
	else
1761
		$my_redirurl = $redirurl;
1762
1763
	if(isset($config['captiveportal']['logoutwin_enable']) && !$passthrumac) {
1764
1765
		if (isset($config['captiveportal']['httpslogin']))
1766
			$logouturl = "https://{$config['captiveportal']['httpsname']}:8001/";
1767
		else {
1768
			$ifip = portal_ip_from_client_ip($clientip);
1769
			if (!$ifip)
1770
				$ourhostname = $config['system']['hostname'] . ":8000";
1771
			else
1772
				$ourhostname = "{$ifip}:8000";
1773
			$logouturl = "http://{$ourhostname}/";
1774
		}
1775
1776
		if (isset($attributes['reply_message']))
1777
			$message = $attributes['reply_message'];
1778
		else
1779
			$message = 0;
1780
1781
		include("{$g['varetc_path']}/captiveportal-logout.html");
1782
1783
	} else {
1784
		header("Location: " . $my_redirurl);
1785
	}
1786
1787
	return $sessionid;
1788
}
1789
1790
1791
/*
1792
 * Used for when pass-through credits are enabled.
1793
 * Returns true when there was at least one free login to deduct for the MAC.
1794
 * Expired entries are removed as they are seen.
1795
 * Active entries are updated according to the configuration.
1796
 */
1797
function portal_consume_passthrough_credit($clientmac) {
1798
	global $config;
1799
1800
	if (!empty($config['captiveportal']['freelogins_count']) && is_numeric($config['captiveportal']['freelogins_count']))
1801
		$freeloginscount = $config['captiveportal']['freelogins_count'];
1802
	else
1803
		return false;
1804
1805
	if (!empty($config['captiveportal']['freelogins_resettimeout']) && is_numeric($config['captiveportal']['freelogins_resettimeout']))
1806
		$resettimeout = $config['captiveportal']['freelogins_resettimeout'];
1807
	else
1808
		return false;
1809
1810 17e7a243 Scott Ullrich
	if ($freeloginscount < 1 || $resettimeout <= 0 || !$clientmac)
1811 ac631bba lgcosta
		return false;
1812
1813
	$updatetimeouts = isset($config['captiveportal']['freelogins_updatetimeouts']);
1814
1815
	/*
1816
	 * Read database of used MACs.  Lines are a comma-separated list
1817
	 * of the time, MAC, then the count of pass-through credits remaining.
1818
	 */
1819
	$usedmacs = captiveportal_read_usedmacs_db();
1820
1821
	$currenttime = time();
1822
	$found = false;
1823
	foreach ($usedmacs as $key => $usedmac) {
1824
		$usedmac = explode(",", $usedmac);
1825
1826
		if ($usedmac[1] == $clientmac) {
1827
			if ($usedmac[0] + ($resettimeout * 3600) > $currenttime) {
1828
				if ($usedmac[2] < 1) {
1829
					if ($updatetimeouts) {
1830
						$usedmac[0] = $currenttime;
1831
						unset($usedmacs[$key]);
1832
						$usedmacs[] = implode(",", $usedmac);
1833
						captiveportal_write_usedmacs_db($usedmacs);
1834
					}
1835
1836
					return false;
1837
				} else {
1838
					$usedmac[2] -= 1;
1839
					$usedmacs[$key] = implode(",", $usedmac);
1840
				}
1841
1842
				$found = true;
1843
			} else
1844
				unset($usedmacs[$key]);
1845
1846
			break;
1847
		} else if ($usedmac[0] + ($resettimeout * 3600) <= $currenttime)
1848
				unset($usedmacs[$key]);
1849
	}
1850
1851
	if (!$found) {
1852
		$usedmac = array($currenttime, $clientmac, $freeloginscount - 1);
1853
		$usedmacs[] = implode(",", $usedmac);
1854
	}
1855
1856
	captiveportal_write_usedmacs_db($usedmacs);
1857
	return true;
1858
}
1859
1860
function captiveportal_read_usedmacs_db() {
1861
	global $g;
1862
1863
	$cpumaclck = lock('captiveusedmacs');
1864
	if (file_exists("{$g['vardb_path']}/captiveportal_usedmacs.db")) {
1865
		$usedmacs = file("{$g['vardb_path']}/captiveportal_usedmacs.db", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
1866 da666ca8 Scott Ullrich
		if (!$usedmacs)
1867 ac631bba lgcosta
			$usedmacs = array();
1868
	} else
1869
		$usedmacs = array();
1870
1871
	unlock($cpumaclck);
1872
	return $usedmacs;
1873
}
1874
1875
function captiveportal_write_usedmacs_db($usedmacs) {
1876
	global $g;
1877
1878
	$cpumaclck = lock('captiveusedmacs', LOCK_EX);
1879
	@file_put_contents("{$g['vardb_path']}/captiveportal_usedmacs.db", implode("\n", $usedmacs));
1880
	unlock($cpumaclck);
1881
}
1882
1883 006802ab Ermal
?>