Project

General

Profile

Download (74.5 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 1444c08e Phil Davis
		/* Trigger a sync of the vouchers on config */
799 5ebe85e9 Ermal
		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 120acbae Ermal LUÇI
		/* Workaround for #46652 */
1166
		if ($ipfwoutput['packets'] > 0) { 
1167
			return $ipfwoutput['timestamp'];
1168 3ce84694 Ermal LUÇI
		} else {
1169
			return 0;
1170 120acbae Ermal LUÇI
		}
1171 d99f7864 Scott Ullrich
	}
1172 36254e4a Scott Ullrich
1173 d99f7864 Scott Ullrich
	return 0;
1174 5b237745 Scott Ullrich
}
1175
1176 d31bc32a Ermal
function captiveportal_init_radius_servers() {
1177 b4792bf8 Ermal
	global $config, $g, $cpzone;
1178 d31bc32a Ermal
1179
	/* generate radius server database */
1180 b4792bf8 Ermal
	if ($config['captiveportal'][$cpzone]['radiusip'] && (!isset($config['captiveportal'][$cpzone]['auth_method']) ||
1181
		($config['captiveportal'][$cpzone]['auth_method'] == "radius"))) {
1182
		$radiusip = $config['captiveportal'][$cpzone]['radiusip'];
1183
		$radiusip2 = ($config['captiveportal'][$cpzone]['radiusip2']) ? $config['captiveportal'][$cpzone]['radiusip2'] : null;
1184 7468a29f jim-p
		$radiusip3 = ($config['captiveportal'][$cpzone]['radiusip3']) ? $config['captiveportal'][$cpzone]['radiusip3'] : null;
1185
		$radiusip4 = ($config['captiveportal'][$cpzone]['radiusip4']) ? $config['captiveportal'][$cpzone]['radiusip4'] : null;
1186 d31bc32a Ermal
1187 b4792bf8 Ermal
		if ($config['captiveportal'][$cpzone]['radiusport'])
1188
			$radiusport = $config['captiveportal'][$cpzone]['radiusport'];
1189 d31bc32a Ermal
		else
1190
			$radiusport = 1812;
1191 b4792bf8 Ermal
		if ($config['captiveportal'][$cpzone]['radiusacctport'])
1192
			$radiusacctport = $config['captiveportal'][$cpzone]['radiusacctport'];
1193 d31bc32a Ermal
		else
1194
			$radiusacctport = 1813;
1195 b4792bf8 Ermal
		if ($config['captiveportal'][$cpzone]['radiusport2'])
1196
			$radiusport2 = $config['captiveportal'][$cpzone]['radiusport2'];
1197 d31bc32a Ermal
		else
1198
			$radiusport2 = 1812;
1199 7468a29f jim-p
		if ($config['captiveportal'][$cpzone]['radiusport3'])
1200
			$radiusport3 = $config['captiveportal'][$cpzone]['radiusport3'];
1201 ebc0e4b6 Ermal
		else
1202
			$radiusport3 = 1812;
1203 7468a29f jim-p
		if ($config['captiveportal'][$cpzone]['radiusport4'])
1204
			$radiusport4 = $config['captiveportal'][$cpzone]['radiusport4'];
1205 ebc0e4b6 Ermal
		else
1206
			$radiusport4 = 1812;
1207
1208 b4792bf8 Ermal
		$radiuskey = $config['captiveportal'][$cpzone]['radiuskey'];
1209 dbce0c2c bcyrill
		$radiuskey2 = $config['captiveportal'][$cpzone]['radiuskey2'];
1210
		$radiuskey3 = $config['captiveportal'][$cpzone]['radiuskey3'];
1211
		$radiuskey4 = $config['captiveportal'][$cpzone]['radiuskey4'];
1212 d31bc32a Ermal
1213 b4792bf8 Ermal
		$cprdsrvlck = lock("captiveportalradius{$cpzone}", LOCK_EX);
1214
		$fd = @fopen("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db", "w");
1215 d31bc32a Ermal
		if (!$fd) {
1216
			captiveportal_syslog("Error: cannot open radius DB file in captiveportal_configure().\n");
1217
			unlock($cprdsrvlck);
1218
			return 1;
1219 ebc0e4b6 Ermal
		}
1220 dbce0c2c bcyrill
		if (isset($radiusip))
1221 ebc0e4b6 Ermal
			fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey . ",first");
1222 dbce0c2c bcyrill
		if (isset($radiusip2))
1223 ebc0e4b6 Ermal
			fwrite($fd,"\n" . $radiusip2 . "," . $radiusport2 . "," . $radiusacctport . "," . $radiuskey2 . ",first");
1224 dbce0c2c bcyrill
		if (isset($radiusip3))
1225 ebc0e4b6 Ermal
			fwrite($fd,"\n" . $radiusip3 . "," . $radiusport3 . "," . $radiusacctport . "," . $radiuskey3 . ",second");
1226 dbce0c2c bcyrill
		if (isset($radiusip4))
1227 ebc0e4b6 Ermal
			fwrite($fd,"\n" . $radiusip4 . "," . $radiusport4 . "," . $radiusacctport . "," . $radiuskey4 . ",second");
1228
		
1229
1230 d31bc32a Ermal
		fclose($fd);
1231
		unlock($cprdsrvlck);
1232
	}
