Project

General

Profile

Download (35.6 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 3db19cf1 Scott Ullrich
require_once("radius_authentication.inc");
45 0bd34ed6 Scott Ullrich
require_once("radius_accounting.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 c6c92abf Scott Ullrich
		/* kill any running minicron */
106
		killbypid("{$g['varrun_path']}/minicron.pid");
107 36254e4a Scott Ullrich
108 2b5b6eb4 Ermal Lu?i
		/* make sure ipfw is loaded */
109
		if (!is_module_loaded("ipfw.ko"))
110
			filter_load_ipfw();
111
		if (isset($config['captiveportal']['peruserbw']) && !is_module_loaded("dummynet.ko"))
112
                        mwexec("/sbin/kldload dummynet");
113
114 3db19cf1 Scott Ullrich
		/* generate ipfw rules */
115 f8b11310 Ermal Lu?i
		$cprules = captiveportal_rules_generate($cpinterface, $cpips);
116 36254e4a Scott Ullrich
117 5b237745 Scott Ullrich
		/* stop accounting on all clients */
118 dedf51a2 Ermal Lu?i
		captiveportal_radius_stop_all(true);
119 5b237745 Scott Ullrich
120 0bd34ed6 Scott Ullrich
		/* initialize minicron interval value */
121
		$croninterval = $config['captiveportal']['croninterval'] ? $config['captiveportal']['croninterval'] : 60;
122
123
		/* double check if the $croninterval is numeric and at least 10 seconds. If not we set it to 60 to avoid problems */
124
		if ((!is_numeric($croninterval)) || ($croninterval < 10)) { $croninterval = 60; }
125
126 5b237745 Scott Ullrich
		/* remove old information */
127 d99f7864 Scott Ullrich
		unlink_if_exists("{$g['vardb_path']}/captiveportal.nextrule");
128
		unlink_if_exists("{$g['vardb_path']}/captiveportal.db");
129
		unlink_if_exists("{$g['vardb_path']}/captiveportal_mac.db");
130
		unlink_if_exists("{$g['vardb_path']}/captiveportal_ip.db");
131
		unlink_if_exists("{$g['vardb_path']}/captiveportal_radius.db");
132 f211aa59 Ermal Lu?i
		mwexec("/sbin/ipfw table all flush");
133 36254e4a Scott Ullrich
134 27d07d2d Scott Ullrich
		/* setup new database in case someone tries to access the status -> captive portal page */
135
		touch("{$g['vardb_path']}/captiveportal.db");
136
137 5b237745 Scott Ullrich
		/* write portal page */
138
		if ($config['captiveportal']['page']['htmltext'])
139
			$htmltext = base64_decode($config['captiveportal']['page']['htmltext']);
140
		else {
141
			/* example/template page */
142
			$htmltext = <<<EOD
143
<html>
144
<head>
145 36d0358b Scott Ullrich
<title>{$g['product_name']} captive portal</title>
146 5b237745 Scott Ullrich
</head>
147 3db19cf1 Scott Ullrich
<body>
148 a515d275 Scott Ullrich
<center>
149 36d0358b Scott Ullrich
<h2>{$g['product_name']} captive portal</h2>
150
Welcome to the {$g['product_name']} Captive Portal!  This is the default page since a custom page has not been defined.
151 14d2d21b Scott Ullrich
<p>
152 a515d275 Scott Ullrich
<form method="post" action="\$PORTAL_ACTION\$">
153
<input name="redirurl" type="hidden" value="\$PORTAL_REDIRURL\$">
154 407a29ca Scott Ullrich
<table>
155
   <tr><td>Username:</td><td><input name="auth_user" type="text"></td></tr>
156
   <tr><td>Password:</td><td><input name="auth_pass" type="password"></td></tr>
157 a515d275 Scott Ullrich
   <tr><td>&nbsp;</td></tr>
158 14d2d21b Scott Ullrich
   <tr>
159
     <td colspan="2">
160 0bd34ed6 Scott Ullrich
	<center><input name="accept" type="submit" value="Continue"></center>
161 14d2d21b Scott Ullrich
     </td>
162
   </tr>
163 407a29ca Scott Ullrich
</table>
164 a515d275 Scott Ullrich
</center>
165 407a29ca Scott Ullrich
</form>
166 5b237745 Scott Ullrich
</body>
167
</html>
168
169 0bd34ed6 Scott Ullrich
170
171 5b237745 Scott Ullrich
EOD;
172
		}
173
174
		$fd = @fopen("{$g['varetc_path']}/captiveportal.html", "w");
175
		if ($fd) {
176
			fwrite($fd, $htmltext);
177 36254e4a Scott Ullrich
			fclose($fd);
178 5b237745 Scott Ullrich
		}
179 36254e4a Scott Ullrich
180 5b237745 Scott Ullrich
		/* write error page */
181
		if ($config['captiveportal']['page']['errtext'])
182
			$errtext = base64_decode($config['captiveportal']['page']['errtext']);
183
		else {
184
			/* example page */
185
			$errtext = <<<EOD
186
<html>
187
<head>
188
<title>Authentication error</title>
189
</head>
190
<body>
191
<font color="#cc0000"><h2>Authentication error</h2></font>
192
<b>
193
Username and/or password invalid.
194
<br><br>
195 2b0eeeaa Ermal Lu?i
<a href="javascript:history.back(); ">Go back</a>
196 5b237745 Scott Ullrich
</b>
197
</body>
198
</html>
199
200
EOD;
201
		}
202
203
		$fd = @fopen("{$g['varetc_path']}/captiveportal-error.html", "w");
204
		if ($fd) {
205
			fwrite($fd, $errtext);
206 36254e4a Scott Ullrich
			fclose($fd);
207 5b237745 Scott Ullrich
		}
208 36254e4a Scott Ullrich
209 0bd34ed6 Scott Ullrich
		/* write elements */
210
		captiveportal_write_elements();
211 5b237745 Scott Ullrich
212 3db19cf1 Scott Ullrich
		/* load rules */
213
		mwexec("/sbin/ipfw -f delete set 1");
214 36254e4a Scott Ullrich
215 63fff79b Scott Ullrich
		/* ipfw cannot accept rules directly on stdin,
216 3db19cf1 Scott Ullrich
		   so we have to write them to a temporary file first */
217
		$fd = @fopen("{$g['tmp_path']}/ipfw.cp.rules", "w");
218
		if (!$fd) {
219
			printf("Cannot open ipfw.cp.rules in captiveportal_configure()\n");
220
			return 1;
221
		}
222 36254e4a Scott Ullrich
223 3db19cf1 Scott Ullrich
		fwrite($fd, $cprules);
224
		fclose($fd);
225 36254e4a Scott Ullrich
226 3db19cf1 Scott Ullrich
		mwexec("/sbin/ipfw {$g['tmp_path']}/ipfw.cp.rules");
227 36254e4a Scott Ullrich
228 3db19cf1 Scott Ullrich
		unlink("{$g['tmp_path']}/ipfw.cp.rules");
229 36254e4a Scott Ullrich
230 3db19cf1 Scott Ullrich
		/* filter on layer2 as well so we can check MAC addresses */
231
		mwexec("/sbin/sysctl net.link.ether.ipfw=1");
232 36254e4a Scott Ullrich
233 5b237745 Scott Ullrich
		chdir($g['captiveportal_path']);
234 36254e4a Scott Ullrich
235 9b5a1292 Scott Ullrich
		if ($config['captiveportal']['maxproc'])
236
			$maxproc = $config['captiveportal']['maxproc'];
237
		else
238
			$maxproc = 16;
239 36254e4a Scott Ullrich
240 3e1b0033 Scott Ullrich
		$use_fastcgi = true;
241
242 40b9f8c0 Scott Ullrich
		if(isset($config['captiveportal']['httpslogin'])) {
243
			$cert = base64_decode($config['captiveportal']['certificate']);
244
			$key = base64_decode($config['captiveportal']['private-key']);
245 63fff79b Scott Ullrich
			/* generate lighttpd configuration */
246
			system_generate_lighty_config("{$g['varetc_path']}/lighty-CaptivePortal-SSL.conf",
247 2cf6ddcb Nigel Graham
				$cert, $key, "", "lighty-CaptivePortal-ssl.pid", "8001", "/usr/local/captiveportal/",
248 29dc1e6e Nigel Graham
					"cert-portal.pem", "ca-portal.pem", "1", $maxproc, $use_fastcgi, true);
249 40b9f8c0 Scott Ullrich
		}
250 36254e4a Scott Ullrich
251 877ac35d Scott Ullrich
		/* generate lighttpd configuration */
252
		system_generate_lighty_config("{$g['varetc_path']}/lighty-CaptivePortal.conf",
253 2cf6ddcb Nigel Graham
			"", "", "", "lighty-CaptivePortal.pid", "8000", "/usr/local/captiveportal/",
254 29dc1e6e Nigel Graham
				"cert-portal.pem", "ca-portal.pem", "1", $maxproc, $use_fastcgi, true);
255 36254e4a Scott Ullrich
256 877ac35d Scott Ullrich
		/* attempt to start lighttpd */
257
		$res = mwexec("/usr/local/sbin/lighttpd -f {$g['varetc_path']}/lighty-CaptivePortal.conf");
258 36254e4a Scott Ullrich
259 63fff79b Scott Ullrich
		/* fire up https instance */
260
		if(isset($config['captiveportal']['httpslogin']))
261
			$res = mwexec("/usr/local/sbin/lighttpd -f {$g['varetc_path']}/lighty-CaptivePortal-SSL.conf");
262 36254e4a Scott Ullrich
263 0bd34ed6 Scott Ullrich
		/* start pruning process (interval defaults to 60 seconds) */
264
		mwexec("/usr/local/bin/minicron $croninterval {$g['varrun_path']}/minicron.pid " .
265 c6c92abf Scott Ullrich
			"/etc/rc.prunecaptiveportal");
266 36254e4a Scott Ullrich
267 5b237745 Scott Ullrich
		/* generate passthru mac database */
268 dedf51a2 Ermal Lu?i
		captiveportal_passthrumac_configure(true);
269 cb0a2913 Ermal Lu?i
		/* allowed ipfw rules to make allowed ip work */
270
		captiveportal_allowedip_configure();
271 5b237745 Scott Ullrich
272 d99f7864 Scott Ullrich
		/* generate radius server database */
273
		if ($config['captiveportal']['radiusip'] && (!isset($config['captiveportal']['auth_method']) ||
274
				($config['captiveportal']['auth_method'] == "radius"))) {
275
			$radiusip = $config['captiveportal']['radiusip'];
276
			$radiusip2 = ($config['captiveportal']['radiusip2']) ? $config['captiveportal']['radiusip2'] : null;
277
278
			if ($config['captiveportal']['radiusport'])
279
				$radiusport = $config['captiveportal']['radiusport'];
280
			else
281
				$radiusport = 1812;
282
283
			if ($config['captiveportal']['radiusacctport'])
284
				$radiusacctport = $config['captiveportal']['radiusacctport'];
285
			else
286
				$radiusacctport = 1813;
287
288
			if ($config['captiveportal']['radiusport2'])
289
				$radiusport2 = $config['captiveportal']['radiusport2'];
290
			else
291
				$radiusport2 = 1812;
292
293
			$radiuskey = $config['captiveportal']['radiuskey'];
294
			$radiuskey2 = ($config['captiveportal']['radiuskey2']) ? $config['captiveportal']['radiuskey2'] : null;
295
296
			$fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db", "w");
297
			if (!$fd) {
298
				printf("Error: cannot open radius DB file in captiveportal_configure().\n");
299
				return 1;
300
			} else if (isset($radiusip2, $radiuskey2)) {
301
				fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey . "\n"
302
					 . $radiusip2 . "," . $radiusport2 . "," . $radiusacctport . "," . $radiuskey2);
303
			} else {
304
				fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey);
305
			}
306
			fclose($fd);
307
		}
