Project

General

Profile

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