1233
}
1234
1235 5b237745 Scott Ullrich
/* read RADIUS servers into array */
1236
function captiveportal_get_radius_servers() {
1237 b4792bf8 Ermal
	global $g, $cpzone;
1238 ac07425a Ermal
1239 b4792bf8 Ermal
	$cprdsrvlck = lock("captiveportalradius{$cpzone}");
1240
	if (file_exists("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db")) {
1241 ac07425a Ermal
		$radiusservers = array();
1242 b4792bf8 Ermal
		$cpradiusdb = file("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db", 
1243 ac07425a Ermal
		FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
1244
		if ($cpradiusdb) {
1245
			foreach($cpradiusdb as $cpradiusentry) {
1246
				$line = trim($cpradiusentry);
1247
				if ($line) {
1248
					$radsrv = array();
1249 ebc0e4b6 Ermal
						list($radsrv['ipaddr'],$radsrv['port'],$radsrv['acctport'],$radsrv['key'], $context) = explode(",",$line);
1250
				}
1251
				if (empty($context)) {
1252
					if (!is_array($radiusservers['first']))
1253
						$radiusservers['first'] = array();
1254
					$radiusservers['first'] = $radsrv;
1255
				} else {
1256
					if (!is_array($radiusservers[$context]))
1257
						$radiusservers[$context] = array();
1258
					$radiusservers[$context][] = $radsrv;
1259 5060dea7 Scott Ullrich
				}
1260
			}
1261 2f70eac7 Ermal Lu?i
		}
1262 60b66b60 Ermal
		unlock($cprdsrvlck);
1263 ac07425a Ermal
		return $radiusservers;
1264
	}
1265
1266
	unlock($cprdsrvlck);
1267
	return false;
1268 5b237745 Scott Ullrich
}
1269
1270 3db19cf1 Scott Ullrich
/* log successful captive portal authentication to syslog */
1271
/* part of this code from php.net */
1272 0bd34ed6 Scott Ullrich
function captiveportal_logportalauth($user,$mac,$ip,$status, $message = null) {
1273 d99f7864 Scott Ullrich
	// Log it
1274
	if (!$message)
1275 12feed15 Ermal
		$message = "{$status}: {$user}, {$mac}, {$ip}";
1276 d31bc32a Ermal
	else {
1277
		$message = trim($message);
1278 12feed15 Ermal
		$message = "{$status}: {$user}, {$mac}, {$ip}, {$message}";
1279 d31bc32a Ermal
	}
1280 f56a73f1 Scott Ullrich
	captiveportal_syslog($message);
1281
}
1282
1283
/* log simple messages to syslog */
1284
function captiveportal_syslog($message) {
1285 12feed15 Ermal
	global $cpzone;
1286
1287 f56a73f1 Scott Ullrich
	$message = trim($message);
1288 9aec47b7 Chris Buechler
	$message = "Zone: {$cpzone} - {$message}";
1289 f56a73f1 Scott Ullrich
	openlog("logportalauth", LOG_PID, LOG_LOCAL4);
1290
	// Log it
1291
	syslog(LOG_INFO, $message);
1292 d99f7864 Scott Ullrich
	closelog();
1293 3db19cf1 Scott Ullrich
}
1294
1295 ebc0e4b6 Ermal
function radius($username,$password,$clientip,$clientmac,$type, $radiusctx = null) {
1296 baec2b00 Ermal
	global $g, $config, $cpzoneid;
1297 d44bccc7 Scott Ullrich
1298 aea56408 Ermal
	$pipeno = captiveportal_get_next_dn_ruleno();
1299 2f70eac7 Ermal Lu?i
1300 5060dea7 Scott Ullrich
	/* If the pool is empty, return appropriate message and fail authentication */
1301 0f50d70d Ermal
	if (empty($pipeno)) {
1302 5060dea7 Scott Ullrich
		$auth_list = array();
1303
		$auth_list['auth_val'] = 1;
1304
		$auth_list['error'] = "System reached maximum login capacity";
1305
		return $auth_list;
1306
	}
1307 9befcca7 Ermal Lu?i
1308 5060dea7 Scott Ullrich
	$radiusservers = captiveportal_get_radius_servers();
1309 d44bccc7 Scott Ullrich
1310 ebc0e4b6 Ermal
	if (is_null($radiusctx))
1311
		$radiusctx = 'first';
1312
1313 5060dea7 Scott Ullrich
	$auth_list = RADIUS_AUTHENTICATION($username,
1314
		$password,
1315 ebc0e4b6 Ermal
		$radiusservers[$radiusctx],
1316 5060dea7 Scott Ullrich
		$clientip,
1317
		$clientmac,
1318 aea56408 Ermal
		$pipeno);
1319 5060dea7 Scott Ullrich
1320
	if ($auth_list['auth_val'] == 2) {
1321
		captiveportal_logportalauth($username,$clientmac,$clientip,$type);
1322
		$sessionid = portal_allow($clientip,
1323
			$clientmac,
1324
			$username,
1325
			$password,
1326
			$auth_list,
1327 aea56408 Ermal
			$pipeno,
1328 ebc0e4b6 Ermal
			$radiusctx);
1329 d2c98878 falbertopl
	} else {
1330
	         captiveportal_free_dn_ruleno($pipeno);
1331
	       }
1332 0bd34ed6 Scott Ullrich
1333 5060dea7 Scott Ullrich
	return $auth_list;
1334 0bd34ed6 Scott Ullrich
}
1335
1336 26ee5aaf Ermal
function captiveportal_opendb() {
1337 b4792bf8 Ermal
	global $g, $cpzone;
1338 5060dea7 Scott Ullrich
1339 5cf91315 Renato Botelho
	$DB = new SQLite3("{$g['vardb_path']}/captiveportal{$cpzone}.db");
1340
	if (! $DB->exec("CREATE TABLE IF NOT EXISTS captiveportal (" .
1341
				"allow_time INTEGER, pipeno INTEGER, ip TEXT, mac TEXT, username TEXT, " .
1342
				"sessionid TEXT, bpassword TEXT, session_timeout INTEGER, idle_timeout INTEGER, " .
1343
				"session_terminate_time INTEGER, interim_interval INTEGER, radiusctx TEXT); " .
1344
			"CREATE UNIQUE INDEX IF NOT EXISTS idx_active ON captiveportal (sessionid, username); " .
1345
			"CREATE INDEX IF NOT EXISTS user ON captiveportal (username); " .
1346
			"CREATE INDEX IF NOT EXISTS ip ON captiveportal (ip); " .
1347
			"CREATE INDEX IF NOT EXISTS starttime ON captiveportal (allow_time)"))
1348
		captiveportal_syslog("Error during table {$cpzone} creation. Error message: {$DB->lastErrorMsg()}");
1349 26ee5aaf Ermal
1350
	return $DB;
1351 0bd34ed6 Scott Ullrich
}
1352
1353 26ee5aaf Ermal
/* read captive portal DB into array */
1354
function captiveportal_read_db($query = "") {
1355 5cf91315 Renato Botelho
	$cpdb = array();
1356 5060dea7 Scott Ullrich
1357 26ee5aaf Ermal
	$DB = captiveportal_opendb();
1358
	if ($DB) {
1359 5cf91315 Renato Botelho
		$response = $DB->query("SELECT * FROM captiveportal {$query}");
1360 d338018f Ermal
		if ($response != FALSE) {
1361
			while ($row = $response->fetchArray())
1362
				$cpdb[] = $row;
1363
		}
1364 5cf91315 Renato Botelho
		$DB->close();
1365 006802ab Ermal
	}
1366 26ee5aaf Ermal
1367
	return $cpdb;
1368
}
1369
1370
function captiveportal_remove_entries($remove) {
1371
1372
	if (!is_array($remove) || empty($remove))
1373
		return;
1374
1375 1974c2d6 bcyrill
	$query = "DELETE FROM captiveportal WHERE sessionid in (";
1376 be0a33ef bcyrill
	foreach($remove as $idx => $unindex) {
1377 26ee5aaf Ermal
		$query .= "'{$unindex}'";
1378
		if ($idx < (count($remove) - 1))
1379
			$query .= ",";
1380 006802ab Ermal
	}
1381 26ee5aaf Ermal
	$query .= ")";
1382
	captiveportal_write_db($query);
1383
}
1384
1385
/* write captive portal DB */
1386
function captiveportal_write_db($queries) {
1387
	global $g;
1388
1389
	if (is_array($queries))
1390
		$query = implode(";", $queries);
1391
	else
1392
		$query = $queries;
1393
1394
	$DB = captiveportal_opendb();
1395
	if ($DB) {
1396 5cf91315 Renato Botelho
		$DB->exec("BEGIN TRANSACTION");
1397
		$result = $DB->exec($query);
1398 26ee5aaf Ermal
		if (!$result)
1399 5cf91315 Renato Botelho
			captiveportal_syslog("Trying to modify DB returned error: {$DB->lastErrorMsg()}");
1400 26ee5aaf Ermal
		else
1401 5cf91315 Renato Botelho
			$DB->exec("END TRANSACTION");
1402
		$DB->close();
1403 26ee5aaf Ermal
		return $result;
1404
	} else
1405
		return true;
1406 0bd34ed6 Scott Ullrich
}
1407
1408
function captiveportal_write_elements() {
1409 b4792bf8 Ermal
	global $g, $config, $cpzone;
1410 5060dea7 Scott Ullrich
	
1411 b4792bf8 Ermal
	$cpcfg = $config['captiveportal'][$cpzone];
1412
1413 bdba4fa7 Ermal
	if (!is_dir($g['captiveportal_element_path']))
1414 769e254e Ermal
		@mkdir($g['captiveportal_element_path']);
1415
1416 b4792bf8 Ermal
	if (is_array($cpcfg['element'])) {
1417 1fadb31d Scott Ullrich
		conf_mount_rw();
1418 b4792bf8 Ermal
		foreach ($cpcfg['element'] as $data) {
1419 3760b867 Ermal
			if (!@file_put_contents("{$g['captiveportal_element_path']}/{$data['name']}", base64_decode($data['content']))) {
1420 fdc55311 Carlos Eduardo Ramos
				printf(gettext("Error: cannot open '%s' in captiveportal_write_elements()%s"), $data['name'], "\n");
1421 1fadb31d Scott Ullrich
				return 1;
1422
			}
1423 bdba4fa7 Ermal
			if (!file_exists("{$g['captiveportal_path']}/{$data['name']}"))
1424
				@symlink("{$g['captiveportal_element_path']}/{$data['name']}", "{$g['captiveportal_path']}/{$data['name']}");
1425 1fadb31d Scott Ullrich
		}
1426
		conf_mount_ro();
1427
	}
1428 5060dea7 Scott Ullrich
	
1429 769e254e Ermal
	return 0;
1430 0bd34ed6 Scott Ullrich
}
1431
1432 7fb23399 Ermal
function captiveportal_free_dnrules($rulenos_start = 2000, $rulenos_range_max = 64500) {
1433
	global $cpzone;
1434
1435
	$cpruleslck = lock("captiveportalrulesdn", LOCK_EX);
1436
	if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) {
1437
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules"));
1438 1f965b69 Ermal
		$ridx = $rulenos_start;
1439
		while ($ridx < $rulenos_range_max) {
1440 7fb23399 Ermal
			if ($rules[$ridx] == $cpzone) {
1441 5b0f7191 Ermal Luçi
				$rules[$ridx] = false;
1442 7fb23399 Ermal
				$ridx++;
1443 5b0f7191 Ermal Luçi
				$rules[$ridx] = false;
1444 1f965b69 Ermal
				$ridx++;
1445
			} else
1446
				$ridx += 2;
1447 7fb23399 Ermal
		}
1448 1f965b69 Ermal
		file_put_contents("{$g['vardb_path']}/captiveportaldn.rules", serialize($rules));
1449
		unset($rules);
1450 7fb23399 Ermal
	}
1451
	unlock($cpruleslck);
1452
}
1453
1454 aea56408 Ermal
function captiveportal_get_next_dn_ruleno($rulenos_start = 2000, $rulenos_range_max = 64500) {
1455 7fb23399 Ermal
	global $config, $g, $cpzone;
1456 6ce61a8f Ermal
1457 aea56408 Ermal
	$cpruleslck = lock("captiveportalrulesdn", LOCK_EX);
1458
	$ruleno = 0;
1459
	if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) {
1460
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules"));
1461 1f965b69 Ermal
		$ridx = $rulenos_start;
1462
		while ($ridx < $rulenos_range_max) {
1463 f38b383b Ermal
			if (empty($rules[$ridx])) {
1464 1f965b69 Ermal
				$ruleno = $ridx;
1465
				$rules[$ridx] = $cpzone;
1466 aea56408 Ermal
				$ridx++;
1467 1f965b69 Ermal
				$rules[$ridx] = $cpzone;
1468
				break;
1469 21f82ab6 Ermal
			} else {
1470
				$ridx += 2;
1471 aea56408 Ermal
			}
1472
		}
1473
	} else {
1474 fe3693cb Ermal
		$rules = array_pad(array(), $rulenos_range_max, false);
1475 d2c98878 falbertopl
		$ruleno = $rulenos_start;
1476 7fb23399 Ermal
		$rules[$rulenos_start] = $cpzone;
1477 1f965b69 Ermal
		$rulenos_start++;
1478
		$rules[$rulenos_start] = $cpzone;
1479 aea56408 Ermal
	}
