Project

General

Profile

Download (38.8 KB) Statistics
| Branch: | Tag: | Revision:
1 5b237745 Scott Ullrich
<?php
2
/*
3
	captiveportal.inc
4 3db19cf1 Scott Ullrich
	part of m0n0wall (http://m0n0.ch/wall)
5 36254e4a Scott Ullrich
6 9568c1a1 Ermal Lu?i
	Copyright (C) 2009 Ermal Lu?i
7 0bd34ed6 Scott Ullrich
	Copyright (C) 2003-2006 Manuel Kasper <mk@neon1.net>.
8 5b237745 Scott Ullrich
	All rights reserved.
9 36254e4a Scott Ullrich
10 5b237745 Scott Ullrich
	Redistribution and use in source and binary forms, with or without
11
	modification, are permitted provided that the following conditions are met:
12 36254e4a Scott Ullrich
13 5b237745 Scott Ullrich
	1. Redistributions of source code must retain the above copyright notice,
14
	   this list of conditions and the following disclaimer.
15 36254e4a Scott Ullrich
16 5b237745 Scott Ullrich
	2. Redistributions in binary form must reproduce the above copyright
17
	   notice, this list of conditions and the following disclaimer in the
18
	   documentation and/or other materials provided with the distribution.
19 36254e4a Scott Ullrich
20 5b237745 Scott Ullrich
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
21
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
24
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
	POSSIBILITY OF SUCH DAMAGE.
30 ca83c6ea Scott Ullrich
31 9699028a Scott Ullrich
	This version of captiveportal.inc has been modified by Rob Parker
32
	<rob.parker@keycom.co.uk> to include changes for per-user bandwidth management
33
	via returned RADIUS attributes. This page has been modified to delete any
34
	added rules which may have been created by other per-user code (index.php, etc).
35
	These changes are (c) 2004 Keycom PLC.
36 523855b0 Scott Ullrich
	
37
	pfSense_BUILDER_BINARIES:	/sbin/ifconfig	/sbin/ipfw	/sbin/sysctl	/sbin/kldunload
38
	pfSense_BUILDER_BINARIES:	/usr/local/sbin/lighttpd	/usr/local/bin/minicron	/sbin/pfctl
39
	pfSense_BUILDER_BINARIES:	/bin/hostname	/bin/cp	
40
	pfSense_MODULE:	captiveportal
41 9699028a Scott Ullrich
*/
42 36254e4a Scott Ullrich
43 5b237745 Scott Ullrich
/* include all configuration functions */
44 a55cdcc0 Ermal Lu?i
require_once("config.inc");
45
require_once("functions.inc");
46 856e58a6 Scott Ullrich
require_once("radius.inc");
47 156487ed Ermal Lu?i
require_once("voucher.inc");
48 0bd34ed6 Scott Ullrich
49 5b237745 Scott Ullrich
function captiveportal_configure() {
50
	global $config, $g;
51 36254e4a Scott Ullrich
52 dedf51a2 Ermal Lu?i
	$captiveportallck = lock('captiveportal');
53 f8b11310 Ermal Lu?i
	
54
	$cpactive = false;
55
	if (isset($config['captiveportal']['enable'])) {
56
		$cpips = array();
57
		$ifaces = get_configured_interface_list();
58 508e5229 Ermal Lu?i
		foreach ($ifaces as $kiface => $kiface2) {
59
			$tmpif = get_real_interface($kiface);
60
			mwexec("/sbin/ifconfig {$tmpif} -ipfwfilter");
61
		}
62 f8b11310 Ermal Lu?i
		$cpinterfaces = explode(",", $config['captiveportal']['interface']);
63
		$firsttime = 0;
64
		foreach ($cpinterfaces as $cpifgrp) {
65
			if (!isset($ifaces[$cpifgrp]))
66
				continue;
67
			$tmpif = get_real_interface($cpifgrp);
68
			if (!empty($tmpif)) {
69 b27f1caf Ermal Lu?i
				if ($firsttime > 0)
70
					$cpinterface .= " or ";
71 f8b11310 Ermal Lu?i
				$cpinterface .= "via {$tmpif}"; 
72 b27f1caf Ermal Lu?i
				$firsttime = 1;
73
				$cpipm = get_interface_ip($cpifgrp);
74 5bdddd2d Ermal Lu?i
				if (is_ipaddr($cpipm)) {
75 bbc6768b Ermal Lu?i
					$carpif = link_ip_to_carp_interface($cpipm);
76
					if (!empty($carpif)) {
77
						$carpsif = explode(" ", $carpif);
78
						foreach ($carpsif as $cpcarp) {
79
							mwexec("/sbin/ifconfig {$cpcarp} ipfwfilter");
80
							$carpip = find_interface_ip($cpcarp);
81
							if (is_ipaddr($carpip))
82
								$cpips[] = $carpip;
83
						}
84
					}
85 b27f1caf Ermal Lu?i
					$cpips[] = $cpipm;
86 5bdddd2d Ermal Lu?i
					mwexec("/sbin/ifconfig {$tmpif} ipfwfilter");
87 85250056 Ermal Lu?i
				}
88 f8b11310 Ermal Lu?i
			}
89
		}
90
		if (count($cpips) > 0) {
91
			$cpactive = true;
92
			$cpinterface = "{ {$cpinterface} } ";
93
		}
94
	}
95 dedf51a2 Ermal Lu?i
96 f8b11310 Ermal Lu?i
	if ($cpactive == true) {
97 36254e4a Scott Ullrich
98 3db19cf1 Scott Ullrich
		if ($g['booting'])
99
			echo "Starting captive portal... ";
100 36254e4a Scott Ullrich
101 5b237745 Scott Ullrich
		/* kill any running mini_httpd */
102 23a0c341 Scott Ullrich
		killbypid("{$g['varrun_path']}/lighty-CaptivePortal.pid");
103 63fff79b Scott Ullrich
		killbypid("{$g['varrun_path']}/lighty-CaptivePortal-SSL.pid");
104 36254e4a Scott Ullrich
105 6ce61a8f Ermal
		/* remove old information */
106
		unlink_if_exists("{$g['vardb_path']}/captiveportal.db");
107
		unlink_if_exists("{$g['vardb_path']}/captiveportal_mac.db");
108
		unlink_if_exists("{$g['vardb_path']}/captiveportal_ip.db");
109
		unlink_if_exists("{$g['vardb_path']}/captiveportal_radius.db");
110 b01792a0 Ermal
		mwexec("/sbin/ipfw -q table all flush");
111 6ce61a8f Ermal
112
		/* setup new database in case someone tries to access the status -> captive portal page */
113
		touch("{$g['vardb_path']}/captiveportal.db");
114
115 c6c92abf Scott Ullrich
		/* kill any running minicron */
116
		killbypid("{$g['varrun_path']}/minicron.pid");
117 36254e4a Scott Ullrich
118 2b5b6eb4 Ermal Lu?i
		/* make sure ipfw is loaded */
119
		if (!is_module_loaded("ipfw.ko"))
120
			filter_load_ipfw();
121 b01792a0 Ermal
		/* Always load dummynet now that even allowed ip and mac passthrough use it. */
122
		if (!is_module_loaded("dummynet.ko"))
123 2b5b6eb4 Ermal Lu?i
                        mwexec("/sbin/kldload dummynet");
124
125 3db19cf1 Scott Ullrich
		/* generate ipfw rules */
126 6ce61a8f Ermal
		captiveportal_init_ipfw_ruleno();
127 f8b11310 Ermal Lu?i
		$cprules = captiveportal_rules_generate($cpinterface, $cpips);
128 6ce61a8f Ermal
		$cprules .= "\n";
129
		/* generate passthru mac database */
130 d5ae560d Ermal
		$cprules .= captiveportal_passthrumac_configure(true);
131
		$cprules .= "\n";
132 6ce61a8f Ermal
		/* allowed ipfw rules to make allowed ip work */
133
		$cprules .= captiveportal_allowedip_configure();
134 36254e4a Scott Ullrich
135 5b237745 Scott Ullrich
		/* stop accounting on all clients */
136 dedf51a2 Ermal Lu?i
		captiveportal_radius_stop_all(true);
137 5b237745 Scott Ullrich
138 0bd34ed6 Scott Ullrich
		/* initialize minicron interval value */
139
		$croninterval = $config['captiveportal']['croninterval'] ? $config['captiveportal']['croninterval'] : 60;
140
141
		/* double check if the $croninterval is numeric and at least 10 seconds. If not we set it to 60 to avoid problems */
142
		if ((!is_numeric($croninterval)) || ($croninterval < 10)) { $croninterval = 60; }
143
144 5b237745 Scott Ullrich
		/* write portal page */
145
		if ($config['captiveportal']['page']['htmltext'])
146
			$htmltext = base64_decode($config['captiveportal']['page']['htmltext']);
147
		else {
148
			/* example/template page */
149
			$htmltext = <<<EOD
150
<html>
151
<head>
152 36d0358b Scott Ullrich
<title>{$g['product_name']} captive portal</title>
153 5b237745 Scott Ullrich
</head>
154 3db19cf1 Scott Ullrich
<body>
155 a515d275 Scott Ullrich
<center>
156 36d0358b Scott Ullrich
<h2>{$g['product_name']} captive portal</h2>
157
Welcome to the {$g['product_name']} Captive Portal!  This is the default page since a custom page has not been defined.
158 14d2d21b Scott Ullrich
<p>
159 a515d275 Scott Ullrich
<form method="post" action="\$PORTAL_ACTION\$">
160
<input name="redirurl" type="hidden" value="\$PORTAL_REDIRURL\$">
161 407a29ca Scott Ullrich
<table>
162
   <tr><td>Username:</td><td><input name="auth_user" type="text"></td></tr>
163
   <tr><td>Password:</td><td><input name="auth_pass" type="password"></td></tr>
164 a515d275 Scott Ullrich
   <tr><td>&nbsp;</td></tr>
165 14d2d21b Scott Ullrich
   <tr>
166
     <td colspan="2">
167 0bd34ed6 Scott Ullrich
	<center><input name="accept" type="submit" value="Continue"></center>
168 14d2d21b Scott Ullrich
     </td>
169
   </tr>
170 407a29ca Scott Ullrich
</table>
171 a515d275 Scott Ullrich
</center>
172 407a29ca Scott Ullrich
</form>
173 5b237745 Scott Ullrich
</body>
174
</html>
175
176 0bd34ed6 Scott Ullrich
177
178 5b237745 Scott Ullrich
EOD;
179
		}
180
181
		$fd = @fopen("{$g['varetc_path']}/captiveportal.html", "w");
182
		if ($fd) {
183
			fwrite($fd, $htmltext);
184 36254e4a Scott Ullrich
			fclose($fd);
185 5b237745 Scott Ullrich
		}
186 36254e4a Scott Ullrich
187 5b237745 Scott Ullrich
		/* write error page */
188
		if ($config['captiveportal']['page']['errtext'])
189
			$errtext = base64_decode($config['captiveportal']['page']['errtext']);
190
		else {
191
			/* example page */
192
			$errtext = <<<EOD
193
<html>
194
<head>
195
<title>Authentication error</title>
196
</head>
197
<body>
198
<font color="#cc0000"><h2>Authentication error</h2></font>
199
<b>
200
Username and/or password invalid.
201
<br><br>
202 2b0eeeaa Ermal Lu?i
<a href="javascript:history.back(); ">Go back</a>
203 5b237745 Scott Ullrich
</b>
204
</body>
205
</html>
206
207
EOD;
208
		}
209
210
		$fd = @fopen("{$g['varetc_path']}/captiveportal-error.html", "w");
211
		if ($fd) {
212
			fwrite($fd, $errtext);
213 36254e4a Scott Ullrich
			fclose($fd);
214 5b237745 Scott Ullrich
		}
215 36254e4a Scott Ullrich
216 0bd34ed6 Scott Ullrich
		/* write elements */
217
		captiveportal_write_elements();
218 5b237745 Scott Ullrich
219 3db19cf1 Scott Ullrich
		/* load rules */
220 6ce61a8f Ermal
		mwexec("/sbin/ipfw -q flush");
221 36254e4a Scott Ullrich
222 63fff79b Scott Ullrich
		/* ipfw cannot accept rules directly on stdin,
223 3db19cf1 Scott Ullrich
		   so we have to write them to a temporary file first */
224
		$fd = @fopen("{$g['tmp_path']}/ipfw.cp.rules", "w");
225
		if (!$fd) {
226
			printf("Cannot open ipfw.cp.rules in captiveportal_configure()\n");
227
			return 1;
228
		}
229 36254e4a Scott Ullrich
230 3db19cf1 Scott Ullrich
		fwrite($fd, $cprules);
231
		fclose($fd);
232 36254e4a Scott Ullrich
233 b01792a0 Ermal
		mwexec("/sbin/ipfw -q {$g['tmp_path']}/ipfw.cp.rules");
234 36254e4a Scott Ullrich
235 6ce61a8f Ermal
		@unlink("{$g['tmp_path']}/ipfw.cp.rules");
236 36254e4a Scott Ullrich
237 3db19cf1 Scott Ullrich
		/* filter on layer2 as well so we can check MAC addresses */
238
		mwexec("/sbin/sysctl net.link.ether.ipfw=1");
239 36254e4a Scott Ullrich
240 5b237745 Scott Ullrich
		chdir($g['captiveportal_path']);
241 36254e4a Scott Ullrich
242 9b5a1292 Scott Ullrich
		if ($config['captiveportal']['maxproc'])
243
			$maxproc = $config['captiveportal']['maxproc'];
244
		else
245
			$maxproc = 16;
246 36254e4a Scott Ullrich
247 3e1b0033 Scott Ullrich
		$use_fastcgi = true;
248
249 40b9f8c0 Scott Ullrich
		if(isset($config['captiveportal']['httpslogin'])) {
250
			$cert = base64_decode($config['captiveportal']['certificate']);
251 c6e1b0f5 Ermal Lu?i
			if (isset($config['captiveportal']['cacertificate']))
252
				$cacert = base64_decode($config['captiveportal']['cacertificate']);
253
			else
254
				$cacert = "";
255 40b9f8c0 Scott Ullrich
			$key = base64_decode($config['captiveportal']['private-key']);
256 63fff79b Scott Ullrich
			/* generate lighttpd configuration */
257
			system_generate_lighty_config("{$g['varetc_path']}/lighty-CaptivePortal-SSL.conf",
258 c6e1b0f5 Ermal Lu?i
				$cert, $key, $cacert, "lighty-CaptivePortal-ssl.pid", "8001", "/usr/local/captiveportal/",
259 29dc1e6e Nigel Graham
					"cert-portal.pem", "ca-portal.pem", "1", $maxproc, $use_fastcgi, true);
260 40b9f8c0 Scott Ullrich
		}
261 36254e4a Scott Ullrich
262 877ac35d Scott Ullrich
		/* generate lighttpd configuration */
263
		system_generate_lighty_config("{$g['varetc_path']}/lighty-CaptivePortal.conf",
264 2cf6ddcb Nigel Graham
			"", "", "", "lighty-CaptivePortal.pid", "8000", "/usr/local/captiveportal/",
265 29dc1e6e Nigel Graham
				"cert-portal.pem", "ca-portal.pem", "1", $maxproc, $use_fastcgi, true);
266 36254e4a Scott Ullrich
267 877ac35d Scott Ullrich
		/* attempt to start lighttpd */
268
		$res = mwexec("/usr/local/sbin/lighttpd -f {$g['varetc_path']}/lighty-CaptivePortal.conf");
269 36254e4a Scott Ullrich
270 63fff79b Scott Ullrich
		/* fire up https instance */
271
		if(isset($config['captiveportal']['httpslogin']))
272
			$res = mwexec("/usr/local/sbin/lighttpd -f {$g['varetc_path']}/lighty-CaptivePortal-SSL.conf");
273 36254e4a Scott Ullrich
274 0bd34ed6 Scott Ullrich
		/* start pruning process (interval defaults to 60 seconds) */
275
		mwexec("/usr/local/bin/minicron $croninterval {$g['varrun_path']}/minicron.pid " .
276 c6c92abf Scott Ullrich
			"/etc/rc.prunecaptiveportal");
277 36254e4a Scott Ullrich
278 d99f7864 Scott Ullrich
		/* generate radius server database */
279
		if ($config['captiveportal']['radiusip'] && (!isset($config['captiveportal']['auth_method']) ||
280
				($config['captiveportal']['auth_method'] == "radius"))) {
281
			$radiusip = $config['captiveportal']['radiusip'];
282
			$radiusip2 = ($config['captiveportal']['radiusip2']) ? $config['captiveportal']['radiusip2'] : null;
283
284
			if ($config['captiveportal']['radiusport'])
285
				$radiusport = $config['captiveportal']['radiusport'];
286
			else
287
				$radiusport = 1812;
288
289
			if ($config['captiveportal']['radiusacctport'])
290
				$radiusacctport = $config['captiveportal']['radiusacctport'];
291
			else
292
				$radiusacctport = 1813;
293
294
			if ($config['captiveportal']['radiusport2'])
295
				$radiusport2 = $config['captiveportal']['radiusport2'];
296
			else
297
				$radiusport2 = 1812;
298
299
			$radiuskey = $config['captiveportal']['radiuskey'];
300
			$radiuskey2 = ($config['captiveportal']['radiuskey2']) ? $config['captiveportal']['radiuskey2'] : null;
301
302
			$fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db", "w");
303
			if (!$fd) {
304
				printf("Error: cannot open radius DB file in captiveportal_configure().\n");
305
				return 1;
306
			} else if (isset($radiusip2, $radiuskey2)) {
307
				fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey . "\n"
308
					 . $radiusip2 . "," . $radiusport2 . "," . $radiusacctport . "," . $radiuskey2);
309
			} else {
310
				fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey);
311
			}
312
			fclose($fd);
313
		}
