Project

General

Profile

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