1480
	file_put_contents("{$g['vardb_path']}/captiveportaldn.rules", serialize($rules));
1481
	unlock($cpruleslck);
1482 1f965b69 Ermal
	unset($rules);
1483 aea56408 Ermal
1484
	return $ruleno;
1485
}
1486
1487
function captiveportal_free_dn_ruleno($ruleno) {
1488 87e7fdea bcyrill
	global $config, $g;
1489
1490
	$cpruleslck = lock("captiveportalrulesdn", LOCK_EX);
1491
	if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) {
1492
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules"));
1493
		$rules[$ruleno] = false;
1494 a2a42c72 Ermal
		$ruleno++;
1495
		$rules[$ruleno] = false;
1496 87e7fdea bcyrill
		file_put_contents("{$g['vardb_path']}/captiveportaldn.rules", serialize($rules));
1497 1f965b69 Ermal
		unset($rules);
1498 87e7fdea bcyrill
	}
1499
	unlock($cpruleslck);
1500 aea56408 Ermal
}
1501
1502
function captiveportal_get_dn_passthru_ruleno($value) {
1503 baec2b00 Ermal
	global $config, $g, $cpzone, $cpzoneid;
1504 b273dd26 Ermal
1505
	$cpcfg = $config['captiveportal'][$cpzone];
1506
	if(!isset($cpcfg['enable']))
1507
		return NULL;
1508 aea56408 Ermal
1509 fe7e987e Ermal
	$cpruleslck = lock("captiveportalrulesdn", LOCK_EX);
1510 1f965b69 Ermal
	$ruleno = NULL;
1511 fe7e987e Ermal
	if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) {
1512
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules"));
1513 1f965b69 Ermal
		unset($output);
1514 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);
1515 1f965b69 Ermal
		$ruleno = intval($output[0]);
1516
		if (!$rules[$ruleno])
1517
			$ruleno = NULL;
1518
		unset($rules);
1519 aea56408 Ermal
	}
1520
	unlock($cpruleslck);
1521 1f965b69 Ermal
1522
	return $ruleno;
1523 6ce61a8f Ermal
}
1524
1525 920cafaf Scott Ullrich
/*
1526
 * This function will calculate the lowest free firewall ruleno
1527 f9f71ad3 Ermal Lu?i
 * within the range specified based on the actual logged on users
1528 920cafaf Scott Ullrich
 *
1529
 */
1530 aea56408 Ermal
function captiveportal_get_next_ipfw_ruleno($rulenos_start = 2, $rulenos_range_max = 64500) {
1531 b4792bf8 Ermal
	global $config, $g, $cpzone;
1532 6ce61a8f Ermal
1533 b4792bf8 Ermal
	$cpcfg = $config['captiveportal'][$cpzone];
1534
	if(!isset($cpcfg['enable']))
1535 01d57b8c Scott Ullrich
		return NULL;
1536 6ce61a8f Ermal
1537 b4792bf8 Ermal
	$cpruleslck = lock("captiveportalrules{$cpzone}", LOCK_EX);
1538 f9f71ad3 Ermal Lu?i
	$ruleno = 0;
1539 b4792bf8 Ermal
	if (file_exists("{$g['vardb_path']}/captiveportal_{$cpzone}.rules")) {
1540
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal_{$cpzone}.rules"));
1541 1f965b69 Ermal
		$ridx = $rulenos_start;
1542
		while ($ridx < $rulenos_range_max) {
1543 21f82ab6 Ermal
			if (empty($rules[$ridx])) {
1544 1f965b69 Ermal
				$ruleno = $ridx;
1545
				$rules[$ridx] = $cpzone;
1546 10b9dfcf Ermal
				$ridx++;
1547 1f965b69 Ermal
				$rules[$ridx] = $cpzone;
1548
				break;
1549 21f82ab6 Ermal
			} else {
1550
				/* 
1551
				 * This allows our traffic shaping pipes to be the in pipe the same as ruleno 
1552
				 * and the out pipe ruleno + 1.
1553
				 */
1554
				$ridx += 2;
1555 6ce61a8f Ermal
			}
1556
		}
1557
	} else {
1558 fe3693cb Ermal
		$rules = array_pad(array(), $rulenos_range_max, false);
1559 1f965b69 Ermal
		$ruleno = $rulenos_start;
1560
		$rules[$rulenos_start] = $cpzone;
1561
		$rulenos_start++;
1562
		$rules[$rulenos_start] = $cpzone;
1563 6ce61a8f Ermal
	}
1564 b4792bf8 Ermal
	file_put_contents("{$g['vardb_path']}/captiveportal_{$cpzone}.rules", serialize($rules));
1565 d31bc32a Ermal
	unlock($cpruleslck);
1566 1f965b69 Ermal
	unset($rules);
1567
1568 6ce61a8f Ermal
	return $ruleno;
1569
}
1570
1571 a413e743 Renato Botelho
function captiveportal_free_ipfw_ruleno($ruleno) {
1572 b4792bf8 Ermal
	global $config, $g, $cpzone;
1573 6ce61a8f Ermal
1574 b4792bf8 Ermal
	$cpcfg = $config['captiveportal'][$cpzone];
1575
	if(!isset($cpcfg['enable']))
1576 6ce61a8f Ermal
		return NULL;
1577
1578 b4792bf8 Ermal
	$cpruleslck = lock("captiveportalrules{$cpzone}", LOCK_EX);
1579
	if (file_exists("{$g['vardb_path']}/captiveportal_{$cpzone}.rules")) {
1580
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal_{$cpzone}.rules"));
1581 6ce61a8f Ermal
		$rules[$ruleno] = false;
1582 1f965b69 Ermal
		$ruleno++;
1583
		$rules[$ruleno] = false;
1584 b4792bf8 Ermal
		file_put_contents("{$g['vardb_path']}/captiveportal_{$cpzone}.rules", serialize($rules));
1585 1f965b69 Ermal
		unset($rules);
1586 f9f71ad3 Ermal Lu?i
	}
1587 d31bc32a Ermal
	unlock($cpruleslck);
1588 6ce61a8f Ermal
}
1589
1590 d5ae560d Ermal
function captiveportal_get_ipfw_passthru_ruleno($value) {
1591 baec2b00 Ermal
	global $config, $g, $cpzone, $cpzoneid;
1592 6ce61a8f Ermal
1593 b4792bf8 Ermal
	$cpcfg = $config['captiveportal'][$cpzone];
1594
	if(!isset($cpcfg['enable']))
1595
		return NULL;
1596 6ce61a8f Ermal
1597 b4792bf8 Ermal
	$cpruleslck = lock("captiveportalrules{$cpzone}", LOCK_EX);
1598 1f965b69 Ermal
	$ruleno = NULL;
1599 b4792bf8 Ermal
	if (file_exists("{$g['vardb_path']}/captiveportal_{$cpzone}.rules")) {
1600
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal_{$cpzone}.rules"));
1601 1f965b69 Ermal
		unset($output);
1602 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);
1603 1f965b69 Ermal
		$ruleno = intval($output[0]);
1604
		if (!$rules[$ruleno])
1605
			$ruleno = NULL;
1606
		unset($rules);
1607 5060dea7 Scott Ullrich
	}
