Project

General

Profile

Download (60.2 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 a135349d Ermal
	$ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, true);
888 d5ae560d Ermal
889
	if ($enBwup) {
890 5060dea7 Scott Ullrich
		$bw_up = $ruleno + 20000;
891
		$rules .= "pipe {$bw_up} config bw {$macent['bw_up']}Kbit/s queue 100\n";
892 d5ae560d Ermal
		$actionup = "pipe {$bw_up}";
893 5060dea7 Scott Ullrich
	}
894
	if ($enBwdown) {
895 d5ae560d Ermal
		$bw_down = $ruleno + 20001;
896
		$rules .= "pipe {$bw_down} config bw {$macent['bw_down']}Kbit/s queue 100\n";
897
		$actiondown = "pipe {$bw_down}";
898 5060dea7 Scott Ullrich
	}
899 ffcf81bb Chris Buechler
	$rules .= "add {$ruleno} {$actiondown} ip from any to any MAC {$macent['mac']} any\n";
900 d5ae560d Ermal
	$ruleno++;
901 ffcf81bb Chris Buechler
	$rules .= "add {$ruleno} {$actionup} ip from any to any MAC any {$macent['mac']}\n";
902 d5ae560d Ermal
903
	return $rules;
904
}
905
906 dedf51a2 Ermal Lu?i
function captiveportal_passthrumac_configure($lock = false) {
907 5b237745 Scott Ullrich
	global $config, $g;
908 36254e4a Scott Ullrich
909 d5ae560d Ermal
	$rules = "";
910 36254e4a Scott Ullrich
911 5b237745 Scott Ullrich
	if (is_array($config['captiveportal']['passthrumac'])) {
912 1dbe445a Ermal
		$macdb = array();
913 5b237745 Scott Ullrich
		foreach ($config['captiveportal']['passthrumac'] as $macent) {
914 d5ae560d Ermal
			$rules .= captiveportal_passthrumac_configure_entry($macent);
915 1dbe445a Ermal
			$macdb[$macent['mac']]['active']  = true;
916
917 5b237745 Scott Ullrich
		}
918
	}
919 0bd34ed6 Scott Ullrich
920 d5ae560d Ermal
	return $rules;
921 5b237745 Scott Ullrich
}
922
923 fac13a5e Ermal
function captiveportal_passthrumac_findbyname($username) {
924
	global $config;
925
926
	if (is_array($config['captiveportal']['passthrumac'])) {
927
		foreach ($config['captiveportal']['passthrumac'] as $macent) {
928
			if ($macent['username'] == $username)
929
				return $macent;
930
		}
931
	}
932
	return NULL;
933
}
934
935 b01792a0 Ermal
/* 
936
 * table (3=IN)/(4=OUT) hold allowed ip's without bw limits
937
 * table (5=IN)/(6=OUT) hold allowed ip's with bw limit.
938
 */
939
function captiveportal_allowedip_configure_entry($ipent) {
940
941 8b73cc7e Scott Ullrich
	/* This function can deal with hostname or ipaddress */
942 0b108eda Scott Ullrich
	if($ipent['ip']) 	
943
		$ipaddress = $ipent['ip'];
944
945 8b73cc7e Scott Ullrich
	/*  Instead of copying this entire function for something
946
	 *  easy such as hostname vs ip address add this check
947
	 */
948 0b108eda Scott Ullrich
	if($ipent['hostname']) {
949
		$ipaddress = gethostbyname($ipent['hostname']);
950
		if(!is_ipaddr($ipaddress)) 
951
			return;
952
	}
953
954 b01792a0 Ermal
	$rules = "";
955 0b108eda Scott Ullrich
	$enBwup = intval($ipent['bw_up']);
956
	$enBwdown = intval($ipent['bw_down']);
957 b01792a0 Ermal
	$bw_up = "";
958 5060dea7 Scott Ullrich
	$bw_down = "";
959
	$tablein = array();
960
	$tableout = array();
961 b01792a0 Ermal
962 8b73cc7e Scott Ullrich
	if (intval($enBwup) > 0 or intval($enBwdown) > 0)
963 b01792a0 Ermal
		$ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, true);
964
	else
965
		$ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, false);
966
967 5060dea7 Scott Ullrich
	if ($ipent['dir'] == "from") {
968
		if ($enBwup)
969
			$tablein[] = 5;
970
		else
971
			$tablein[] = 3;
972
		if ($enBwdown)
973
			$tableout[] = 6;
974
		else
975
			$tableout[] = 4;
976
	} else if ($ipent['dir'] == "to") {
977
		if ($enBwup)
978
			$tablein[] = 9;
979
		else
980
			$tablein[] = 7;
981
		if ($enBwdown)
982
			$tableout[] = 10;
983
		else
984
			$tableout[] = 8;
985
	} else if ($ipent['dir'] == "both") {
986
		if ($enBwup) {
987
			$tablein[] = 5;
988
			$tablein[] = 9;
989
		} else {
990
			$tablein[] = 3;
991
			$tablein[] = 7;
992
		}
993
		if ($enBwdown) {
994
			$tableout[] = 6;
995
			$tableout[] = 10;
996
		} else {
997
			$tableout[] = 4;
998
			$tableout[] = 8;
999
		}
1000
	}
1001
	if ($enBwup) {
1002
		$bw_up = $ruleno + 20000;
1003
		$rules .= "pipe {$bw_up} config bw {$ipent['bw_up']}Kbit/s queue 100\n";
1004
	}
1005 d6a0379d Ermal
	$subnet = "";
1006
	if (!empty($ipent['sn']))
1007
		$subnet = "/{$ipent['sn']}";
1008 b01792a0 Ermal
	foreach ($tablein as $table)
1009 0b108eda Scott Ullrich
		$rules .= "table {$table} add {$ipaddress}{$subnet} {$bw_up}\n";
1010 5060dea7 Scott Ullrich
	if ($enBwdown) {
1011
		$bw_down = $ruleno + 20001;
1012
		$rules .= "pipe {$bw_down} config bw {$ipent['bw_down']}Kbit/s queue 100\n";
1013
	}
1014
	foreach ($tableout as $table)
1015 0b108eda Scott Ullrich
		$rules .= "table {$table} add {$ipaddress}{$subnet} {$bw_down}\n";
1016 b01792a0 Ermal
1017
	return $rules;