314 36254e4a Scott Ullrich
315 d99f7864 Scott Ullrich
		if ($g['booting'])
316
			echo "done\n";
317 36254e4a Scott Ullrich
318 5b237745 Scott Ullrich
	} else {
319 23a0c341 Scott Ullrich
		killbypid("{$g['varrun_path']}/lighty-CaptivePortal.pid");
320 c6c92abf Scott Ullrich
		killbypid("{$g['varrun_path']}/minicron.pid");
321 12ee8fe4 Scott Ullrich
322 dedf51a2 Ermal Lu?i
		captiveportal_radius_stop_all(true);
323 3db19cf1 Scott Ullrich
324
		mwexec("/sbin/sysctl net.link.ether.ipfw=0");
325
326 5209079f Ermal Luçi
		/* unload ipfw */
327 faebbab3 Scott Ullrich
		if (is_module_loaded("ipfw.ko"))		
328
			mwexec("/sbin/kldunload ipfw.ko");
329 85250056 Ermal Lu?i
		$listifs = get_configured_interface_list_by_realif();
330 bbc6768b Ermal Lu?i
		foreach ($listifs as $listrealif => $listif) {
331 7c587b9f Ermal Lu?i
			if (!empty($listrealif)) {
332
				mwexec("/sbin/ifconfig {$listrealif} -ipfwfilter");
333
				$carpif = link_ip_to_carp_interface(find_interface_ip($listrealif));
334 2ee45728 Ermal Lu?i
                        	if (!empty($carpif)) {
335
					$carpsif = explode(" ", $carpif);
336
					foreach ($carpsif as $cpcarp)
337
						mwexec("/sbin/ifconfig {$cpcarp} -ipfwfilter");
338
				}
339 bbc6768b Ermal Lu?i
			}
340
		}
341 3db19cf1 Scott Ullrich
	}