1608 d31bc32a Ermal
	unlock($cpruleslck);
1609 1f965b69 Ermal
1610
	return $ruleno;
1611 920cafaf Scott Ullrich
}
1612
1613 360d815d Scott Ullrich
/**
1614
 * This function will calculate the traffic produced by a client
1615
 * based on its firewall rule
1616
 *
1617
 * Point of view: NAS
1618
 *
1619
 * Input means: from the client
1620
 * Output means: to the client
1621
 *
1622
 */
1623
1624 fcaf1709 Ermal
function getVolume($ip, $mac = NULL) {
1625 27c2e32e Renato Botelho
	global $config, $cpzone, $cpzoneid;
1626 360d815d Scott Ullrich
1627 f48abba2 Michael Newton
	$reverse = empty($config['captiveportal'][$cpzone]['reverseacct']) ? false : true;
1628 5060dea7 Scott Ullrich
	$volume = array();
1629
	// Initialize vars properly, since we don't want NULL vars
1630
	$volume['input_pkts'] = $volume['input_bytes'] = $volume['output_pkts'] = $volume['output_bytes'] = 0 ;
1631 360d815d Scott Ullrich
1632 553803f4 Ermal LUÇI
	$ipfw = pfSense_ipfw_getTablestats($cpzoneid, IP_FW_TABLE_XLISTENTRY, 1, $ip, $mac);
1633 1272429c Ermal
	if (is_array($ipfw)) {
1634 f48abba2 Michael Newton
		if ($reverse) {
1635
			$volume['output_pkts'] = $ipfw['packets'];
1636
			$volume['output_bytes'] = $ipfw['bytes'];
1637
		}
1638
		else {
1639
			$volume['input_pkts'] = $ipfw['packets'];
1640
			$volume['input_bytes'] = $ipfw['bytes'];
1641
		}
1642 5060dea7 Scott Ullrich
	}
1643 f9f71ad3 Ermal Lu?i
1644 553803f4 Ermal LUÇI
	$ipfw = pfSense_ipfw_getTablestats($cpzoneid, IP_FW_TABLE_XLISTENTRY, 2, $ip, $mac);
1645 1272429c Ermal
	if (is_array($ipfw)) {
1646 f48abba2 Michael Newton
		if ($reverse) {
1647
			$volume['input_pkts'] = $ipfw['packets'];
1648
			$volume['input_bytes'] = $ipfw['bytes'];
1649
		}
1650
		else {
1651
			$volume['output_pkts'] = $ipfw['packets'];
1652
			$volume['output_bytes'] = $ipfw['bytes'];
1653
		}
1654 5060dea7 Scott Ullrich
	}
1655 360d815d Scott Ullrich
1656 5060dea7 Scott Ullrich
	return $volume;
1657 360d815d Scott Ullrich
}
1658
1659 856e58a6 Scott Ullrich
/**
1660
 * Get the NAS-IP-Address based on the current wan address
1661
 *
1662
 * Use functions in interfaces.inc to find this out
1663
 *
1664
 */