1018
}
1019
1020 f23a6091 Scott Ullrich
/* 
1021
	Adds a dnsfilter entry and watches for hostname changes.
1022
	A change results in reloading the ruleset.
1023
*/
1024 0b108eda Scott Ullrich
function setup_dnsfilter_entries() {
1025 55c18b30 Scott Ullrich
	global $g, $config;
1026 422b8b4e Ermal
1027 f23a6091 Scott Ullrich
	$cp_filterdns_filename = "{$g['varetc_path']}/filterdns-captiveportal.conf";
1028 422b8b4e Ermal
	$cp_filterdns_conf = "";
1029
	if (is_array($config['captiveportal']['allowedhostname'])) {
1030
		foreach ($config['captiveportal']['allowedhostname'] as $hostnameent)  {
1031 e35d6cda Ermal
			$cp_filterdns_conf .= "ipfw {$hostnameent['hostname']} 3\n";
1032
			$cp_filterdns_conf .= "ipfw {$hostnameent['hostname']} 4\n";
1033
			$cp_filterdns_conf .= "ipfw {$hostnameent['hostname']} 7\n";
1034
			$cp_filterdns_conf .= "ipfw {$hostnameent['hostname']} 8\n";
1035 422b8b4e Ermal
		}
1036
	}
1037
	file_put_contents($cp_filterdns_filename, $cp_filterdns_conf);
1038 f7f22750 Ermal
	killbypid("{$g['tmp_path']}/filterdns-cpah.pid");
1039
	mwexec("/usr/local/sbin/filterdns -p {$g['tmp_path']}/filterdns-cpah.pid -i 300 -c {$cp_filterdns_filename} -d 1");
1040 f23a6091 Scott Ullrich
}
1041
1042
function captiveportal_allowedhostname_configure() {
1043
	global $config, $g;
1044
1045 0b108eda Scott Ullrich
	$rules = "\n# captiveportal_allowedhostname_configure()\n";
1046 f23a6091 Scott Ullrich
	setup_dnsfilter_entries();
1047 55c18b30 Scott Ullrich
	if (is_array($config['captiveportal']['allowedhostname'])) {
1048 0b108eda Scott Ullrich
		foreach ($config['captiveportal']['allowedhostname'] as $hostnameent) 
1049
			$rules .= captiveportal_allowedip_configure_entry($hostnameent);
1050 55c18b30 Scott Ullrich
	}
1051 f23a6091 Scott Ullrich
	return $rules;
1052
}
1053
1054 cb0a2913 Ermal Lu?i
function captiveportal_allowedip_configure() {
1055 5b237745 Scott Ullrich
	global $config, $g;
1056 36254e4a Scott Ullrich
1057 6ce61a8f Ermal
	$rules = "";
1058 5b237745 Scott Ullrich
	if (is_array($config['captiveportal']['allowedip'])) {
1059 55c18b30 Scott Ullrich
		foreach ($config['captiveportal']['allowedip'] as $ipent) 
1060 b01792a0 Ermal
			$rules .= captiveportal_allowedip_configure_entry($ipent);
1061 cb0a2913 Ermal Lu?i
	}
1062 36254e4a Scott Ullrich
1063 6ce61a8f Ermal
	return $rules;
1064 5b237745 Scott Ullrich
}
1065
1066 2d53158f stompro
/* get last activity timestamp given client IP address */
1067 f9f71ad3 Ermal Lu?i
function captiveportal_get_last_activity($ip) {
1068 36254e4a Scott Ullrich
1069 d99f7864 Scott Ullrich
	$ipfwoutput = "";
1070 36254e4a Scott Ullrich
1071 6ce61a8f Ermal
	exec("/sbin/ipfw table 1 entrystats {$ip} 2>/dev/null", $ipfwoutput);
1072 f9f71ad3 Ermal Lu?i
	/* Reading only from one of the tables is enough of approximation. */
1073 d99f7864 Scott Ullrich
	if ($ipfwoutput[0]) {
1074
		$ri = explode(" ", $ipfwoutput[0]);
1075 f9f71ad3 Ermal Lu?i
		if ($ri[4])
1076
			return $ri[4];
1077 d99f7864 Scott Ullrich
	}
1078 36254e4a Scott Ullrich
1079 d99f7864 Scott Ullrich
	return 0;
1080 5b237745 Scott Ullrich
}
1081
1082 d31bc32a Ermal
function captiveportal_init_radius_servers() {
1083
	global $config, $g;
1084
1085
	/* generate radius server database */
1086
	if ($config['captiveportal']['radiusip'] && (!isset($config['captiveportal']['auth_method']) ||
1087 5060dea7 Scott Ullrich
		($config['captiveportal']['auth_method'] == "radius"))) {
1088 d31bc32a Ermal
		$radiusip = $config['captiveportal']['radiusip'];
1089
		$radiusip2 = ($config['captiveportal']['radiusip2']) ? $config['captiveportal']['radiusip2'] : null;
1090
1091
		if ($config['captiveportal']['radiusport'])
1092
			$radiusport = $config['captiveportal']['radiusport'];
1093
		else
1094
			$radiusport = 1812;
1095
		if ($config['captiveportal']['radiusacctport'])
1096
			$radiusacctport = $config['captiveportal']['radiusacctport'];
1097
		else
1098
			$radiusacctport = 1813;
1099
		if ($config['captiveportal']['radiusport2'])
1100
			$radiusport2 = $config['captiveportal']['radiusport2'];
1101
		else
1102
			$radiusport2 = 1812;
1103
		$radiuskey = $config['captiveportal']['radiuskey'];
1104
		$radiuskey2 = ($config['captiveportal']['radiuskey2']) ? $config['captiveportal']['radiuskey2'] : null;
1105
1106
		$cprdsrvlck = lock('captiveportalradius', LOCK_EX);
1107
		$fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db", "w");
1108
		if (!$fd) {
1109
			captiveportal_syslog("Error: cannot open radius DB file in captiveportal_configure().\n");
1110
			unlock($cprdsrvlck);
1111
			return 1;
1112
		} else if (isset($radiusip2, $radiuskey2))
1113
			fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey . "\n"
1114
			. $radiusip2 . "," . $radiusport2 . "," . $radiusacctport . "," . $radiuskey2);
1115
		else
1116
			fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey);
1117
		fclose($fd);
1118
		unlock($cprdsrvlck);
1119
	}