308 36254e4a Scott Ullrich
309 d99f7864 Scott Ullrich
		if ($g['booting'])
310
			echo "done\n";
311 36254e4a Scott Ullrich
312 5b237745 Scott Ullrich
	} else {
313 23a0c341 Scott Ullrich
		killbypid("{$g['varrun_path']}/lighty-CaptivePortal.pid");
314 c6c92abf Scott Ullrich
		killbypid("{$g['varrun_path']}/minicron.pid");
315 12ee8fe4 Scott Ullrich
316 dedf51a2 Ermal Lu?i
		captiveportal_radius_stop_all(true);
317 3db19cf1 Scott Ullrich
318
		mwexec("/sbin/sysctl net.link.ether.ipfw=0");
319
320 5209079f Ermal Luçi
		/* unload ipfw */
321 faebbab3 Scott Ullrich
		if (is_module_loaded("ipfw.ko"))		
322
			mwexec("/sbin/kldunload ipfw.ko");
323 85250056 Ermal Lu?i
		$listifs = get_configured_interface_list_by_realif();
324 bbc6768b Ermal Lu?i
		foreach ($listifs as $listrealif => $listif) {
325 7c587b9f Ermal Lu?i
			if (!empty($listrealif)) {
326
				mwexec("/sbin/ifconfig {$listrealif} -ipfwfilter");
327
				$carpif = link_ip_to_carp_interface(find_interface_ip($listrealif));
328
			}
329 bbc6768b Ermal Lu?i
                        if (!empty($carpif)) {
330
				$carpsif = explode(" ", $carpif);
331
				foreach ($carpsif as $cpcarp)
332
					mwexec("/sbin/ifconfig {$cpcarp} -ipfwfilter");
333
			}
334
		}
335 3db19cf1 Scott Ullrich
	}
336 36254e4a Scott Ullrich
337 f8b11310 Ermal Lu?i
	unlock($captiveportallck);