1665
1666
function getNasIP()
1667
{
1668 b4792bf8 Ermal
	global $config, $cpzone;
1669 64c0462b Ermal
1670 b4792bf8 Ermal
	if (empty($config['captiveportal'][$cpzone]['radiussrcip_attribute'])) {
1671 5060dea7 Scott Ullrich
			$nasIp = get_interface_ip();
1672
	} else {
1673 b4792bf8 Ermal
		if (is_ipaddr($config['captiveportal'][$cpzone]['radiussrcip_attribute']))
1674
			$nasIp = $config['captiveportal'][$cpzone]['radiussrcip_attribute'];
1675 5060dea7 Scott Ullrich
		else
1676 b4792bf8 Ermal
			$nasIp = get_interface_ip($config['captiveportal'][$cpzone]['radiussrcip_attribute']);
1677 36ff7f81 Ermal
	}
1678 64c0462b Ermal
		
1679 5060dea7 Scott Ullrich
	if(!is_ipaddr($nasIp))
1680
		$nasIp = "0.0.0.0";
1681 64c0462b Ermal
1682
	return $nasIp;
1683 856e58a6 Scott Ullrich
}
1684
1685 f8b11310 Ermal Lu?i
function portal_ip_from_client_ip($cliip) {
1686 b4792bf8 Ermal
	global $config, $cpzone;
1687 f8b11310 Ermal Lu?i
1688 45bef774 bcyrill
	$isipv6 = is_ipaddrv6($cliip);
1689 b4792bf8 Ermal
	$interfaces = explode(",", $config['captiveportal'][$cpzone]['interface']);
1690 f8b11310 Ermal Lu?i
	foreach ($interfaces as $cpif) {
1691 45bef774 bcyrill
		if ($isipv6) {
1692
			$ip = get_interface_ipv6($cpif);
1693
			$sn = get_interface_subnetv6($cpif);
1694
		} else {
1695
			$ip = get_interface_ip($cpif);
1696
			$sn = get_interface_subnet($cpif);
1697
		}
1698 f8b11310 Ermal Lu?i
		if (ip_in_subnet($cliip, "{$ip}/{$sn}"))
1699
			return $ip;
1700
	}
1701
1702 45bef774 bcyrill
	$inet = ($isipv6) ? '-inet6' : '-inet';
1703
	$iface = exec_command("/sbin/route -n get {$inet} {$cliip} | /usr/bin/awk '/interface/ { print \$2; };'");
1704 f86fa91c jim-p
	$iface = trim($iface, "\n");
1705
	if (!empty($iface)) {
1706 45bef774 bcyrill
		$ip = ($isipv6) ? find_interface_ipv6($iface) : find_interface_ip($iface);
1707 f86fa91c jim-p
		if (is_ipaddr($ip))
1708
			return $ip;
1709
	}
1710
1711 cc125e13 Chris Buechler
	// doesn't match up to any particular interface
1712
	// so let's set the portal IP to what PHP says 
1713
	// the server IP issuing the request is. 
1714
	// allows same behavior as 1.2.x where IP isn't 
1715
	// in the subnet of any CP interface (static routes, etc.)
1716
	// rather than forcing to DNS hostname resolution
1717
	$ip = $_SERVER['SERVER_ADDR'];
1718
	if (is_ipaddr($ip))
1719
		return $ip;
1720
1721 f8b11310 Ermal Lu?i
	return false;
1722
}
1723
1724 de132ae3 bcyrill
function portal_hostname_from_client_ip($cliip) {
1725
	global $config, $cpzone;
1726
1727
	$cpcfg = $config['captiveportal'][$cpzone];
1728
1729
	if (isset($cpcfg['httpslogin'])) {
1730 4320083f Renato Botelho
		$listenporthttps = $cpcfg['listenporthttps'] ? $cpcfg['listenporthttps'] : ($cpcfg['zoneid'] + 8001);
1731 de132ae3 bcyrill
		$ourhostname = $cpcfg['httpsname'];
1732
		
1733
		if ($listenporthttps != 443)
1734
			$ourhostname .= ":" . $listenporthttps;
1735
	} else {
1736 4320083f Renato Botelho
		$listenporthttp  = $cpcfg['listenporthttp']  ? $cpcfg['listenporthttp']  : ($cpcfg['zoneid'] + 8000);
1737 de132ae3 bcyrill
		$ifip = portal_ip_from_client_ip($cliip);
1738
		if (!$ifip)
1739
			$ourhostname = "{$config['system']['hostname']}.{$config['system']['domain']}";
1740
		else
1741
			$ourhostname = (is_ipaddrv6($ifip)) ? "[{$ifip}]" : "{$ifip}";
1742
		
1743
		if ($listenporthttp != 80)
1744
			$ourhostname .= ":" . $listenporthttp;
1745
	}
1746 d5ac388b bcyrill
	
1747
	return $ourhostname;
1748 de132ae3 bcyrill
}
1749
1750 ac631bba lgcosta
/* functions move from index.php */
1751
1752
function portal_reply_page($redirurl, $type = null, $message = null, $clientmac = null, $clientip = null, $username = null, $password = null) {
1753 b4792bf8 Ermal
	global $g, $config, $cpzone;
1754 ac631bba lgcosta
1755
	/* Get captive portal layout */
1756
	if ($type == "redir") {
1757
		header("Location: {$redirurl}");
1758
		return;
1759
	} else if ($type == "login")
1760 b4792bf8 Ermal
		$htmltext = get_include_contents("{$g['varetc_path']}/captiveportal_{$cpzone}.html");
1761 ac631bba lgcosta
	else
1762 b4792bf8 Ermal
		$htmltext = get_include_contents("{$g['varetc_path']}/captiveportal-{$cpzone}-error.html");
1763
1764
	$cpcfg = $config['captiveportal'][$cpzone];
1765 ac631bba lgcosta
1766
	/* substitute the PORTAL_REDIRURL variable */
1767 de132ae3 bcyrill
	if ($cpcfg['preauthurl']) {
1768
		$htmltext = str_replace("\$PORTAL_REDIRURL\$", "{$cpcfg['preauthurl']}", $htmltext);
1769
		$htmltext = str_replace("#PORTAL_REDIRURL#", "{$cpcfg['preauthurl']}", $htmltext);
1770 ac631bba lgcosta
	}
1771
1772
	/* substitute other variables */
1773 de132ae3 bcyrill
	$ourhostname = portal_hostname_from_client_ip($clientip);
1774
	$protocol = (isset($cpcfg['httpslogin'])) ? 'https://' : 'http://';
1775
	$htmltext = str_replace("\$PORTAL_ACTION\$", "{$protocol}{$ourhostname}/", $htmltext);
1776
	$htmltext = str_replace("#PORTAL_ACTION#", "{$protocol}{$ourhostname}/", $htmltext);
1777 ac631bba lgcosta
1778 b4792bf8 Ermal
	$htmltext = str_replace("\$PORTAL_ZONE\$", htmlspecialchars($cpzone), $htmltext);
1779 ac631bba lgcosta
	$htmltext = str_replace("\$PORTAL_REDIRURL\$", htmlspecialchars($redirurl), $htmltext);
1780
	$htmltext = str_replace("\$PORTAL_MESSAGE\$", htmlspecialchars($message), $htmltext);
1781
	$htmltext = str_replace("\$CLIENT_MAC\$", htmlspecialchars($clientmac), $htmltext);
1782
	$htmltext = str_replace("\$CLIENT_IP\$", htmlspecialchars($clientip), $htmltext);
1783
1784
	// Special handling case for captive portal master page so that it can be ran 
1785
	// through the PHP interpreter using the include method above.  We convert the
1786
	// $VARIABLE$ case to #VARIABLE# in /etc/inc/captiveportal.inc before writing out.
1787 b4792bf8 Ermal
	$htmltext = str_replace("#PORTAL_ZONE#", htmlspecialchars($cpzone), $htmltext);
1788 ac631bba lgcosta
	$htmltext = str_replace("#PORTAL_REDIRURL#", htmlspecialchars($redirurl), $htmltext);
1789
	$htmltext = str_replace("#PORTAL_MESSAGE#", htmlspecialchars($message), $htmltext);
1790
	$htmltext = str_replace("#CLIENT_MAC#", htmlspecialchars($clientmac), $htmltext);
1791
	$htmltext = str_replace("#CLIENT_IP#", htmlspecialchars($clientip), $htmltext);
1792
	$htmltext = str_replace("#USERNAME#", htmlspecialchars($username), $htmltext);
1793
	$htmltext = str_replace("#PASSWORD#", htmlspecialchars($password), $htmltext);
1794
1795 87e7fdea bcyrill
	echo $htmltext;
1796 ac631bba lgcosta
}
1797
1798
function portal_mac_radius($clientmac,$clientip) {
1799 87e7fdea bcyrill
	global $config, $cpzone;
1800 ac631bba lgcosta
1801 87e7fdea bcyrill
	$radmac_secret = $config['captiveportal'][$cpzone]['radmac_secret'];
1802 ac631bba lgcosta
1803 87e7fdea bcyrill
	/* authentication against the radius server */
1804
	$username = mac_format($clientmac);
1805
	$auth_list = radius($username,$radmac_secret,$clientip,$clientmac,"MACHINE LOGIN");
1806
	if ($auth_list['auth_val'] == 2)
1807
		return TRUE;
1808 ac631bba lgcosta
1809 87e7fdea bcyrill
	if (!empty($auth_list['url_redirection']))
1810
		portal_reply_page($auth_list['url_redirection'], "redir");
1811
1812
	return FALSE;
1813 ac631bba lgcosta
}
1814
1815 aec0f2fd Ermal
function captiveportal_reapply_attributes($cpentry, $attributes) {
1816 2ec063f9 Warren Baker
	global $config, $cpzone, $g;
1817 87e7fdea bcyrill
1818 1eff6ee0 Ermal LUÇI
	if (isset($config['captiveportal'][$cpzone]['peruserbw'])) {
1819 64ed3e60 Ermal LUÇI
		$dwfaultbw_up = !empty($config['captiveportal'][$cpzone]['bwdefaultup']) ? $config['captiveportal'][$cpzone]['bwdefaultup'] : 0;
1820
		$dwfaultbw_down = !empty($config['captiveportal'][$cpzone]['bwdefaultdn']) ? $config['captiveportal'][$cpzone]['bwdefaultdn'] : 0;
1821 1eff6ee0 Ermal LUÇI
	} else
1822
		$dwfaultbw_up = $dwfaultbw_down = 0;
1823 64ed3e60 Ermal LUÇI
	$bw_up = !empty($attributes['bw_up']) ? round(intval($attributes['bw_up'])/1000, 2) : $dwfaultbw_up;
1824
	$bw_down = !empty($attributes['bw_down']) ? round(intval($attributes['bw_down'])/1000, 2) : $dwfaultbw_down;
1825 5705c60a Renato Botelho
	$bw_up_pipeno = $cpentry[1];
1826
	$bw_down_pipeno = $cpentry[1]+1;
1827 aec0f2fd Ermal
1828 c2e2d133 Ermal
	$_gb = @pfSense_pipe_action("pipe {$bw_up_pipeno} config bw {$bw_up}Kbit/s queue 100 buckets 16");
1829
	$_gb = @pfSense_pipe_action("pipe {$bw_down_pipeno} config bw {$bw_down}Kbit/s queue 100 buckets 16");
1830 10b9dfcf Ermal
	//captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "RADIUS_BANDWIDTH_REAPPLY", "{$bw_up}/{$bw_down}");
1831 aec0f2fd Ermal
1832 87e7fdea bcyrill
	unset($bw_up_pipeno, $bw_down_pipeno, $bw_up, $bw_down);
1833 aec0f2fd Ermal
}
1834
1835 aea56408 Ermal
function portal_allow($clientip,$clientmac,$username,$password = null, $attributes = null, $pipeno = null, $radiusctx = null)  {
1836 baec2b00 Ermal
	global $redirurl, $g, $config, $type, $passthrumac, $_POST, $cpzone, $cpzoneid;
1837 ac631bba lgcosta
1838
	// Ensure we create an array if we are missing attributes
1839
	if (!is_array($attributes))
1840
		$attributes = array();
1841
1842 26ee5aaf Ermal
	unset($sessionid);
1843 ac631bba lgcosta
1844 006802ab Ermal
	/* Do not allow concurrent login execution. */
1845 b4792bf8 Ermal
	$cpdblck = lock("captiveportaldb{$cpzone}", LOCK_EX);
1846 006802ab Ermal
1847 ac631bba lgcosta
	if ($attributes['voucher'])
1848
		$remaining_time = $attributes['session_timeout'];
1849
1850
	$writecfg = false;
1851
	/* Find an existing session */
1852 b4792bf8 Ermal
	if ((isset($config['captiveportal'][$cpzone]['noconcurrentlogins'])) && $passthrumac) {
1853
		if (isset($config['captiveportal'][$cpzone]['passthrumacadd'])) {
1854 ac631bba lgcosta
			$mac = captiveportal_passthrumac_findbyname($username);
1855
			if (!empty($mac)) {
1856
				if ($_POST['replacemacpassthru']) {
1857 b4792bf8 Ermal
					foreach ($config['captiveportal'][$cpzone]['passthrumac'] as $idx => $macent) {
1858 ac631bba lgcosta
						if ($macent['mac'] == $mac['mac']) {
1859
							$macrules = "";
1860
							$ruleno = captiveportal_get_ipfw_passthru_ruleno($mac['mac']);
1861 aea56408 Ermal
							$pipeno = captiveportal_get_dn_passthru_ruleno($mac['mac']);
1862 87e7fdea bcyrill
							if ($ruleno) {
1863 aea56408 Ermal
								captiveportal_free_ipfw_ruleno($ruleno);
1864 87e7fdea bcyrill
								$macrules .= "delete {$ruleno}\n";
1865 ac631bba lgcosta
								++$ruleno;
1866 87e7fdea bcyrill
								$macrules .= "delete {$ruleno}\n";
1867
							}
1868 aea56408 Ermal
							if ($pipeno) {
1869
								captiveportal_free_dn_ruleno($pipeno);
1870 87e7fdea bcyrill
								$macrules .= "pipe delete {$pipeno}\n";
1871 aea56408 Ermal
								++$pipeno;
1872 87e7fdea bcyrill
								$macrules .= "pipe delete {$pipeno}\n";
1873 aea56408 Ermal
							}
1874 b4792bf8 Ermal
							unset($config['captiveportal'][$cpzone]['passthrumac'][$idx]);
1875 6b1cb3fd Renato Botelho
							$mac['action'] = 'pass';
1876 ac631bba lgcosta
							$mac['mac'] = $clientmac;
1877 b4792bf8 Ermal
							$config['captiveportal'][$cpzone]['passthrumac'][] = $mac;
1878 ac631bba lgcosta
							$macrules .= captiveportal_passthrumac_configure_entry($mac);
1879 b4792bf8 Ermal
							file_put_contents("{$g['tmp_path']}/macentry_{$cpzone}.rules.tmp", $macrules);
1880 baec2b00 Ermal
							mwexec("/sbin/ipfw -x {$cpzoneid} -q {$g['tmp_path']}/macentry_{$cpzone}.rules.tmp");
1881 ac631bba lgcosta
							$writecfg = true;
1882
							$sessionid = true;
1883
							break;
1884
						}
1885
					}
1886 87e7fdea bcyrill
				} else {
1887 ac631bba lgcosta
					portal_reply_page($redirurl, "error", "Username: {$username} is already authenticated using another MAC address.",
1888
						$clientmac, $clientip, $username, $password);
1889 ee79fcda Ermal
					unlock($cpdblck);
1890 aea56408 Ermal
					return;
1891 ac631bba lgcosta
				}
1892
			}
1893
		}
1894
	}
1895
1896 26ee5aaf Ermal
	/* read in client database */
1897
	$query = "WHERE ip = '{$clientip}'";
1898
	$tmpusername = strtolower($username);
1899 4586abb7 bcyrill
	if (isset($config['captiveportal'][$cpzone]['noconcurrentlogins']))
1900 26ee5aaf Ermal
		$query .= " OR (username != 'unauthenticated' AND lower(username) = '{$tmpusername}')";
1901
	$cpdb = captiveportal_read_db($query);
1902
1903 ebc0e4b6 Ermal
	/* Snapshot the timestamp */
1904 b09c2d86 Ermal
	$allow_time = time();
1905 26ee5aaf Ermal
	$radiusservers = captiveportal_get_radius_servers();
1906
	$unsetindexes = array();
1907 ebc0e4b6 Ermal
	if (is_null($radiusctx))
1908
		$radiusctx = 'first';
1909 26ee5aaf Ermal
1910
	foreach ($cpdb as $cpentry) {
1911 74a40221 Ermal
		if (empty($cpentry[11])) {
1912
			$cpentry[11] = 'first';
1913
		}
1914 ac631bba lgcosta
		/* on the same ip */
1915 5705c60a Renato Botelho
		if ($cpentry[2] == $clientip) {
1916
			if (isset($config['captiveportal'][$cpzone]['nomacfilter']) || $cpentry[3] == $clientmac)
1917
				captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - REUSING OLD SESSION");
1918 2890cae5 Ermal
			else
1919 5705c60a Renato Botelho
				captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - REUSING IP {$cpentry[2]} WITH DIFFERENT MAC ADDRESS {$cpentry[3]}");
1920
			$sessionid = $cpentry[5];
1921 ac631bba lgcosta
			break;
1922
		}
1923 5705c60a Renato Botelho
		elseif (($attributes['voucher']) && ($username != 'unauthenticated') && ($cpentry[4] == $username)) {
1924 ac631bba lgcosta
			// user logged in with an active voucher. Check for how long and calculate 
1925
			// how much time we can give him (voucher credit - used time)
1926 5705c60a Renato Botelho
			$remaining_time = $cpentry[0] + $cpentry[7] - $allow_time;
1927
			if ($remaining_time < 0)    // just in case. 
1928 ac631bba lgcosta
				$remaining_time = 0;
1929
1930
			/* This user was already logged in so we disconnect the old one */
1931 74a40221 Ermal
			captiveportal_disconnect($cpentry,$radiusservers[$cpentry[11]],13);
1932 5705c60a Renato Botelho
			captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - TERMINATING OLD SESSION");
1933
			$unsetindexes[] = $cpentry[5];
1934 ac631bba lgcosta
			break;
1935
		}
1936 b4792bf8 Ermal
		elseif ((isset($config['captiveportal'][$cpzone]['noconcurrentlogins'])) && ($username != 'unauthenticated')) {
1937 ac631bba lgcosta
			/* on the same username */
1938 5705c60a Renato Botelho
			if (strcasecmp($cpentry[4], $username) == 0) {
1939 ac631bba lgcosta
				/* This user was already logged in so we disconnect the old one */
1940 74a40221 Ermal
				captiveportal_disconnect($cpentry,$radiusservers[$cpentry[11]],13);
1941 5705c60a Renato Botelho
				captiveportal_logportalauth($cpentry[4],$cpentry[3],$cpentry[2],"CONCURRENT LOGIN - TERMINATING OLD SESSION");
1942
				$unsetindexes[] = $cpentry[5];
1943 ac631bba lgcosta
				break;
1944
			}
1945
		}
1946
	}
1947 f32eae2d Ermal
	unset($cpdb);
1948 ac631bba lgcosta
1949 26ee5aaf Ermal
	if (!empty($unsetindexes))
1950
		captiveportal_remove_entries($unsetindexes);
1951
1952 ac631bba lgcosta
	if ($attributes['voucher'] && $remaining_time <= 0)
1953
		return 0;       // voucher already used and no time left
1954
1955
	if (!isset($sessionid)) {
1956
		/* generate unique session ID */
1957
		$tod = gettimeofday();
1958
		$sessionid = substr(md5(mt_rand() . $tod['sec'] . $tod['usec'] . $clientip . $clientmac), 0, 16);
1959
1960
		if ($passthrumac) {
1961
			$mac = array();
1962 6ffb064f Renato Botelho
			$mac['action'] = 'pass';
1963 ac631bba lgcosta
			$mac['mac'] = $clientmac;
1964 522f1cc7 Ermal
			$mac['ip'] = $clientip; /* Used only for logging */
1965
			if (isset($config['captiveportal'][$cpzone]['passthrumacaddusername'])) {
1966 ac631bba lgcosta
				$mac['username'] = $username;
1967 522f1cc7 Ermal
				if ($attributes['voucher'])
1968
					$mac['logintype'] = "voucher";
1969
			}
1970 580f4f3f Phil Davis
			if ($username == "unauthenticated")
1971 ab8d50ac Chris Buechler
				$mac['descr'] =  "Auto-added";
1972
			else
1973
				$mac['descr'] =  "Auto-added for user {$username}";
1974 ac631bba lgcosta
			if (!empty($bw_up))
1975
				$mac['bw_up'] = $bw_up;
1976
			if (!empty($bw_down))
1977
				$mac['bw_down'] = $bw_down;
1978 b4792bf8 Ermal
			if (!is_array($config['captiveportal'][$cpzone]['passthrumac']))
1979
				$config['captiveportal'][$cpzone]['passthrumac'] = array();
1980
			$config['captiveportal'][$cpzone]['passthrumac'][] = $mac;
1981 006802ab Ermal
			unlock($cpdblck);
1982 ac631bba lgcosta
			$macrules = captiveportal_passthrumac_configure_entry($mac);
1983 b4792bf8 Ermal
			file_put_contents("{$g['tmp_path']}/macentry_{$cpzone}.rules.tmp", $macrules);
1984 baec2b00 Ermal
			mwexec("/sbin/ipfw -x {$cpzoneid} -q {$g['tmp_path']}/macentry_{$cpzone}.rules.tmp");
1985 ac631bba lgcosta
			$writecfg = true;
1986
		} else {
1987 aea56408 Ermal
			/* See if a pipeno is passed, if not start sessions because this means there isn't one atm */
1988
			if (is_null($pipeno))
1989
				$pipeno = captiveportal_get_next_dn_ruleno();
1990
1991
			/* if the pool is empty, return appropriate message and exit */
1992
			if (is_null($pipeno)) {
1993
				portal_reply_page($redirurl, "error", "System reached maximum login capacity");
1994 12feed15 Ermal
				log_error("Zone: {$cpzone} - WARNING!  Captive portal has reached maximum login capacity");
1995 aea56408 Ermal
				unlock($cpdblck);
1996
				return;
1997
			}
1998
1999 1eff6ee0 Ermal LUÇI
			if (isset($config['captiveportal'][$cpzone]['peruserbw'])) {
2000 64ed3e60 Ermal LUÇI
				$dwfaultbw_up = !empty($config['captiveportal'][$cpzone]['bwdefaultup']) ? $config['captiveportal'][$cpzone]['bwdefaultup'] : 0;
2001
				$dwfaultbw_down = !empty($config['captiveportal'][$cpzone]['bwdefaultdn']) ? $config['captiveportal'][$cpzone]['bwdefaultdn'] : 0;
2002 1eff6ee0 Ermal LUÇI
			} else
2003
				$dwfaultbw_up = $dwfaultbw_down = 0;
2004 64ed3e60 Ermal LUÇI
			$bw_up = !empty($attributes['bw_up']) ? round(intval($attributes['bw_up'])/1000, 2) : $dwfaultbw_up;
2005
			$bw_down = !empty($attributes['bw_down']) ? round(intval($attributes['bw_down'])/1000, 2) : $dwfaultbw_down;
2006 553abb0d Ermal
2007 aea56408 Ermal
			$bw_up_pipeno = $pipeno;
2008
			$bw_down_pipeno = $pipeno + 1;
2009 10b9dfcf Ermal
			//$bw_up /= 1000; // Scale to Kbit/s
2010 c2e2d133 Ermal
			$_gb = @pfSense_pipe_action("pipe {$bw_up_pipeno} config bw {$bw_up}Kbit/s queue 100 buckets 16");
2011
			$_gb = @pfSense_pipe_action("pipe {$bw_down_pipeno} config bw {$bw_down}Kbit/s queue 100 buckets 16");
2012 ac631bba lgcosta
2013 60884727 bcyrill
			$clientsn = (is_ipaddrv6($clientip)) ? 128 : 32;
2014 10b9dfcf Ermal
			if (!isset($config['captiveportal'][$cpzone]['nomacfilter']))
2015 cbe38717 Ermal
				$_gb = @pfSense_ipfw_Tableaction($cpzoneid, IP_FW_TABLE_XADD, 1, $clientip, $clientsn, $clientmac, $bw_up_pipeno);
2016 10b9dfcf Ermal
			else
2017 cbe38717 Ermal
				$_gb = @pfSense_ipfw_Tableaction($cpzoneid, IP_FW_TABLE_XADD, 1, $clientip, $clientsn, NULL, $bw_up_pipeno);
2018 10b9dfcf Ermal
2019
			if (!isset($config['captiveportal'][$cpzone]['nomacfilter']))
2020 cbe38717 Ermal
				$_gb = @pfSense_ipfw_Tableaction($cpzoneid, IP_FW_TABLE_XADD, 2, $clientip, $clientsn, $clientmac, $bw_down_pipeno);
2021 10b9dfcf Ermal
			else
2022 cbe38717 Ermal
				$_gb = @pfSense_ipfw_Tableaction($cpzoneid, IP_FW_TABLE_XADD, 2, $clientip, $clientsn, NULL, $bw_down_pipeno);
2023 ac631bba lgcosta
2024
			if ($attributes['voucher'])
2025
				$attributes['session_timeout'] = $remaining_time;
2026 1974c2d6 bcyrill
			
2027
			/* handle empty attributes */
2028
			$session_timeout = (!empty($attributes['session_timeout'])) ? $attributes['session_timeout'] : 'NULL';
2029
			$idle_timeout = (!empty($attributes['idle_timeout'])) ? $attributes['idle_timeout'] : 'NULL';
2030
			$session_terminate_time = (!empty($attributes['session_terminate_time'])) ? $attributes['session_terminate_time'] : 'NULL';
2031 338c0941 Ermal
			$interim_interval = (!empty($attributes['interim_interval'])) ? $attributes['interim_interval'] : 'NULL';
2032 1974c2d6 bcyrill
2033
			/* escape username */
2034 5cf91315 Renato Botelho
			$safe_username = SQLite3::escapeString($username);
2035 ac631bba lgcosta
2036
			/* encode password in Base64 just in case it contains commas */
2037
			$bpassword = base64_encode($password);
2038 74a40221 Ermal
			$insertquery  = "INSERT INTO captiveportal (allow_time, pipeno, ip, mac, username, sessionid, bpassword, session_timeout, idle_timeout, session_terminate_time, interim_interval, radiusctx) ";
2039 1974c2d6 bcyrill
			$insertquery .= "VALUES ({$allow_time}, {$pipeno}, '{$clientip}', '{$clientmac}', '{$safe_username}', '{$sessionid}', '{$bpassword}', ";
2040 ac420abd Ermal
			$insertquery .= "{$session_timeout}, {$idle_timeout}, {$session_terminate_time}, {$interim_interval}, '{$radiusctx}')";
2041 ac631bba lgcosta
2042 26ee5aaf Ermal
			/* store information to database */
2043
			captiveportal_write_db($insertquery);
2044 006802ab Ermal
			unlock($cpdblck);
2045 1f965b69 Ermal
			unset($insertquery, $bpassword);
2046 006802ab Ermal
2047 ebc0e4b6 Ermal
			if (isset($config['captiveportal'][$cpzone]['radacct_enable']) && !empty($radiusservers[$radiusctx])) {
2048 87e7fdea bcyrill
				$acct_val = RADIUS_ACCOUNTING_START($pipeno, $username, $sessionid, $radiusservers[$radiusctx], $clientip, $clientmac);
2049 ac631bba lgcosta
				if ($acct_val == 1)
2050
					captiveportal_logportalauth($username,$clientmac,$clientip,$type,"RADIUS ACCOUNTING FAILED");
2051
			}
2052
		}
2053 a7ee038b Ermal
	} else {
2054
		/* NOTE: #3062-11 If the pipeno has been allocated free it to not DoS the CP and maintain proper operation as in radius() case */
2055
		if (!is_null($pipeno))
2056
			captiveportal_free_dn_ruleno($pipeno);
2057
2058 006802ab Ermal
		unlock($cpdblck);
2059 a7ee038b Ermal
	}
2060 ac631bba lgcosta
2061
	if ($writecfg == true)
2062
		write_config();
2063
2064
	/* redirect user to desired destination */
2065
	if (!empty($attributes['url_redirection']))
2066
		$my_redirurl = $attributes['url_redirection'];
2067 ac420abd Ermal
	else if (!empty($redirurl))
2068
		$my_redirurl = $redirurl;
2069 b4792bf8 Ermal
	else if (!empty($config['captiveportal'][$cpzone]['redirurl']))
2070
		$my_redirurl = $config['captiveportal'][$cpzone]['redirurl'];
2071 ac631bba lgcosta
2072 b4792bf8 Ermal
	if(isset($config['captiveportal'][$cpzone]['logoutwin_enable']) && !$passthrumac) {
2073 de132ae3 bcyrill
		$ourhostname = portal_hostname_from_client_ip($clientip);
2074
		$protocol = (isset($config['captiveportal'][$cpzone]['httpslogin'])) ? 'https://' : 'http://';
2075
		$logouturl = "{$protocol}{$ourhostname}/";
2076 ac631bba lgcosta
2077
		if (isset($attributes['reply_message']))
2078
			$message = $attributes['reply_message'];
2079
		else
2080
			$message = 0;
2081
2082 b4792bf8 Ermal
		include("{$g['varetc_path']}/captiveportal-{$cpzone}-logout.html");
2083 ac631bba lgcosta
2084
	} else {
2085 fb0c2bd6 Ermal
		portal_reply_page($my_redirurl, "redir", "Just redirect the user.");
2086 ac631bba lgcosta
	}
2087
2088
	return $sessionid;
2089
}
2090
2091
2092
/*
2093
 * Used for when pass-through credits are enabled.
2094
 * Returns true when there was at least one free login to deduct for the MAC.
2095
 * Expired entries are removed as they are seen.
2096
 * Active entries are updated according to the configuration.
2097
 */