342 36254e4a Scott Ullrich
343 f8b11310 Ermal Lu?i
	unlock($captiveportallck);
344 c3214b80 Scott Ullrich
	
345 5b237745 Scott Ullrich
	return 0;
346
}
347
348 f8b11310 Ermal Lu?i
function captiveportal_rules_generate($cpif, &$cpiparray) {
349 3db19cf1 Scott Ullrich
	global $config, $g;
350 36254e4a Scott Ullrich
351 b01792a0 Ermal
	$cprules =  "add 65291 set 1 allow pfsync from any to any\n";
352
	$cprules .= "add 65292 set 1 allow carp from any to any\n";
353 181a843c Scott Ullrich
354 3db19cf1 Scott Ullrich
	$cprules .= <<<EOD
355 b01792a0 Ermal
# add 65300 set 1 skipto 65534 all from any to any not layer2
356 3db19cf1 Scott Ullrich
# layer 2: pass ARP
357 b01792a0 Ermal
add 65301 set 1 pass layer2 mac-type arp
358 b9d1d810 Scott Ullrich
# pfsense requires for WPA
359 b01792a0 Ermal
add 65302 set 1 pass layer2 mac-type 0x888e
360
add 65303 set 1 pass layer2 mac-type 0x88c7
361 684c787e Scott Ullrich
362 36254e4a Scott Ullrich
# PPP Over Ethernet Discovery Stage
363 b01792a0 Ermal
add 65304 set 1 pass layer2 mac-type 0x8863
364 684c787e Scott Ullrich
# PPP Over Ethernet Session Stage
365 b01792a0 Ermal
add 65305 set 1 pass layer2 mac-type 0x8864
366 55f5c311 Ermal Lu?i
# Allow WPA
367 b01792a0 Ermal
add 65306 set 1 pass layer2 mac-type 0x888e
368 684c787e Scott Ullrich
369 3db19cf1 Scott Ullrich
# layer 2: block anything else non-IP
370 b01792a0 Ermal
add 65307 set 1 deny layer2 not mac-type ip
371 3db19cf1 Scott Ullrich
372
EOD;
373
374 b01792a0 Ermal
	$rulenum = 65310;
375 2f27dffd Ermal
	$ips = "255.255.255.255 ";
376
	foreach ($cpiparray as $cpip)
377
		$ips .= "or {$cpip} ";
378
	$ips = "{ {$ips} }";
379
	//# allow access to our DHCP server (which needs to be able to ping clients as well)
380
	$cprules .= "add {$rulenum} set 1 pass udp from any 68 to {$ips} 67 in \n";
381
	$rulenum++;
382
	$cprules .= "add {$rulenum} set 1 pass udp from any 68 to {$ips} 67 in \n";
383
	$rulenum++;
384
	$cprules .= "add {$rulenum} set 1 pass udp from {$ips} 67 to any 68 out \n";
385
	$rulenum++;
386
	$cprules .= "add {$rulenum} set 1 pass icmp from {$ips} to any out icmptype 0\n";
387
	$rulenum++;
388
	$cprules .= "add {$rulenum} set 1 pass icmp from any to {$ips} in icmptype 8 \n";
389
	$rulenum++;
390
	//# allow access to our DNS forwarder
391
	$cprules .= "add {$rulenum} set 1 pass udp from any to {$ips} 53 in \n";
392
	$rulenum++;
393
	$cprules .= "add {$rulenum} set 1 pass udp from {$ips} 53 to any out \n";
394
	$rulenum++;
395
	# allow access to our web server
396
	$cprules .= "add {$rulenum} set 1 pass tcp from any to {$ips} 8000 in \n";
397
	$rulenum++;
398
	$cprules .= "add {$rulenum} set 1 pass tcp from {$ips} 8000 to any out \n";
399
400
	if (isset($config['captiveportal']['httpslogin'])) {
401 f8b11310 Ermal Lu?i
		$rulenum++;
402 2f27dffd Ermal
		$cprules .= "add {$rulenum} set 1 pass tcp from any to {$ips} 8001 in \n";
403 f8b11310 Ermal Lu?i
		$rulenum++;
404 2f27dffd Ermal
		$cprules .= "add {$rulenum} set 1 pass tcp from {$ips} 8001 to any out \n";
405 3db19cf1 Scott Ullrich
	}
406 2f27dffd Ermal
	if (!empty($config['system']['webgui']['port']))
407
		$port = $config['system']['webgui']['port'];
408
	else if ($config['system']['webgui']['proto'] == "https")
409
		$port = 443;
410
	else
411
		$port = 80;
412
	$rulenum++;
413
	$cprules .= "add {$rulenum} set 1 pass tcp from any to {$ips} {$port} in \n";
414
	$rulenum++;
415
	$cprules .= "add {$rulenum} set 1 pass tcp from {$ips} {$port} to any out \n";
416 f8b11310 Ermal Lu?i
	$rulenum++;
417 5480497a Scott Ullrich
418 b01792a0 Ermal
	/* Allowed ips */
419
	$cprules .= "add {$rulenum} allow ip from table(3) to any in\n";
420
	$rulenum++;
421
	$cprules .= "add {$rulenum} allow ip from any to table(4) out\n";
422
	$rulenum++;
423
	$cprules .= "add {$rulenum} pipe tablearg ip from table(5) to any in\n";
424
	$rulenum++;
425
	$cprules .= "add {$rulenum} pipe tablearg ip from any to table(6) out\n";
426
	$rulenum++;
427
	$cprules .= "add {$rulenum} allow ip from any to table(7) in\n";
428
	$rulenum++;
429
	$cprules .= "add {$rulenum} allow ip from table(8) to any out\n";
430
	$rulenum++;
431
	$cprules .= "add {$rulenum} pipe tablearg ip from any to table(9) in\n";
432
	$rulenum++;
433
	$cprules .= "add {$rulenum} pipe tablearg ip from table(10) to any out\n";
434
	$rulenum++;
435
436
	/* Authenticated users rules. */
437 f9f71ad3 Ermal Lu?i
	if (isset($config['captiveportal']['peruserbw'])) {
438 6ce61a8f Ermal
		$cprules .= "add {$rulenum} set 1 pipe tablearg ip from table(1) to any in\n";
439 f9f71ad3 Ermal Lu?i
		$rulenum++;
440 6ce61a8f Ermal
		$cprules .= "add {$rulenum} set 1 pipe tablearg ip from any to table(2) out\n";
441 f9f71ad3 Ermal Lu?i
		$rulenum++;
442
	} else {
443 6ce61a8f Ermal
		$cprules .= "add {$rulenum} set 1 allow ip from table(1) to any in\n";
444 f9f71ad3 Ermal Lu?i
                $rulenum++;
445 6ce61a8f Ermal
                $cprules .= "add {$rulenum} set 1 allow ip from any to table(2) out\n";
446 f9f71ad3 Ermal Lu?i
                $rulenum++;
447
	}
448
	
449
       $cprules .= <<<EOD
450 5480497a Scott Ullrich
451 d44bccc7 Scott Ullrich
# redirect non-authenticated clients to captive portal
452 6ce61a8f Ermal
add 65531 set 1 fwd 127.0.0.1,8000 tcp from any to any in
453 3db19cf1 Scott Ullrich
# let the responses from the captive portal web server back out
454 6ce61a8f Ermal
add 65532 set 1 pass tcp from any to any out
455 3db19cf1 Scott Ullrich
# block everything else
456 6ce61a8f Ermal
add 65533 set 1 deny all from any to any
457 3db19cf1 Scott Ullrich
# pass everything else on layer2
458 6ce61a8f Ermal
add 65534 set 1 pass all from any to any layer2
459 3db19cf1 Scott Ullrich
460
EOD;
461
462 d44bccc7 Scott Ullrich
    return $cprules;
463 3db19cf1 Scott Ullrich
}
464
465 5b237745 Scott Ullrich
/* remove clients that have been around for longer than the specified amount of time */
466 36254e4a Scott Ullrich
/* db file structure:
467 0bd34ed6 Scott Ullrich
timestamp,ipfw_rule_no,clientip,clientmac,username,sessionid,password,session_timeout,idle_timeout,session_terminate_time */
468
469 3db19cf1 Scott Ullrich
/* (password is in Base64 and only saved when reauthentication is enabled) */
470 5b237745 Scott Ullrich
function captiveportal_prune_old() {
471 0bd34ed6 Scott Ullrich
472 23c4f978 Scott Ullrich
    global $g, $config;
473
474
    /* check for expired entries */
475
    if ($config['captiveportal']['timeout'])
476
        $timeout = $config['captiveportal']['timeout'] * 60;
477
    else
478
        $timeout = 0;
479
480
    if ($config['captiveportal']['idletimeout'])
481
        $idletimeout = $config['captiveportal']['idletimeout'] * 60;
482
    else
483
        $idletimeout = 0;
484
485 336e3c1c Charlie
    if (!$timeout && !$idletimeout && !isset($config['captiveportal']['reauthenticate']) && 
486
		!isset($config['captiveportal']['radiussession_timeout']) && !isset($config['voucher']['enable']))
487 23c4f978 Scott Ullrich
        return;
488
489 dedf51a2 Ermal Lu?i
    $captiveportallck = lock('captiveportal');
490 23c4f978 Scott Ullrich
491
    /* read database */
492
    $cpdb = captiveportal_read_db();
493
494
    $radiusservers = captiveportal_get_radius_servers();
495
496 2d53158f stompro
    /*  To make sure we iterate over ALL accounts on every run the count($cpdb) is moved
497
     *  outside of the loop. Otherwise the loop would evaluate count() on every iteration
498
     *  and since $i would increase and count() would decrement they would meet before we
499
     *  had a chance to iterate over all accounts.
500 f56a73f1 Scott Ullrich
     */
501 8e51cc6a Ermal Lu?i
    $unsetindexes = array();
502 f56a73f1 Scott Ullrich
    $no_users = count($cpdb);
503
    for ($i = 0; $i < $no_users; $i++) {
504 23c4f978 Scott Ullrich
505
        $timedout = false;
506
        $term_cause = 1;
507
508
        /* hard timeout? */
509
        if ($timeout) {
510
            if ((time() - $cpdb[$i][0]) >= $timeout) {
511
                $timedout = true;
512
                $term_cause = 5; // Session-Timeout
513
            }
514
        }
515
516
        /* Session-Terminate-Time */
517
        if (!$timedout && !empty($cpdb[$i][9])) {
518
            if (time() >= $cpdb[$i][9]) {
519
                $timedout = true;
520
                $term_cause = 5; // Session-Timeout
521
            }
522
        }
523
524
        /* check if the radius idle_timeout attribute has been set and if its set change the idletimeout to this value */
525
        $idletimeout = (is_numeric($cpdb[$i][8])) ? $cpdb[$i][8] : $idletimeout;
526
        /* if an idle timeout is specified, get last activity timestamp from ipfw */
527
        if (!$timedout && $idletimeout) {
528 f9f71ad3 Ermal Lu?i
            $lastact = captiveportal_get_last_activity($cpdb[$i][2]);
529 2d53158f stompro
			/*  If the user has logged on but not sent any traffic they will never be logged out.
530
			 *  We "fix" this by setting lastact to the login timestamp. 
531 f56a73f1 Scott Ullrich
			 */
532
			$lastact = $lastact ? $lastact : $cpdb[$i][0];
533 23c4f978 Scott Ullrich
            if ($lastact && ((time() - $lastact) >= $idletimeout)) {
534
                $timedout = true;
535
                $term_cause = 4; // Idle-Timeout
536
                $stop_time = $lastact; // Entry added to comply with WISPr
537
            }
538
        }
539
540 336e3c1c Charlie
	/* if vouchers are configured, activate session timeouts */
541
	if (!$timedout && isset($config['voucher']['enable']) && !empty($cpdb[$i][7])) {
542
		if (time() >= ($cpdb[$i][0] + $cpdb[$i][7])) {
543
			$timedout = true;
544
			$term_cause = 5; // Session-Timeout
545
		}
546
	}
547
548 23c4f978 Scott Ullrich
        /* if radius session_timeout is enabled and the session_timeout is not null, then check if the user should be logged out */
549
        if (!$timedout && isset($config['captiveportal']['radiussession_timeout']) && !empty($cpdb[$i][7])) {
550
            if (time() >= ($cpdb[$i][0] + $cpdb[$i][7])) {
551
                $timedout = true;
552
                $term_cause = 5; // Session-Timeout
553
            }
554
        }
555
556
        if ($timedout) {
557
            captiveportal_disconnect($cpdb[$i], $radiusservers,$term_cause,$stop_time);
558
            captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "TIMEOUT");
559 8e51cc6a Ermal Lu?i
	    $unsetindexes[$i] = $i;
560 23c4f978 Scott Ullrich
        }
561
562
        /* do periodic RADIUS reauthentication? */
563
        if (!$timedout && isset($config['captiveportal']['reauthenticate']) &&
564 40b48c6c Ermal Lu?i
            !empty($radiusservers)) {
565 23c4f978 Scott Ullrich
566
            if (isset($config['captiveportal']['radacct_enable'])) {
567
                if ($config['captiveportal']['reauthenticateacct'] == "stopstart") {
568
                    /* stop and restart accounting */
569
                    RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
570
                                           $cpdb[$i][4], // username
571
                                           $cpdb[$i][5], // sessionid
572
                                           $cpdb[$i][0], // start time
573 40b48c6c Ermal Lu?i
                                           $radiusservers,
574 23c4f978 Scott Ullrich
                                           $cpdb[$i][2], // clientip
575
                                           $cpdb[$i][3], // clientmac
576
                                           10); // NAS Request
577 6ce61a8f Ermal
                    exec("/sbin/ipfw table 1 entryzerostats {$cpdb[$i][2]}");
578
                    exec("/sbin/ipfw table 2 entryzerostats {$cpdb[$i][2]}");
579 23c4f978 Scott Ullrich
                    RADIUS_ACCOUNTING_START($cpdb[$i][1], // ruleno
580
                                            $cpdb[$i][4], // username
581
                                            $cpdb[$i][5], // sessionid
582 40b48c6c Ermal Lu?i
                                            $radiusservers,
583 23c4f978 Scott Ullrich
                                            $cpdb[$i][2], // clientip
584
                                            $cpdb[$i][3]); // clientmac
585
                } else if ($config['captiveportal']['reauthenticateacct'] == "interimupdate") {
586
                    RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
587
                                           $cpdb[$i][4], // username
588
                                           $cpdb[$i][5], // sessionid
589
                                           $cpdb[$i][0], // start time
590 40b48c6c Ermal Lu?i
                                           $radiusservers,
591 23c4f978 Scott Ullrich
                                           $cpdb[$i][2], // clientip
592
                                           $cpdb[$i][3], // clientmac
593
                                           10, // NAS Request
594
                                           true); // Interim Updates
595
                }
596
            }