338 c3214b80 Scott Ullrich
	
339 5b237745 Scott Ullrich
	return 0;
340
}
341
342 f8b11310 Ermal Lu?i
function captiveportal_rules_generate($cpif, &$cpiparray) {
343 3db19cf1 Scott Ullrich
	global $config, $g;
344 36254e4a Scott Ullrich
345 3db19cf1 Scott Ullrich
	$cpifn = $config['captiveportal']['interface'];
346 a55e9c70 Ermal Lu?i
	$lanip = get_interface_ip("lan");
347 421f8b5f Scott Ullrich
	
348 3db19cf1 Scott Ullrich
	/* note: the captive portal daemon inserts all pass rules for authenticated
349
	   clients as skipto 50000 rules to make traffic shaping work */
350
351 258d082a Scott Ullrich
	$cprules =  "add 500 set 1 allow pfsync from any to any\n";
352
	$cprules .= "add 500 set 1 allow carp from any to any\n";
353 181a843c Scott Ullrich
354 3db19cf1 Scott Ullrich
	$cprules .= <<<EOD
355 f9f71ad3 Ermal Lu?i
add 1000 set 1 skipto 1150 all from any to any not layer2
356 3db19cf1 Scott Ullrich
# layer 2: pass ARP
357
add 1100 set 1 pass layer2 mac-type arp
358 b9d1d810 Scott Ullrich
# pfsense requires for WPA
359
add 1100 set 1 pass layer2 mac-type 0x888e
360 68f34650 Scott Ullrich
add 1100 set 1 pass layer2 mac-type 0x88c7
361 684c787e Scott Ullrich
362 36254e4a Scott Ullrich
# PPP Over Ethernet Discovery Stage
363 684c787e Scott Ullrich
add 1100 set 1 pass layer2 mac-type 0x8863
364
# PPP Over Ethernet Session Stage
365
add 1100 set 1 pass layer2 mac-type 0x8864
366 55f5c311 Ermal Lu?i
# Allow WPA
367
add 1100 set 1 pass layer2 mac-type 0x888e
368 684c787e Scott Ullrich
369 3db19cf1 Scott Ullrich
# layer 2: block anything else non-IP
370
add 1101 set 1 deny layer2 not mac-type ip
371
372
EOD;
373
374 f9f71ad3 Ermal Lu?i
	$rulenum = 1150;
375 f8b11310 Ermal Lu?i
	foreach ($cpiparray as $cpip) {
376
		//# allow access to our DHCP server (which needs to be able to ping clients as well)
377
		$cprules .= "add {$rulenum} set 1 pass udp from any 68 to 255.255.255.255 67 in \n";
378
		$rulenum++;
379
		$cprules .= "add {$rulenum} set 1 pass udp from any 68 to {$cpip} 67 in \n";
380
		$rulenum++;
381
		$cprules .= "add {$rulenum} set 1 pass udp from {$cpip} 67 to any 68 out \n";
382
		$rulenum++;
383
		$cprules .= "add {$rulenum} set 1 pass icmp from {$cpip} to any out icmptype 8\n";
384
		$rulenum++;
385
		$cprules .= "add {$rulenum} set 1 pass icmp from any to {$cpip} in icmptype 0 \n";
386
		$rulenum++;
387
		//# allow access to our DNS forwarder
388 0bcff7b2 Ermal Lu?i
		$cprules .= "add {$rulenum} set 1 pass udp from {$cpip} to any 53 in \n";
389 f8b11310 Ermal Lu?i
		$rulenum++;
390 734f3966 Chris Buechler
		$cprules .= "add {$rulenum} set 1 pass udp from any to {$cpip} 53 in \n";
391
		$rulenum++;
392 0bcff7b2 Ermal Lu?i
		$cprules .= "add {$rulenum} set 1 pass udp from {$cpip} 53 to any out \n";
393 f8b11310 Ermal Lu?i
		$rulenum++;
394
		# allow access to our web server
395
		$cprules .= "add {$rulenum} set 1 pass tcp from any to {$cpip} 8000 in \n";
396
		$rulenum++;
397
		$cprules .= "add {$rulenum} set 1 pass tcp from {$cpip} 8000 to any out \n";
398
399
		if (isset($config['captiveportal']['httpslogin'])) {
400
			$rulenum++;
401
			$cprules .= "add {$rulenum} set 1 pass tcp from any to {$cpip} 8001 in \n";
402
			$rulenum++;
403
			$cprules .= "add {$rulenum} set 1 pass tcp from {$cpip} 8001 to any out \n";
404
		}
405 3db19cf1 Scott Ullrich
	}
406 f8b11310 Ermal Lu?i
	$rulenum++;
407 5480497a Scott Ullrich
408 f9f71ad3 Ermal Lu?i
	if (isset($config['captiveportal']['peruserbw'])) {
409 f6fbd03e Ermal Lu?i
		$cprules .= "add {$rulenum} set 1 pipe tablearg ip from table(3) to any in\n";
410 f9f71ad3 Ermal Lu?i
		$rulenum++;
411 f6fbd03e Ermal Lu?i
		$cprules .= "add {$rulenum} set 1 pipe tablearg ip from any to table(4) out\n";
412 f9f71ad3 Ermal Lu?i
		$rulenum++;
413
	} else {
414 f6fbd03e Ermal Lu?i
		$cprules .= "add {$rulenum} set 1 skipto 50000 ip from table(3) to any in\n";
415 f9f71ad3 Ermal Lu?i
                $rulenum++;
416 f6fbd03e Ermal Lu?i
                $cprules .= "add {$rulenum} set 1 skipto 50000 ip from any to table(4) out\n";
417 f9f71ad3 Ermal Lu?i
                $rulenum++;
418
	}
419
	
420
       $cprules .= <<<EOD
421 5480497a Scott Ullrich
422 d44bccc7 Scott Ullrich
# redirect non-authenticated clients to captive portal
423 98a414a4 Ermal Lu?i
add 1990 set 1 fwd 127.0.0.1,8000 tcp from any to any in
424 3db19cf1 Scott Ullrich
# let the responses from the captive portal web server back out
425 6b20b7a8 Ermal Lu?i
add 1991 set 1 pass tcp from any to any out
426 3db19cf1 Scott Ullrich
# block everything else
427 f9f71ad3 Ermal Lu?i
add 1992 set 1 deny all from any to any
428 3db19cf1 Scott Ullrich
429 f9f71ad3 Ermal Lu?i
# ... 2000-49899: layer2 block rules per authenticated client go here...
430 3db19cf1 Scott Ullrich
431
# pass everything else on layer2
432 f9f71ad3 Ermal Lu?i
add 49900 set 1 pass all from any to any layer2
433 3db19cf1 Scott Ullrich
434
EOD;
435
436 d44bccc7 Scott Ullrich
    return $cprules;
437 3db19cf1 Scott Ullrich
}
438
439 5b237745 Scott Ullrich
/* remove clients that have been around for longer than the specified amount of time */
440 36254e4a Scott Ullrich
/* db file structure:
441 0bd34ed6 Scott Ullrich
timestamp,ipfw_rule_no,clientip,clientmac,username,sessionid,password,session_timeout,idle_timeout,session_terminate_time */
442
443 3db19cf1 Scott Ullrich
/* (password is in Base64 and only saved when reauthentication is enabled) */
444 5b237745 Scott Ullrich
function captiveportal_prune_old() {
445 0bd34ed6 Scott Ullrich
446 23c4f978 Scott Ullrich
    global $g, $config;
447
448
    /* check for expired entries */
449
    if ($config['captiveportal']['timeout'])
450
        $timeout = $config['captiveportal']['timeout'] * 60;
451
    else
452
        $timeout = 0;
453
454
    if ($config['captiveportal']['idletimeout'])
455
        $idletimeout = $config['captiveportal']['idletimeout'] * 60;
456
    else
457
        $idletimeout = 0;
458
459 336e3c1c Charlie
    if (!$timeout && !$idletimeout && !isset($config['captiveportal']['reauthenticate']) && 
460
		!isset($config['captiveportal']['radiussession_timeout']) && !isset($config['voucher']['enable']))
461 23c4f978 Scott Ullrich
        return;
462
463 dedf51a2 Ermal Lu?i
    $captiveportallck = lock('captiveportal');
464 23c4f978 Scott Ullrich
465
    /* read database */
466
    $cpdb = captiveportal_read_db();
467
468
    $radiusservers = captiveportal_get_radius_servers();
469
470 2d53158f stompro
    /*  To make sure we iterate over ALL accounts on every run the count($cpdb) is moved
471
     *  outside of the loop. Otherwise the loop would evaluate count() on every iteration
472
     *  and since $i would increase and count() would decrement they would meet before we
473
     *  had a chance to iterate over all accounts.
474 f56a73f1 Scott Ullrich
     */
475 8e51cc6a Ermal Lu?i
    $unsetindexes = array();
476 f56a73f1 Scott Ullrich
    $no_users = count($cpdb);
477
    for ($i = 0; $i < $no_users; $i++) {
478 23c4f978 Scott Ullrich
479
        $timedout = false;
480
        $term_cause = 1;
481
482 5bada54e Scott Ullrich
		/* no pruning for fixed mac address entry */
483
		if (portal_mac_fixed($cpdb[$i][3])) {
484
			continue; // check next value
485
		}
486 23c4f978 Scott Ullrich
        /* hard timeout? */
487
        if ($timeout) {
488
            if ((time() - $cpdb[$i][0]) >= $timeout) {
489
                $timedout = true;
490
                $term_cause = 5; // Session-Timeout
491
            }
492
        }
493
494
        /* Session-Terminate-Time */
495
        if (!$timedout && !empty($cpdb[$i][9])) {
496
            if (time() >= $cpdb[$i][9]) {
497
                $timedout = true;
498
                $term_cause = 5; // Session-Timeout
499
            }
500
        }
501
502
        /* check if the radius idle_timeout attribute has been set and if its set change the idletimeout to this value */
503
        $idletimeout = (is_numeric($cpdb[$i][8])) ? $cpdb[$i][8] : $idletimeout;
504
        /* if an idle timeout is specified, get last activity timestamp from ipfw */
505
        if (!$timedout && $idletimeout) {
506 f9f71ad3 Ermal Lu?i
            $lastact = captiveportal_get_last_activity($cpdb[$i][2]);
507 2d53158f stompro
			/*  If the user has logged on but not sent any traffic they will never be logged out.
508
			 *  We "fix" this by setting lastact to the login timestamp. 
509 f56a73f1 Scott Ullrich
			 */
510
			$lastact = $lastact ? $lastact : $cpdb[$i][0];
511 23c4f978 Scott Ullrich
            if ($lastact && ((time() - $lastact) >= $idletimeout)) {
512
                $timedout = true;
513
                $term_cause = 4; // Idle-Timeout
514
                $stop_time = $lastact; // Entry added to comply with WISPr
515
            }
516
        }
517
518 336e3c1c Charlie
	/* if vouchers are configured, activate session timeouts */
519
	if (!$timedout && isset($config['voucher']['enable']) && !empty($cpdb[$i][7])) {
520
		if (time() >= ($cpdb[$i][0] + $cpdb[$i][7])) {
521
			$timedout = true;
522
			$term_cause = 5; // Session-Timeout
523
		}
524
	}
525
526 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 */
527
        if (!$timedout && isset($config['captiveportal']['radiussession_timeout']) && !empty($cpdb[$i][7])) {
528
            if (time() >= ($cpdb[$i][0] + $cpdb[$i][7])) {
529
                $timedout = true;
530
                $term_cause = 5; // Session-Timeout
531
            }
532
        }
533
534
        if ($timedout) {
535
            captiveportal_disconnect($cpdb[$i], $radiusservers,$term_cause,$stop_time);
536
            captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "TIMEOUT");
537 8e51cc6a Ermal Lu?i
	    $unsetindexes[$i] = $i;
538 23c4f978 Scott Ullrich
        }
539
540
        /* do periodic RADIUS reauthentication? */
541
        if (!$timedout && isset($config['captiveportal']['reauthenticate']) &&
542 40b48c6c Ermal Lu?i
            !empty($radiusservers)) {
543 23c4f978 Scott Ullrich
544
            if (isset($config['captiveportal']['radacct_enable'])) {
545
                if ($config['captiveportal']['reauthenticateacct'] == "stopstart") {
546
                    /* stop and restart accounting */
547
                    RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
548
                                           $cpdb[$i][4], // username
549
                                           $cpdb[$i][5], // sessionid
550
                                           $cpdb[$i][0], // start time
551 40b48c6c Ermal Lu?i
                                           $radiusservers,
552 23c4f978 Scott Ullrich
                                           $cpdb[$i][2], // clientip
553
                                           $cpdb[$i][3], // clientmac
554
                                           10); // NAS Request
555 f9f71ad3 Ermal Lu?i
                    exec("/sbin/ipfw table 3 entryzerostats {$cpdb[$i][2]}");
556
                    exec("/sbin/ipfw table 4 entryzerostats {$cpdb[$i][2]}");
557 23c4f978 Scott Ullrich
                    RADIUS_ACCOUNTING_START($cpdb[$i][1], // ruleno
558
                                            $cpdb[$i][4], // username
559
                                            $cpdb[$i][5], // sessionid
560 40b48c6c Ermal Lu?i
                                            $radiusservers,
561 23c4f978 Scott Ullrich
                                            $cpdb[$i][2], // clientip
562
                                            $cpdb[$i][3]); // clientmac
563
                } else if ($config['captiveportal']['reauthenticateacct'] == "interimupdate") {
564
                    RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
565
                                           $cpdb[$i][4], // username
566
                                           $cpdb[$i][5], // sessionid
567
                                           $cpdb[$i][0], // start time
568 40b48c6c Ermal Lu?i
                                           $radiusservers,
569 23c4f978 Scott Ullrich
                                           $cpdb[$i][2], // clientip
570
                                           $cpdb[$i][3], // clientmac
571
                                           10, // NAS Request
572
                                           true); // Interim Updates
573
                }
574
            }
