Project

General

Profile

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