Project

General

Profile

Download (70 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 fad4d451 Ermal
	Copyright (C) 2009-2012 Ermal Lu?i <eri@pfsense.org>
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 3a4b0147 Ermal
	pfSense_BUILDER_BINARIES:	/sbin/ipfw	/sbin/sysctl
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 b4792bf8 Ermal
	global $config, $g, $cpzone;
54 0e296bce Ermal
55
	$htmltext = <<<EOD
56 023aa1f2 Scott Ullrich
<html> 
57 0e296bce Ermal
<body> 
58
<form method="post" action="\$PORTAL_ACTION\$"> 
59
	<input name="redirurl" type="hidden" value="\$PORTAL_REDIRURL\$">
60
	<input name="zone" type="hidden" value="\$PORTAL_ZONE\$">
61
	<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
				{$g['product_name']} captive portal
68
			</b>
69
			</font>
70
		</td>
71
	</tr>
72
	<tr>
73
		<td>
74
			<div id="mainlevel">
75 023aa1f2 Scott Ullrich
			<center>
76 0e296bce Ermal
			<table width="100%" border="0" cellpadding="5" cellspacing="0">
77
			<tr>
78
				<td>
79
					<center>
80
					<div id="mainarea">
81
					<center>
82
					<table width="100%" border="0" cellpadding="5" cellspacing="5">
83 023aa1f2 Scott Ullrich
					<tr>
84
						<td>
85 0e296bce Ermal
							<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
								<br/>
95
								<div id='loginbox'>
96
								<table>
97 87e7fdea bcyrill
									<tr><td colspan="2"><center>Welcome to the {$g['product_name']} Captive Portal!</td></tr>
98
									<tr><td>&nbsp;</td></tr>
99
									<tr><td align="right">Username:</td><td><input name="auth_user" type="text" style="border: 1px dashed;"></td></tr>
100
									<tr><td align="right">Password:</td><td><input name="auth_pass" type="password" style="border: 1px dashed;"></td></tr>
101
									<tr><td>&nbsp;</td></tr>
102 0e296bce Ermal
103
EOD;
104
105
	if(isset($config['voucher'][$cpzone]['enable'])) {
106
	$htmltext .= <<<EOD
107 87e7fdea bcyrill
									<tr>
108
										<td align="right">Enter Voucher Code: </td>
109
										<td><input name="auth_voucher" type="text" style="border:1px dashed;" size="22"></td>
110
									</tr>
111 36254e4a Scott Ullrich
112 023aa1f2 Scott Ullrich
EOD;
113 c2056357 Scott Ullrich
	}
114 0bd34ed6 Scott Ullrich
115 0e296bce Ermal
	$htmltext .= <<<EOD
116 87e7fdea bcyrill
									<tr>
117
										<td colspan="2"><center><input name="accept" type="submit" value="Continue"></center></td>
118
									</tr>
119 0e296bce Ermal
								</table>
120
								</div>
121
							</center>
122 b260c8e0 Scott Ullrich
							</div>
123
						</td>
124
					</tr>
125 0e296bce Ermal
					</table>
126
					</center>
127
					</div>
128
					</center>
129
				</td>
130
			</tr>
131
			</table>
132 b260c8e0 Scott Ullrich
			</center>
133 0e296bce Ermal
			</div>
134
		</td>
135
	</tr>
136
	</table>
137
	</center>
138
</form>
139
</body> 
140 5b237745 Scott Ullrich
</html>
141
142 023aa1f2 Scott Ullrich
EOD;
143 0bd34ed6 Scott Ullrich
144 023aa1f2 Scott Ullrich
	return $htmltext;
145
}
146 0bd34ed6 Scott Ullrich
147 3a4b0147 Ermal
function captiveportal_load_modules() {
148 87e7fdea bcyrill
	global $config;
149 3a4b0147 Ermal
150
	mute_kernel_msgs();
151 87e7fdea bcyrill
	if (!is_module_loaded("ipfw.ko")) {
152
		mwexec("/sbin/kldload ipfw");
153
		/* make sure ipfw is not on pfil hooks */
154
		mwexec("/sbin/sysctl net.inet.ip.pfil.inbound=\"pf\" net.inet6.ip6.pfil.inbound=\"pf\"" .
155
		       " net.inet.ip.pfil.outbound=\"pf\" net.inet6.ip6.pfil.outbound=\"pf\"");
156 aea56408 Ermal
		/* Activate layer2 filtering */
157
		mwexec("/sbin/sysctl net.link.ether.ipfw=1 net.inet.ip.fw.one_pass=1");
158 87e7fdea bcyrill
	}
159 c06bdb94 Ermal
160 3a4b0147 Ermal
	/* Always load dummynet now that even allowed ip and mac passthrough use it. */
161
	if (!is_module_loaded("dummynet.ko")) {
162
		mwexec("/sbin/kldload dummynet");
163
		mwexec("/sbin/sysctl net.inet.ip.dummynet.io_fast=1 net.inet.ip.dummynet.hash_size=256");
164
	}
165
	unmute_kernel_msgs();
166
167 87e7fdea bcyrill
	/* XXX: This are not used in pfSense, if needed can be tuned 
168
	if($config['system']['maximumstates'] <> "" && is_numeric($config['system']['maximumstates'])) {
169
			mwexec("sysctl net.inet.ip.fw.dyn_max={$config['system']['maximumstates']}");
170
	} else {
171
			mwexec("sysctl net.inet.ip.fw.dyn_max=10000");
172
	}
173 3a4b0147 Ermal
	*/
174
}
175
176 023aa1f2 Scott Ullrich
function captiveportal_configure() {
177 b4792bf8 Ermal
	global $config, $cpzone;
178 023aa1f2 Scott Ullrich
179 b4792bf8 Ermal
	if (is_array($config['captiveportal'])) {
180
		foreach ($config['captiveportal'] as $cpkey => $cp) {
181
			$cpzone = $cpkey;
182
			captiveportal_configure_zone($cp);
183
		}
184
	} else
185
		mwexec("/sbin/sysctl net.link.ether.ipfw=0");
186
}
187
188
function captiveportal_configure_zone($cpcfg) {
189
	global $config, $g, $cpzone;
190
191
	$captiveportallck = lock("captiveportal{$cpzone}", LOCK_EX);
192 023aa1f2 Scott Ullrich
	
193 b4792bf8 Ermal
	if (isset($cpcfg['enable'])) {
194 023aa1f2 Scott Ullrich
195 37e67d04 Ermal
		if ($g['booting']) {
196 b4792bf8 Ermal
			echo "Starting captive portal({$cpcfg['zone']})... ";
197 023aa1f2 Scott Ullrich
198 37e67d04 Ermal
			/* remove old information */
199 26ee5aaf Ermal
			unlink_if_exists("{$g['vardb_path']}/captiveportal{$cpzone}.db");
200 37e67d04 Ermal
		} else
201
			captiveportal_syslog("Reconfiguring captive portal({$cpcfg['zone']}).");
202 023aa1f2 Scott Ullrich
203 37e67d04 Ermal
		/* init ipfw rules */
204
		captiveportal_init_rules(true);
205 023aa1f2 Scott Ullrich
206
		/* kill any running minicron */
207 b4792bf8 Ermal
		killbypid("{$g['varrun_path']}/cp_prunedb_{$cpzone}.pid");
208
209 023aa1f2 Scott Ullrich
		/* initialize minicron interval value */
210 b4792bf8 Ermal
		$croninterval = $cpcfg['croninterval'] ? $cpcfg['croninterval'] : 60;
211 023aa1f2 Scott Ullrich
212
		/* double check if the $croninterval is numeric and at least 10 seconds. If not we set it to 60 to avoid problems */
213 eb7aa263 Ermal
		if ((!is_numeric($croninterval)) || ($croninterval < 10))
214
			$croninterval = 60;
215 023aa1f2 Scott Ullrich
216
		/* write portal page */
217 6cf64278 Ermal
		if (is_array($cpcfg['page']) && $cpcfg['page']['htmltext'])
218 b4792bf8 Ermal
			$htmltext = base64_decode($cpcfg['page']['htmltext']);
219 023aa1f2 Scott Ullrich
		else {
220
			/* example/template page */
221
			$htmltext = get_default_captive_portal_html();
222 5b237745 Scott Ullrich
		}
223
224 b4792bf8 Ermal
		$fd = @fopen("{$g['varetc_path']}/captiveportal_{$cpzone}.html", "w");
225 5b237745 Scott Ullrich
		if ($fd) {
226 7a7e94a7 Scott Ullrich
			// Special case handling.  Convert so that we can pass this page
227
			// through the PHP interpreter later without clobbering the vars.
228 b4792bf8 Ermal
			$htmltext = str_replace("\$PORTAL_ZONE\$", "#PORTAL_ZONE#", $htmltext);
229 7a7e94a7 Scott Ullrich
			$htmltext = str_replace("\$PORTAL_REDIRURL\$", "#PORTAL_REDIRURL#", $htmltext);
230
			$htmltext = str_replace("\$PORTAL_MESSAGE\$", "#PORTAL_MESSAGE#", $htmltext);
231
			$htmltext = str_replace("\$CLIENT_MAC\$", "#CLIENT_MAC#", $htmltext);
232
			$htmltext = str_replace("\$CLIENT_IP\$", "#CLIENT_IP#", $htmltext);
233
			$htmltext = str_replace("\$ORIGINAL_PORTAL_IP\$", "#ORIGINAL_PORTAL_IP#", $htmltext);
234
			$htmltext = str_replace("\$PORTAL_ACTION\$", "#PORTAL_ACTION#", $htmltext);
235 b4792bf8 Ermal
			if($cpcfg['preauthurl']) {
236
				$htmltext = str_replace("\$PORTAL_REDIRURL\$", "{$cpcfg['preauthurl']}", $htmltext);
237
				$htmltext = str_replace("#PORTAL_REDIRURL#", "{$cpcfg['preauthurl']}", $htmltext);
238 c4e228f3 Scott Ullrich
			}
239 5b237745 Scott Ullrich
			fwrite($fd, $htmltext);
240 36254e4a Scott Ullrich
			fclose($fd);
241 5b237745 Scott Ullrich
		}
242 2e62a7c4 Ermal
		unset($htmltext);
243 36254e4a Scott Ullrich
244 5b237745 Scott Ullrich
		/* write error page */
245 c535b28c Ermal
		if (is_array($cpcfg['page']) && $cpcfg['page']['errtext'])
246 b4792bf8 Ermal
			$errtext = base64_decode($cpcfg['page']['errtext']);
247 5b237745 Scott Ullrich
		else {
248 a34b8b3b Ermal
			/* example page  */
249
			$errtext = get_default_captive_portal_html();
250 5b237745 Scott Ullrich
		}
251
252 b4792bf8 Ermal
		$fd = @fopen("{$g['varetc_path']}/captiveportal-{$cpzone}-error.html", "w");
253 5b237745 Scott Ullrich
		if ($fd) {
254 7a7e94a7 Scott Ullrich
			// Special case handling.  Convert so that we can pass this page
255
			// through the PHP interpreter later without clobbering the vars.
256 b4792bf8 Ermal
			$errtext = str_replace("\$PORTAL_ZONE\$", "#PORTAL_ZONE#", $errtext);
257 7a7e94a7 Scott Ullrich
			$errtext = str_replace("\$PORTAL_REDIRURL\$", "#PORTAL_REDIRURL#", $errtext);
258
			$errtext = str_replace("\$PORTAL_MESSAGE\$", "#PORTAL_MESSAGE#", $errtext);
259
			$errtext = str_replace("\$CLIENT_MAC\$", "#CLIENT_MAC#", $errtext);
260
			$errtext = str_replace("\$CLIENT_IP\$", "#CLIENT_IP#", $errtext);
261
			$errtext = str_replace("\$ORIGINAL_PORTAL_IP\$", "#ORIGINAL_PORTAL_IP#", $errtext);
262
			$errtext = str_replace("\$PORTAL_ACTION\$", "#PORTAL_ACTION#", $errtext);
263 b4792bf8 Ermal
			if($cpcfg['preauthurl']) {
264
				$errtext = str_replace("\$PORTAL_REDIRURL\$", "{$cpcfg['preauthurl']}", $errtext);
265
				$errtext = str_replace("#PORTAL_REDIRURL#", "{$cpcfg['preauthurl']}", $errtext);
266 c4e228f3 Scott Ullrich
			}
267 5b237745 Scott Ullrich
			fwrite($fd, $errtext);
268 36254e4a Scott Ullrich
			fclose($fd);
269 5b237745 Scott Ullrich
		}
270 2e62a7c4 Ermal
		unset($errtext);
271 36254e4a Scott Ullrich
272 b4792bf8 Ermal
		/* write logout page */
273 6cf64278 Ermal
		if (is_array($cpcfg['page']) && $cpcfg['page']['logouttext'])
274 b4792bf8 Ermal
			$logouttext = base64_decode($cpcfg['page']['logouttext']);
275 5b87b24e Ermal
		else {
276
			/* example page */
277
			$logouttext = <<<EOD
278
<HTML>
279
<HEAD><TITLE>Redirecting...</TITLE></HEAD>
280
<BODY>
281
<SPAN STYLE="font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">
282 6991e1a6 Erik Fonnesbeck
<B>Redirecting to <A HREF="<?=\$my_redirurl;?>"><?=\$my_redirurl;?></A>...</B>
283 5b87b24e Ermal
</SPAN>
284
<SCRIPT LANGUAGE="JavaScript">
285
<!--
286
LogoutWin = window.open('', 'Logout', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=256,height=64');
287
if (LogoutWin) {
288 5060dea7 Scott Ullrich
	LogoutWin.document.write('<HTML>');
289
	LogoutWin.document.write('<HEAD><TITLE>Logout</TITLE></HEAD>') ;
290
	LogoutWin.document.write('<BODY BGCOLOR="#435370">');
291
	LogoutWin.document.write('<DIV ALIGN="center" STYLE="color: #ffffff; font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">') ;
292
	LogoutWin.document.write('<B>Click the button below to disconnect</B><P>');
293
	LogoutWin.document.write('<FORM METHOD="POST" ACTION="<?=\$logouturl;?>">');
294
	LogoutWin.document.write('<INPUT NAME="logout_id" TYPE="hidden" VALUE="<?=\$sessionid;?>">');
295 b4792bf8 Ermal
	LogoutWin.document.write('<INPUT NAME="zone" TYPE="hidden" VALUE="<?=\$cpzone;?>">');
296 5060dea7 Scott Ullrich
	LogoutWin.document.write('<INPUT NAME="logout" TYPE="submit" VALUE="Logout">');
297
	LogoutWin.document.write('</FORM>');
298
	LogoutWin.document.write('</DIV></BODY>');
299
	LogoutWin.document.write('</HTML>');
300
	LogoutWin.document.close();
301 5b87b24e Ermal
}
302
303 6991e1a6 Erik Fonnesbeck
document.location.href="<?=\$my_redirurl;?>";
304 5b87b24e Ermal
-->
305
</SCRIPT>
306
</BODY>
307
</HTML>
308
309
EOD;
310
		}
311
312 b4792bf8 Ermal
		$fd = @fopen("{$g['varetc_path']}/captiveportal-{$cpzone}-logout.html", "w");
313 5b87b24e Ermal
		if ($fd) {
314
			fwrite($fd, $logouttext);
315
			fclose($fd);
316
		}
317 2e62a7c4 Ermal
		unset($logouttext);
318
319 0bd34ed6 Scott Ullrich
		/* write elements */
320
		captiveportal_write_elements();
321 5b237745 Scott Ullrich
322 37e67d04 Ermal
		/* kill any running mini_httpd */
323
		killbypid("{$g['varrun_path']}/lighty-{$cpzone}-CaptivePortal.pid");
324
		killbypid("{$g['varrun_path']}/lighty-{$cpzone}-CaptivePortal-SSL.pid");
325
326 769e254e Ermal
		/* start up the webserving daemon */
327 e3cf528e bcyrill
		captiveportal_init_webgui_zone($cpcfg);
328 36254e4a Scott Ullrich
329 aa69dbd2 Scott Ullrich
		/* Kill any existing prunecaptiveportal processes */
330 8b34498c Ermal
		if (file_exists("{$g['varrun_path']}/cp_prunedb_{$cpzone}.pid"))
331 b4792bf8 Ermal
			killbypid("{$g['varrun_path']}/cp_prunedb_{$cpzone}.pid");
332 aa69dbd2 Scott Ullrich
333 0bd34ed6 Scott Ullrich
		/* start pruning process (interval defaults to 60 seconds) */
334 b4792bf8 Ermal
		mwexec("/usr/local/bin/minicron $croninterval {$g['varrun_path']}/cp_prunedb_{$cpzone}.pid " .
335
			"/etc/rc.prunecaptiveportal {$cpzone}");
336 36254e4a Scott Ullrich
337 d99f7864 Scott Ullrich
		/* generate radius server database */
338 37e67d04 Ermal
		unlink_if_exists("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db");
339 d31bc32a Ermal
		captiveportal_init_radius_servers();
340 36254e4a Scott Ullrich
341 e35ab948 Michael Newton
		if ($g['booting']) {
342
			/* send Accounting-On to server */
343
			captiveportal_send_server_accounting();
344 b4792bf8 Ermal
			echo "done\n";
345 e35ab948 Michael Newton
		}
346 36254e4a Scott Ullrich
347 5b237745 Scott Ullrich
	} else {
348 b4792bf8 Ermal
		killbypid("{$g['varrun_path']}/lighty-{$cpzone}-CaptivePortal.pid");
349
		killbypid("{$g['varrun_path']}/lighty-{$cpzone}-CaptivePortal-SSL.pid");
350
		killbypid("{$g['varrun_path']}/cp_prunedb_{$cpzone}.pid");
351
		@unlink("{$g['varetc_path']}/captiveportal_{$cpzone}.html");
352
		@unlink("{$g['varetc_path']}/captiveportal-{$cpzone}-error.html");
353
		@unlink("{$g['varetc_path']}/captiveportal-{$cpzone}-logout.html");
354 f8cb8685 Ermal
355
		captiveportal_radius_stop_all();
356
357 62f20eab Michael Newton
		/* send Accounting-Off to server */
358
		if (!$g['booting']) {
359
			captiveportal_send_server_accounting(true);
360
		}
361
362 8b34498c Ermal
		/* remove old information */
363 26ee5aaf Ermal
		unlink_if_exists("{$g['vardb_path']}/captiveportal{$cpzone}.db");
364 8b34498c Ermal
		unlink_if_exists("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db");
365 12ee8fe4 Scott Ullrich
366 b4792bf8 Ermal
		mwexec("/usr/local/sbin/ipfw_context -d {$cpzone}", true);
367
368
		if (empty($config['captiveportal']))
369
			mwexec("/sbin/sysctl net.link.ether.ipfw=0");
370 c06bdb94 Ermal
		else {
371
			/* Deactivate ipfw(4) if not needed */
372
			$cpactive = false;
373 52034432 Renato Botelho
			if (is_array($config['captiveportal'])) {
374
				foreach ($config['captiveportal'] as $cpkey => $cp) {
375
					if (isset($cp['enable'])) {
376
						$cpactive = true;
377
						break;
378
					}
379 c06bdb94 Ermal
				}
380
			}
381
			if ($cpactive === false)
382
				mwexec("/sbin/sysctl net.link.ether.ipfw=0");
383
				
384
		}
385 3db19cf1 Scott Ullrich
	}
386 36254e4a Scott Ullrich
387 f8b11310 Ermal Lu?i
	unlock($captiveportallck);
388 c3214b80 Scott Ullrich
	
389 5b237745 Scott Ullrich
	return 0;
390
}
391
392 769e254e Ermal
function captiveportal_init_webgui() {
393 b4792bf8 Ermal
	global $config, $cpzone;
394 769e254e Ermal
395 b4792bf8 Ermal
	if (is_array($config['captiveportal'])) {
396 e3cf528e bcyrill
		foreach ($config['captiveportal'] as $cpkey => $cp) {
397 b4792bf8 Ermal
			$cpzone = $cpkey;
398 e3cf528e bcyrill
			captiveportal_init_webgui_zone($cp);
399 b4792bf8 Ermal
		}
400
	}
401
}
402 769e254e Ermal
403 e3cf528e bcyrill
function captiveportal_init_webgui_zonename($zone) {
404
	global $config, $cpzone;
405
	
406
	if (isset($config['captiveportal'][$zone])) {
407
		$cpzone = $zone;
408
		captiveportal_init_webgui_zone($config['captiveportal'][$zone]);
409
	}
410
}
411
412
function captiveportal_init_webgui_zone($cpcfg) {
413 b4792bf8 Ermal
	global $g, $config, $cpzone;
414
415 e3cf528e bcyrill
	if (!isset($cpcfg['enable']))
416 b4792bf8 Ermal
		return;
417
418
	if (isset($cpcfg['httpslogin'])) {
419 36f6ed35 bcyrill
		$cert = lookup_cert($cpcfg['certref']);
420 adca02c4 bcyrill
		$crt = base64_decode($cert['crt']);
421
		$key = base64_decode($cert['prv']);
422
		$ca = ca_chain($cert);
423 87e7fdea bcyrill
424 769e254e Ermal
		/* generate lighttpd configuration */
425 470d24a3 Darren Embry
		$listenporthttps = $cpcfg['listenporthttps'] ? $cpcfg['listenporthttps'] : ($cpcfg['zoneid'] + 1);
426 b4792bf8 Ermal
		system_generate_lighty_config("{$g['varetc_path']}/lighty-{$cpzone}-CaptivePortal-SSL.conf",
427 adca02c4 bcyrill
			$crt, $key, $ca, "lighty-{$cpzone}-CaptivePortal-SSL.pid", $listenporthttps, "/usr/local/captiveportal",
428 a96f2d3d Ermal
			"cert-{$cpzone}-portal.pem", "ca-{$cpzone}-portal.pem", $cpzone);
429 769e254e Ermal
	}
430
431
	/* generate lighttpd configuration */
432 470d24a3 Darren Embry
	$listenporthttp = $cpcfg['listenporthttp'] ? $cpcfg['listenporthttp'] : $cpcfg['zoneid'];
433 b4792bf8 Ermal
	system_generate_lighty_config("{$g['varetc_path']}/lighty-{$cpzone}-CaptivePortal.conf",
434 470d24a3 Darren Embry
		"", "", "", "lighty-{$cpzone}-CaptivePortal.pid", $listenporthttp, "/usr/local/captiveportal",
435 a96f2d3d Ermal
		"", "", $cpzone);
436 769e254e Ermal
437 fe2eb995 Ermal
	@unlink("{$g['varrun']}/lighty-{$cpzone}-CaptivePortal.pid");
438 769e254e Ermal
	/* attempt to start lighttpd */
439 b4792bf8 Ermal
	$res = mwexec("/usr/local/sbin/lighttpd -f {$g['varetc_path']}/lighty-{$cpzone}-CaptivePortal.conf");
440 769e254e Ermal
441
	/* fire up https instance */
442 fe2eb995 Ermal
	if (isset($cpcfg['httpslogin'])) {
443
		@unlink("{$g['varrun']}/lighty-{$cpzone}-CaptivePortal-SSL.pid");
444 b4792bf8 Ermal
		$res = mwexec("/usr/local/sbin/lighttpd -f {$g['varetc_path']}/lighty-{$cpzone}-CaptivePortal-SSL.conf");
445 fe2eb995 Ermal
	}
446 769e254e Ermal
}
447
448 847e5e82 Scott Ullrich
/* reinit will disconnect all users, be careful! */
449 1d9e9cca Ermal
function captiveportal_init_rules($reinit = false) {
450 b4792bf8 Ermal
	global $config, $g, $cpzone;
451 36254e4a Scott Ullrich
452 b4792bf8 Ermal
	if (!isset($config['captiveportal'][$cpzone]['enable']))
453 769e254e Ermal
		return;
454
455 3a4b0147 Ermal
	captiveportal_load_modules();
456 c06bdb94 Ermal
	mwexec("/usr/local/sbin/ipfw_context -a {$cpzone}", true);
457 3a4b0147 Ermal
458 769e254e Ermal
	$cpips = array();
459
	$ifaces = get_configured_interface_list();
460 b4792bf8 Ermal
	$cpinterfaces = explode(",", $config['captiveportal'][$cpzone]['interface']);
461 769e254e Ermal
	$firsttime = 0;
462
	foreach ($cpinterfaces as $cpifgrp) {
463
		if (!isset($ifaces[$cpifgrp]))
464
			continue;
465
		$tmpif = get_real_interface($cpifgrp);
466
		if (!empty($tmpif)) {
467
			$cpipm = get_interface_ip($cpifgrp);
468
			if (is_ipaddr($cpipm)) {
469
				$carpif = link_ip_to_carp_interface($cpipm);
470
				if (!empty($carpif)) {
471
					$carpsif = explode(" ", $carpif);
472
					foreach ($carpsif as $cpcarp) {
473 b4792bf8 Ermal
						mwexec("/usr/local/sbin/ipfw_context -a {$cpzone} -n {$cpcarp}", true);
474 769e254e Ermal
						$carpip = find_interface_ip($cpcarp);
475
						if (is_ipaddr($carpip))
476
							$cpips[] = $carpip;
477
					}
478
				}
479
				$cpips[] = $cpipm;
480
			}
481 8b34498c Ermal
			mwexec("/usr/local/sbin/ipfw_context -a {$cpzone} -n {$tmpif}", true);
482 769e254e Ermal
		}
483
	}
484
	if (count($cpips) > 0) {
485
		$cpactive = true;
486 8b34498c Ermal
	} else
487 769e254e Ermal
		return false;
488
489 eade409a Ermal
	if ($reinit == false)
490 b4792bf8 Ermal
		$captiveportallck = lock("captiveportal{$cpzone}");
491 eade409a Ermal
492 ec509679 Ermal
	$cprules =	"add 65291 allow pfsync from any to any\n";
493
	$cprules .= "add 65292 allow carp from any to any\n";
494 181a843c Scott Ullrich
495 3db19cf1 Scott Ullrich
	$cprules .= <<<EOD
496
# layer 2: pass ARP
497 ec509679 Ermal
add 65301 pass layer2 mac-type arp,rarp
498 b9d1d810 Scott Ullrich
# pfsense requires for WPA
499 ec509679 Ermal
add 65302 pass layer2 mac-type 0x888e,0x88c7
500 ee79fcda Ermal
# PPP Over Ethernet Session Stage/Discovery Stage
501 ec509679 Ermal
add 65303 pass layer2 mac-type 0x8863,0x8864
502 684c787e Scott Ullrich
503 ee79fcda Ermal
# layer 2: block anything else non-IP(v4/v6)
504 ec509679 Ermal
add 65307 deny layer2 not mac-type ip,ipv6
505 3db19cf1 Scott Ullrich
506
EOD;
507
508 b01792a0 Ermal
	$rulenum = 65310;
509 fb516dda Chris Buechler
	$ipcount = 0;
510 0ba17c67 Ermal
	$ips = "";
511 fb516dda Chris Buechler
	foreach ($cpips as $cpip) {
512
		if($ipcount == 0) {
513
			$ips = "{$cpip} ";
514
		} else {
515
			$ips .= "or {$cpip} ";
516
		}
517
		$ipcount++;
518
	}
519 0ba17c67 Ermal
	$ips = "{ 255.255.255.255 or {$ips} }";
520 ec509679 Ermal
	$cprules .= "add {$rulenum} pass ip from any to {$ips} in\n";
521 2f27dffd Ermal
	$rulenum++;
522 ec509679 Ermal
	$cprules .= "add {$rulenum} pass ip from {$ips} to any out\n";
523 2f27dffd Ermal
	$rulenum++;
524 ec509679 Ermal
	$cprules .= "add {$rulenum} pass icmp from {$ips} to any out icmptype 0\n";
525 2f27dffd Ermal
	$rulenum++;
526 ec509679 Ermal
	$cprules .= "add {$rulenum} pass icmp from any to {$ips} in icmptype 8 \n";
527 2f27dffd Ermal
	$rulenum++;
528 b01792a0 Ermal
	/* Allowed ips */
529 aea56408 Ermal
	$cprules .= "add {$rulenum} pipe tablearg ip from table(3) to any in\n";
530 b01792a0 Ermal
	$rulenum++;
531 7aff41fe Ermal
	$cprules .= "add {$rulenum} pipe tablearg ip from any to table(4) in\n";
532
	$rulenum++;
533
	$cprules .= "add {$rulenum} pipe tablearg ip from table(3) to any out\n";
534
	$rulenum++;
535 aea56408 Ermal
	$cprules .= "add {$rulenum} pipe tablearg ip from any to table(4) out\n";
536 b01792a0 Ermal
	$rulenum++;
537
538
	/* Authenticated users rules. */
539 ec509679 Ermal
	$cprules .= "add {$rulenum} pipe tablearg ip from table(1) to any in\n";
540 10b9dfcf Ermal
	$rulenum++;
541 ec509679 Ermal
	$cprules .= "add {$rulenum} pipe tablearg ip from any to table(2) out\n";
542 10b9dfcf Ermal
	$rulenum++;
543 bb58ed63 Ermal
544 470d24a3 Darren Embry
	$listenporthttp =
545
		$config['captiveportal'][$cpzone]['listenporthttp'] ?
546
		$config['captiveportal'][$cpzone]['listenporthttp'] :
547
		$config['captiveportal'][$cpzone]['zoneid'];
548 06a45374 Ermal
549 d61cbd50 bcyrill
	if (isset($config['captiveportal'][$cpzone]['httpslogin'])) {
550 06a45374 Ermal
		$listenporthttps = $listenporthttp + 1;
551 ec509679 Ermal
		$cprules .= "add 65531 fwd 127.0.0.1,{$listenporthttps} tcp from any to any dst-port 443 in\n";
552 06a45374 Ermal
	}
553 f9f71ad3 Ermal Lu?i
	
554 b4792bf8 Ermal
	$cprules .= <<<EOD
555 5480497a Scott Ullrich
556 d44bccc7 Scott Ullrich
# redirect non-authenticated clients to captive portal
557 ec509679 Ermal
add 65532 fwd 127.0.0.1,{$listenporthttp} tcp from any to any dst-port 80 in 
558 3db19cf1 Scott Ullrich
# let the responses from the captive portal web server back out
559 ec509679 Ermal
add 65533 pass tcp from any to any out
560 3db19cf1 Scott Ullrich
# block everything else
561 ec509679 Ermal
add 65534 deny all from any to any
562 3db19cf1 Scott Ullrich
563
EOD;
564
565 769e254e Ermal
	/* generate passthru mac database */
566
	$cprules .= captiveportal_passthrumac_configure(true);
567
	$cprules .= "\n";
568 55c18b30 Scott Ullrich
569 769e254e Ermal
	/* allowed ipfw rules to make allowed ip work */
570
	$cprules .= captiveportal_allowedip_configure();
571
572 55c18b30 Scott Ullrich
	/* allowed ipfw rules to make allowed hostnames work */
573
	$cprules .= captiveportal_allowedhostname_configure();
574
	
575 769e254e Ermal
	/* load rules */
576 37e67d04 Ermal
	$cprules = "flush\n{$cprules}";
577 b4792bf8 Ermal
	file_put_contents("{$g['tmp_path']}/ipfw_{$cpzone}.cp.rules", $cprules);
578 287f7e26 Ermal
	mwexec("/sbin/ipfw -x {$cpzone} -q {$g['tmp_path']}/ipfw_{$cpzone}.cp.rules", true);
579 b4792bf8 Ermal
	//@unlink("{$g['tmp_path']}/ipfw_{$cpzone}.cp.rules");
580 c06bdb94 Ermal
	unset($cprules, $tmprules);
581 eade409a Ermal
582
	if ($reinit == false)
583
		unlock($captiveportallck);
584 3db19cf1 Scott Ullrich
}
585
586 f1f58a6f Ermal
/* 
587
 * Remove clients that have been around for longer than the specified amount of time
588 eb7aa263 Ermal
 * db file structure:
589 338c0941 Ermal
 * timestamp,ipfw_rule_no,clientip,clientmac,username,sessionid,password,session_timeout,idle_timeout,session_terminate_time,interim_interval
590 eb7aa263 Ermal
 * (password is in Base64 and only saved when reauthentication is enabled)
591
 */
592 5b237745 Scott Ullrich
function captiveportal_prune_old() {
593 b4792bf8 Ermal
	global $g, $config, $cpzone;
594
595
	if (empty($cpzone))
596
		return;
597 23c4f978 Scott Ullrich
598 f1f58a6f Ermal
	$cpcfg = $config['captiveportal'][$cpzone];
599
	$vcpcfg = $config['voucher'][$cpzone];
600
601 5060dea7 Scott Ullrich
	/* check for expired entries */
602 f1f58a6f Ermal
	$idletimeout = 0;
603
	$timeout = 0;
604
	if (!empty($cpcfg['timeout']) && is_numeric($cpcfg['timeout']))
605
		$timeout = $cpcfg['timeout'] * 60;
606
607
	if (!empty($cpcfg['idletimeout']) && is_numeric($cpcfg['idletimeout']))
608
		$idletimeout = $cpcfg['idletimeout'] * 60;
609
610
	/* Is there any job to do? */
611 87e7fdea bcyrill
	if (!$timeout && !$idletimeout && !isset($cpcfg['reauthenticate']) &&
612 f1f58a6f Ermal
	    !isset($cpcfg['radiussession_timeout']) && !isset($vcpcfg['enable']))
613 5060dea7 Scott Ullrich
		return;
614
615 ebc0e4b6 Ermal
	$radiussrvs = captiveportal_get_radius_servers();
616 006802ab Ermal
617 26ee5aaf Ermal
	/* Read database */
618
	/* NOTE: while this can be simplified in non radius case keep as is for now */
619 5060dea7 Scott Ullrich
	$cpdb = captiveportal_read_db();
620 0bd34ed6 Scott Ullrich
621 f1f58a6f Ermal
	/*
622
	 * To make sure we iterate over ALL accounts on every run the count($cpdb) is moved
623
	 * outside of the loop. Otherwise the loop would evaluate count() on every iteration
624
	 * and since $i would increase and count() would decrement they would meet before we
625
	 * had a chance to iterate over all accounts.
626 5060dea7 Scott Ullrich
	 */
627
	$unsetindexes = array();
628 5ebe85e9 Ermal
	$voucher_needs_sync = false;
629 b09c2d86 Ermal
	/* 
630
	 * Snapshot the time here to use for calculation to speed up the process.
631
	 * If something is missed next run will catch it!
632
	 */
633
	$pruning_time = time();
634
	$stop_time = $pruning_time;
635 3e5c0ab7 Ermal
	foreach ($cpdb as $cpentry) {
636 5060dea7 Scott Ullrich
637
		$timedout = false;
638
		$term_cause = 1;
639 5705c60a Renato Botelho
		if (empty($cpentry[10]))
640
			$cpentry[10] = 'first';
641
		$radiusservers = $radiussrvs[$cpentry[10]];
642 5060dea7 Scott Ullrich
643
		/* hard timeout? */
644
		if ($timeout) {
645 5705c60a Renato Botelho
			if (($pruning_time - $cpentry[0]) >= $timeout) {
646 5060dea7 Scott Ullrich
				$timedout = true;
647 5705c60a Renato Botelho
				$term_cause = 5; // Session-Timeout
648 5060dea7 Scott Ullrich
			}
649 eb7aa263 Ermal
		}
650 23c4f978 Scott Ullrich
651 5060dea7 Scott Ullrich
		/* Session-Terminate-Time */
652 5705c60a Renato Botelho
		if (!$timedout && !empty($cpentry[9])) {
653
			if ($pruning_time >= $cpentry[9]) {
654 5060dea7 Scott Ullrich
				$timedout = true;
655 5705c60a Renato Botelho
				$term_cause = 5; // Session-Timeout
656 5060dea7 Scott Ullrich
			}
657
		}
658
659
		/* check if the radius idle_timeout attribute has been set and if its set change the idletimeout to this value */
660 5705c60a Renato Botelho
		$uidletimeout = (is_numeric($cpentry[8])) ? $cpentry[8] : $idletimeout;
661 5060dea7 Scott Ullrich
		/* if an idle timeout is specified, get last activity timestamp from ipfw */
662 1a1967d6 Ermal
		if (!$timedout && $uidletimeout > 0) {
663 5705c60a Renato Botelho
			$lastact = captiveportal_get_last_activity($cpentry[2]);
664 5060dea7 Scott Ullrich
			/*	If the user has logged on but not sent any traffic they will never be logged out.
665
			 *	We "fix" this by setting lastact to the login timestamp. 
666 f56a73f1 Scott Ullrich
			 */
667 5705c60a Renato Botelho
			$lastact = $lastact ? $lastact : $cpentry[0];
668 b09c2d86 Ermal
			if ($lastact && (($pruning_time - $lastact) >= $uidletimeout)) {
669 5060dea7 Scott Ullrich
				$timedout = true;
670 5705c60a Renato Botelho
				$term_cause = 4; // Idle-Timeout
671 5060dea7 Scott Ullrich
				$stop_time = $lastact; // Entry added to comply with WISPr
672
			}
673 336e3c1c Charlie
		}
674
675 5060dea7 Scott Ullrich
		/* if vouchers are configured, activate session timeouts */
676 5705c60a Renato Botelho
		if (!$timedout && isset($vcpcfg['enable']) && !empty($cpentry[7])) {
677
			if ($pruning_time >= ($cpentry[0] + $cpentry[7])) {
678 5060dea7 Scott Ullrich
				$timedout = true;
679 5705c60a Renato Botelho
				$term_cause = 5; // Session-Timeout
680 5ebe85e9 Ermal
				$voucher_needs_sync = true;
681 5060dea7 Scott Ullrich
			}
682
		}
683
684
		/* if radius session_timeout is enabled and the session_timeout is not null, then check if the user should be logged out */
685 5705c60a Renato Botelho
		if (!$timedout && isset($cpcfg['radiussession_timeout']) && !empty($cpentry[7])) {
686
			if ($pruning_time >= ($cpentry[0] + $cpentry[7])) {
687 5060dea7 Scott Ullrich
				$timedout = true;
688 5705c60a Renato Botelho
				$term_cause = 5; // Session-Timeout
689 5060dea7 Scott Ullrich
			}
690
		}
691
692
		if ($timedout) {
693 3e5c0ab7 Ermal
			captiveportal_disconnect($cpentry, $radiusservers,$term_cause,$stop_time);
694 5705c60a Renato Botelho
			captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "TIMEOUT");
695
			$unsetindexes[] = $cpentry[5];
696 5060dea7 Scott Ullrich
		}
697
698
		/* do periodic RADIUS reauthentication? */
699
		if (!$timedout && !empty($radiusservers)) {
700 f1f58a6f Ermal
			if (isset($cpcfg['radacct_enable'])) {
701
				if ($cpcfg['reauthenticateacct'] == "stopstart") {
702 5060dea7 Scott Ullrich
					/* stop and restart accounting */
703 5705c60a Renato Botelho
					RADIUS_ACCOUNTING_STOP($cpentry[1], // ruleno
704
						$cpentry[4], // username
705
						$cpentry[5], // sessionid
706
						$cpentry[0], // start time
707 5060dea7 Scott Ullrich
						$radiusservers,
708 5705c60a Renato Botelho
						$cpentry[2], // clientip
709
						$cpentry[3], // clientmac
710
						10); // NAS Request
711 6cbda317 Ermal
					pfSense_ipfw_Tableaction($cpzone, IP_FW_TABLE_ZERO_ENTRY_STATS, 1, $cpentry[2]);
712
					pfSense_ipfw_Tableaction($cpzone, IP_FW_TABLE_ZERO_ENTRY_STATS, 2, $cpentry[2]);
713 5705c60a Renato Botelho
					RADIUS_ACCOUNTING_START($cpentry[1], // ruleno
714
						$cpentry[4], // username
715
						$cpentry[5], // sessionid
716 5060dea7 Scott Ullrich
						$radiusservers,
717 5705c60a Renato Botelho
						$cpentry[2], // clientip
718
						$cpentry[3]); // clientmac
719 f1f58a6f Ermal
				} else if ($cpcfg['reauthenticateacct'] == "interimupdate") {
720 5705c60a Renato Botelho
					$session_time = $pruning_time - $cpentry[0];
721
					if (!empty($cpentry[10]) && $cpentry[10] > 60)
722
						$interval = $cpentry[10];
723 338c0941 Ermal
					else
724
						$interval = 0;
725
					$past_interval_min = ($session_time > $interval);
726
					$within_interval = ($session_time % $interval >= 0 && $session_time % $interval <= 59);
727
					if (($interval > 0 && $past_interval_min && $within_interval) || $interval === 0) {
728 5705c60a Renato Botelho
						RADIUS_ACCOUNTING_STOP($cpentry[1], // ruleno
729
							$cpentry[4], // username
730
							$cpentry[5], // sessionid
731
							$cpentry[0], // start time
732 338c0941 Ermal
							$radiusservers,
733 5705c60a Renato Botelho
							$cpentry[2], // clientip
734
							$cpentry[3], // clientmac
735
							10, // NAS Request
736
							true); // Interim Updates
737 338c0941 Ermal
					}
738 5060dea7 Scott Ullrich
				}
739
			}
740 23c4f978 Scott Ullrich
741 5060dea7 Scott Ullrich
			/* check this user against RADIUS again */
742 f1f58a6f Ermal
			if (isset($cpcfg['reauthenticate'])) {
743 5705c60a Renato Botelho
				$auth_list = RADIUS_AUTHENTICATION($cpentry[4], // username
744
					base64_decode($cpentry[6]), // password
745 5060dea7 Scott Ullrich
					$radiusservers,
746 5705c60a Renato Botelho
					$cpentry[2], // clientip
747
					$cpentry[3], // clientmac
748
					$cpentry[1]); // ruleno
749 5060dea7 Scott Ullrich
				if ($auth_list['auth_val'] == 3) {
750 5705c60a Renato Botelho
					captiveportal_disconnect($cpentry, $radiusservers, 17);
751
					captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "RADIUS_DISCONNECT", $auth_list['reply_message']);
752
					$unsetindexes[] = $cpentry[5];
753 aec0f2fd Ermal
				} else if ($auth_list['auth_val'] == 2)
754
					captiveportal_reapply_attributes($cpentry, $auth_list);
755 5060dea7 Scott Ullrich
			}
756
		}
757
	}