575
576
            /* check this user against RADIUS again */
577
            $auth_list = RADIUS_AUTHENTICATION($cpdb[$i][4], // username
578
                                          base64_decode($cpdb[$i][6]), // password
579
                                            $radiusservers,
580
                                          $cpdb[$i][2], // clientip
581
                                          $cpdb[$i][3], // clientmac
582
                                          $cpdb[$i][1]); // ruleno
583
584
            if ($auth_list['auth_val'] == 3) {
585
                captiveportal_disconnect($cpdb[$i], $radiusservers, 17);
586
                captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "RADIUS_DISCONNECT", $auth_list['reply_message']);
587 8e51cc6a Ermal Lu?i
	        $unsetindexes[$i] = $i;
588 23c4f978 Scott Ullrich
            }
589
        }
590
    }
591 8e51cc6a Ermal Lu?i
    /* This is a kludge to overcome some php weirdness */
592
    foreach($unsetindexes as $unsetindex)
593
	unset($cpdb[$unsetindex]);
594 23c4f978 Scott Ullrich
595
    /* write database */
596
    captiveportal_write_db($cpdb);
597
598 dedf51a2 Ermal Lu?i
    unlock($captiveportallck);
599 5b237745 Scott Ullrich
}
600
601 3db19cf1 Scott Ullrich
/* remove a single client according to the DB entry */
602 0bd34ed6 Scott Ullrich
function captiveportal_disconnect($dbent, $radiusservers,$term_cause = 1,$stop_time = null) {
603 36254e4a Scott Ullrich
604 d99f7864 Scott Ullrich
	global $g, $config;
605
606
	$stop_time = (empty($stop_time)) ? time() : $stop_time;
607
608
	/* this client needs to be deleted - remove ipfw rules */
609 40b48c6c Ermal Lu?i
	if (isset($config['captiveportal']['radacct_enable']) && !empty($radiusservers)) {
610 d99f7864 Scott Ullrich
		RADIUS_ACCOUNTING_STOP($dbent[1], // ruleno
611
							   $dbent[4], // username
612
							   $dbent[5], // sessionid
613
							   $dbent[0], // start time
614 40b48c6c Ermal Lu?i
							   $radiusservers,
615 d99f7864 Scott Ullrich
							   $dbent[2], // clientip
616
							   $dbent[3], // clientmac
617
							   $term_cause, // Acct-Terminate-Cause
618
							   false,
619
							   $stop_time);
620
	}
621 2d53158f stompro
	/* Delete client's ip entry from tables 3 and 4. */
622 93958dab Ermal Lu?i
	mwexec("/sbin/ipfw table 3 delete {$dbent[2]}");
623 f9f71ad3 Ermal Lu?i
	mwexec("/sbin/ipfw table 4 delete {$dbent[2]}");
624
625
	/* 
626
	* These are the pipe numbers we use to control traffic shaping for each logged in user via captive portal
627
	* We could get an error if the pipe doesn't exist but everything should still be fine
628
	*/
629
	if (isset($config['captiveportal']['peruserbw'])) {
630
		mwexec("/sbin/ipfw pipe " . ($dbent[1]+20000) . " delete");
631
		mwexec("/sbin/ipfw pipe " . ($dbent[1]+20001) . " delete");
632
	}
633 7a7abeba Scott Ullrich
634 f9f71ad3 Ermal Lu?i
	/* Ensure all pf(4) states are killed. */
635 7a7abeba Scott Ullrich
	mwexec("pfctl -k {$dbent[2]}");
636 b1cc2eb2 Ermal Luçi
	mwexec("pfctl -K {$dbent[2]}");
637 7a7abeba Scott Ullrich
638 3db19cf1 Scott Ullrich
}
639 12ee8fe4 Scott Ullrich
640 3db19cf1 Scott Ullrich
/* remove a single client by ipfw rule number */
641 0bd34ed6 Scott Ullrich
function captiveportal_disconnect_client($id,$term_cause = 1) {
642 36254e4a Scott Ullrich
643 d99f7864 Scott Ullrich
	global $g, $config;
644 36254e4a Scott Ullrich
645 dedf51a2 Ermal Lu?i
	$captiveportallck = lock('captiveportal');
646 36254e4a Scott Ullrich
647 d99f7864 Scott Ullrich
	/* read database */
648
	$cpdb = captiveportal_read_db();
649
	$radiusservers = captiveportal_get_radius_servers();
650
651
	/* find entry */
652 889b0934 Ermal Lu?i
	$tmpindex = 0;
653 d99f7864 Scott Ullrich
	for ($i = 0; $i < count($cpdb); $i++) {
654
		if ($cpdb[$i][1] == $id) {
655
			captiveportal_disconnect($cpdb[$i], $radiusservers, $term_cause);
656
			captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "DISCONNECT");
657 dd35bb5a Chris Buechler
			unset($cpdb[$i]);
658 d99f7864 Scott Ullrich
			break;
659
		}
660 dd35bb5a Chris Buechler
	}		