1120
}
1121
1122 5b237745 Scott Ullrich
/* read RADIUS servers into array */
1123
function captiveportal_get_radius_servers() {
1124 ac07425a Ermal
	global $g;
1125
1126
	$cprdsrvlck = lock('captiveportalradius');
1127
	if (file_exists("{$g['vardb_path']}/captiveportal_radius.db")) {
1128
		$radiusservers = array();
1129
		$cpradiusdb = file("{$g['vardb_path']}/captiveportal_radius.db", 
1130
		FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
1131
		if ($cpradiusdb) {
1132
			foreach($cpradiusdb as $cpradiusentry) {
1133
				$line = trim($cpradiusentry);
1134
				if ($line) {
1135
					$radsrv = array();
1136
					list($radsrv['ipaddr'],$radsrv['port'],$radsrv['acctport'],$radsrv['key']) = explode(",",$line);
1137
					$radiusservers[] = $radsrv;
1138 5060dea7 Scott Ullrich
				}
1139
			}
1140 2f70eac7 Ermal Lu?i
		}
1141 60b66b60 Ermal
		unlock($cprdsrvlck);
1142 ac07425a Ermal
		return $radiusservers;
1143
	}
1144
1145
	unlock($cprdsrvlck);
1146
	return false;
1147 5b237745 Scott Ullrich
}
1148
1149 3db19cf1 Scott Ullrich
/* log successful captive portal authentication to syslog */
1150
/* part of this code from php.net */
1151 0bd34ed6 Scott Ullrich
function captiveportal_logportalauth($user,$mac,$ip,$status, $message = null) {
1152 d99f7864 Scott Ullrich
	// Log it
1153
	if (!$message)
1154 f56a73f1 Scott Ullrich
		$message = "$status: $user, $mac, $ip";
1155 d31bc32a Ermal
	else {
1156
		$message = trim($message);
1157 f56a73f1 Scott Ullrich
		$message = "$status: $user, $mac, $ip, $message";
1158 d31bc32a Ermal
	}
1159 f56a73f1 Scott Ullrich
	captiveportal_syslog($message);
1160
}
1161
1162
/* log simple messages to syslog */
1163
function captiveportal_syslog($message) {
1164
	define_syslog_variables();
1165
	$message = trim($message);
1166
	openlog("logportalauth", LOG_PID, LOG_LOCAL4);
1167
	// Log it
1168
	syslog(LOG_INFO, $message);
1169 d99f7864 Scott Ullrich
	closelog();
1170 3db19cf1 Scott Ullrich
}
1171
1172 0bd34ed6 Scott Ullrich
function radius($username,$password,$clientip,$clientmac,$type) {
1173 5060dea7 Scott Ullrich
	global $g, $config;
1174
1175
	$ruleno = captiveportal_get_next_ipfw_ruleno();
1176
1177
	/* If the pool is empty, return appropriate message and fail authentication */
1178
	if (is_null($ruleno)) {
1179
		$auth_list = array();
1180
		$auth_list['auth_val'] = 1;
1181
		$auth_list['error'] = "System reached maximum login capacity";
1182
		return $auth_list;
1183
	}
1184
1185
	$radiusservers = captiveportal_get_radius_servers();
1186
1187
	$auth_list = RADIUS_AUTHENTICATION($username,
1188
		$password,
1189
		$radiusservers,
1190
		$clientip,
1191
		$clientmac,
1192
		$ruleno);
1193
1194
	if ($auth_list['auth_val'] == 2) {
1195
		captiveportal_logportalauth($username,$clientmac,$clientip,$type);
1196
		$sessionid = portal_allow($clientip,
1197
			$clientmac,
1198
			$username,
1199
			$password,
1200
			$auth_list,
1201
			$ruleno);
1202
	}
1203
1204
	return $auth_list;
1205 0bd34ed6 Scott Ullrich
}
1206
1207
/* read captive portal DB into array */
1208 e64c894f Ermal
function captiveportal_read_db($locked = false, $index = 5 /* sessionid by default */) {
1209 006802ab Ermal
	global $g;
1210 5060dea7 Scott Ullrich
1211 006802ab Ermal
	$cpdb = array();
1212 5060dea7 Scott Ullrich
1213 006802ab Ermal
	if ($locked == false)
1214 5060dea7 Scott Ullrich
		$cpdblck = lock('captiveportaldb');
1215 006802ab Ermal
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r");
1216
	if ($fd) {
1217
		while (!feof($fd)) {
1218
			$line = trim(fgets($fd));
1219
			if ($line) {
1220
				$cpe = explode(",", $line);
1221
				/* Hash by session id */
1222 e64c894f Ermal
				$cpdb[$cpe[$index]] = $cpe;
1223 5060dea7 Scott Ullrich
			}
1224
		}
1225 006802ab Ermal
		fclose($fd);
1226
	}
1227
	if ($locked == false)
1228 5060dea7 Scott Ullrich
		unlock($cpdblck);
1229 006802ab Ermal
	return $cpdb;
1230 0bd34ed6 Scott Ullrich
}
1231
1232
/* write captive portal DB */
1233 e92916d6 Ermal
function captiveportal_write_db($cpdb, $locked = false, $remove = false) {
1234 006802ab Ermal
	global $g;
1235 5060dea7 Scott Ullrich
1236 006802ab Ermal
	if ($locked == false)
1237 5060dea7 Scott Ullrich
		$cpdblck = lock('captiveportaldb', LOCK_EX);
1238 006802ab Ermal
1239 e92916d6 Ermal
	if (is_array($remove)) {
1240
		if (!empty($remove)) {
1241
			$cpdb = captiveportal_read_db(true);
1242 1540194f Ermal
			foreach ($remove as $key) {
1243
				if (is_array($key))
1244
					log_error("Captive portal Array passed as unset index: " . print_r($key, true));
1245
				else
1246
					unset($cpdb[$key]);
1247
			}
1248 e92916d6 Ermal
		} else
1249
			return; //This makes sure no record removal calls
1250 006802ab Ermal
	}
1251
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w");
1252
	if ($fd) {
1253
		foreach ($cpdb as $cpent) {
1254
			fwrite($fd, join(",", $cpent) . "\n");
1255 5060dea7 Scott Ullrich
		}
1256 006802ab Ermal
		fclose($fd);
1257
	}
1258
	if ($locked == false)
1259
		unlock($cpdblck);
1260 0bd34ed6 Scott Ullrich
}
1261
1262
function captiveportal_write_elements() {
1263 769e254e Ermal
	global $g, $config;
1264 5060dea7 Scott Ullrich
	
1265 769e254e Ermal
	/* delete any existing elements */
1266
	if (is_dir($g['captiveportal_element_path'])) {
1267
		$dh = opendir($g['captiveportal_element_path']);
1268
		while (($file = readdir($dh)) !== false) {
1269
			if ($file != "." && $file != "..")
1270
				unlink($g['captiveportal_element_path'] . "/" . $file);
1271
		}
1272
		closedir($dh);
1273 5060dea7 Scott Ullrich
	} else {
1274 769e254e Ermal
		@mkdir($g['captiveportal_element_path']);
1275 5060dea7 Scott Ullrich
	}
1276 769e254e Ermal
1277 1fadb31d Scott Ullrich
	if (is_array($config['captiveportal']['element'])) {
1278
		conf_mount_rw();
1279
		foreach ($config['captiveportal']['element'] as $data) {
1280
			$fd = @fopen($g['captiveportal_element_path'] . '/' . $data['name'], "wb");
1281
			if (!$fd) {
1282
				printf("Error: cannot open '{$data['name']}' in captiveportal_write_elements().\n");
1283
				return 1;
1284
			}
1285
			$decoded = base64_decode($data['content']);
1286
			fwrite($fd,$decoded);
1287
			fclose($fd);
1288
			unlink_if_exists("{$g['captiveportal_path']}/{$data['name']}");
1289
			unlink_if_exists("{$g['captiveportal_path']}/{$data['name']}");
1290
			mwexec("cd {$g['captiveportal_path']}/ && ln -s {$g['captiveportal_element_path']}/{$data['name']} {$data['name']}");
1291
		}
1292
		conf_mount_ro();
1293
	}
1294 5060dea7 Scott Ullrich
	
1295 769e254e Ermal
	return 0;
1296 0bd34ed6 Scott Ullrich
}
1297
1298 6ce61a8f Ermal
function captiveportal_init_ipfw_ruleno($rulenos_start = 2000, $rulenos_range_max = 49899) {
1299
	global $g;
1300
1301
	@unlink("{$g['vardb_path']}/captiveportal.rules");
1302
	$rules = array_pad(array(), $rulenos_range_max - $rulenos_start, false);
1303
	file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules));