758 f32eae2d Ermal
	unset($cpdb);
759 23c4f978 Scott Ullrich
760 522f1cc7 Ermal
	captiveportal_prune_old_automac();
761
762 5ebe85e9 Ermal
	if ($voucher_needs_sync == true)
763
		/* Triger a sync of the vouchers on config */
764
		send_event("service sync vouchers");
765
766 5060dea7 Scott Ullrich
	/* write database */
767 e92916d6 Ermal
	if (!empty($unsetindexes))
768 26ee5aaf Ermal
		captiveportal_remove_entries($unsetindexes);
769 5b237745 Scott Ullrich
}
770
771 522f1cc7 Ermal
function captiveportal_prune_old_automac() {
772
	global $g, $config, $cpzone;
773
774
	if (is_array($config['captiveportal'][$cpzone]['passthrumac']) && isset($config['captiveportal'][$cpzone]['passthrumacaddusername'])) {
775
		$tmpvoucherdb = array();
776
		$macrules = "";
777
		$writecfg = false;
778
		foreach ($config['captiveportal'][$cpzone]['passthrumac'] as $eid => $emac) {
779
			if ($emac['logintype'] == "voucher") {
780
				if (isset($tmpvoucherdb[$emac['username']])) {
781
					$temac = $config['captiveportal'][$cpzone]['passthrumac'][$tmpvoucherdb[$emac['username']]];
782
					$ruleno = captiveportal_get_ipfw_passthru_ruleno($temac['mac']);
783 aea56408 Ermal
					$pipeno = captiveportal_get_dn_passthru_ruleno($temac['mac']);
784 522f1cc7 Ermal
					if ($ruleno) {
785 aea56408 Ermal
						captiveportal_free_ipfw_ruleno($ruleno);
786 522f1cc7 Ermal
						$macrules .= "delete {$ruleno}";
787
						++$ruleno;
788
						$macrules .= "delete {$ruleno}";
789
					}
790 aea56408 Ermal
					if ($pipeno) {
791
						captiveportal_free_dn_ruleno($pipeno);
792
						$macrules .= "pipe delete {$pipeno}\n";
793
						++$pipeno;
794
						$macrules .= "pipe delete {$pipeno}\n";
795
					}
796 522f1cc7 Ermal
					$writecfg = true;
797
					captiveportal_logportalauth($temac['username'], $temac['mac'], $temac['ip'], "DUPLICATE {$temac['username']} LOGIN - TERMINATING OLD SESSION");
798
					unset($config['captiveportal'][$cpzone]['passthrumac'][$tmpvoucherdb[$emac['username']]]);
799
				}
800
				$tmpvoucherdb[$emac['username']] = $eid;
801
				if (voucher_auth($emac['username']) <= 0) {
802
					$ruleno = captiveportal_get_ipfw_passthru_ruleno($emac['mac']);
803 aea56408 Ermal
					$pipeno = captiveportal_get_dn_passthru_ruleno($emac['mac']);
804 522f1cc7 Ermal
					if ($ruleno) {
805 aea56408 Ermal
						captiveportal_free_ipfw_ruleno($ruleno);
806 522f1cc7 Ermal
						$macrules .= "delete {$ruleno}";
807
						++$ruleno;
808
						$macrules .= "delete {$ruleno}";
809
					}
810 aea56408 Ermal
					if ($pipeno) {
811
						captiveportal_free_dn_ruleno($pipeno);
812
						$macrules .= "pipe delete {$pipeno}\n";
813
						++$pipeno;
814
						$macrules .= "pipe delete {$pipeno}\n";
815
					}
816 522f1cc7 Ermal
					$writecfg = true;
817
					captiveportal_logportalauth($emac['username'], $emac['mac'], $emac['ip'], "EXPIRED {$emac['username']} LOGIN - TERMINATING SESSION");
818
					unset($config['captiveportal'][$cpzone]['passthrumac'][$eid]);
819
				}
820
			}
821
		}
822
		if (!empty($macrules)) {
823
			@file_put_contents("{$g['tmp_path']}/macentry.prunerules.tmp", $macrules);
824 2e62a7c4 Ermal
			unset($macrules);
825 287f7e26 Ermal
			mwexec("/sbin/ipfw -x {$cpzone} -q {$g['tmp_path']}/macentry.prunerules.tmp");
826 522f1cc7 Ermal
		}
827
		if ($writecfg === true)
828
			write_config("Prune session for auto-added macs");
829
	}
830
}
831
832 3db19cf1 Scott Ullrich
/* remove a single client according to the DB entry */
833 0bd34ed6 Scott Ullrich
function captiveportal_disconnect($dbent, $radiusservers,$term_cause = 1,$stop_time = null) {
834 b4792bf8 Ermal
	global $g, $config, $cpzone;
835 d99f7864 Scott Ullrich
836
	$stop_time = (empty($stop_time)) ? time() : $stop_time;
837
838
	/* this client needs to be deleted - remove ipfw rules */
839 b4792bf8 Ermal
	if (isset($config['captiveportal'][$cpzone]['radacct_enable']) && !empty($radiusservers)) {
840 5705c60a Renato Botelho
		RADIUS_ACCOUNTING_STOP($dbent[1], // ruleno
841
			$dbent[4], // username
842
			$dbent[5], // sessionid
843
			$dbent[0], // start time
844 5060dea7 Scott Ullrich
			$radiusservers,
845 5705c60a Renato Botelho
			$dbent[2], // clientip
846
			$dbent[3], // clientmac
847 5060dea7 Scott Ullrich
			$term_cause, // Acct-Terminate-Cause
848
			false,
849
			$stop_time);
850 d99f7864 Scott Ullrich
	}
851 32c392aa Ermal
	
852 5705c60a Renato Botelho
	if (is_ipaddr($dbent[2])) {
853 ddd69ea9 bcyrill
		/* Delete client's ip entry from tables 1 and 2. */
854 5705c60a Renato Botelho
		pfSense_ipfw_Tableaction($cpzone, IP_FW_TABLE_DEL, 1, $dbent[2]);
855
		pfSense_ipfw_Tableaction($cpzone, IP_FW_TABLE_DEL, 2, $dbent[2]);
856 32c392aa Ermal
		/* XXX: Redundant?! Ensure all pf(4) states are killed. */
857 5705c60a Renato Botelho
		pfSense_kill_states($dbent[2]);
858
		pfSense_kill_srcstates($dbent[2]);
859 32c392aa Ermal
	}
860 f9f71ad3 Ermal Lu?i
861
	/* 
862
	* These are the pipe numbers we use to control traffic shaping for each logged in user via captive portal
863
	* We could get an error if the pipe doesn't exist but everything should still be fine
864
	*/
865 5705c60a Renato Botelho
	if (!empty($dbent[1])) {
866
		pfSense_pipe_action("pipe delete {$dbent[1]}");
867
		pfSense_pipe_action("pipe delete " . ($dbent[1]+1));
868 7a7abeba Scott Ullrich
869 6cbda317 Ermal
		/* Release the ruleno so it can be reallocated to new clients. */
870 5705c60a Renato Botelho
		captiveportal_free_dn_ruleno($dbent[1]);
871 6cbda317 Ermal
	}
872 d322e3b3 Scott Ullrich
873
	// XMLRPC Call over to the master Voucher node
874 b4792bf8 Ermal
	if(!empty($config['voucher'][$cpzone]['vouchersyncdbip'])) {
875
		$syncip   = $config['voucher'][$cpzone]['vouchersyncdbip'];
876
		$syncport = $config['voucher'][$cpzone]['vouchersyncport'];
877
		$syncpass = $config['voucher'][$cpzone]['vouchersyncpass'];
878
		$vouchersyncusername = $config['voucher'][$cpzone]['vouchersyncusername'];
879 f989aa5b Ermal
		$remote_status = xmlrpc_sync_voucher_disconnect($dbent, $syncip, $syncport, $syncpass, $vouchersyncusername, $term_cause, $stop_time);
880 d322e3b3 Scott Ullrich
	}
881
882 3db19cf1 Scott Ullrich
}
883 12ee8fe4 Scott Ullrich
884 006802ab Ermal
/* remove a single client by sessionid */
885
function captiveportal_disconnect_client($sessionid, $term_cause = 1, $logoutReason = "LOGOUT") {
886 26ee5aaf Ermal
	global $g, $config;
887 36254e4a Scott Ullrich
888 d99f7864 Scott Ullrich
	$radiusservers = captiveportal_get_radius_servers();
889 006802ab Ermal
890
	/* read database */
891 5038fb53 bcyrill
	$result = captiveportal_read_db("WHERE sessionid = '{$sessionid}'");
892 d99f7864 Scott Ullrich
893
	/* find entry */
894 5038fb53 bcyrill
	if (!empty($result)) {
895 26ee5aaf Ermal
		captiveportal_write_db("DELETE FROM captiveportal WHERE sessionid = '{$sessionid}'");
896
897 5038fb53 bcyrill
		foreach ($result as $cpentry) {
898 5705c60a Renato Botelho
			if (empty($cpentry[10]))
899
				$cpentry[10] = 'first';
900
			captiveportal_disconnect($cpentry, $radiusservers[$cpentry[10]], $term_cause);
901
			captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "DISCONNECT");
902 5038fb53 bcyrill
		}
903
		unset($result);
904 26ee5aaf Ermal
	}