597
598
            /* check this user against RADIUS again */
599
            $auth_list = RADIUS_AUTHENTICATION($cpdb[$i][4], // username
600
                                          base64_decode($cpdb[$i][6]), // password
601
                                            $radiusservers,
602
                                          $cpdb[$i][2], // clientip
603
                                          $cpdb[$i][3], // clientmac
604
                                          $cpdb[$i][1]); // ruleno
605
606
            if ($auth_list['auth_val'] == 3) {
607
                captiveportal_disconnect($cpdb[$i], $radiusservers, 17);
608
                captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "RADIUS_DISCONNECT", $auth_list['reply_message']);
609 8e51cc6a Ermal Lu?i
	        $unsetindexes[$i] = $i;
610 23c4f978 Scott Ullrich
            }
611
        }
612
    }
613 8e51cc6a Ermal Lu?i
    /* This is a kludge to overcome some php weirdness */
614
    foreach($unsetindexes as $unsetindex)
615
	unset($cpdb[$unsetindex]);
616 23c4f978 Scott Ullrich
617
    /* write database */
618
    captiveportal_write_db($cpdb);
619
620 dedf51a2 Ermal Lu?i
    unlock($captiveportallck);
621 5b237745 Scott Ullrich
}
622
623 3db19cf1 Scott Ullrich
/* remove a single client according to the DB entry */
624 0bd34ed6 Scott Ullrich
function captiveportal_disconnect($dbent, $radiusservers,$term_cause = 1,$stop_time = null) {
625 36254e4a Scott Ullrich
626 d99f7864 Scott Ullrich
	global $g, $config;
627
628
	$stop_time = (empty($stop_time)) ? time() : $stop_time;
629
630
	/* this client needs to be deleted - remove ipfw rules */
631 40b48c6c Ermal Lu?i
	if (isset($config['captiveportal']['radacct_enable']) && !empty($radiusservers)) {
632 d99f7864 Scott Ullrich
		RADIUS_ACCOUNTING_STOP($dbent[1], // ruleno
633
							   $dbent[4], // username
634
							   $dbent[5], // sessionid
635
							   $dbent[0], // start time
636 40b48c6c Ermal Lu?i
							   $radiusservers,
637 d99f7864 Scott Ullrich
							   $dbent[2], // clientip
638
							   $dbent[3], // clientmac
639
							   $term_cause, // Acct-Terminate-Cause
640
							   false,
641
							   $stop_time);
642
	}
643 2d53158f stompro
	/* Delete client's ip entry from tables 3 and 4. */
644 6ce61a8f Ermal
	mwexec("/sbin/ipfw table 1 delete {$dbent[2]}");
645
	mwexec("/sbin/ipfw table 2 delete {$dbent[2]}");
646 1dbe445a Ermal
647 6ce61a8f Ermal
	/* Release the ruleno so it can be reallocated to new clients. */
648
	captiveportal_free_ipfw_ruleno($dbent[1]);
649 f9f71ad3 Ermal Lu?i
650
	/* 
651
	* These are the pipe numbers we use to control traffic shaping for each logged in user via captive portal
652
	* We could get an error if the pipe doesn't exist but everything should still be fine
653
	*/
654
	if (isset($config['captiveportal']['peruserbw'])) {
655
		mwexec("/sbin/ipfw pipe " . ($dbent[1]+20000) . " delete");
656
		mwexec("/sbin/ipfw pipe " . ($dbent[1]+20001) . " delete");
657
	}
658 7a7abeba Scott Ullrich
659 6ce61a8f Ermal
	/* XXX: Redundant?! Ensure all pf(4) states are killed. */
660 7a7abeba Scott Ullrich
	mwexec("pfctl -k {$dbent[2]}");
661 b1cc2eb2 Ermal Luçi
	mwexec("pfctl -K {$dbent[2]}");
662 7a7abeba Scott Ullrich
663 3db19cf1 Scott Ullrich
}
664 12ee8fe4 Scott Ullrich
665 3db19cf1 Scott Ullrich
/* remove a single client by ipfw rule number */
666 0bd34ed6 Scott Ullrich
function captiveportal_disconnect_client($id,$term_cause = 1) {
667 36254e4a Scott Ullrich
668 d99f7864 Scott Ullrich
	global $g, $config;
669 36254e4a Scott Ullrich
670 dedf51a2 Ermal Lu?i
	$captiveportallck = lock('captiveportal');
671 36254e4a Scott Ullrich
672 d99f7864 Scott Ullrich
	/* read database */
673
	$cpdb = captiveportal_read_db();
674
	$radiusservers = captiveportal_get_radius_servers();
675
676
	/* find entry */
677 889b0934 Ermal Lu?i
	$tmpindex = 0;
678 6ce61a8f Ermal
	$cpdbcount = count($cpdb);
679
	for ($i = 0; $i < $cpdbcount; $i++) {
680 d99f7864 Scott Ullrich
		if ($cpdb[$i][1] == $id) {
681
			captiveportal_disconnect($cpdb[$i], $radiusservers, $term_cause);
682
			captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "DISCONNECT");
683 dd35bb5a Chris Buechler
			unset($cpdb[$i]);
684 d99f7864 Scott Ullrich
			break;
685
		}
686 dd35bb5a Chris Buechler
	}		