1304
}
1305
1306 920cafaf Scott Ullrich
/*
1307
 * This function will calculate the lowest free firewall ruleno
1308 f9f71ad3 Ermal Lu?i
 * within the range specified based on the actual logged on users
1309 920cafaf Scott Ullrich
 *
1310
 */
1311 b01792a0 Ermal
function captiveportal_get_next_ipfw_ruleno($rulenos_start = 2000, $rulenos_range_max = 49899, $usebw = false) {
1312 f9f71ad3 Ermal Lu?i
	global $config, $g;
1313 6ce61a8f Ermal
1314 de752609 Scott Ullrich
	if(!isset($config['captiveportal']['enable']))
1315 01d57b8c Scott Ullrich
		return NULL;
1316 6ce61a8f Ermal
1317 d31bc32a Ermal
	$cpruleslck = lock('captiveportalrules', LOCK_EX);
1318 f9f71ad3 Ermal Lu?i
	$ruleno = 0;
1319 6ce61a8f Ermal
	if (file_exists("{$g['vardb_path']}/captiveportal.rules")) {
1320
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules"));
1321
		for ($ridx = 2; $ridx < ($rulenos_range_max - $rulenos_start); $ridx++) {
1322
			if ($rules[$ridx]) {
1323
				/* 
1324 5060dea7 Scott Ullrich
				 * This allows our traffic shaping pipes to be the in pipe the same as ruleno 
1325
				 * and the out pipe ruleno + 1. This removes limitation that where present in 
1326
				 * previous version of the peruserbw.
1327
				 */
1328 2279a587 Ermal
				if (isset($config['captiveportal']['peruserbw']) || $usebw == true)
1329 6ce61a8f Ermal
					$ridx++;
1330
				continue;
1331
			}
1332
			$ruleno = $ridx;
1333
			$rules[$ridx] = "used";
1334 b01792a0 Ermal
			if (isset($config['captiveportal']['peruserbw']) || $usebw == true)
1335 6ce61a8f Ermal
				$rules[++$ridx] = "used";
1336
			break;
1337
		}
1338
	} else {
1339
		$rules = array_pad(array(), $rulenos_range_max - $rulenos_start, false);
1340
		$rules[2] = "used";
1341
		$ruleno = 2;
1342
	}
1343
	file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules));
1344 d31bc32a Ermal
	unlock($cpruleslck);
1345 6ce61a8f Ermal
	return $ruleno;
1346
}
1347
1348 b01792a0 Ermal
function captiveportal_free_ipfw_ruleno($ruleno, $usedbw = false) {
1349 6ce61a8f Ermal
	global $config, $g;
1350
1351
	if(!isset($config['captiveportal']['enable']))
1352
		return NULL;
1353
1354 d31bc32a Ermal
	$cpruleslck = lock('captiveportalrules', LOCK_EX);
1355 6ce61a8f Ermal
	if (file_exists("{$g['vardb_path']}/captiveportal.rules")) {
1356
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules"));
1357
		$rules[$ruleno] = false;
1358 b01792a0 Ermal
		if (isset($config['captiveportal']['peruserbw']) || $usedbw == true)
1359 6ce61a8f Ermal
			$rules[++$ruleno] = false;
1360
		file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules));
1361 f9f71ad3 Ermal Lu?i
	}
1362 d31bc32a Ermal
	unlock($cpruleslck);