905 5b237745 Scott Ullrich
}
906
907
/* send RADIUS acct stop for all current clients */
908 d31bc32a Ermal
function captiveportal_radius_stop_all() {
909 b4792bf8 Ermal
	global $config, $cpzone;
910 d99f7864 Scott Ullrich
911 b4792bf8 Ermal
	if (!isset($config['captiveportal'][$cpzone]['radacct_enable']))
912 d99f7864 Scott Ullrich
		return;
913
914
	$radiusservers = captiveportal_get_radius_servers();
915 40b48c6c Ermal Lu?i
	if (!empty($radiusservers)) {
916 d31bc32a Ermal
		$cpdb = captiveportal_read_db();
917
		foreach ($cpdb as $cpentry) {
918 5705c60a Renato Botelho
			if (empty($cpentry[10]))
919
				$cpentry[10] = 'first';
920
			if (!empty($radiusservers[$cpentry[10]])) {
921
				RADIUS_ACCOUNTING_STOP($cpentry[1], // ruleno
922
					$cpentry[4], // username
923
					$cpentry[5], // sessionid
924
					$cpentry[0], // start time
925
					$radiusservers[$cpentry[10]],
926
					$cpentry[2], // clientip
927
					$cpentry[3], // clientmac
928
					7); // Admin Reboot
929 ebc0e4b6 Ermal
			}
930 d99f7864 Scott Ullrich
		}
931
	}
932 5b237745 Scott Ullrich
}
933
934 d5ae560d Ermal
function captiveportal_passthrumac_configure_entry($macent) {
935 aea56408 Ermal
936
	$bwUp = empty($macent['bw_up']) ? 0 : $macent['bw_up'];
937
	$bwDown = empty($macent['bw_down']) ? 0 : $macent['bw_down'];
938 d5ae560d Ermal
939 10b9dfcf Ermal
	$ruleno = captiveportal_get_next_ipfw_ruleno();
940 aea56408 Ermal
	$pipeno = captiveportal_get_next_dn_ruleno();
941 d5ae560d Ermal
942 aea56408 Ermal
	$rules = "";
943
	$pipeup = $pipeno;
944 0674bc42 Ermal
	pfSense_pipe_action("pipe {$pipeup} config bw {$bwUp}Kbit/s queue 100 buckets 16");
945 aea56408 Ermal
	$pipedown = $pipeno + 1;
946 0674bc42 Ermal
	pfSense_pipe_action("pipe {$pipedown} config bw {$bwDown}Kbit/s queue 100 buckets 16");
947 aea56408 Ermal
	$rules .= "add {$ruleno} pipe {$pipeup} ip from any to any MAC {$macent['mac']} any\n";
948 d5ae560d Ermal
	$ruleno++;
949 aea56408 Ermal
	$rules .= "add {$ruleno} pipe {$pipedown} ip from any to any MAC any {$macent['mac']}\n";
950 d5ae560d Ermal
951
	return $rules;
952
}
953
954 dedf51a2 Ermal Lu?i
function captiveportal_passthrumac_configure($lock = false) {
955 b4792bf8 Ermal
	global $config, $g, $cpzone;
956 36254e4a Scott Ullrich
957 d5ae560d Ermal
	$rules = "";
958 36254e4a Scott Ullrich
959 b4792bf8 Ermal
	if (is_array($config['captiveportal'][$cpzone]['passthrumac'])) {
960 1dbe445a Ermal
		$macdb = array();
961 b4792bf8 Ermal
		foreach ($config['captiveportal'][$cpzone]['passthrumac'] as $macent) {
962 d5ae560d Ermal
			$rules .= captiveportal_passthrumac_configure_entry($macent);
963 b4792bf8 Ermal
			$macdb[$macent['mac']][$cpzone]['active']  = true;
964 1dbe445a Ermal
965 5b237745 Scott Ullrich
		}
966
	}
967 0bd34ed6 Scott Ullrich
968 d5ae560d Ermal
	return $rules;
969 5b237745 Scott Ullrich
}
970
971 fac13a5e Ermal
function captiveportal_passthrumac_findbyname($username) {
972 b4792bf8 Ermal
	global $config, $cpzone;
973 fac13a5e Ermal
974 b4792bf8 Ermal
	if (is_array($config['captiveportal'][$cpzone]['passthrumac'])) {
975
		foreach ($config['captiveportal'][$cpzone]['passthrumac'] as $macent) {
976 fac13a5e Ermal
			if ($macent['username'] == $username)
977
				return $macent;
978
		}
979
	}
980
	return NULL;
981
}
982
983 b01792a0 Ermal
/* 
984
 * table (3=IN)/(4=OUT) hold allowed ip's without bw limits
985
 */
