Project

General

Profile

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