2098
function portal_consume_passthrough_credit($clientmac) {
2099 b4792bf8 Ermal
	global $config, $cpzone;
2100 ac631bba lgcosta
2101 b4792bf8 Ermal
	if (!empty($config['captiveportal'][$cpzone]['freelogins_count']) && is_numeric($config['captiveportal'][$cpzone]['freelogins_count']))
2102
		$freeloginscount = $config['captiveportal'][$cpzone]['freelogins_count'];
2103 ac631bba lgcosta
	else
2104
		return false;
2105
2106 b4792bf8 Ermal
	if (!empty($config['captiveportal'][$cpzone]['freelogins_resettimeout']) && is_numeric($config['captiveportal'][$cpzone]['freelogins_resettimeout']))
2107
		$resettimeout = $config['captiveportal'][$cpzone]['freelogins_resettimeout'];
2108 ac631bba lgcosta
	else
2109
		return false;
2110
2111 17e7a243 Scott Ullrich
	if ($freeloginscount < 1 || $resettimeout <= 0 || !$clientmac)
2112 ac631bba lgcosta
		return false;
2113
2114 b4792bf8 Ermal
	$updatetimeouts = isset($config['captiveportal'][$cpzone]['freelogins_updatetimeouts']);
2115 ac631bba lgcosta
2116
	/*
2117
	 * Read database of used MACs.  Lines are a comma-separated list
2118
	 * of the time, MAC, then the count of pass-through credits remaining.
2119
	 */
2120
	$usedmacs = captiveportal_read_usedmacs_db();
2121
2122
	$currenttime = time();
2123
	$found = false;
2124
	foreach ($usedmacs as $key => $usedmac) {
2125
		$usedmac = explode(",", $usedmac);
2126
2127
		if ($usedmac[1] == $clientmac) {
2128
			if ($usedmac[0] + ($resettimeout * 3600) > $currenttime) {
2129
				if ($usedmac[2] < 1) {
2130
					if ($updatetimeouts) {
2131
						$usedmac[0] = $currenttime;
2132
						unset($usedmacs[$key]);
2133
						$usedmacs[] = implode(",", $usedmac);
2134
						captiveportal_write_usedmacs_db($usedmacs);
2135
					}
2136
2137
					return false;
2138
				} else {
2139
					$usedmac[2] -= 1;
2140
					$usedmacs[$key] = implode(",", $usedmac);
2141
				}
2142
2143
				$found = true;
2144
			} else
2145
				unset($usedmacs[$key]);
2146
2147
			break;
2148
		} else if ($usedmac[0] + ($resettimeout * 3600) <= $currenttime)
2149
				unset($usedmacs[$key]);
2150
	}
2151
2152
	if (!$found) {
2153
		$usedmac = array($currenttime, $clientmac, $freeloginscount - 1);
2154
		$usedmacs[] = implode(",", $usedmac);
2155
	}
2156
2157
	captiveportal_write_usedmacs_db($usedmacs);
2158
	return true;
2159
}
2160
2161
function captiveportal_read_usedmacs_db() {
2162 b4792bf8 Ermal
	global $g, $cpzone;
2163 ac631bba lgcosta
2164 b4792bf8 Ermal
	$cpumaclck = lock("captiveusedmacs{$cpzone}");
2165
	if (file_exists("{$g['vardb_path']}/captiveportal_usedmacs_{$cpzone}.db")) {
2166
		$usedmacs = file("{$g['vardb_path']}/captiveportal_usedmacs_{$cpzone}.db", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2167 da666ca8 Scott Ullrich
		if (!$usedmacs)
2168 ac631bba lgcosta
			$usedmacs = array();
2169
	} else
2170
		$usedmacs = array();
2171
2172
	unlock($cpumaclck);
2173
	return $usedmacs;
2174
}
2175
2176
function captiveportal_write_usedmacs_db($usedmacs) {
2177 b4792bf8 Ermal
	global $g, $cpzone;
2178 ac631bba lgcosta
2179 b4792bf8 Ermal
	$cpumaclck = lock("captiveusedmacs{$cpzone}", LOCK_EX);
2180
	@file_put_contents("{$g['vardb_path']}/captiveportal_usedmacs_{$cpzone}.db", implode("\n", $usedmacs));
2181 ac631bba lgcosta
	unlock($cpumaclck);
2182
}
2183
2184 81ce28d8 Renato Botelho
function captiveportal_blocked_mac($mac) {
2185
	global $config, $g, $cpzone;
2186 0d33f1fc Renato Botelho
2187 81ce28d8 Renato Botelho
	if (empty($mac) || !is_macaddr($mac))
2188
		return false;
2189 0d33f1fc Renato Botelho
2190 81ce28d8 Renato Botelho
	if (!is_array($config['captiveportal'][$cpzone]['passthrumac']))
2191
		return false;
2192 0d33f1fc Renato Botelho
2193 81ce28d8 Renato Botelho
	foreach ($config['captiveportal'][$cpzone]['passthrumac'] as $passthrumac)
2194
		if (($passthrumac['action'] == 'block') &&
2195
		    ($passthrumac['mac'] == strtolower($mac)))
2196
			return true;
2197 0d33f1fc Renato Botelho
2198 81ce28d8 Renato Botelho
	return false;
2199 3b2769be Renato Botelho
2200
}
2201
2202 62f20eab Michael Newton
function captiveportal_send_server_accounting($off = false) {
2203
	global $cpzone, $config;
2204
2205
	if (!isset($config['captiveportal'][$cpzone]['radacct_enable'])) {
2206
		return;
2207
	}
2208
	if ($off) {
2209
		$racct = new Auth_RADIUS_Acct_Off;
2210
	} else {
2211
		$racct = new Auth_RADIUS_Acct_On;
2212
	}
2213
	$radiusservers = captiveportal_get_radius_servers();
2214
	if (empty($radiusservers)) {
2215
		return;
2216
	}
2217
	foreach ($radiusservers['first'] as $radsrv) {
2218
		// Add a new server to our instance
2219
		$racct->addServer($radsrv['ipaddr'], $radsrv['acctport'], $radsrv['key']);
2220
	}
2221
	if (PEAR::isError($racct->start())) {
2222
		$retvalue['acct_val'] = 1;
2223
		$retvalue['error'] = $racct->getMessage();
2224
2225
		// If we encounter an error immediately stop this function and go back
2226
		$racct->close();
2227
		return $retvalue;
2228
	}
2229
	// Send request
2230
	$result = $racct->send();
2231
	// Evaluation of the response
2232
	// 5 -> Accounting-Response
2233
	// See RFC2866 for this.
2234
	if (PEAR::isError($result)) {
2235
		$retvalue['acct_val'] = 1;
2236
		$retvalue['error'] = $result->getMessage();
2237
	} else if ($result === true) {
2238
		$retvalue['acct_val'] = 5 ;
2239
	} else {
2240
		$retvalue['acct_val'] = 1 ;
2241
	}
2242
2243
	$racct->close();
2244
	return $retvalue;
2245
}
2246 64c0462b Ermal
?>