986 1272429c Ermal
function captiveportal_allowedip_configure_entry($ipent, $ishostname = false) {
987 1b584e3f Ermal
	global $g;
988 b01792a0 Ermal
989 8b73cc7e Scott Ullrich
	/*  Instead of copying this entire function for something
990
	 *  easy such as hostname vs ip address add this check
991
	 */
992 bb58ed63 Ermal
	if ($ishostname === true) {
993 1b584e3f Ermal
		if (!$g['booting']) {
994
			$ipaddress = gethostbyname($ipent['hostname']);
995
			if (!is_ipaddr($ipaddress)) 
996
				return;
997 46209e0b Ermal
		} else
998
			$ipaddress = "";
999 bb58ed63 Ermal
	} else
1000
		$ipaddress = $ipent['ip'];
1001 0b108eda Scott Ullrich
1002 b01792a0 Ermal
	$rules = "";
1003 1272429c Ermal
	$cp_filterdns_conf = "";
1004 aea56408 Ermal
	$enBwup = empty($ipent['bw_up']) ? 0 : intval($ipent['bw_up']);
1005
	$enBwdown = empty($ipent['bw_down']) ? 0 : intval($ipent['bw_down']);
1006 b01792a0 Ermal
1007 aea56408 Ermal
	$pipeno = captiveportal_get_next_dn_ruleno();
1008 657d2c66 Ermal
	pfSense_pipe_action("pipe {$pipeno} config bw {$enBwup}Kbit/s queue 100 buckets 16");
1009 aea56408 Ermal
	$pipedown = $pipeno + 1;
1010 657d2c66 Ermal
	pfSense_pipe_action("pipe {$pipedown} config bw {$enBwdown}Kbit/s queue 100 buckets 16");
1011 1272429c Ermal
	if ($ishostname === true) {
1012
		$cp_filterdns_conf .= "ipfw {$ipent['hostname']} 3 pipe {$pipeno}\n";
1013
		$cp_filterdns_conf .= "ipfw {$ipent['hostname']} 4 pipe {$pipedown}\n";
1014 1b584e3f Ermal
		if (!is_ipaddr($ipaddress))
1015
			return array("", $cp_filterdns_conf);
1016 1272429c Ermal
	}
1017 d6a0379d Ermal
	$subnet = "";
1018
	if (!empty($ipent['sn']))
1019
		$subnet = "/{$ipent['sn']}";
1020 aea56408 Ermal
	$rules .= "table 3 add {$ipaddress}{$subnet} {$pipeno}\n";
1021
	$rules .= "table 4 add {$ipaddress}{$subnet} {$pipedown}\n";
1022 b01792a0 Ermal
1023 1272429c Ermal
	if ($ishostname === true)
1024
		return array($rules, $cp_filterdns_conf);
1025
	else
1026
		return $rules;
1027 f23a6091 Scott Ullrich
}
1028
1029
function captiveportal_allowedhostname_configure() {
1030 b4792bf8 Ermal
	global $config, $g, $cpzone;
1031 f23a6091 Scott Ullrich
1032 1272429c Ermal
	$rules = "";
1033 b4792bf8 Ermal
	if (is_array($config['captiveportal'][$cpzone]['allowedhostname'])) {
1034 1272429c Ermal
		$rules = "\n# captiveportal_allowedhostname_configure()\n";
1035
		$cp_filterdns_conf = "";
1036
		foreach ($config['captiveportal'][$cpzone]['allowedhostname'] as $hostnameent) {
1037
			$tmprules = captiveportal_allowedip_configure_entry($hostnameent, true);
1038
			$rules .= $tmprules[0];
1039
			$cp_filterdns_conf .= $tmprules[1];
1040
		}
1041
		$cp_filterdns_filename = "{$g['varetc_path']}/filterdns-{$cpzone}-captiveportal.conf";
1042
		@file_put_contents($cp_filterdns_filename, $cp_filterdns_conf);
1043
		unset($cp_filterdns_conf);
1044 081320a4 Ermal
		if (isvalidpid("{$g['varrun_path']}/filterdns-{$cpzone}-cpah.pid"))
1045
			sigkillbypid("{$g['varrun_path']}/filterdns-{$cpzone}-cpah.pid", "HUP");
1046 bb58ed63 Ermal
		else
1047 081320a4 Ermal
			mwexec("/usr/local/sbin/filterdns -p {$g['varrun_path']}/filterdns-{$cpzone}-cpah.pid -i 300 -c {$cp_filterdns_filename} -y {$cpzone} -d 1");
1048 7b5eab84 bcyrill
	} else {
1049
		killbypid("{$g['varrun_path']}/filterdns-{$cpzone}-cpah.pid");
1050
		@unlink("{$g['varrun_path']}/filterdns-{$cpzone}-cpah.pid");
1051 55c18b30 Scott Ullrich
	}
1052 1272429c Ermal
1053 f23a6091 Scott Ullrich
	return $rules;
1054
}
1055
1056 cb0a2913 Ermal Lu?i
function captiveportal_allowedip_configure() {
1057 b4792bf8 Ermal
	global $config, $g, $cpzone;
1058 36254e4a Scott Ullrich
1059 6ce61a8f Ermal
	$rules = "";
1060 b4792bf8 Ermal
	if (is_array($config['captiveportal'][$cpzone]['allowedip'])) {
1061
		foreach ($config['captiveportal'][$cpzone]['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 b4792bf8 Ermal
	global $cpzone;
1071 36254e4a Scott Ullrich
1072 1272429c Ermal
	$ipfwoutput = pfSense_ipfw_getTablestats($cpzone, 1, $ip);
1073 f9f71ad3 Ermal Lu?i
	/* Reading only from one of the tables is enough of approximation. */
1074 1272429c Ermal
	if (is_array($ipfwoutput)) {
1075
		return $ipfwoutput['timestamp'];
1076 d99f7864 Scott Ullrich
	}
1077 36254e4a Scott Ullrich
1078 d99f7864 Scott Ullrich
	return 0;
1079 5b237745 Scott Ullrich
}
1080
1081 d31bc32a Ermal
function captiveportal_init_radius_servers() {
1082 b4792bf8 Ermal
	global $config, $g, $cpzone;
1083 d31bc32a Ermal
1084
	/* generate radius server database */
1085 b4792bf8 Ermal
	if ($config['captiveportal'][$cpzone]['radiusip'] && (!isset($config['captiveportal'][$cpzone]['auth_method']) ||
1086
		($config['captiveportal'][$cpzone]['auth_method'] == "radius"))) {
1087
		$radiusip = $config['captiveportal'][$cpzone]['radiusip'];
1088
		$radiusip2 = ($config['captiveportal'][$cpzone]['radiusip2']) ? $config['captiveportal'][$cpzone]['radiusip2'] : null;
1089 7468a29f jim-p
		$radiusip3 = ($config['captiveportal'][$cpzone]['radiusip3']) ? $config['captiveportal'][$cpzone]['radiusip3'] : null;
1090
		$radiusip4 = ($config['captiveportal'][$cpzone]['radiusip4']) ? $config['captiveportal'][$cpzone]['radiusip4'] : null;
1091 d31bc32a Ermal
1092 b4792bf8 Ermal
		if ($config['captiveportal'][$cpzone]['radiusport'])
1093
			$radiusport = $config['captiveportal'][$cpzone]['radiusport'];
1094 d31bc32a Ermal
		else
1095
			$radiusport = 1812;
1096 b4792bf8 Ermal
		if ($config['captiveportal'][$cpzone]['radiusacctport'])
1097
			$radiusacctport = $config['captiveportal'][$cpzone]['radiusacctport'];
1098 d31bc32a Ermal
		else
1099
			$radiusacctport = 1813;
1100 b4792bf8 Ermal
		if ($config['captiveportal'][$cpzone]['radiusport2'])
1101
			$radiusport2 = $config['captiveportal'][$cpzone]['radiusport2'];
1102 d31bc32a Ermal
		else
1103
			$radiusport2 = 1812;
1104 7468a29f jim-p
		if ($config['captiveportal'][$cpzone]['radiusport3'])
1105
			$radiusport3 = $config['captiveportal'][$cpzone]['radiusport3'];
1106 ebc0e4b6 Ermal
		else
1107
			$radiusport3 = 1812;
1108 7468a29f jim-p
		if ($config['captiveportal'][$cpzone]['radiusport4'])
1109
			$radiusport4 = $config['captiveportal'][$cpzone]['radiusport4'];
1110 ebc0e4b6 Ermal
		else
1111
			$radiusport4 = 1812;
1112
1113 b4792bf8 Ermal
		$radiuskey = $config['captiveportal'][$cpzone]['radiuskey'];
1114 dbce0c2c bcyrill
		$radiuskey2 = $config['captiveportal'][$cpzone]['radiuskey2'];
1115
		$radiuskey3 = $config['captiveportal'][$cpzone]['radiuskey3'];
1116
		$radiuskey4 = $config['captiveportal'][$cpzone]['radiuskey4'];
1117 d31bc32a Ermal
1118 b4792bf8 Ermal
		$cprdsrvlck = lock("captiveportalradius{$cpzone}", LOCK_EX);
1119
		$fd = @fopen("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db", "w");
1120 d31bc32a Ermal
		if (!$fd) {
1121
			captiveportal_syslog("Error: cannot open radius DB file in captiveportal_configure().\n");
1122
			unlock($cprdsrvlck);
1123
			return 1;
1124 ebc0e4b6 Ermal
		}
1125 dbce0c2c bcyrill
		if (isset($radiusip))
1126 ebc0e4b6 Ermal
			fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey . ",first");
1127 dbce0c2c bcyrill
		if (isset($radiusip2))
1128 ebc0e4b6 Ermal
			fwrite($fd,"\n" . $radiusip2 . "," . $radiusport2 . "," . $radiusacctport . "," . $radiuskey2 . ",first");
1129 dbce0c2c bcyrill
		if (isset($radiusip3))
1130 ebc0e4b6 Ermal
			fwrite($fd,"\n" . $radiusip3 . "," . $radiusport3 . "," . $radiusacctport . "," . $radiuskey3 . ",second");
1131 dbce0c2c bcyrill
		if (isset($radiusip4))
1132 ebc0e4b6 Ermal
			fwrite($fd,"\n" . $radiusip4 . "," . $radiusport4 . "," . $radiusacctport . "," . $radiuskey4 . ",second");
1133
		
1134
1135 d31bc32a Ermal
		fclose($fd);
1136
		unlock($cprdsrvlck);
1137
	}
1138
}
1139
1140 5b237745 Scott Ullrich
/* read RADIUS servers into array */
1141
function captiveportal_get_radius_servers() {
1142 b4792bf8 Ermal
	global $g, $cpzone;
1143 ac07425a Ermal
1144 b4792bf8 Ermal
	$cprdsrvlck = lock("captiveportalradius{$cpzone}");
1145
	if (file_exists("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db")) {
1146 ac07425a Ermal
		$radiusservers = array();
1147 b4792bf8 Ermal
		$cpradiusdb = file("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db", 
1148 ac07425a Ermal
		FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
1149
		if ($cpradiusdb) {
1150
			foreach($cpradiusdb as $cpradiusentry) {
1151
				$line = trim($cpradiusentry);
1152
				if ($line) {
1153
					$radsrv = array();
1154 ebc0e4b6 Ermal
						list($radsrv['ipaddr'],$radsrv['port'],$radsrv['acctport'],$radsrv['key'], $context) = explode(",",$line);
1155
				}
1156
				if (empty($context)) {
1157
					if (!is_array($radiusservers['first']))
1158
						$radiusservers['first'] = array();
1159
					$radiusservers['first'] = $radsrv;
1160
				} else {
1161
					if (!is_array($radiusservers[$context]))
1162
						$radiusservers[$context] = array();
1163
					$radiusservers[$context][] = $radsrv;
1164 5060dea7 Scott Ullrich
				}
1165
			}
1166 2f70eac7 Ermal Lu?i
		}
1167 60b66b60 Ermal
		unlock($cprdsrvlck);
1168 ac07425a Ermal
		return $radiusservers;
1169
	}
1170
1171
	unlock($cprdsrvlck);
1172
	return false;
1173 5b237745 Scott Ullrich
}
1174
1175 3db19cf1 Scott Ullrich
/* log successful captive portal authentication to syslog */
1176
/* part of this code from php.net */
1177 0bd34ed6 Scott Ullrich
function captiveportal_logportalauth($user,$mac,$ip,$status, $message = null) {
1178 d99f7864 Scott Ullrich
	// Log it
1179
	if (!$message)
1180 f56a73f1 Scott Ullrich
		$message = "$status: $user, $mac, $ip";
1181 d31bc32a Ermal
	else {
1182
		$message = trim($message);
1183 f56a73f1 Scott Ullrich
		$message = "$status: $user, $mac, $ip, $message";
1184 d31bc32a Ermal
	}
1185 f56a73f1 Scott Ullrich
	captiveportal_syslog($message);
1186
}
1187
1188
/* log simple messages to syslog */
1189
function captiveportal_syslog($message) {
1190
	$message = trim($message);
1191
	openlog("logportalauth", LOG_PID, LOG_LOCAL4);
1192
	// Log it
1193
	syslog(LOG_INFO, $message);
1194 d99f7864 Scott Ullrich
	closelog();
1195 3db19cf1 Scott Ullrich
}
1196
1197 ebc0e4b6 Ermal
function radius($username,$password,$clientip,$clientmac,$type, $radiusctx = null) {
1198 5060dea7 Scott Ullrich
	global $g, $config;
1199 d44bccc7 Scott Ullrich
1200 aea56408 Ermal
	$pipeno = captiveportal_get_next_dn_ruleno();
1201 2f70eac7 Ermal Lu?i
1202 5060dea7 Scott Ullrich
	/* If the pool is empty, return appropriate message and fail authentication */
1203 aea56408 Ermal
	if (is_null($pipeno)) {
1204 5060dea7 Scott Ullrich
		$auth_list = array();
1205
		$auth_list['auth_val'] = 1;
1206
		$auth_list['error'] = "System reached maximum login capacity";
1207
		return $auth_list;
1208
	}
1209 9befcca7 Ermal Lu?i
1210 5060dea7 Scott Ullrich
	$radiusservers = captiveportal_get_radius_servers();
1211 d44bccc7 Scott Ullrich
1212 ebc0e4b6 Ermal
	if (is_null($radiusctx))
1213
		$radiusctx = 'first';
1214
1215 5060dea7 Scott Ullrich
	$auth_list = RADIUS_AUTHENTICATION($username,
1216
		$password,
1217 ebc0e4b6 Ermal
		$radiusservers[$radiusctx],
1218 5060dea7 Scott Ullrich
		$clientip,
1219
		$clientmac,
1220 aea56408 Ermal
		$pipeno);
1221 5060dea7 Scott Ullrich
1222
	if ($auth_list['auth_val'] == 2) {
1223
		captiveportal_logportalauth($username,$clientmac,$clientip,$type);
1224
		$sessionid = portal_allow($clientip,
1225
			$clientmac,
1226
			$username,
1227
			$password,
1228
			$auth_list,
1229 aea56408 Ermal
			$pipeno,
1230 ebc0e4b6 Ermal
			$radiusctx);
1231 5060dea7 Scott Ullrich
	}
1232 0bd34ed6 Scott Ullrich
1233 5060dea7 Scott Ullrich
	return $auth_list;
1234 0bd34ed6 Scott Ullrich
}
1235
1236 26ee5aaf Ermal
function captiveportal_opendb() {
1237 b4792bf8 Ermal
	global $g, $cpzone;
1238 5060dea7 Scott Ullrich
1239 26ee5aaf Ermal
	if (file_exists("{$g['vardb_path']}/captiveportal{$cpzone}.db"))
1240
		$DB = @sqlite_open("{$g['vardb_path']}/captiveportal{$cpzone}.db");
1241
	else {
1242
		$errormsg = "";
1243
		$DB = @sqlite_open("{$g['vardb_path']}/captiveportal{$cpzone}.db");
1244 5705c60a Renato Botelho
		if (@sqlite_exec($DB, "CREATE TABLE captiveportal (allow_time INTEGER, pipeno INTEGER, ip TEXT, mac TEXT, username TEXT, sessionid TEXT, bpassword TEXT, session_timeout INTEGER, idle_timeout INTEGER, session_terminate_time INTEGER, interim_interval INTEGER) ", $errormsg)) {
1245 a19ea478 Ermal
			@sqlite_exec($DB, "CREATE UNIQUE INDEX idx_active ON captiveportal (sessionid, username)");
1246
			@sqlite_exec($DB, "CREATE INDEX user ON captiveportal (username)");
1247
			@sqlite_exec($DB, "CREATE INDEX ip ON captiveportal (ip)");
1248 26ee5aaf Ermal
			@sqlite_exec($DB, "CREATE INDEX starttime ON captiveportal (allow_time)");
1249
			@sqlite_exec($DB, "CREATE INDEX serviceid ON captiveportal (serviceid)");
1250
		} else
1251
			captiveportal_syslog("Error during table {$cpzone} creation. Error message: {$errormsg}");
1252 006802ab Ermal
	}
1253 26ee5aaf Ermal
1254
	return $DB;
1255 0bd34ed6 Scott Ullrich
}
1256
1257 26ee5aaf Ermal
/* read captive portal DB into array */
1258
function captiveportal_read_db($query = "") {
1259 5060dea7 Scott Ullrich
1260 26ee5aaf Ermal
	$DB = captiveportal_opendb();
1261
	if ($DB) {
1262
		sqlite_exec($DB, "BEGIN");
1263 08d93848 bcyrill
		if (!empty($query))
1264 5705c60a Renato Botelho
			$cpdb = @sqlite_array_query($DB, "SELECT * FROM captiveportal {$query}", SQLITE_NUM);
1265 26ee5aaf Ermal
		else {
1266 5705c60a Renato Botelho
			$response = @sqlite_unbuffered_query($DB, "SELECT * FROM captiveportal", SQLITE_NUM);
1267
			$cpdb = @sqlite_fetch_all($response, SQLITE_NUM);
1268 5c0b5f64 Ermal
		}
1269 26ee5aaf Ermal
		sqlite_exec($DB, "END");
1270
		@sqlite_close($DB);
1271 006802ab Ermal
	}
1272 26ee5aaf Ermal
	if (!$cpdb)
1273
		$cpdb = array();
1274
1275
	return $cpdb;
1276
}
1277
1278
function captiveportal_remove_entries($remove) {
1279
1280
	if (!is_array($remove) || empty($remove))
1281
		return;
1282
1283 1974c2d6 bcyrill
	$query = "DELETE FROM captiveportal WHERE sessionid in (";
1284 be0a33ef bcyrill
	foreach($remove as $idx => $unindex) {
1285 26ee5aaf Ermal
		$query .= "'{$unindex}'";
1286
		if ($idx < (count($remove) - 1))
1287
			$query .= ",";
1288 006802ab Ermal
	}
1289 26ee5aaf Ermal
	$query .= ")";
1290
	captiveportal_write_db($query);
1291
}
1292
1293
/* write captive portal DB */
1294
function captiveportal_write_db($queries) {
1295
	global $g;
1296
1297
	if (is_array($queries))
1298
		$query = implode(";", $queries);
1299
	else
1300
		$query = $queries;
1301
1302
	$DB = captiveportal_opendb();
1303
	if ($DB) {
1304
		$error_msg = "";
1305
		sqlite_exec($DB, "BEGIN TRANSACTION");
1306
		$result = @sqlite_exec($DB, $query, $error_msg);
1307
		if (!$result)
1308
			captiveportal_syslog("Trying to modify DB returned error: {$error_msg}");
1309
		else
1310
			sqlite_exec($DB, "END TRANSACTION");
1311
		@sqlite_close($DB);
1312
		return $result;
1313
	} else
1314
		return true;
1315 0bd34ed6 Scott Ullrich
}
1316
1317
function captiveportal_write_elements() {
1318 b4792bf8 Ermal
	global $g, $config, $cpzone;
1319 5060dea7 Scott Ullrich
	
1320 b4792bf8 Ermal
	$cpcfg = $config['captiveportal'][$cpzone];
1321
1322 769e254e Ermal
	/* delete any existing elements */
1323
	if (is_dir($g['captiveportal_element_path'])) {
1324
		$dh = opendir($g['captiveportal_element_path']);
1325
		while (($file = readdir($dh)) !== false) {
1326
			if ($file != "." && $file != "..")
1327
				unlink($g['captiveportal_element_path'] . "/" . $file);
1328
		}
1329
		closedir($dh);
1330 5060dea7 Scott Ullrich
	} else {
1331 769e254e Ermal
		@mkdir($g['captiveportal_element_path']);
1332 5060dea7 Scott Ullrich
	}
1333 769e254e Ermal
1334 b4792bf8 Ermal
	if (is_array($cpcfg['element'])) {
1335 1fadb31d Scott Ullrich
		conf_mount_rw();
1336 b4792bf8 Ermal
		foreach ($cpcfg['element'] as $data) {
1337 3760b867 Ermal
			if (!@file_put_contents("{$g['captiveportal_element_path']}/{$data['name']}", base64_decode($data['content']))) {
1338 fdc55311 Carlos Eduardo Ramos
				printf(gettext("Error: cannot open '%s' in captiveportal_write_elements()%s"), $data['name'], "\n");
1339 1fadb31d Scott Ullrich
				return 1;
1340
			}
1341
			unlink_if_exists("{$g['captiveportal_path']}/{$data['name']}");
1342 3760b867 Ermal
			@symlink("{$g['captiveportal_element_path']}/{$data['name']}", "{$g['captiveportal_path']}/{$data['name']}");
1343 1fadb31d Scott Ullrich
		}
1344
		conf_mount_ro();
1345
	}
1346 5060dea7 Scott Ullrich
	
1347 769e254e Ermal
	return 0;
1348 0bd34ed6 Scott Ullrich
}
1349
1350 aea56408 Ermal
function captiveportal_get_next_dn_ruleno($rulenos_start = 2000, $rulenos_range_max = 64500) {
1351
	global $config, $g;
1352 6ce61a8f Ermal
1353 aea56408 Ermal
	$cpruleslck = lock("captiveportalrulesdn", LOCK_EX);
1354
	$ruleno = 0;
1355
	if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) {
1356
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules"));
1357
		for ($ridx = $rulenos_start; $ridx < $rulenos_range_max; $ridx++) {
1358
			if ($rules[$ridx]) {
1359
				$ridx++;
1360
				continue;
1361
			}
1362
			$ruleno = $ridx;
1363
			$rules[$ridx] = "used";
1364
			$rules[++$ridx] = "used";
1365
			break;
1366
		}
1367
	} else {
1368 fe3693cb Ermal
		$rules = array_pad(array(), $rulenos_range_max, false);
1369 aea56408 Ermal
		$rules[$rulenos_start] = "used";
1370
		$rules[++$rulenos_start] = "used";
1371
		$ruleno = $rulenos_start;
1372
	}
1373
	file_put_contents("{$g['vardb_path']}/captiveportaldn.rules", serialize($rules));
1374
	unlock($cpruleslck);
1375
1376
	return $ruleno;
1377
}
1378
1379
function captiveportal_free_dn_ruleno($ruleno) {
1380 87e7fdea bcyrill
	global $config, $g;
1381
1382
	$cpruleslck = lock("captiveportalrulesdn", LOCK_EX);
1383
	if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) {
1384
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules"));
1385
		$rules[$ruleno] = false;
1386
		$rules[++$ruleno] = false;
1387
		file_put_contents("{$g['vardb_path']}/captiveportaldn.rules", serialize($rules));
1388
	}
1389
	unlock($cpruleslck);
1390 aea56408 Ermal
}
1391
1392
function captiveportal_get_dn_passthru_ruleno($value) {
1393 b273dd26 Ermal
	global $config, $g, $cpzone;
1394
1395
	$cpcfg = $config['captiveportal'][$cpzone];
1396
	if(!isset($cpcfg['enable']))
1397
		return NULL;
1398 aea56408 Ermal
1399 fe7e987e Ermal
	$cpruleslck = lock("captiveportalrulesdn", LOCK_EX);
1400
	if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) {
1401
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules"));
1402 287f7e26 Ermal
		$ruleno = intval(`/sbin/ipfw -x {$cpzone} show | /usr/bin/grep {$value} |  /usr/bin/grep -v grep | /usr/bin/cut -d " " -f 5 | /usr/bin/head -n 1`);
1403 aea56408 Ermal
		if ($rules[$ruleno]) {
1404
			unlock($cpruleslck);
1405
			return $ruleno;
1406
		}
1407
	}
1408
1409
	unlock($cpruleslck);
1410
	return NULL;
1411 6ce61a8f Ermal
}
1412
1413 920cafaf Scott Ullrich
/*
1414
 * This function will calculate the lowest free firewall ruleno
1415 f9f71ad3 Ermal Lu?i
 * within the range specified based on the actual logged on users
1416 920cafaf Scott Ullrich
 *
1417
 */