1363 6ce61a8f Ermal
}
1364
1365 d5ae560d Ermal
function captiveportal_get_ipfw_passthru_ruleno($value) {
1366 6ce61a8f Ermal
	global $config, $g;
1367
1368
	if(!isset($config['captiveportal']['enable']))
1369 a135349d Ermal
		return NULL;
1370 6ce61a8f Ermal
1371 d31bc32a Ermal
	$cpruleslck = lock('captiveportalrules', LOCK_EX);
1372 5060dea7 Scott Ullrich
	if (file_exists("{$g['vardb_path']}/captiveportal.rules")) {
1373
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules"));
1374 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`);
1375 d31bc32a Ermal
		if ($rules[$ruleno]) {
1376
			unlock($cpruleslck);
1377 6ce61a8f Ermal
			return $ruleno;
1378 d31bc32a Ermal
		}
1379 5060dea7 Scott Ullrich
	}
1380 6ce61a8f Ermal
1381 d31bc32a Ermal
	unlock($cpruleslck);
1382 f9f71ad3 Ermal Lu?i
	return NULL;
1383 920cafaf Scott Ullrich
}
1384
1385 360d815d Scott Ullrich
/**
1386
 * This function will calculate the traffic produced by a client
1387
 * based on its firewall rule
1388
 *
1389
 * Point of view: NAS
1390
 *
1391
 * Input means: from the client
1392
 * Output means: to the client
1393
 *
1394
 */
1395
1396 f9f71ad3 Ermal Lu?i
function getVolume($ip) {
1397 360d815d Scott Ullrich
1398 5060dea7 Scott Ullrich
	$volume = array();
1399 360d815d Scott Ullrich
1400 5060dea7 Scott Ullrich
	// Initialize vars properly, since we don't want NULL vars
1401
	$volume['input_pkts'] = $volume['input_bytes'] = $volume['output_pkts'] = $volume['output_bytes'] = 0 ;
1402 360d815d Scott Ullrich
1403 5060dea7 Scott Ullrich
	// Ingress
1404
	$ipfwin = "";
1405
	$ipfwout = "";
1406
	$matchesin = "";
1407
	$matchesout = "";
1408
	exec("/sbin/ipfw table 1 entrystats {$ip}", $ipfwin);
1409
	if ($ipfwin[0]) {
1410 523855b0 Scott Ullrich
		$ipfwin = split(" ", $ipfwin[0]);
1411
		$volume['input_pkts'] = $ipfwin[2];
1412 c8950140 Evgeny Yurchenko
		$volume['input_bytes'] = $ipfwin[3];
1413 5060dea7 Scott Ullrich
	}
1414 f9f71ad3 Ermal Lu?i
1415 5060dea7 Scott Ullrich
	exec("/sbin/ipfw table 2 entrystats {$ip}", $ipfwout);
1416
	if ($ipfwout[0]) {
1417
		$ipfwout = split(" ", $ipfwout[0]);
1418
		$volume['output_pkts'] = $ipfwout[2];
1419 c8950140 Evgeny Yurchenko
		$volume['output_bytes'] = $ipfwout[3];
1420 5060dea7 Scott Ullrich
	}
1421 360d815d Scott Ullrich
1422 5060dea7 Scott Ullrich
	return $volume;
1423 360d815d Scott Ullrich
}
1424
1425 856e58a6 Scott Ullrich
/**
1426
 * Get the NAS-Identifier
1427
 *
1428
 * We will use our local hostname to make up the nas_id
1429
 */
1430
function getNasID()
1431
{
1432 5060dea7 Scott Ullrich
	$nasId = "";
1433
	exec("/bin/hostname", $nasId);
1434
	if(!$nasId[0])
1435
		$nasId[0] = "{$g['product_name']}";
1436
	return $nasId[0];
1437 856e58a6 Scott Ullrich
}
1438
1439
/**
1440
 * Get the NAS-IP-Address based on the current wan address
1441
 *
1442
 * Use functions in interfaces.inc to find this out
1443
 *
1444
 */
1445
1446
function getNasIP()
1447
{
1448 64c0462b Ermal
	global $config;
1449
1450 5060dea7 Scott Ullrich
	if (empty($config['captiveportal']['radiussrcip_attribute'])) {
1451
			$nasIp = get_interface_ip();
1452
	} else {
1453 4a756e9b Ermal
		if (is_ipaddr($config['captiveportal']['radiussrcip_attribute']))
1454 5060dea7 Scott Ullrich
			$nasIp = $config['captiveportal']['radiussrcip_attribute'];
1455
		else
1456
			$nasIp = get_interface_ip($config['captiveportal']['radiussrcip_attribute']);
1457 36ff7f81 Ermal
	}
1458 64c0462b Ermal
		
1459 5060dea7 Scott Ullrich
	if(!is_ipaddr($nasIp))
1460
		$nasIp = "0.0.0.0";
1461 64c0462b Ermal
1462
	return $nasIp;
1463 856e58a6 Scott Ullrich
}
1464
1465 f8b11310 Ermal Lu?i
function portal_ip_from_client_ip($cliip) {
1466
	global $config;
1467
1468
	$interfaces = explode(",", $config['captiveportal']['interface']);
1469
	foreach ($interfaces as $cpif) {
1470
		$ip = get_interface_ip($cpif);
1471
		$sn = get_interface_subnet($cpif);
1472
		if (ip_in_subnet($cliip, "{$ip}/{$sn}"))
1473
			return $ip;
1474
	}
1475
1476 4d41d0b8 jim-p
	$iface = exec_command("/sbin/route -n get {$cliip} | /usr/bin/awk '/interface/ { print \$2; };'");
1477
	$iface = trim($iface, "\n");
1478
	if (!empty($iface)) {
1479
		$ip = find_interface_ip($iface);
1480
		if (is_ipaddr($ip))
1481
			return $ip;
1482
	}
1483
1484 cc125e13 Chris Buechler
	// doesn't match up to any particular interface
1485
	// so let's set the portal IP to what PHP says 
1486
	// the server IP issuing the request is. 
1487
	// allows same behavior as 1.2.x where IP isn't 
1488
	// in the subnet of any CP interface (static routes, etc.)
1489
	// rather than forcing to DNS hostname resolution
1490
	$ip = $_SERVER['SERVER_ADDR'];
1491
	if (is_ipaddr($ip))
1492
		return $ip;
1493
1494 f8b11310 Ermal Lu?i
	return false;
1495
}
1496
1497 ac631bba lgcosta
/* functions move from index.php */
1498
1499
function portal_reply_page($redirurl, $type = null, $message = null, $clientmac = null, $clientip = null, $username = null, $password = null) {
1500
	global $g, $config;
1501
1502
	/* Get captive portal layout */
1503
	if ($type == "redir") {
1504
		header("Location: {$redirurl}");
1505
		return;
1506
	} else if ($type == "login")
1507
		$htmltext = get_include_contents("{$g['varetc_path']}/captiveportal.html");
1508
	else
1509
		$htmltext = get_include_contents("{$g['varetc_path']}/captiveportal-error.html");
1510
1511
	/* substitute the PORTAL_REDIRURL variable */
1512
	if ($config['captiveportal']['preauthurl']) {
1513
		$htmltext = str_replace("\$PORTAL_REDIRURL\$", "{$config['captiveportal']['preauthurl']}", $htmltext);
1514
		$htmltext = str_replace("#PORTAL_REDIRURL#", "{$config['captiveportal']['preauthurl']}", $htmltext);
1515
	}
1516
1517
	/* substitute other variables */
1518
	if (isset($config['captiveportal']['httpslogin'])) {
1519
		$htmltext = str_replace("\$PORTAL_ACTION\$", "https://{$config['captiveportal']['httpsname']}:8001/", $htmltext);
1520
		$htmltext = str_replace("#PORTAL_ACTION#", "https://{$config['captiveportal']['httpsname']}:8001/", $htmltext);
1521
	} else {
1522
		$ifip = portal_ip_from_client_ip($clientip);
1523
		if (!$ifip)
1524
			$ourhostname = $config['system']['hostname'] . ":8000";
1525
		else
1526
			$ourhostname = "{$ifip}:8000";
1527
		$htmltext = str_replace("\$PORTAL_ACTION\$", "http://{$ourhostname}/", $htmltext);
1528
		$htmltext = str_replace("#PORTAL_ACTION#", "http://{$ourhostname}/", $htmltext);
1529
	}
1530
1531
	$htmltext = str_replace("\$PORTAL_REDIRURL\$", htmlspecialchars($redirurl), $htmltext);
1532
	$htmltext = str_replace("\$PORTAL_MESSAGE\$", htmlspecialchars($message), $htmltext);
1533
	$htmltext = str_replace("\$CLIENT_MAC\$", htmlspecialchars($clientmac), $htmltext);
1534
	$htmltext = str_replace("\$CLIENT_IP\$", htmlspecialchars($clientip), $htmltext);
1535
1536
	// Special handling case for captive portal master page so that it can be ran 
1537
	// through the PHP interpreter using the include method above.  We convert the
1538
	// $VARIABLE$ case to #VARIABLE# in /etc/inc/captiveportal.inc before writing out.
1539
	$htmltext = str_replace("#PORTAL_REDIRURL#", htmlspecialchars($redirurl), $htmltext);
1540
	$htmltext = str_replace("#PORTAL_MESSAGE#", htmlspecialchars($message), $htmltext);
1541
	$htmltext = str_replace("#CLIENT_MAC#", htmlspecialchars($clientmac), $htmltext);
1542
	$htmltext = str_replace("#CLIENT_IP#", htmlspecialchars($clientip), $htmltext);
1543
	$htmltext = str_replace("#USERNAME#", htmlspecialchars($username), $htmltext);
1544
	$htmltext = str_replace("#PASSWORD#", htmlspecialchars($password), $htmltext);
1545
1546
    echo $htmltext;
1547
}
1548
1549
function portal_mac_radius($clientmac,$clientip) {
1550
    global $config ;
1551
1552
    $radmac_secret = $config['captiveportal']['radmac_secret'];
1553
1554
    /* authentication against the radius server */
1555
    $username = mac_format($clientmac);
1556
    $auth_list = radius($username,$radmac_secret,$clientip,$clientmac,"MACHINE LOGIN");
1557
    if ($auth_list['auth_val'] == 2)
1558
        return TRUE;
1559
    if (!empty($auth_list['url_redirection']))
1560
	portal_reply_page($auth_list['url_redirection'], "redir");
1561
1562
    return FALSE;
1563
}
1564
1565
function portal_allow($clientip,$clientmac,$username,$password = null, $attributes = null, $ruleno = null)  {
1566
1567
	global $redirurl, $g, $config, $type, $passthrumac, $_POST;
1568
1569
	/* See if a ruleno is passed, if not start sessions because this means there isn't one atm */
1570
	if ($ruleno == null)
1571
		$ruleno = captiveportal_get_next_ipfw_ruleno();
1572
1573
	/* if the pool is empty, return appropriate message and exit */
1574
	if (is_null($ruleno)) {
1575
		portal_reply_page($redirurl, "error", "System reached maximum login capacity");
1576
		log_error("WARNING!  Captive portal has reached maximum login capacity");
1577
		exit;
1578
	}
1579
1580
	// Ensure we create an array if we are missing attributes
1581
	if (!is_array($attributes))
1582
		$attributes = array();
1583
1584
	$radiusservers = captiveportal_get_radius_servers();
1585
1586 006802ab Ermal
	/* Do not allow concurrent login execution. */
1587
	$cpdblck = lock('captiveportaldb', LOCK_EX);
1588
1589
	unset($sessionid);
1590
1591
	/* read in client database */
1592
	$cpdb = captiveportal_read_db(true);
1593
1594 ac631bba lgcosta
	if ($attributes['voucher'])
1595
		$remaining_time = $attributes['session_timeout'];
1596
1597
	$writecfg = false;
1598
	/* Find an existing session */
1599
	if ((isset($config['captiveportal']['noconcurrentlogins'])) && $passthrumac) {
1600
		if (isset($config['captiveportal']['passthrumacadd'])) {
1601
			$mac = captiveportal_passthrumac_findbyname($username);
1602
			if (!empty($mac)) {
1603
				if ($_POST['replacemacpassthru']) {
1604
					foreach ($config['captiveportal']['passthrumac'] as $idx => $macent) {
1605
						if ($macent['mac'] == $mac['mac']) {
1606
							$macrules = "";
1607
							$ruleno = captiveportal_get_ipfw_passthru_ruleno($mac['mac']);
1608
                                			if ($ruleno) {
1609
								captiveportal_free_ipfw_ruleno($ruleno, true);
1610
                                        			$macrules .= "delete {$ruleno}\n";
1611
								++$ruleno;
1612
                                        			$macrules .= "delete {$ruleno}\n";
1613
                                			}
1614
							unset($config['captiveportal']['passthrumac'][$idx]);
1615
							$mac['mac'] = $clientmac;
1616
							$config['captiveportal']['passthrumac'][] = $mac;
1617
							$macrules .= captiveportal_passthrumac_configure_entry($mac);
1618
							file_put_contents("{$g['tmp_path']}/macentry.rules.tmp", $macrules);
1619
							mwexec("/sbin/ipfw -q {$g['tmp_path']}/macentry.rules.tmp");
1620
							$writecfg = true;
1621
							$sessionid = true;
1622
							break;
1623
						}
1624
					}
1625
                                } else {
1626
					portal_reply_page($redirurl, "error", "Username: {$username} is already authenticated using another MAC address.",
1627
						$clientmac, $clientip, $username, $password);
1628
					exit;
1629
				}
1630
			}
1631
		}
1632
	}
1633
1634 b09c2d86 Ermal
	/* Snaphost the timestamp */
1635
	$allow_time = time();
1636
1637 006802ab Ermal
	foreach ($cpdb as $sid => $cpentry) {
1638 ac631bba lgcosta
		/* on the same ip */
1639 006802ab Ermal
		if($cpentry[2] == $clientip) {
1640
			captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - REUSING OLD SESSION");
1641
			$sessionid = $sid;
1642 ac631bba lgcosta
			break;
1643
		}
1644 006802ab Ermal
		elseif (($attributes['voucher']) && ($username != 'unauthenticated') && ($cpentry[4] == $username)) {
1645 ac631bba lgcosta
			// user logged in with an active voucher. Check for how long and calculate 
1646
			// how much time we can give him (voucher credit - used time)
1647 b09c2d86 Ermal
			$remaining_time = $cpentry[0] + $cpentry[7] - $allow_time;
1648 ac631bba lgcosta
			if ($remaining_time < 0)    // just in case. 
1649
				$remaining_time = 0;
1650
1651
			/* This user was already logged in so we disconnect the old one */
1652 006802ab Ermal
			captiveportal_disconnect($cpentry,$radiusservers,13);
1653
			captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - TERMINATING OLD SESSION");
1654 ce1942d6 Ermal
			unset($cpdb[$sid]);
1655 ac631bba lgcosta
			break;
1656
		}
1657
		elseif ((isset($config['captiveportal']['noconcurrentlogins'])) && ($username != 'unauthenticated')) {
1658
			/* on the same username */
1659 006802ab Ermal
			if (strcasecmp($cpentry[4], $username) == 0) {
1660 ac631bba lgcosta
				/* This user was already logged in so we disconnect the old one */
1661 006802ab Ermal
				captiveportal_disconnect($cpentry,$radiusservers,13);
1662
				captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - TERMINATING OLD SESSION");
1663 ce1942d6 Ermal
				unset($cpdb[$sid]);
1664 ac631bba lgcosta
				break;
1665
			}
1666
		}
1667
	}
1668
1669
	if ($attributes['voucher'] && $remaining_time <= 0)
1670
		return 0;       // voucher already used and no time left
1671
1672
	if (!isset($sessionid)) {
1673
		/* generate unique session ID */
1674
		$tod = gettimeofday();
1675
		$sessionid = substr(md5(mt_rand() . $tod['sec'] . $tod['usec'] . $clientip . $clientmac), 0, 16);
1676
1677
		/* Add rules for traffic shaping
1678
		 * We don't need to add extra rules since traffic will pass due to the following kernel option
1679
		 * net.inet.ip.fw.one_pass: 1
1680
		 */
1681
		$peruserbw = isset($config['captiveportal']['peruserbw']);
1682
1683 2d4003aa Ermal
		$bw_up = isset($attributes['bw_up']) ? round(intval($attributes['bw_up'])/1000, 2) : $config['captiveportal']['bwdefaultup'];
1684
		$bw_down = isset($attributes['bw_down']) ? round(intval($attributes['bw_down'])/1000, 2) : $config['captiveportal']['bwdefaultdn'];
1685 ac631bba lgcosta
1686
		if ($passthrumac) {
1687
			$mac = array();
1688
			$mac['mac'] = $clientmac;
1689
			if (isset($config['captiveportal']['passthrumacaddusername']))
1690
				$mac['username'] = $username;
1691
			$mac['descr'] =  "Auto added pass-through MAC for user {$username}";
1692
			if (!empty($bw_up))
1693
				$mac['bw_up'] = $bw_up;
1694
			if (!empty($bw_down))
1695
				$mac['bw_down'] = $bw_down;
1696
			if (!is_array($config['captiveportal']['passthrumac']))
1697
				$config['captiveportal']['passthrumac'] = array();
1698
			$config['captiveportal']['passthrumac'][] = $mac;
1699 006802ab Ermal
			unlock($cpdblck);
1700 ac631bba lgcosta
			$macrules = captiveportal_passthrumac_configure_entry($mac);
1701
			file_put_contents("{$g['tmp_path']}/macentry.rules.tmp", $macrules);
1702
			mwexec("/sbin/ipfw -q {$g['tmp_path']}/macentry.rules.tmp");
1703
			$writecfg = true;
1704
		} else {
1705
			if ($peruserbw && !empty($bw_up) && is_numeric($bw_up)) {
1706
				$bw_up_pipeno = $ruleno + 20000;
1707
				//$bw_up /= 1000; // Scale to Kbit/s
1708
				mwexec("/sbin/ipfw pipe {$bw_up_pipeno} config bw {$bw_up}Kbit/s queue 100");
1709
1710
				if (!isset($config['captiveportal']['nomacfilter']))
1711
					mwexec("/sbin/ipfw table 1 add {$clientip} mac {$clientmac} {$bw_up_pipeno}");
1712
				else
1713
					mwexec("/sbin/ipfw table 1 add {$clientip} {$bw_up_pipeno}");
1714
			} else {
1715
				if (!isset($config['captiveportal']['nomacfilter']))
1716
					mwexec("/sbin/ipfw table 1 add {$clientip} mac {$clientmac}");
1717
				else
1718
					mwexec("/sbin/ipfw table 1 add {$clientip}");
1719
			}
1720
			if ($peruserbw && !empty($bw_down) && is_numeric($bw_down)) {
1721
				$bw_down_pipeno = $ruleno + 20001;
1722
				//$bw_down /= 1000; // Scale to Kbit/s
1723
				mwexec("/sbin/ipfw pipe {$bw_down_pipeno} config bw {$bw_down}Kbit/s queue 100");
1724
1725
				if (!isset($config['captiveportal']['nomacfilter']))
1726
					mwexec("/sbin/ipfw table 2 add {$clientip} mac {$clientmac} {$bw_down_pipeno}");
1727
				else
1728
					mwexec("/sbin/ipfw table 2 add {$clientip} {$bw_down_pipeno}");
1729
			} else {
1730
				if (!isset($config['captiveportal']['nomacfilter']))
1731
					mwexec("/sbin/ipfw table 2 add {$clientip} mac {$clientmac}");
1732
				else
1733
					mwexec("/sbin/ipfw table 2 add {$clientip}");
1734
			}
1735
1736
			if ($attributes['voucher'])
1737
				$attributes['session_timeout'] = $remaining_time;
1738
1739
			/* encode password in Base64 just in case it contains commas */
1740
			$bpassword = base64_encode($password);
1741 b09c2d86 Ermal
			$cpdb[] = array($allow_time, $ruleno, $clientip, $clientmac, $username, $sessionid, $bpassword,
1742 ac631bba lgcosta
				$attributes['session_timeout'], $attributes['idle_timeout'], $attributes['session_terminate_time']);
1743
1744 006802ab Ermal
			/* rewrite information to database */
1745
			captiveportal_write_db($cpdb, true);
1746
			unlock($cpdblck);
1747
1748 ac631bba lgcosta
			if (isset($config['captiveportal']['radacct_enable']) && !empty($radiusservers)) {
1749
				$acct_val = RADIUS_ACCOUNTING_START($ruleno,
1750
                                		$username, $sessionid, $radiusservers, $clientip, $clientmac);
1751
				if ($acct_val == 1)
1752
					captiveportal_logportalauth($username,$clientmac,$clientip,$type,"RADIUS ACCOUNTING FAILED");
1753
			}
1754
		}
1755 006802ab Ermal
	} else
1756
		unlock($cpdblck);
1757 ac631bba lgcosta
1758
	if ($writecfg == true)
1759
		write_config();
1760
1761
	/* redirect user to desired destination */
1762
	if (!empty($attributes['url_redirection']))
1763
		$my_redirurl = $attributes['url_redirection'];
1764 cd2c71fc Ermal
	else if (!empty($config['captiveportal']['redirurl']))
1765 ac631bba lgcosta
		$my_redirurl = $config['captiveportal']['redirurl'];
1766
	else
1767
		$my_redirurl = $redirurl;
1768
1769
	if(isset($config['captiveportal']['logoutwin_enable']) && !$passthrumac) {
1770
1771
		if (isset($config['captiveportal']['httpslogin']))
1772
			$logouturl = "https://{$config['captiveportal']['httpsname']}:8001/";
1773
		else {
1774
			$ifip = portal_ip_from_client_ip($clientip);
1775
			if (!$ifip)
1776
				$ourhostname = $config['system']['hostname'] . ":8000";
1777
			else
1778
				$ourhostname = "{$ifip}:8000";
1779
			$logouturl = "http://{$ourhostname}/";
1780
		}
1781
1782
		if (isset($attributes['reply_message']))
1783
			$message = $attributes['reply_message'];
1784
		else
1785
			$message = 0;
1786
1787
		include("{$g['varetc_path']}/captiveportal-logout.html");
1788
1789
	} else {
1790
		header("Location: " . $my_redirurl);
1791
	}
1792
1793
	return $sessionid;
1794
}
1795
1796
1797
/*
1798
 * Used for when pass-through credits are enabled.
1799
 * Returns true when there was at least one free login to deduct for the MAC.
1800
 * Expired entries are removed as they are seen.
1801
 * Active entries are updated according to the configuration.
1802
 */
1803
function portal_consume_passthrough_credit($clientmac) {
1804
	global $config;
1805
1806
	if (!empty($config['captiveportal']['freelogins_count']) && is_numeric($config['captiveportal']['freelogins_count']))
1807
		$freeloginscount = $config['captiveportal']['freelogins_count'];
1808
	else
1809
		return false;
1810
1811
	if (!empty($config['captiveportal']['freelogins_resettimeout']) && is_numeric($config['captiveportal']['freelogins_resettimeout']))
1812
		$resettimeout = $config['captiveportal']['freelogins_resettimeout'];
1813
	else
1814
		return false;
1815
1816 17e7a243 Scott Ullrich
	if ($freeloginscount < 1 || $resettimeout <= 0 || !$clientmac)
1817 ac631bba lgcosta
		return false;
1818
1819
	$updatetimeouts = isset($config['captiveportal']['freelogins_updatetimeouts']);
1820
1821
	/*
1822
	 * Read database of used MACs.  Lines are a comma-separated list
1823
	 * of the time, MAC, then the count of pass-through credits remaining.
1824
	 */
1825
	$usedmacs = captiveportal_read_usedmacs_db();
1826
1827
	$currenttime = time();
1828
	$found = false;
1829
	foreach ($usedmacs as $key => $usedmac) {
1830
		$usedmac = explode(",", $usedmac);
1831
1832
		if ($usedmac[1] == $clientmac) {
1833
			if ($usedmac[0] + ($resettimeout * 3600) > $currenttime) {
1834
				if ($usedmac[2] < 1) {
1835
					if ($updatetimeouts) {
1836
						$usedmac[0] = $currenttime;
1837
						unset($usedmacs[$key]);
1838
						$usedmacs[] = implode(",", $usedmac);
1839
						captiveportal_write_usedmacs_db($usedmacs);
1840
					}
1841
1842
					return false;
1843
				} else {
1844
					$usedmac[2] -= 1;
1845
					$usedmacs[$key] = implode(",", $usedmac);
1846
				}
1847
1848
				$found = true;
1849
			} else
1850
				unset($usedmacs[$key]);
1851
1852
			break;
1853
		} else if ($usedmac[0] + ($resettimeout * 3600) <= $currenttime)
1854
				unset($usedmacs[$key]);
1855
	}
1856
1857
	if (!$found) {
1858
		$usedmac = array($currenttime, $clientmac, $freeloginscount - 1);
1859
		$usedmacs[] = implode(",", $usedmac);
1860
	}
1861
1862
	captiveportal_write_usedmacs_db($usedmacs);
1863
	return true;
1864
}
1865
1866
function captiveportal_read_usedmacs_db() {
1867
	global $g;
1868
1869
	$cpumaclck = lock('captiveusedmacs');
1870
	if (file_exists("{$g['vardb_path']}/captiveportal_usedmacs.db")) {
1871
		$usedmacs = file("{$g['vardb_path']}/captiveportal_usedmacs.db", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
1872 da666ca8 Scott Ullrich
		if (!$usedmacs)
1873 ac631bba lgcosta
			$usedmacs = array();
1874
	} else
1875
		$usedmacs = array();
1876
1877
	unlock($cpumaclck);
1878
	return $usedmacs;
1879
}
1880
1881
function captiveportal_write_usedmacs_db($usedmacs) {
1882
	global $g;
1883
1884
	$cpumaclck = lock('captiveusedmacs', LOCK_EX);
1885
	@file_put_contents("{$g['vardb_path']}/captiveportal_usedmacs.db", implode("\n", $usedmacs));
1886
	unlock($cpumaclck);
1887
}
1888
1889 006802ab Ermal
?>