661 36254e4a Scott Ullrich
662 d99f7864 Scott Ullrich
	/* write database */
663
	captiveportal_write_db($cpdb);
664 36254e4a Scott Ullrich
665 dedf51a2 Ermal Lu?i
	unlock($captiveportallck);
666 5b237745 Scott Ullrich
}
667
668
/* send RADIUS acct stop for all current clients */
669 dedf51a2 Ermal Lu?i
function captiveportal_radius_stop_all($lock = false) {
670 d99f7864 Scott Ullrich
	global $g, $config;
671
672
	if (!isset($config['captiveportal']['radacct_enable']))
673
		return;
674
675 90455aeb Ermal Lu?i
	if (!$lock)
676 dedf51a2 Ermal Lu?i
		$captiveportallck = lock('captiveportal');
677
678 d99f7864 Scott Ullrich
	$cpdb = captiveportal_read_db();
679
680
	$radiusservers = captiveportal_get_radius_servers();
681 40b48c6c Ermal Lu?i
	if (!empty($radiusservers)) {
682 d99f7864 Scott Ullrich
		for ($i = 0; $i < count($cpdb); $i++) {
683
			RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
684
								   $cpdb[$i][4], // username
685
								   $cpdb[$i][5], // sessionid
686
								   $cpdb[$i][0], // start time
687 40b48c6c Ermal Lu?i
								   $radiusservers,
688 d99f7864 Scott Ullrich
								   $cpdb[$i][2], // clientip
689
								   $cpdb[$i][3], // clientmac
690
								   7); // Admin Reboot
691
		}
692
	}
693 90455aeb Ermal Lu?i
	if (!$lock)