1418 aea56408 Ermal
function captiveportal_get_next_ipfw_ruleno($rulenos_start = 2, $rulenos_range_max = 64500) {
1419 b4792bf8 Ermal
	global $config, $g, $cpzone;
1420 6ce61a8f Ermal
1421 b4792bf8 Ermal
	$cpcfg = $config['captiveportal'][$cpzone];
1422
	if(!isset($cpcfg['enable']))
1423 01d57b8c Scott Ullrich
		return NULL;
1424 6ce61a8f Ermal
1425 b4792bf8 Ermal
	$cpruleslck = lock("captiveportalrules{$cpzone}", LOCK_EX);
1426 f9f71ad3 Ermal Lu?i
	$ruleno = 0;
1427 b4792bf8 Ermal
	if (file_exists("{$g['vardb_path']}/captiveportal_{$cpzone}.rules")) {
1428
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal_{$cpzone}.rules"));
1429 6ce61a8f Ermal
		for ($ridx = 2; $ridx < ($rulenos_range_max - $rulenos_start); $ridx++) {
1430
			if ($rules[$ridx]) {
1431
				/* 
1432 5060dea7 Scott Ullrich
				 * This allows our traffic shaping pipes to be the in pipe the same as ruleno 
1433 10b9dfcf Ermal
				 * and the out pipe ruleno + 1.
1434 5060dea7 Scott Ullrich
				 */
1435 10b9dfcf Ermal
				$ridx++;
1436 6ce61a8f Ermal
				continue;
1437
			}
1438
			$ruleno = $ridx;
1439
			$rules[$ridx] = "used";
1440 10b9dfcf Ermal
			$rules[++$ridx] = "used";
1441 6ce61a8f Ermal
			break;
1442
		}
1443
	} else {
1444 fe3693cb Ermal
		$rules = array_pad(array(), $rulenos_range_max, false);
1445 aea56408 Ermal
		$rules[$rulenos_start] = "used";
1446
		$rules[++$rulenos_start] = "used";
1447 6ce61a8f Ermal
		$ruleno = 2;
1448
	}
1449 b4792bf8 Ermal
	file_put_contents("{$g['vardb_path']}/captiveportal_{$cpzone}.rules", serialize($rules));
1450 d31bc32a Ermal
	unlock($cpruleslck);
1451 6ce61a8f Ermal
	return $ruleno;
1452
}
1453
1454 aea56408 Ermal
function captiveportal_free_ipfw_ruleno($ruleno) {
1455 b4792bf8 Ermal
	global $config, $g, $cpzone;
1456 6ce61a8f Ermal
1457 b4792bf8 Ermal
	$cpcfg = $config['captiveportal'][$cpzone];
1458
	if(!isset($cpcfg['enable']))
1459 6ce61a8f Ermal
		return NULL;
1460
1461 b4792bf8 Ermal
	$cpruleslck = lock("captiveportalrules{$cpzone}", LOCK_EX);
1462
	if (file_exists("{$g['vardb_path']}/captiveportal_{$cpzone}.rules")) {
1463
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal_{$cpzone}.rules"));
1464 6ce61a8f Ermal
		$rules[$ruleno] = false;
1465 10b9dfcf Ermal
		$rules[++$ruleno] = false;
1466 b4792bf8 Ermal
		file_put_contents("{$g['vardb_path']}/captiveportal_{$cpzone}.rules", serialize($rules));
1467 f9f71ad3 Ermal Lu?i
	}
1468 d31bc32a Ermal
	unlock($cpruleslck);
1469 6ce61a8f Ermal
}
1470
1471 d5ae560d Ermal
function captiveportal_get_ipfw_passthru_ruleno($value) {
1472 b4792bf8 Ermal
	global $config, $g, $cpzone;
1473 6ce61a8f Ermal
1474 b4792bf8 Ermal
	$cpcfg = $config['captiveportal'][$cpzone];
1475
	if(!isset($cpcfg['enable']))
1476
		return NULL;
1477 6ce61a8f Ermal
1478 b4792bf8 Ermal
	$cpruleslck = lock("captiveportalrules{$cpzone}", LOCK_EX);
1479
	if (file_exists("{$g['vardb_path']}/captiveportal_{$cpzone}.rules")) {
1480
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal_{$cpzone}.rules"));
1481 287f7e26 Ermal
		$ruleno = intval(`/sbin/ipfw -x {$cpzone} show | /usr/bin/grep {$value} |  /usr/bin/grep -v grep | /usr/bin/cut -d " " -f 1 | /usr/bin/head -n 1`);
1482 d31bc32a Ermal
		if ($rules[$ruleno]) {
1483
			unlock($cpruleslck);
1484 6ce61a8f Ermal
			return $ruleno;
1485 d31bc32a Ermal
		}
1486 5060dea7 Scott Ullrich
	}
1487 6ce61a8f Ermal
1488 d31bc32a Ermal
	unlock($cpruleslck);
1489 f9f71ad3 Ermal Lu?i
	return NULL;
1490 920cafaf Scott Ullrich
}
1491
1492 360d815d Scott Ullrich
/**
1493
 * This function will calculate the traffic produced by a client
1494
 * based on its firewall rule
1495
 *
1496
 * Point of view: NAS
1497
 *
1498
 * Input means: from the client
1499
 * Output means: to the client
1500
 *
1501
 */