687 36254e4a Scott Ullrich
688 d99f7864 Scott Ullrich
	/* write database */
689
	captiveportal_write_db($cpdb);
690 36254e4a Scott Ullrich
691 dedf51a2 Ermal Lu?i
	unlock($captiveportallck);
692 5b237745 Scott Ullrich
}
693
694
/* send RADIUS acct stop for all current clients */
695 dedf51a2 Ermal Lu?i
function captiveportal_radius_stop_all($lock = false) {
696 d99f7864 Scott Ullrich
	global $g, $config;
697
698
	if (!isset($config['captiveportal']['radacct_enable']))
699
		return;
700
701 90455aeb Ermal Lu?i
	if (!$lock)
702 dedf51a2 Ermal Lu?i
		$captiveportallck = lock('captiveportal');
703
704 d99f7864 Scott Ullrich
	$cpdb = captiveportal_read_db();
705
706
	$radiusservers = captiveportal_get_radius_servers();
707 40b48c6c Ermal Lu?i
	if (!empty($radiusservers)) {
708 d99f7864 Scott Ullrich
		for ($i = 0; $i < count($cpdb); $i++) {
709
			RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
710
								   $cpdb[$i][4], // username
711
								   $cpdb[$i][5], // sessionid
712
								   $cpdb[$i][0], // start time
713 40b48c6c Ermal Lu?i
								   $radiusservers,
714 d99f7864 Scott Ullrich
								   $cpdb[$i][2], // clientip
715
								   $cpdb[$i][3], // clientmac
716
								   7); // Admin Reboot
717
		}
718
	}