694 dedf51a2 Ermal Lu?i
		unlock($captiveportallck);
695 5b237745 Scott Ullrich
}
696
697 dedf51a2 Ermal Lu?i
function captiveportal_passthrumac_configure($lock = false) {
698 5b237745 Scott Ullrich
	global $config, $g;
699 36254e4a Scott Ullrich
700 90455aeb Ermal Lu?i
	if (!$lock)
701 dedf51a2 Ermal Lu?i
		$captiveportallck = lock('captiveportal');
702 36254e4a Scott Ullrich
703 5b237745 Scott Ullrich
	/* clear out passthru macs, if necessary */
704 3db19cf1 Scott Ullrich
	unlink_if_exists("{$g['vardb_path']}/captiveportal_mac.db");
705 36254e4a Scott Ullrich
706 5b237745 Scott Ullrich
	if (is_array($config['captiveportal']['passthrumac'])) {
707 36254e4a Scott Ullrich
708 5b237745 Scott Ullrich
		$fd = @fopen("{$g['vardb_path']}/captiveportal_mac.db", "w");
709
		if (!$fd) {
710
			printf("Error: cannot open passthru mac DB file in captiveportal_passthrumac_configure().\n");
711 dedf51a2 Ermal Lu?i
			unlock($captiveportallck);
712 36254e4a Scott Ullrich
			return 1;
713 5b237745 Scott Ullrich
		}
714 36254e4a Scott Ullrich
715 5b237745 Scott Ullrich
		foreach ($config['captiveportal']['passthrumac'] as $macent) {
716
			/* record passthru mac so it can be recognized and let thru */
717
			fwrite($fd, $macent['mac'] . "\n");
718
		}
719 36254e4a Scott Ullrich
720
		fclose($fd);
721 5b237745 Scott Ullrich
	}
722 0bd34ed6 Scott Ullrich
723 d44bccc7 Scott Ullrich
	/*    pfSense:
724
	 * 	  pass through mac entries should always exist.  the reason
725 1de584c9 Scott Ullrich
	 *    for this is because we do not have native mac address filtering
726 d44bccc7 Scott Ullrich
	 *    mechanisms.  this allows us to filter by mac address easily
727 1de584c9 Scott Ullrich
	 *    and get around this limitation.   I consider this a bug in
728 d44bccc7 Scott Ullrich
	 *    m0n0wall and pfSense as m0n0wall does not have native mac
729
	 *    filtering mechanisms as well. -Scott Ullrich
730
	 */
731 1de584c9 Scott Ullrich
	if (is_array($config['captiveportal']['passthrumac'])) {
732
		mwexec("/sbin/ipfw delete 50");
733
		foreach($config['captiveportal']['passthrumac'] as $ptm) {
734
			/* create the pass through mac entry */
735 12249cad Scott Ullrich
			//system("echo /sbin/ipfw add 50 skipto 65535 ip from any to any MAC {$ptm['mac']} any > /tmp/cp");
736 f9f71ad3 Ermal Lu?i
			mwexec("/sbin/ipfw add 50 skipto 49900 ip from any to any MAC {$ptm['mac']} any keep-state");
737
			mwexec("/sbin/ipfw add 50 skipto 49900 ip from any to any MAC any {$ptm['mac']} keep-state");
738 1de584c9 Scott Ullrich
		}
739
	}
740 0bd34ed6 Scott Ullrich
741 90455aeb Ermal Lu?i
	if (!$lock)
742 dedf51a2 Ermal Lu?i
		unlock($captiveportallck);
743 36254e4a Scott Ullrich
744 5b237745 Scott Ullrich
	return 0;
745
}
746
747 cb0a2913 Ermal Lu?i
function captiveportal_allowedip_configure() {
748 5b237745 Scott Ullrich
	global $config, $g;
749 36254e4a Scott Ullrich
750 5b237745 Scott Ullrich
	/* clear out existing allowed ips, if necessary */
751 cb0a2913 Ermal Lu?i
	mwexec("/sbin/ipfw table 1 flush");
752
	mwexec("/sbin/ipfw table 2 flush");
753 5b237745 Scott Ullrich
754
	if (is_array($config['captiveportal']['allowedip'])) {
755 cb0a2913 Ermal Lu?i
		$tableone = false;
756
		$tabletwo = false;
757
		foreach ($config['captiveportal']['allowedip'] as $ipent) {
758
			/* insert address in ipfw table */
759
			if ($ipent['dir'] == "from") {
760
				mwexec("/sbin/ipfw table 1 add {$ipent['ip']}");
761
				$tableone = true;
762
			} else { 
763
				mwexec("/sbin/ipfw table 2 add {$ipent['ip']}");
764
				$tabletwo = true;
765
			}
766
		}
767
		if ($tableone == true) {
768 cbed73fd Ermal Lu?i
			mwexec("/sbin/ipfw add 1890 set 1 skipto 50000 ip from table\(1\) to any in");
769
			mwexec("/sbin/ipfw add 1891 set 1 skipto 50000 ip from any to table\(1\) out");
770 cb0a2913 Ermal Lu?i
		}
771
		if ($tabletwo == true) {
772 cbed73fd Ermal Lu?i
			mwexec("/sbin/ipfw add 1892 set 1 skipto 50000 ip from any to table\(2\) in");
773
			mwexec("/sbin/ipfw add 1893 set 1 skipto 50000 ip from table\(2\) to any out");
774 cb0a2913 Ermal Lu?i
		}
775
	}
776 36254e4a Scott Ullrich
777 d44bccc7 Scott Ullrich
    return 0;
778 5b237745 Scott Ullrich
}
779
780 2d53158f stompro
/* get last activity timestamp given client IP address */
781 f9f71ad3 Ermal Lu?i
function captiveportal_get_last_activity($ip) {
782 36254e4a Scott Ullrich
783 d99f7864 Scott Ullrich
	$ipfwoutput = "";
784 36254e4a Scott Ullrich
785 f9f71ad3 Ermal Lu?i
	exec("/sbin/ipfw table 3 entrystats {$ip} 2>/dev/null", $ipfwoutput);
786
	/* Reading only from one of the tables is enough of approximation. */
787 d99f7864 Scott Ullrich
	if ($ipfwoutput[0]) {
788
		$ri = explode(" ", $ipfwoutput[0]);
789 f9f71ad3 Ermal Lu?i
		if ($ri[4])
790
			return $ri[4];
791 d99f7864 Scott Ullrich
	}
792 36254e4a Scott Ullrich
793 d99f7864 Scott Ullrich
	return 0;
794 5b237745 Scott Ullrich
}
795
796
/* read RADIUS servers into array */
797
function captiveportal_get_radius_servers() {
798 0bd34ed6 Scott Ullrich
799
        global $g;
800
801
        if (file_exists("{$g['vardb_path']}/captiveportal_radius.db")) {
802 2f70eac7 Ermal Lu?i
                $radiusservers = array();
803 a48acf9a Ermal Lu?i
		$cpradiusdb = file("{$g['vardb_path']}/captiveportal_radius.db", 
804
			FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
805
		if ($cpradiusdb)
806 2f70eac7 Ermal Lu?i
		foreach($cpradiusdb as $cpradiusentry) {
807
                	$line = trim($cpradiusentry);
808
                        if ($line) {
809
                        	$radsrv = array();
810
                                list($radsrv['ipaddr'],$radsrv['port'],$radsrv['acctport'],$radsrv['key']) = explode(",",$line);
811
                        	$radiusservers[] = $radsrv;
812 0bd34ed6 Scott Ullrich
                        }
813 2f70eac7 Ermal Lu?i
		}
814 0bd34ed6 Scott Ullrich
815 2f70eac7 Ermal Lu?i
		return $radiusservers;
816 0bd34ed6 Scott Ullrich
        }
817
818
        return false;
819 5b237745 Scott Ullrich
}
820
821 3db19cf1 Scott Ullrich
/* log successful captive portal authentication to syslog */
822
/* part of this code from php.net */
823 0bd34ed6 Scott Ullrich
function captiveportal_logportalauth($user,$mac,$ip,$status, $message = null) {
824 d99f7864 Scott Ullrich
	$message = trim($message);
825
	// Log it
826
	if (!$message)
827 f56a73f1 Scott Ullrich
		$message = "$status: $user, $mac, $ip";
828 d99f7864 Scott Ullrich
	else
829 f56a73f1 Scott Ullrich
		$message = "$status: $user, $mac, $ip, $message";
830
	captiveportal_syslog($message);
831
	closelog();
832
}
833
834
/* log simple messages to syslog */
835
function captiveportal_syslog($message) {
836
	define_syslog_variables();
837
	$message = trim($message);
838
	openlog("logportalauth", LOG_PID, LOG_LOCAL4);
839
	// Log it
840
	syslog(LOG_INFO, $message);
841 d99f7864 Scott Ullrich
	closelog();
842 3db19cf1 Scott Ullrich
}
843
844 0bd34ed6 Scott Ullrich
function radius($username,$password,$clientip,$clientmac,$type) {
845 d44bccc7 Scott Ullrich
    global $g, $config;
846 d99f7864 Scott Ullrich
847 d44bccc7 Scott Ullrich
    /* Start locking from the beginning of an authentication session */
848 dedf51a2 Ermal Lu?i
    $captiveportallck = lock('captiveportal');
849 0bd34ed6 Scott Ullrich
850 d44bccc7 Scott Ullrich
    $ruleno = captiveportal_get_next_ipfw_ruleno();
851
852 2d53158f stompro
    /* If the pool is empty, return appropriate message and fail authentication */
853 d44bccc7 Scott Ullrich
    if (is_null($ruleno)) {
854
        $auth_list = array();
855
        $auth_list['auth_val'] = 1;
856
        $auth_list['error'] = "System reached maximum login capacity";
857 dedf51a2 Ermal Lu?i
        unlock($captiveportallck);
858 d44bccc7 Scott Ullrich
        return $auth_list;
859
    }
860
861 2f70eac7 Ermal Lu?i
    /*
862
     * Drop the lock since radius takes some time to finish.
863
     * The implementation is reentrant so we gain speed with this.
864
     */
865
    unlock($captiveportallck);
866
867 d44bccc7 Scott Ullrich
    $radiusservers = captiveportal_get_radius_servers();
868
869
    $auth_list = RADIUS_AUTHENTICATION($username,
870
                    $password,
871
                    $radiusservers,
872
                    $clientip,
873
                    $clientmac,
874
                    $ruleno);
875
876 2f70eac7 Ermal Lu?i
    $captiveportallck = lock('captiveportal');
877
878 d44bccc7 Scott Ullrich
    if ($auth_list['auth_val'] == 2) {
879
        captiveportal_logportalauth($username,$clientmac,$clientip,$type);
880
        $sessionid = portal_allow($clientip,
881
                    $clientmac,
882
                    $username,
883
                    $password,
884
                    $auth_list,
885
                    $ruleno);
886
    }
887 9befcca7 Ermal Lu?i
888 dedf51a2 Ermal Lu?i
    unlock($captiveportallck);
889 d44bccc7 Scott Ullrich
890
    return $auth_list;
891 0bd34ed6 Scott Ullrich
892
}
893
894
/* read captive portal DB into array */
895
function captiveportal_read_db() {
896
897
        global $g;
898
899
        $cpdb = array();
900
        $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r");
901
        if ($fd) {
902
                while (!feof($fd)) {
903
                        $line = trim(fgets($fd));
904
                        if ($line) {
905
                                $cpdb[] = explode(",", $line);
906
                        }
907
                }
908
                fclose($fd);
909
        }
910
        return $cpdb;
911
}
912
913
/* write captive portal DB */
914
function captiveportal_write_db($cpdb) {
915 d44bccc7 Scott Ullrich
                 
916 0bd34ed6 Scott Ullrich
        global $g;
917 d44bccc7 Scott Ullrich
                
918 0bd34ed6 Scott Ullrich
        $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w");
919 d44bccc7 Scott Ullrich
        if ($fd) { 
920 0bd34ed6 Scott Ullrich
                foreach ($cpdb as $cpent) {
921
                        fwrite($fd, join(",", $cpent) . "\n");
922 d44bccc7 Scott Ullrich
                }       
923 0bd34ed6 Scott Ullrich
                fclose($fd);
924 d44bccc7 Scott Ullrich
        }       
925 0bd34ed6 Scott Ullrich
}
926
927
function captiveportal_write_elements() {
928 d44bccc7 Scott Ullrich
    global $g, $config;
929
    
930
    /* delete any existing elements */
931
    if (is_dir($g['captiveportal_element_path'])) {
932
        $dh = opendir($g['captiveportal_element_path']);
933
        while (($file = readdir($dh)) !== false) {
934
            if ($file != "." && $file != "..")
935
                unlink($g['captiveportal_element_path'] . "/" . $file);
936
        }
937
        closedir($dh);
938
    } else {
939 5209079f Ermal Luçi
        @mkdir($g['captiveportal_element_path']);
940 d44bccc7 Scott Ullrich
    }
941
    
942 1fadb31d Scott Ullrich
	if (is_array($config['captiveportal']['element'])) {
943
		conf_mount_rw();
944
		foreach ($config['captiveportal']['element'] as $data) {
945
			$fd = @fopen($g['captiveportal_element_path'] . '/' . $data['name'], "wb");
946
			if (!$fd) {
947
				printf("Error: cannot open '{$data['name']}' in captiveportal_write_elements().\n");
948
				return 1;
949
			}
950
			$decoded = base64_decode($data['content']);
951
			fwrite($fd,$decoded);
952
			fclose($fd);
953
			unlink_if_exists("{$g['captiveportal_path']}/{$data['name']}");
954
			unlink_if_exists("{$g['captiveportal_path']}/{$data['name']}");
955
			mwexec("cd {$g['captiveportal_path']}/ && ln -s {$g['captiveportal_element_path']}/{$data['name']} {$data['name']}");
956
		}
957
		conf_mount_ro();
958
	}
959 d44bccc7 Scott Ullrich
    
960
    return 0;
961 0bd34ed6 Scott Ullrich
}
962
963 920cafaf Scott Ullrich
/*
964
 * This function will calculate the lowest free firewall ruleno
965 f9f71ad3 Ermal Lu?i
 * within the range specified based on the actual logged on users
966 920cafaf Scott Ullrich
 *
967
 */
968 b3167422 Ermal Lu?i
function captiveportal_get_next_ipfw_ruleno($rulenos_start = 2000, $rulenos_range_max = 49899) {
969 f9f71ad3 Ermal Lu?i
	global $config, $g;
970 de752609 Scott Ullrich
	if(!isset($config['captiveportal']['enable']))
971 01d57b8c Scott Ullrich
		return NULL;
972 f9f71ad3 Ermal Lu?i
	$ruleno = 0;
973
	if (file_exists("{$g['vardb_path']}/captiveportal.nextrule"))
974
		$ruleno = intval(file_get_contents("{$g['vardb_path']}/captiveportal.nextrule"));
975
	else
976
		$ruleno = 1;
977
	if ($ruleno > 0 && (($rulenos_start + $ruleno) < $rulenos_range_max)) {
978
		/* 
979
		 * This allows our traffic shaping pipes to be the in pipe the same as ruleno 
980
		 * and the out pipe ruleno + 1. This removes limitation that where present in 
981
		 * previous version of the peruserbw.
982
		 */
983
		if (isset($config['captiveportal']['peruserbw']))
984
			$ruleno += 2;
985
		else
986
			$ruleno++;
987
		file_put_contents("{$g['vardb_path']}/captiveportal.nextrule", $ruleno);
988
		return $rulenos_start + $ruleno;
989
	}
990
	return NULL;
991 920cafaf Scott Ullrich
}
992
993 360d815d Scott Ullrich
/**
994
 * This function will calculate the traffic produced by a client
995
 * based on its firewall rule
996
 *
997
 * Point of view: NAS
998
 *
999
 * Input means: from the client
1000
 * Output means: to the client
1001
 *
1002
 */
1003
1004 f9f71ad3 Ermal Lu?i
function getVolume($ip) {
1005 360d815d Scott Ullrich
1006
    $volume = array();
1007
1008
    // Initialize vars properly, since we don't want NULL vars
1009
    $volume['input_pkts'] = $volume['input_bytes'] = $volume['output_pkts'] = $volume['output_bytes'] = 0 ;
1010
1011
    // Ingress
1012 f9f71ad3 Ermal Lu?i
    $ipfwin = "";
1013
    $ipfwout = "";
1014
    $matchesin = "";
1015
    $matchesout = "";
1016
    exec("/sbin/ipfw table 3 entrystats {$ip}", $ipfwin);
1017
    if ($ipfwin[0]) {
1018 523855b0 Scott Ullrich
		$ipfwin = split(" ", $ipfwin[0]);
1019
		$volume['input_pkts'] = $ipfwin[2];
1020
		$volume['input_bytes'] = $ipfwin[3];
1021 f9f71ad3 Ermal Lu?i
    }
1022
1023
    exec("/sbin/ipfw table 4 entrystats {$ip}", $ipfwout);
1024
    if ($ipfwout[0]) {
1025
        $ipfwout = split(" ", $ipfwout[0]);
1026
        $volume['output_pkts'] = $ipfwout[2];
1027
        $volume['output_bytes'] = $ipfwout[3];
1028
    }
1029 360d815d Scott Ullrich
1030
    return $volume;
1031
}
1032
1033 856e58a6 Scott Ullrich
/**
1034
 * Get the NAS-Identifier
1035
 *
1036
 * We will use our local hostname to make up the nas_id
1037
 */
1038
function getNasID()
1039
{
1040 84e5047d Scott Ullrich
    $nasId = "";
1041 856e58a6 Scott Ullrich
    exec("/bin/hostname", $nasId);
1042
    if(!$nasId[0])
1043 36d0358b Scott Ullrich
        $nasId[0] = "{$g['product_name']}";
1044 856e58a6 Scott Ullrich
    return $nasId[0];
1045
}
1046
1047
/**
1048
 * Get the NAS-IP-Address based on the current wan address
1049
 *
1050
 * Use functions in interfaces.inc to find this out
1051
 *
1052
 */
1053
1054
function getNasIP()
1055
{
1056 85a5da13 Ermal Luçi
    $nasIp = get_interface_ip();
1057 856e58a6 Scott Ullrich
    if(!$nasIp)
1058
        $nasIp = "0.0.0.0";
1059
    return $nasIp;
1060
}
1061
1062 5bada54e Scott Ullrich
function portal_mac_fixed($clientmac) {
1063 8abb1030 Scott Ullrich
    global $g ;
1064 5bada54e Scott Ullrich
1065 8abb1030 Scott Ullrich
    /* open captive portal mac db */
1066
    if (file_exists("{$g['vardb_path']}/captiveportal_mac.db")) {
1067
        $fd = @fopen("{$g['vardb_path']}/captiveportal_mac.db","r") ;
1068
        if (!$fd) {
1069
            return FALSE;
1070
        }
1071
        while (!feof($fd)) {
1072
            $mac = trim(fgets($fd)) ;
1073
            if(strcasecmp($clientmac, $mac) == 0) {
1074
                fclose($fd) ;
1075
                return TRUE ;
1076
            }
1077
        }
1078
        fclose($fd) ;
1079
    }
1080
    return FALSE ;
1081 5bada54e Scott Ullrich
}
1082
1083 f8b11310 Ermal Lu?i
function portal_ip_from_client_ip($cliip) {
1084
	global $config;
1085
1086
	$interfaces = explode(",", $config['captiveportal']['interface']);
1087
	foreach ($interfaces as $cpif) {
1088
		$ip = get_interface_ip($cpif);
1089
		$sn = get_interface_subnet($cpif);
1090
		if (ip_in_subnet($cliip, "{$ip}/{$sn}"))
1091
			return $ip;
1092
	}
1093
1094 cc125e13 Chris Buechler
	// doesn't match up to any particular interface
1095
	// so let's set the portal IP to what PHP says 
1096
	// the server IP issuing the request is. 
1097
	// allows same behavior as 1.2.x where IP isn't 
1098
	// in the subnet of any CP interface (static routes, etc.)
1099
	// rather than forcing to DNS hostname resolution
1100
	$ip = $_SERVER['SERVER_ADDR'];
1101
	if (is_ipaddr($ip))
1102
		return $ip;
1103
1104 f8b11310 Ermal Lu?i
	return false;
1105
}
1106
1107 6b20b7a8 Ermal Lu?i
?>