1502
1503 f9f71ad3 Ermal Lu?i
function getVolume($ip) {
1504 f48abba2 Michael Newton
	global $config, $cpzone;
1505 360d815d Scott Ullrich
1506 f48abba2 Michael Newton
	$reverse = empty($config['captiveportal'][$cpzone]['reverseacct']) ? false : true;
1507 5060dea7 Scott Ullrich
	$volume = array();
1508
	// Initialize vars properly, since we don't want NULL vars
1509
	$volume['input_pkts'] = $volume['input_bytes'] = $volume['output_pkts'] = $volume['output_bytes'] = 0 ;
1510 360d815d Scott Ullrich
1511 1272429c Ermal
	$ipfw = pfSense_ipfw_getTablestats($cpzone, 1, $ip);
1512
	if (is_array($ipfw)) {
1513 f48abba2 Michael Newton
		if ($reverse) {
1514
			$volume['output_pkts'] = $ipfw['packets'];
1515
			$volume['output_bytes'] = $ipfw['bytes'];
1516
		}
1517
		else {
1518
			$volume['input_pkts'] = $ipfw['packets'];
1519
			$volume['input_bytes'] = $ipfw['bytes'];
1520
		}
1521 5060dea7 Scott Ullrich
	}
1522 f9f71ad3 Ermal Lu?i
1523 1272429c Ermal
	$ipfw = pfSense_ipfw_getTablestats($cpzone, 2, $ip);
1524
	if (is_array($ipfw)) {
1525 f48abba2 Michael Newton
		if ($reverse) {
1526
			$volume['input_pkts'] = $ipfw['packets'];
1527
			$volume['input_bytes'] = $ipfw['bytes'];
1528
		}
1529
		else {
1530
			$volume['output_pkts'] = $ipfw['packets'];
1531
			$volume['output_bytes'] = $ipfw['bytes'];
1532
		}
1533 5060dea7 Scott Ullrich
	}
1534 360d815d Scott Ullrich
1535 5060dea7 Scott Ullrich
	return $volume;
1536 360d815d Scott Ullrich
}
1537
1538 856e58a6 Scott Ullrich
/**
1539
 * Get the NAS-IP-Address based on the current wan address
1540
 *
1541
 * Use functions in interfaces.inc to find this out
1542
 *
1543
 */