719 90455aeb Ermal Lu?i
	if (!$lock)
720 dedf51a2 Ermal Lu?i
		unlock($captiveportallck);
721 5b237745 Scott Ullrich
}
722
723 d5ae560d Ermal
function captiveportal_passthrumac_configure_entry($macent) {
724
	$rules = "";
725
        $enBwup = isset($macent['bw_up']);
726
        $enBwdown = isset($macent['bw_down']);
727
	$actionup = "allow";
728
	$actiondown = "allow";
729
730
        if ($enBwup && $enBwdown)
731
                $ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, true);
732
        else
733
                $ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, false);
734
735
	if ($enBwup) {
736
                $bw_up = $ruleno + 20000;
737
                $rules .= "pipe {$bw_up} config bw {$macent['bw_up']}Kbit/s queue 100\n";
738
		$actionup = "pipe {$bw_up}";
739
        }
740
        if ($enBwdown) {
741
		$bw_down = $ruleno + 20001;
742
		$rules .= "pipe {$bw_down} config bw {$macent['bw_down']}Kbit/s queue 100\n";
743
		$actiondown = "pipe {$bw_down}";
744
        }
745
	$rules .= "add {$ruleno} {$actionup} ip from any to any MAC {$macent['mac']} any\n";
746
	$ruleno++;
747
	$rules .= "add {$ruleno} {$actiondown} ip from any to any MAC any {$macent['mac']}\n";
748
749
	return $rules;
750
}
751
752 dedf51a2 Ermal Lu?i
function captiveportal_passthrumac_configure($lock = false) {
753 5b237745 Scott Ullrich
	global $config, $g;
754 36254e4a Scott Ullrich
755 d5ae560d Ermal
	$rules = "";
756 36254e4a Scott Ullrich
757 5b237745 Scott Ullrich
	if (is_array($config['captiveportal']['passthrumac'])) {
758 1dbe445a Ermal
		$macdb = array();
759 5b237745 Scott Ullrich
		foreach ($config['captiveportal']['passthrumac'] as $macent) {
760 d5ae560d Ermal
			$rules .= captiveportal_passthrumac_configure_entry($macent);
761 1dbe445a Ermal
			$macdb[$macent['mac']]['active']  = true;
762
763 5b237745 Scott Ullrich
		}
764
	}
765 0bd34ed6 Scott Ullrich
766 d5ae560d Ermal
	return $rules;
767 5b237745 Scott Ullrich
}
768
769 b01792a0 Ermal
/* 
770
 * table (3=IN)/(4=OUT) hold allowed ip's without bw limits
771
 * table (5=IN)/(6=OUT) hold allowed ip's with bw limit.
772
 */
773
function captiveportal_allowedip_configure_entry($ipent) {
774
775
	$rules = "";
776
	$enBwup = isset($ipent['bw_up']);
777
	$enBwdown = isset($ipent['bw_down']);
778
	$bw_up = "";
779
        $bw_down = "";
780
        $tablein = array();
781
        $tableout = array();
782
783
	if ($enBwup && $enBwdown)
784
		$ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, true);
785
	else
786
		$ruleno = captiveportal_get_next_ipfw_ruleno(2000, 49899, false);
787
788
        if ($ipent['dir'] == "from") {
789
        	if ($enBwup)
790
                	$tablein[] = 5;
791
                else
792
                	$tablein[] = 3;
793
                if ($enBwdown)
794
                        $tableout[] = 6;
795
                else
796
                        $tableout[] = 4;
797
        } else if ($ipent['dir'] == "to") {
798
                if ($enBwup)
799
                	$tablein[] = 9;
800
                else
801
                        $tablein[] = 7;
802
                if ($enBwdown)
803
                        $tableout[] = 10;
804
                else
805
                        $tableout[] = 8;
806
        } else if ($ipent['dir'] == "both") {
807
                if ($enBwup) {
808
                        $tablein[] = 5;
809
                        $tablein[] = 9;
810
                } else {
811
                        $tablein[] = 3;
812
                        $tablein[] = 7;
813
                }
814
        	if ($enBwdown) {
815
                        $tableout[] = 6;
816
                        $tableout[] = 10;
817
                } else {
818
                        $tableout[] = 4;
819
                	$tableout[] = 8;
820
                }
821
        }
822
        if ($enBwup) {
823
                $bw_up = $ruleno + 20000;
824
        	$rules .= "pipe {$bw_up} config bw {$ipent['bw_up']}Kbit/s queue 100\n";
825
        }
826
	foreach ($tablein as $table)
827
               $rules .= "table {$table} add {$ipent['ip']} {$bw_up}\n";
828
        if ($enBwdown) {
829
               $bw_down = $ruleno + 20001;
830
               $rules .= "pipe {$bw_down} config bw {$ipent['bw_down']}Kbit/s queue 100\n";
831
        }
832
        foreach ($tableout as $table)
833
        	$rules .= "table {$table} add {$ipent['ip']} {$bw_down}\n";
834
835
	return $rules;