1544
1545
function getNasIP()
1546
{
1547 b4792bf8 Ermal
	global $config, $cpzone;
1548 64c0462b Ermal
1549 b4792bf8 Ermal
	if (empty($config['captiveportal'][$cpzone]['radiussrcip_attribute'])) {
1550 5060dea7 Scott Ullrich
			$nasIp = get_interface_ip();
1551
	} else {
1552 b4792bf8 Ermal
		if (is_ipaddr($config['captiveportal'][$cpzone]['radiussrcip_attribute']))
1553
			$nasIp = $config['captiveportal'][$cpzone]['radiussrcip_attribute'];
1554 5060dea7 Scott Ullrich
		else
1555 b4792bf8 Ermal
			$nasIp = get_interface_ip($config['captiveportal'][$cpzone]['radiussrcip_attribute']);
1556 36ff7f81 Ermal
	}
1557 64c0462b Ermal
		
1558 5060dea7 Scott Ullrich
	if(!is_ipaddr($nasIp))
1559
		$nasIp = "0.0.0.0";
1560 64c0462b Ermal
1561
	return $nasIp;
1562 856e58a6 Scott Ullrich
}
1563
1564 f8b11310 Ermal Lu?i
function portal_ip_from_client_ip($cliip) {
1565 b4792bf8 Ermal
	global $config, $cpzone;
1566 f8b11310 Ermal Lu?i
1567 45bef774 bcyrill
	$isipv6 = is_ipaddrv6($cliip);
1568 b4792bf8 Ermal
	$interfaces = explode(",", $config['captiveportal'][$cpzone]['interface']);
1569 f8b11310 Ermal Lu?i
	foreach ($interfaces as $cpif) {
1570 45bef774 bcyrill
		if ($isipv6) {
1571
			$ip = get_interface_ipv6($cpif);
1572
			$sn = get_interface_subnetv6($cpif);
1573
		} else {
1574
			$ip = get_interface_ip($cpif);
1575
			$sn = get_interface_subnet($cpif);
1576
		}
1577 f8b11310 Ermal Lu?i
		if (ip_in_subnet($cliip, "{$ip}/{$sn}"))
1578
			return $ip;
1579
	}
1580
1581 45bef774 bcyrill
	$inet = ($isipv6) ? '-inet6' : '-inet';
1582
	$iface = exec_command("/sbin/route -n get {$inet} {$cliip} | /usr/bin/awk '/interface/ { print \$2; };'");
1583 f86fa91c jim-p
	$iface = trim($iface, "\n");
1584
	if (!empty($iface)) {
1585 45bef774 bcyrill
		$ip = ($isipv6) ? find_interface_ipv6($iface) : find_interface_ip($iface);
1586 f86fa91c jim-p
		if (is_ipaddr($ip))
1587
			return $ip;
1588
	}
1589
1590 cc125e13 Chris Buechler
	// doesn't match up to any particular interface
1591
	// so let's set the portal IP to what PHP says 
1592
	// the server IP issuing the request is. 
1593
	// allows same behavior as 1.2.x where IP isn't 
1594
	// in the subnet of any CP interface (static routes, etc.)
1595
	// rather than forcing to DNS hostname resolution
1596
	$ip = $_SERVER['SERVER_ADDR'];
1597
	if (is_ipaddr($ip))
1598
		return $ip;
1599
1600 f8b11310 Ermal Lu?i
	return false;
1601
}
1602
1603 de132ae3 bcyrill
function portal_hostname_from_client_ip($cliip) {
1604
	global $config, $cpzone;
1605
1606
	$cpcfg = $config['captiveportal'][$cpzone];
1607
1608
	if (isset($cpcfg['httpslogin'])) {
1609
		$listenporthttps = $cpcfg['listenporthttps'] ? $cpcfg['listenporthttps'] : ($cpcfg['zoneid'] + 1);
1610
		$ourhostname = $cpcfg['httpsname'];
1611
		
1612
		if ($listenporthttps != 443)
1613
			$ourhostname .= ":" . $listenporthttps;
1614
	} else {
1615
		$listenporthttp  = $cpcfg['listenporthttp']  ? $cpcfg['listenporthttp']  : $cpcfg['zoneid'];
1616
		$ifip = portal_ip_from_client_ip($cliip);
1617
		if (!$ifip)
1618
			$ourhostname = "{$config['system']['hostname']}.{$config['system']['domain']}";
1619
		else
1620
			$ourhostname = (is_ipaddrv6($ifip)) ? "[{$ifip}]" : "{$ifip}";
1621
		
1622
		if ($listenporthttp != 80)
1623
			$ourhostname .= ":" . $listenporthttp;
1624
	}
1625 d5ac388b bcyrill
	
1626
	return $ourhostname;
1627 de132ae3 bcyrill
}
1628
1629 ac631bba lgcosta
/* functions move from index.php */
1630
1631
function portal_reply_page($redirurl, $type = null, $message = null, $clientmac = null, $clientip = null, $username = null, $password = null) {
1632 b4792bf8 Ermal
	global $g, $config, $cpzone;
1633 ac631bba lgcosta
1634
	/* Get captive portal layout */
1635
	if ($type == "redir") {
1636
		header("Location: {$redirurl}");
1637
		return;
1638
	} else if ($type == "login")
1639 b4792bf8 Ermal
		$htmltext = get_include_contents("{$g['varetc_path']}/captiveportal_{$cpzone}.html");
1640 ac631bba lgcosta
	else
1641 b4792bf8 Ermal
		$htmltext = get_include_contents("{$g['varetc_path']}/captiveportal-{$cpzone}-error.html");
1642
1643
	$cpcfg = $config['captiveportal'][$cpzone];
1644 ac631bba lgcosta
1645
	/* substitute the PORTAL_REDIRURL variable */
1646 de132ae3 bcyrill
	if ($cpcfg['preauthurl']) {
1647
		$htmltext = str_replace("\$PORTAL_REDIRURL\$", "{$cpcfg['preauthurl']}", $htmltext);
1648
		$htmltext = str_replace("#PORTAL_REDIRURL#", "{$cpcfg['preauthurl']}", $htmltext);
1649 ac631bba lgcosta
	}
1650
1651
	/* substitute other variables */
1652 de132ae3 bcyrill
	$ourhostname = portal_hostname_from_client_ip($clientip);
1653
	$protocol = (isset($cpcfg['httpslogin'])) ? 'https://' : 'http://';
1654
	$htmltext = str_replace("\$PORTAL_ACTION\$", "{$protocol}{$ourhostname}/", $htmltext);
1655
	$htmltext = str_replace("#PORTAL_ACTION#", "{$protocol}{$ourhostname}/", $htmltext);
1656 ac631bba lgcosta
1657 b4792bf8 Ermal
	$htmltext = str_replace("\$PORTAL_ZONE\$", htmlspecialchars($cpzone), $htmltext);
1658 ac631bba lgcosta
	$htmltext = str_replace("\$PORTAL_REDIRURL\$", htmlspecialchars($redirurl), $htmltext);
1659
	$htmltext = str_replace("\$PORTAL_MESSAGE\$", htmlspecialchars($message), $htmltext);
1660
	$htmltext = str_replace("\$CLIENT_MAC\$", htmlspecialchars($clientmac), $htmltext);
1661
	$htmltext = str_replace("\$CLIENT_IP\$", htmlspecialchars($clientip), $htmltext);
1662
1663
	// Special handling case for captive portal master page so that it can be ran 
1664
	// through the PHP interpreter using the include method above.  We convert the
1665
	// $VARIABLE$ case to #VARIABLE# in /etc/inc/captiveportal.inc before writing out.
1666 b4792bf8 Ermal
	$htmltext = str_replace("#PORTAL_ZONE#", htmlspecialchars($cpzone), $htmltext);
1667 ac631bba lgcosta
	$htmltext = str_replace("#PORTAL_REDIRURL#", htmlspecialchars($redirurl), $htmltext);
1668
	$htmltext = str_replace("#PORTAL_MESSAGE#", htmlspecialchars($message), $htmltext);
1669
	$htmltext = str_replace("#CLIENT_MAC#", htmlspecialchars($clientmac), $htmltext);
1670
	$htmltext = str_replace("#CLIENT_IP#", htmlspecialchars($clientip), $htmltext);
1671
	$htmltext = str_replace("#USERNAME#", htmlspecialchars($username), $htmltext);
1672
	$htmltext = str_replace("#PASSWORD#", htmlspecialchars($password), $htmltext);
1673
1674 87e7fdea bcyrill
	echo $htmltext;
1675 ac631bba lgcosta
}
1676
1677
function portal_mac_radius($clientmac,$clientip) {
1678 87e7fdea bcyrill
	global $config, $cpzone;
1679 ac631bba lgcosta
1680 87e7fdea bcyrill
	$radmac_secret = $config['captiveportal'][$cpzone]['radmac_secret'];
1681 ac631bba lgcosta
1682 87e7fdea bcyrill
	/* authentication against the radius server */
1683
	$username = mac_format($clientmac);
1684
	$auth_list = radius($username,$radmac_secret,$clientip,$clientmac,"MACHINE LOGIN");
1685
	if ($auth_list['auth_val'] == 2)
1686
		return TRUE;
1687 ac631bba lgcosta
1688 87e7fdea bcyrill
	if (!empty($auth_list['url_redirection']))
1689
		portal_reply_page($auth_list['url_redirection'], "redir");
1690
1691
	return FALSE;
1692 ac631bba lgcosta
}
1693
1694 aec0f2fd Ermal
function captiveportal_reapply_attributes($cpentry, $attributes) {
1695 2ec063f9 Warren Baker
	global $config, $cpzone, $g;
1696 87e7fdea bcyrill
1697 10b9dfcf Ermal
	$dwfaultbw_up = isset($config['captiveportal'][$cpzone]['bwdefaultup']) ? $config['captiveportal'][$cpzone]['bwdefaultup'] : 0;
1698
	$dwfaultbw_down = isset($config['captiveportal'][$cpzone]['bwdefaultdn']) ? $config['captiveportal'][$cpzone]['bwdefaultdn'] : 0;
1699 87e7fdea bcyrill
	$bw_up = isset($attributes['bw_up']) ? round(intval($attributes['bw_up'])/1000, 2) : $dwfaultbw_up;
1700
	$bw_down = isset($attributes['bw_down']) ? round(intval($attributes['bw_down'])/1000, 2) : $dwfaultbw_down;
1701 5705c60a Renato Botelho
	$bw_up_pipeno = $cpentry[1];
1702
	$bw_down_pipeno = $cpentry[1]+1;
1703 aec0f2fd Ermal
1704 dcf68058 Ermal
	pfSense_pipe_action("pipe {$bw_up_pipeno} config bw {$bw_up}Kbit/s queue 100 buckets 16");
1705
	pfSense_pipe_action("pipe {$bw_down_pipeno} config bw {$bw_down}Kbit/s queue 100 buckets 16");
1706 10b9dfcf Ermal
	//captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "RADIUS_BANDWIDTH_REAPPLY", "{$bw_up}/{$bw_down}");
1707 aec0f2fd Ermal
1708 87e7fdea bcyrill
	unset($bw_up_pipeno, $bw_down_pipeno, $bw_up, $bw_down);
1709 aec0f2fd Ermal
}
1710
1711 aea56408 Ermal
function portal_allow($clientip,$clientmac,$username,$password = null, $attributes = null, $pipeno = null, $radiusctx = null)  {
1712 b4792bf8 Ermal
	global $redirurl, $g, $config, $type, $passthrumac, $_POST, $cpzone;
1713 ac631bba lgcosta
1714
	// Ensure we create an array if we are missing attributes
1715
	if (!is_array($attributes))
1716
		$attributes = array();
1717
1718 26ee5aaf Ermal
	unset($sessionid);
1719 ac631bba lgcosta
1720 006802ab Ermal
	/* Do not allow concurrent login execution. */
1721 b4792bf8 Ermal
	$cpdblck = lock("captiveportaldb{$cpzone}", LOCK_EX);
1722 006802ab Ermal
1723 ac631bba lgcosta
	if ($attributes['voucher'])
1724
		$remaining_time = $attributes['session_timeout'];
1725
1726
	$writecfg = false;
1727
	/* Find an existing session */
1728 b4792bf8 Ermal
	if ((isset($config['captiveportal'][$cpzone]['noconcurrentlogins'])) && $passthrumac) {
1729
		if (isset($config['captiveportal'][$cpzone]['passthrumacadd'])) {
1730 ac631bba lgcosta
			$mac = captiveportal_passthrumac_findbyname($username);
1731
			if (!empty($mac)) {
1732
				if ($_POST['replacemacpassthru']) {
1733 b4792bf8 Ermal
					foreach ($config['captiveportal'][$cpzone]['passthrumac'] as $idx => $macent) {
1734 ac631bba lgcosta
						if ($macent['mac'] == $mac['mac']) {
1735
							$macrules = "";
1736
							$ruleno = captiveportal_get_ipfw_passthru_ruleno($mac['mac']);
1737 aea56408 Ermal
							$pipeno = captiveportal_get_dn_passthru_ruleno($mac['mac']);
1738 87e7fdea bcyrill
							if ($ruleno) {
1739 aea56408 Ermal
								captiveportal_free_ipfw_ruleno($ruleno);
1740 87e7fdea bcyrill
								$macrules .= "delete {$ruleno}\n";
1741 ac631bba lgcosta
								++$ruleno;
1742 87e7fdea bcyrill
								$macrules .= "delete {$ruleno}\n";
1743
							}
1744 aea56408 Ermal
							if ($pipeno) {
1745
								captiveportal_free_dn_ruleno($pipeno);
1746 87e7fdea bcyrill
								$macrules .= "pipe delete {$pipeno}\n";
1747 aea56408 Ermal
								++$pipeno;
1748 87e7fdea bcyrill
								$macrules .= "pipe delete {$pipeno}\n";
1749 aea56408 Ermal
							}
1750 b4792bf8 Ermal
							unset($config['captiveportal'][$cpzone]['passthrumac'][$idx]);
1751 ac631bba lgcosta
							$mac['mac'] = $clientmac;
1752 b4792bf8 Ermal
							$config['captiveportal'][$cpzone]['passthrumac'][] = $mac;
1753 ac631bba lgcosta
							$macrules .= captiveportal_passthrumac_configure_entry($mac);
1754 b4792bf8 Ermal
							file_put_contents("{$g['tmp_path']}/macentry_{$cpzone}.rules.tmp", $macrules);
1755 287f7e26 Ermal
							mwexec("/sbin/ipfw -x {$cpzone} -q {$g['tmp_path']}/macentry_{$cpzone}.rules.tmp");
1756 ac631bba lgcosta
							$writecfg = true;
1757
							$sessionid = true;
1758
							break;
1759
						}
1760
					}
1761 87e7fdea bcyrill
				} else {
1762 ac631bba lgcosta
					portal_reply_page($redirurl, "error", "Username: {$username} is already authenticated using another MAC address.",
1763
						$clientmac, $clientip, $username, $password);
1764 ee79fcda Ermal
					unlock($cpdblck);
1765 aea56408 Ermal
					return;
1766 ac631bba lgcosta
				}
1767
			}
1768
		}
1769
	}
1770
1771 26ee5aaf Ermal
	/* read in client database */
1772
	$query = "WHERE ip = '{$clientip}'";
1773
	$tmpusername = strtolower($username);
1774 4586abb7 bcyrill
	if (isset($config['captiveportal'][$cpzone]['noconcurrentlogins']))
1775 26ee5aaf Ermal
		$query .= " OR (username != 'unauthenticated' AND lower(username) = '{$tmpusername}')";
1776
	$cpdb = captiveportal_read_db($query);
1777
1778 ebc0e4b6 Ermal
	/* Snapshot the timestamp */
1779 b09c2d86 Ermal
	$allow_time = time();
1780 26ee5aaf Ermal
	$radiusservers = captiveportal_get_radius_servers();
1781
	$unsetindexes = array();
1782 ebc0e4b6 Ermal
	if (is_null($radiusctx))
1783
		$radiusctx = 'first';
1784 26ee5aaf Ermal
1785
	foreach ($cpdb as $cpentry) {
1786 5705c60a Renato Botelho
		if (empty($cpentry[10]))
1787
			$cpentry[10] = 'first';
1788 ac631bba lgcosta
		/* on the same ip */
1789 5705c60a Renato Botelho
		if ($cpentry[2] == $clientip) {
1790
			if (isset($config['captiveportal'][$cpzone]['nomacfilter']) || $cpentry[3] == $clientmac)
1791
				captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - REUSING OLD SESSION");
1792 2890cae5 Ermal
			else
1793 5705c60a Renato Botelho
				captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - REUSING IP {$cpentry[2]} WITH DIFFERENT MAC ADDRESS {$cpentry[3]}");
1794
			$sessionid = $cpentry[5];
1795 ac631bba lgcosta
			break;
1796
		}
1797 5705c60a Renato Botelho
		elseif (($attributes['voucher']) && ($username != 'unauthenticated') && ($cpentry[4] == $username)) {
1798 ac631bba lgcosta
			// user logged in with an active voucher. Check for how long and calculate 
1799
			// how much time we can give him (voucher credit - used time)
1800 5705c60a Renato Botelho
			$remaining_time = $cpentry[0] + $cpentry[7] - $allow_time;
1801
			if ($remaining_time < 0)    // just in case. 
1802 ac631bba lgcosta
				$remaining_time = 0;
1803
1804
			/* This user was already logged in so we disconnect the old one */
1805 5705c60a Renato Botelho
			captiveportal_disconnect($cpentry,$radiusservers[$cpentry[10]],13);
1806
			captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - TERMINATING OLD SESSION");
1807
			$unsetindexes[] = $cpentry[5];
1808 ac631bba lgcosta
			break;
1809
		}
1810 b4792bf8 Ermal
		elseif ((isset($config['captiveportal'][$cpzone]['noconcurrentlogins'])) && ($username != 'unauthenticated')) {
1811 ac631bba lgcosta
			/* on the same username */
1812 5705c60a Renato Botelho
			if (strcasecmp($cpentry[4], $username) == 0) {
1813 ac631bba lgcosta
				/* This user was already logged in so we disconnect the old one */
1814 5705c60a Renato Botelho
				captiveportal_disconnect($cpentry,$radiusservers[$cpentry[10]],13);
1815
				captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - TERMINATING OLD SESSION");
1816
				$unsetindexes[] = $cpentry[5];
1817 ac631bba lgcosta
				break;
1818
			}
1819
		}
1820
	}
1821 f32eae2d Ermal
	unset($cpdb);
1822 ac631bba lgcosta
1823 26ee5aaf Ermal
	if (!empty($unsetindexes))
1824
		captiveportal_remove_entries($unsetindexes);
1825
1826 ac631bba lgcosta
	if ($attributes['voucher'] && $remaining_time <= 0)
1827
		return 0;       // voucher already used and no time left
1828
1829
	if (!isset($sessionid)) {
1830
		/* generate unique session ID */
1831
		$tod = gettimeofday();
1832
		$sessionid = substr(md5(mt_rand() . $tod['sec'] . $tod['usec'] . $clientip . $clientmac), 0, 16);
1833
1834
		if ($passthrumac) {
1835
			$mac = array();
1836
			$mac['mac'] = $clientmac;
1837 522f1cc7 Ermal
			$mac['ip'] = $clientip; /* Used only for logging */
1838
			if (isset($config['captiveportal'][$cpzone]['passthrumacaddusername'])) {
1839 ac631bba lgcosta
				$mac['username'] = $username;
1840 522f1cc7 Ermal
				if ($attributes['voucher'])
1841
					$mac['logintype'] = "voucher";
1842
			}
1843 ac631bba lgcosta
			$mac['descr'] =  "Auto added pass-through MAC for user {$username}";
1844
			if (!empty($bw_up))
1845
				$mac['bw_up'] = $bw_up;
1846
			if (!empty($bw_down))
1847
				$mac['bw_down'] = $bw_down;
1848 b4792bf8 Ermal
			if (!is_array($config['captiveportal'][$cpzone]['passthrumac']))
1849
				$config['captiveportal'][$cpzone]['passthrumac'] = array();
1850
			$config['captiveportal'][$cpzone]['passthrumac'][] = $mac;
1851 006802ab Ermal
			unlock($cpdblck);
1852 ac631bba lgcosta
			$macrules = captiveportal_passthrumac_configure_entry($mac);
1853 b4792bf8 Ermal
			file_put_contents("{$g['tmp_path']}/macentry_{$cpzone}.rules.tmp", $macrules);
1854 287f7e26 Ermal
			mwexec("/sbin/ipfw -x {$cpzone}-q {$g['tmp_path']}/macentry_{$cpzone}.rules.tmp");
1855 ac631bba lgcosta
			$writecfg = true;
1856
		} else {
1857 aea56408 Ermal
			/* See if a pipeno is passed, if not start sessions because this means there isn't one atm */
1858
			if (is_null($pipeno))
1859
				$pipeno = captiveportal_get_next_dn_ruleno();
1860
1861
			/* if the pool is empty, return appropriate message and exit */
1862
			if (is_null($pipeno)) {
1863
				portal_reply_page($redirurl, "error", "System reached maximum login capacity");
1864
				log_error("WARNING!  Captive portal has reached maximum login capacity");
1865
				unlock($cpdblck);
1866
				return;
1867
			}
1868
1869 553abb0d Ermal
			$dwfaultbw_up = isset($config['captiveportal'][$cpzone]['bwdefaultup']) ? $config['captiveportal'][$cpzone]['bwdefaultup'] : 0;
1870
			$dwfaultbw_down = isset($config['captiveportal'][$cpzone]['bwdefaultdn']) ? $config['captiveportal'][$cpzone]['bwdefaultdn'] : 0;
1871
			$bw_up = isset($attributes['bw_up']) ? round(intval($attributes['bw_up'])/1000, 2) : $dwfaultbw_up;
1872
			$bw_down = isset($attributes['bw_down']) ? round(intval($attributes['bw_down'])/1000, 2) : $dwfaultbw_down;
1873
1874 aea56408 Ermal
			$bw_up_pipeno = $pipeno;
1875
			$bw_down_pipeno = $pipeno + 1;
1876 10b9dfcf Ermal
			//$bw_up /= 1000; // Scale to Kbit/s
1877 dcf68058 Ermal
			pfSense_pipe_action("pipe {$bw_up_pipeno} config bw {$bw_up}Kbit/s queue 100 buckets 16");
1878
			pfSense_pipe_action("pipe {$bw_down_pipeno} config bw {$bw_down}Kbit/s queue 100 buckets 16");
1879 ac631bba lgcosta
1880 60884727 bcyrill
			$clientsn = (is_ipaddrv6($clientip)) ? 128 : 32;
1881 10b9dfcf Ermal
			if (!isset($config['captiveportal'][$cpzone]['nomacfilter']))
1882 60884727 bcyrill
				pfSense_ipfw_Tableaction($cpzone, IP_FW_TABLE_ADD, 1, $clientip, $clientsn, $clientmac, $bw_up_pipeno);
1883 10b9dfcf Ermal
			else
1884 60884727 bcyrill
				pfSense_ipfw_Tableaction($cpzone, IP_FW_TABLE_ADD, 1, $clientip, $clientsn, NULL, $bw_up_pipeno);
1885 10b9dfcf Ermal
1886
			if (!isset($config['captiveportal'][$cpzone]['nomacfilter']))
1887 287f7e26 Ermal
				pfSense_ipfw_Tableaction($cpzone, IP_FW_TABLE_ADD, 2, $clientip, $clientsn, $clientmac, $bw_down_pipeno);
1888 10b9dfcf Ermal
			else
1889 287f7e26 Ermal
				pfSense_ipfw_Tableaction($cpzone, IP_FW_TABLE_ADD, 2, $clientip, $clientsn, NULL, $bw_down_pipeno);
1890 ac631bba lgcosta
1891
			if ($attributes['voucher'])
1892
				$attributes['session_timeout'] = $remaining_time;
1893 1974c2d6 bcyrill
			
1894
			/* handle empty attributes */
1895
			$session_timeout = (!empty($attributes['session_timeout'])) ? $attributes['session_timeout'] : 'NULL';
1896
			$idle_timeout = (!empty($attributes['idle_timeout'])) ? $attributes['idle_timeout'] : 'NULL';
1897
			$session_terminate_time = (!empty($attributes['session_terminate_time'])) ? $attributes['session_terminate_time'] : 'NULL';
1898 338c0941 Ermal
			$interim_interval = (!empty($attributes['interim_interval'])) ? $attributes['interim_interval'] : 'NULL';
1899 1974c2d6 bcyrill
1900
			/* escape username */
1901
			$safe_username = sqlite_escape_string($username);
1902 ac631bba lgcosta
1903
			/* encode password in Base64 just in case it contains commas */
1904
			$bpassword = base64_encode($password);
1905 5705c60a Renato Botelho
			$insertquery  = "INSERT INTO captiveportal (allow_time, pipeno, ip, mac, username, sessionid, bpassword, session_timeout, idle_timeout, session_terminate_time, interim_interval) ";
1906 1974c2d6 bcyrill
			$insertquery .= "VALUES ({$allow_time}, {$pipeno}, '{$clientip}', '{$clientmac}', '{$safe_username}', '{$sessionid}', '{$bpassword}', ";
1907 5705c60a Renato Botelho
			$insertquery .= "{$session_timeout}, {$idle_timeout}, {$session_terminate_time}, {$interim_interval})";
1908 ac631bba lgcosta
1909 26ee5aaf Ermal
			/* store information to database */
1910
			captiveportal_write_db($insertquery);
1911 006802ab Ermal
			unlock($cpdblck);
1912
1913 ebc0e4b6 Ermal
			if (isset($config['captiveportal'][$cpzone]['radacct_enable']) && !empty($radiusservers[$radiusctx])) {
1914 87e7fdea bcyrill
				$acct_val = RADIUS_ACCOUNTING_START($pipeno, $username, $sessionid, $radiusservers[$radiusctx], $clientip, $clientmac);
1915 ac631bba lgcosta
				if ($acct_val == 1)
1916
					captiveportal_logportalauth($username,$clientmac,$clientip,$type,"RADIUS ACCOUNTING FAILED");
1917
			}
1918
		}
1919 006802ab Ermal
	} else
1920
		unlock($cpdblck);
1921 ac631bba lgcosta
1922
	if ($writecfg == true)
1923
		write_config();
1924
1925
	/* redirect user to desired destination */
1926
	if (!empty($attributes['url_redirection']))
1927
		$my_redirurl = $attributes['url_redirection'];
1928 b4792bf8 Ermal
	else if (!empty($config['captiveportal'][$cpzone]['redirurl']))
1929
		$my_redirurl = $config['captiveportal'][$cpzone]['redirurl'];
1930 ac631bba lgcosta
	else
1931
		$my_redirurl = $redirurl;
1932
1933 b4792bf8 Ermal
	if(isset($config['captiveportal'][$cpzone]['logoutwin_enable']) && !$passthrumac) {
1934 de132ae3 bcyrill
		$ourhostname = portal_hostname_from_client_ip($clientip);
1935
		$protocol = (isset($config['captiveportal'][$cpzone]['httpslogin'])) ? 'https://' : 'http://';
1936
		$logouturl = "{$protocol}{$ourhostname}/";
1937 ac631bba lgcosta
1938
		if (isset($attributes['reply_message']))
1939
			$message = $attributes['reply_message'];
1940
		else
1941
			$message = 0;
1942
1943 b4792bf8 Ermal
		include("{$g['varetc_path']}/captiveportal-{$cpzone}-logout.html");
1944 ac631bba lgcosta
1945
	} else {
1946
		header("Location: " . $my_redirurl);
1947
	}
1948
1949
	return $sessionid;
1950
}
1951
1952
1953
/*
1954
 * Used for when pass-through credits are enabled.
1955
 * Returns true when there was at least one free login to deduct for the MAC.
1956
 * Expired entries are removed as they are seen.
1957
 * Active entries are updated according to the configuration.
1958
 */