836
}
837
838 cb0a2913 Ermal Lu?i
function captiveportal_allowedip_configure() {
839 5b237745 Scott Ullrich
	global $config, $g;
840 36254e4a Scott Ullrich
841 6ce61a8f Ermal
	$rules = "";
842 5b237745 Scott Ullrich
	if (is_array($config['captiveportal']['allowedip'])) {
843 cb0a2913 Ermal Lu?i
		foreach ($config['captiveportal']['allowedip'] as $ipent) {
844 b01792a0 Ermal
			$rules .= captiveportal_allowedip_configure_entry($ipent);
845 cb0a2913 Ermal Lu?i
		}
846
	}
847 36254e4a Scott Ullrich
848 6ce61a8f Ermal
	return $rules;
849 5b237745 Scott Ullrich
}
850
851 2d53158f stompro
/* get last activity timestamp given client IP address */
852 f9f71ad3 Ermal Lu?i
function captiveportal_get_last_activity($ip) {
853 36254e4a Scott Ullrich
854 d99f7864 Scott Ullrich
	$ipfwoutput = "";
855 36254e4a Scott Ullrich
856 6ce61a8f Ermal
	exec("/sbin/ipfw table 1 entrystats {$ip} 2>/dev/null", $ipfwoutput);
857 f9f71ad3 Ermal Lu?i
	/* Reading only from one of the tables is enough of approximation. */
858 d99f7864 Scott Ullrich
	if ($ipfwoutput[0]) {
859
		$ri = explode(" ", $ipfwoutput[0]);
860 f9f71ad3 Ermal Lu?i
		if ($ri[4])
861
			return $ri[4];
862 d99f7864 Scott Ullrich
	}
863 36254e4a Scott Ullrich
864 d99f7864 Scott Ullrich
	return 0;
865 5b237745 Scott Ullrich
}
866
867
/* read RADIUS servers into array */
868
function captiveportal_get_radius_servers() {
869 0bd34ed6 Scott Ullrich
870
        global $g;
871
872
        if (file_exists("{$g['vardb_path']}/captiveportal_radius.db")) {
873 2f70eac7 Ermal Lu?i
                $radiusservers = array();
874 a48acf9a Ermal Lu?i
		$cpradiusdb = file("{$g['vardb_path']}/captiveportal_radius.db", 
875
			FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
876
		if ($cpradiusdb)
877 2f70eac7 Ermal Lu?i
		foreach($cpradiusdb as $cpradiusentry) {
878
                	$line = trim($cpradiusentry);
879
                        if ($line) {
880
                        	$radsrv = array();
881
                                list($radsrv['ipaddr'],$radsrv['port'],$radsrv['acctport'],$radsrv['key']) = explode(",",$line);
882
                        	$radiusservers[] = $radsrv;
883 0bd34ed6 Scott Ullrich
                        }
884 2f70eac7 Ermal Lu?i
		}
885 0bd34ed6 Scott Ullrich
886 2f70eac7 Ermal Lu?i
		return $radiusservers;
887 0bd34ed6 Scott Ullrich
        }
888
889
        return false;
890 5b237745 Scott Ullrich
}
891
892 3db19cf1 Scott Ullrich
/* log successful captive portal authentication to syslog */
893
/* part of this code from php.net */
894 0bd34ed6 Scott Ullrich
function captiveportal_logportalauth($user,$mac,$ip,$status, $message = null) {
895 d99f7864 Scott Ullrich
	$message = trim($message);
896
	// Log it
897
	if (!$message)
898 f56a73f1 Scott Ullrich
		$message = "$status: $user, $mac, $ip";
899 d99f7864 Scott Ullrich
	else
900 f56a73f1 Scott Ullrich
		$message = "$status: $user, $mac, $ip, $message";
901
	captiveportal_syslog($message);
902
	closelog();
903
}
904
905
/* log simple messages to syslog */
906
function captiveportal_syslog($message) {
907
	define_syslog_variables();
908
	$message = trim($message);
909
	openlog("logportalauth", LOG_PID, LOG_LOCAL4);
910
	// Log it
911
	syslog(LOG_INFO, $message);
912 d99f7864 Scott Ullrich
	closelog();
913 3db19cf1 Scott Ullrich
}
914
915 0bd34ed6 Scott Ullrich
function radius($username,$password,$clientip,$clientmac,$type) {
916 d44bccc7 Scott Ullrich
    global $g, $config;
917 d99f7864 Scott Ullrich
918 d44bccc7 Scott Ullrich
    /* Start locking from the beginning of an authentication session */
919 dedf51a2 Ermal Lu?i
    $captiveportallck = lock('captiveportal');
920 0bd34ed6 Scott Ullrich
921 d44bccc7 Scott Ullrich
    $ruleno = captiveportal_get_next_ipfw_ruleno();
922
923 2d53158f stompro
    /* If the pool is empty, return appropriate message and fail authentication */
924 d44bccc7 Scott Ullrich
    if (is_null($ruleno)) {
925
        $auth_list = array();
926
        $auth_list['auth_val'] = 1;
927
        $auth_list['error'] = "System reached maximum login capacity";
928 dedf51a2 Ermal Lu?i
        unlock($captiveportallck);
929 d44bccc7 Scott Ullrich
        return $auth_list;
930
    }
931
932 2f70eac7 Ermal Lu?i
    /*
933
     * Drop the lock since radius takes some time to finish.
934
     * The implementation is reentrant so we gain speed with this.
935
     */
936
    unlock($captiveportallck);
937
938 d44bccc7 Scott Ullrich
    $radiusservers = captiveportal_get_radius_servers();
939
940
    $auth_list = RADIUS_AUTHENTICATION($username,
941
                    $password,
942
                    $radiusservers,
943
                    $clientip,
944
                    $clientmac,
945
                    $ruleno);
946
947 2f70eac7 Ermal Lu?i
    $captiveportallck = lock('captiveportal');
948
949 d44bccc7 Scott Ullrich
    if ($auth_list['auth_val'] == 2) {
950
        captiveportal_logportalauth($username,$clientmac,$clientip,$type);
951
        $sessionid = portal_allow($clientip,
952
                    $clientmac,
953
                    $username,
954
                    $password,
955
                    $auth_list,
956
                    $ruleno);
957
    }
958 9befcca7 Ermal Lu?i
959 dedf51a2 Ermal Lu?i
    unlock($captiveportallck);
960 d44bccc7 Scott Ullrich
961
    return $auth_list;
962 0bd34ed6 Scott Ullrich
963
}
964
965
/* read captive portal DB into array */
966
function captiveportal_read_db() {
967
968
        global $g;
969
970
        $cpdb = array();
971
        $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r");
972
        if ($fd) {
973
                while (!feof($fd)) {
974
                        $line = trim(fgets($fd));
975
                        if ($line) {
976
                                $cpdb[] = explode(",", $line);
977
                        }
978
                }
979
                fclose($fd);
980
        }
981
        return $cpdb;
982
}
983
984
/* write captive portal DB */
985
function captiveportal_write_db($cpdb) {
986 d44bccc7 Scott Ullrich
                 
987 0bd34ed6 Scott Ullrich
        global $g;
988 d44bccc7 Scott Ullrich
                
989 0bd34ed6 Scott Ullrich
        $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w");
990 d44bccc7 Scott Ullrich
        if ($fd) { 
991 0bd34ed6 Scott Ullrich
                foreach ($cpdb as $cpent) {
992
                        fwrite($fd, join(",", $cpent) . "\n");
993 d44bccc7 Scott Ullrich
                }       
994 0bd34ed6 Scott Ullrich
                fclose($fd);
995 d44bccc7 Scott Ullrich
        }       
996 0bd34ed6 Scott Ullrich
}
997
998
function captiveportal_write_elements() {
999 d44bccc7 Scott Ullrich
    global $g, $config;
1000
    
1001
    /* delete any existing elements */
1002
    if (is_dir($g['captiveportal_element_path'])) {
1003
        $dh = opendir($g['captiveportal_element_path']);
1004
        while (($file = readdir($dh)) !== false) {
1005
            if ($file != "." && $file != "..")
1006
                unlink($g['captiveportal_element_path'] . "/" . $file);
1007
        }
1008
        closedir($dh);
1009
    } else {
1010 5209079f Ermal Luçi
        @mkdir($g['captiveportal_element_path']);
1011 d44bccc7 Scott Ullrich
    }
1012
    
1013 1fadb31d Scott Ullrich
	if (is_array($config['captiveportal']['element'])) {
1014
		conf_mount_rw();
1015
		foreach ($config['captiveportal']['element'] as $data) {
1016
			$fd = @fopen($g['captiveportal_element_path'] . '/' . $data['name'], "wb");
1017
			if (!$fd) {
1018
				printf("Error: cannot open '{$data['name']}' in captiveportal_write_elements().\n");
1019
				return 1;
1020
			}
1021
			$decoded = base64_decode($data['content']);
1022
			fwrite($fd,$decoded);
1023
			fclose($fd);
1024
			unlink_if_exists("{$g['captiveportal_path']}/{$data['name']}");
1025
			unlink_if_exists("{$g['captiveportal_path']}/{$data['name']}");
1026
			mwexec("cd {$g['captiveportal_path']}/ && ln -s {$g['captiveportal_element_path']}/{$data['name']} {$data['name']}");
1027
		}
1028
		conf_mount_ro();
1029
	}
1030 d44bccc7 Scott Ullrich
    
1031
    return 0;
1032 0bd34ed6 Scott Ullrich
}
1033
1034 6ce61a8f Ermal
function captiveportal_init_ipfw_ruleno($rulenos_start = 2000, $rulenos_range_max = 49899) {
1035
	global $g;
1036
1037
	@unlink("{$g['vardb_path']}/captiveportal.rules");
1038
	$rules = array_pad(array(), $rulenos_range_max - $rulenos_start, false);
1039
	file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules));
1040
}
1041
1042 920cafaf Scott Ullrich
/*
1043
 * This function will calculate the lowest free firewall ruleno
1044 f9f71ad3 Ermal Lu?i
 * within the range specified based on the actual logged on users
1045 920cafaf Scott Ullrich
 *
1046
 */
1047 b01792a0 Ermal
function captiveportal_get_next_ipfw_ruleno($rulenos_start = 2000, $rulenos_range_max = 49899, $usebw = false) {
1048 f9f71ad3 Ermal Lu?i
	global $config, $g;
1049 6ce61a8f Ermal
1050 de752609 Scott Ullrich
	if(!isset($config['captiveportal']['enable']))
1051 01d57b8c Scott Ullrich
		return NULL;
1052 6ce61a8f Ermal
1053 f9f71ad3 Ermal Lu?i
	$ruleno = 0;
1054 6ce61a8f Ermal
	if (file_exists("{$g['vardb_path']}/captiveportal.rules")) {
1055
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules"));
1056
		for ($ridx = 2; $ridx < ($rulenos_range_max - $rulenos_start); $ridx++) {
1057
			if ($rules[$ridx]) {
1058
				/* 
1059
	 			 * This allows our traffic shaping pipes to be the in pipe the same as ruleno 
1060
	 			 * and the out pipe ruleno + 1. This removes limitation that where present in 
1061
	 			 * previous version of the peruserbw.
1062
	 			 */
1063
				if (isset($config['captiveportal']['peruserbw']))
1064
					$ridx++;
1065
				continue;
1066
			}
1067
			$ruleno = $ridx;
1068
			$rules[$ridx] = "used";
1069 b01792a0 Ermal
			if (isset($config['captiveportal']['peruserbw']) || $usebw == true)
1070 6ce61a8f Ermal
				$rules[++$ridx] = "used";
1071
			break;
1072
		}
1073
	} else {
1074
		$rules = array_pad(array(), $rulenos_range_max - $rulenos_start, false);
1075
		$rules[2] = "used";
1076
		$ruleno = 2;
1077
	}
1078
	file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules));
1079
	return $ruleno;
1080
}
1081
1082 b01792a0 Ermal
function captiveportal_free_ipfw_ruleno($ruleno, $usedbw = false) {
1083 6ce61a8f Ermal
	global $config, $g;
1084
1085
	if(!isset($config['captiveportal']['enable']))
1086
		return NULL;
1087
1088
	if (file_exists("{$g['vardb_path']}/captiveportal.rules")) {
1089
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules"));
1090
		$rules[$ruleno] = false;
1091 b01792a0 Ermal
		if (isset($config['captiveportal']['peruserbw']) || $usedbw == true)
1092 6ce61a8f Ermal
			$rules[++$ruleno] = false;
1093
		file_put_contents("{$g['vardb_path']}/captiveportal.rules", serialize($rules));
1094 f9f71ad3 Ermal Lu?i
	}
1095 6ce61a8f Ermal
}
1096
1097 d5ae560d Ermal
function captiveportal_get_ipfw_passthru_ruleno($value) {
1098 6ce61a8f Ermal
	global $config, $g;
1099
1100
	if(!isset($config['captiveportal']['enable']))
1101
                return NULL;
1102
1103
        if (file_exists("{$g['vardb_path']}/captiveportal.rules")) {
1104
                $rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportal.rules"));
1105 d5ae560d Ermal
		$ruleno = intval(`/sbin/ipfw show | /usr/bin/grep {$value} |  /usr/bin/grep -v grep | /usr/bin/cut -d " " -f 1 | /usr/bin/head -n 1`);
1106 6ce61a8f Ermal
		if ($rules[$ruleno])
1107
			return $ruleno;
1108
        }
1109
1110 f9f71ad3 Ermal Lu?i
	return NULL;
1111 920cafaf Scott Ullrich
}
1112
1113 360d815d Scott Ullrich
/**
1114
 * This function will calculate the traffic produced by a client
1115
 * based on its firewall rule
1116
 *
1117
 * Point of view: NAS
1118
 *
1119
 * Input means: from the client
1120
 * Output means: to the client
1121
 *
1122
 */
1123
1124 f9f71ad3 Ermal Lu?i
function getVolume($ip) {
1125 360d815d Scott Ullrich
1126
    $volume = array();
1127
1128
    // Initialize vars properly, since we don't want NULL vars
1129
    $volume['input_pkts'] = $volume['input_bytes'] = $volume['output_pkts'] = $volume['output_bytes'] = 0 ;
1130
1131
    // Ingress
1132 f9f71ad3 Ermal Lu?i
    $ipfwin = "";
1133
    $ipfwout = "";
1134
    $matchesin = "";
1135
    $matchesout = "";
1136 6ce61a8f Ermal
    exec("/sbin/ipfw table 1 entrystats {$ip}", $ipfwin);
1137 f9f71ad3 Ermal Lu?i
    if ($ipfwin[0]) {
1138 523855b0 Scott Ullrich
		$ipfwin = split(" ", $ipfwin[0]);
1139
		$volume['input_pkts'] = $ipfwin[2];
1140
		$volume['input_bytes'] = $ipfwin[3];
1141 f9f71ad3 Ermal Lu?i
    }
1142
1143 6ce61a8f Ermal
    exec("/sbin/ipfw table 2 entrystats {$ip}", $ipfwout);
1144 f9f71ad3 Ermal Lu?i
    if ($ipfwout[0]) {
1145
        $ipfwout = split(" ", $ipfwout[0]);
1146
        $volume['output_pkts'] = $ipfwout[2];
1147
        $volume['output_bytes'] = $ipfwout[3];
1148
    }
1149 360d815d Scott Ullrich
1150
    return $volume;
1151
}
1152
1153 856e58a6 Scott Ullrich
/**
1154
 * Get the NAS-Identifier
1155
 *
1156
 * We will use our local hostname to make up the nas_id
1157
 */
1158
function getNasID()
1159
{
1160 84e5047d Scott Ullrich
    $nasId = "";
1161 856e58a6 Scott Ullrich
    exec("/bin/hostname", $nasId);
1162
    if(!$nasId[0])
1163 36d0358b Scott Ullrich
        $nasId[0] = "{$g['product_name']}";
1164 856e58a6 Scott Ullrich
    return $nasId[0];
1165
}
1166
1167
/**
1168
 * Get the NAS-IP-Address based on the current wan address
1169
 *
1170
 * Use functions in interfaces.inc to find this out
1171
 *
1172
 */
1173
1174
function getNasIP()
1175
{
1176 85a5da13 Ermal Luçi
    $nasIp = get_interface_ip();
1177 856e58a6 Scott Ullrich
    if(!$nasIp)
1178
        $nasIp = "0.0.0.0";
1179
    return $nasIp;
1180
}
1181
1182 f8b11310 Ermal Lu?i
function portal_ip_from_client_ip($cliip) {
1183
	global $config;
1184
1185
	$interfaces = explode(",", $config['captiveportal']['interface']);
1186
	foreach ($interfaces as $cpif) {
1187
		$ip = get_interface_ip($cpif);
1188
		$sn = get_interface_subnet($cpif);
1189
		if (ip_in_subnet($cliip, "{$ip}/{$sn}"))
1190
			return $ip;
1191
	}
1192
1193 cc125e13 Chris Buechler
	// doesn't match up to any particular interface
1194
	// so let's set the portal IP to what PHP says 
1195
	// the server IP issuing the request is. 
1196
	// allows same behavior as 1.2.x where IP isn't 
1197
	// in the subnet of any CP interface (static routes, etc.)
1198
	// rather than forcing to DNS hostname resolution
1199
	$ip = $_SERVER['SERVER_ADDR'];
1200
	if (is_ipaddr($ip))
1201
		return $ip;
1202
1203 f8b11310 Ermal Lu?i
	return false;
1204
}
1205
1206 2ee45728 Ermal Lu?i
?>