1959
function portal_consume_passthrough_credit($clientmac) {
1960 b4792bf8 Ermal
	global $config, $cpzone;
1961 ac631bba lgcosta
1962 b4792bf8 Ermal
	if (!empty($config['captiveportal'][$cpzone]['freelogins_count']) && is_numeric($config['captiveportal'][$cpzone]['freelogins_count']))
1963
		$freeloginscount = $config['captiveportal'][$cpzone]['freelogins_count'];
1964 ac631bba lgcosta
	else
1965
		return false;
1966
1967 b4792bf8 Ermal
	if (!empty($config['captiveportal'][$cpzone]['freelogins_resettimeout']) && is_numeric($config['captiveportal'][$cpzone]['freelogins_resettimeout']))
1968
		$resettimeout = $config['captiveportal'][$cpzone]['freelogins_resettimeout'];
1969 ac631bba lgcosta
	else
1970
		return false;
1971
1972 17e7a243 Scott Ullrich
	if ($freeloginscount < 1 || $resettimeout <= 0 || !$clientmac)
1973 ac631bba lgcosta
		return false;
1974
1975 b4792bf8 Ermal
	$updatetimeouts = isset($config['captiveportal'][$cpzone]['freelogins_updatetimeouts']);
1976 ac631bba lgcosta
1977
	/*
1978
	 * Read database of used MACs.  Lines are a comma-separated list
1979
	 * of the time, MAC, then the count of pass-through credits remaining.
1980
	 */
1981
	$usedmacs = captiveportal_read_usedmacs_db();
1982
1983
	$currenttime = time();
1984
	$found = false;
1985
	foreach ($usedmacs as $key => $usedmac) {
1986
		$usedmac = explode(",", $usedmac);
1987
1988
		if ($usedmac[1] == $clientmac) {
1989
			if ($usedmac[0] + ($resettimeout * 3600) > $currenttime) {
1990
				if ($usedmac[2] < 1) {
1991
					if ($updatetimeouts) {
1992
						$usedmac[0] = $currenttime;
1993
						unset($usedmacs[$key]);
1994
						$usedmacs[] = implode(",", $usedmac);
1995
						captiveportal_write_usedmacs_db($usedmacs);
1996
					}
1997
1998
					return false;
1999
				} else {
2000
					$usedmac[2] -= 1;
2001
					$usedmacs[$key] = implode(",", $usedmac);
2002
				}
2003
2004
				$found = true;
2005
			} else
2006
				unset($usedmacs[$key]);
2007
2008
			break;
2009
		} else if ($usedmac[0] + ($resettimeout * 3600) <= $currenttime)
2010
				unset($usedmacs[$key]);
2011
	}
2012
2013
	if (!$found) {
2014
		$usedmac = array($currenttime, $clientmac, $freeloginscount - 1);
2015
		$usedmacs[] = implode(",", $usedmac);
2016
	}
2017
2018
	captiveportal_write_usedmacs_db($usedmacs);
2019
	return true;
2020
}
2021
2022
function captiveportal_read_usedmacs_db() {
2023 b4792bf8 Ermal
	global $g, $cpzone;
2024 ac631bba lgcosta
2025 b4792bf8 Ermal
	$cpumaclck = lock("captiveusedmacs{$cpzone}");
2026
	if (file_exists("{$g['vardb_path']}/captiveportal_usedmacs_{$cpzone}.db")) {
2027
		$usedmacs = file("{$g['vardb_path']}/captiveportal_usedmacs_{$cpzone}.db", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2028 da666ca8 Scott Ullrich
		if (!$usedmacs)
2029 ac631bba lgcosta
			$usedmacs = array();
2030
	} else
2031
		$usedmacs = array();
2032
2033
	unlock($cpumaclck);
2034
	return $usedmacs;
2035
}
2036
2037
function captiveportal_write_usedmacs_db($usedmacs) {
2038 b4792bf8 Ermal
	global $g, $cpzone;
2039 ac631bba lgcosta
2040 b4792bf8 Ermal
	$cpumaclck = lock("captiveusedmacs{$cpzone}", LOCK_EX);
2041
	@file_put_contents("{$g['vardb_path']}/captiveportal_usedmacs_{$cpzone}.db", implode("\n", $usedmacs));
2042 ac631bba lgcosta
	unlock($cpumaclck);
2043
}
2044
2045 62f20eab Michael Newton
function captiveportal_send_server_accounting($off = false) {
2046
	global $cpzone, $config;
2047
2048
	if (!isset($config['captiveportal'][$cpzone]['radacct_enable'])) {
2049
		return;
2050
	}
2051
	if ($off) {
2052
		$racct = new Auth_RADIUS_Acct_Off;
2053
	} else {
2054
		$racct = new Auth_RADIUS_Acct_On;
2055
	}
2056
	$radiusservers = captiveportal_get_radius_servers();
2057
	if (empty($radiusservers)) {
2058
		return;
2059
	}
2060
	foreach ($radiusservers['first'] as $radsrv) {
2061
		// Add a new server to our instance
2062
		$racct->addServer($radsrv['ipaddr'], $radsrv['acctport'], $radsrv['key']);
2063
	}
2064
	if (PEAR::isError($racct->start())) {
2065
		$retvalue['acct_val'] = 1;
2066
		$retvalue['error'] = $racct->getMessage();
2067
2068
		// If we encounter an error immediately stop this function and go back
2069
		$racct->close();
2070
		return $retvalue;
2071
	}
2072
	// Send request
2073
	$result = $racct->send();
2074
	// Evaluation of the response
2075
	// 5 -> Accounting-Response
2076
	// See RFC2866 for this.
2077
	if (PEAR::isError($result)) {
2078
		$retvalue['acct_val'] = 1;
2079
		$retvalue['error'] = $result->getMessage();
2080
	} else if ($result === true) {
2081
		$retvalue['acct_val'] = 5 ;
2082
	} else {
2083
		$retvalue['acct_val'] = 1 ;
2084
	}
2085
2086
	$racct->close();
2087
	return $retvalue;
2088
}
2089 64c0462b Ermal
?>