Project

General

Profile

Download (36.5 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 0bd34ed6 Scott Ullrich
	Copyright (C) 2003-2006 Manuel Kasper <mk@neon1.net>.
7 5b237745 Scott Ullrich
	All rights reserved.
8 36254e4a Scott Ullrich
9 5b237745 Scott Ullrich
	Redistribution and use in source and binary forms, with or without
10
	modification, are permitted provided that the following conditions are met:
11 36254e4a Scott Ullrich
12 5b237745 Scott Ullrich
	1. Redistributions of source code must retain the above copyright notice,
13
	   this list of conditions and the following disclaimer.
14 36254e4a Scott Ullrich
15 5b237745 Scott Ullrich
	2. Redistributions in binary form must reproduce the above copyright
16
	   notice, this list of conditions and the following disclaimer in the
17
	   documentation and/or other materials provided with the distribution.
18 36254e4a Scott Ullrich
19 5b237745 Scott Ullrich
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
20
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
21
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
22
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
23
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
24
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
25
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
26
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
27
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
28
	POSSIBILITY OF SUCH DAMAGE.
29 ca83c6ea Scott Ullrich
30 9699028a Scott Ullrich
	This version of captiveportal.inc has been modified by Rob Parker
31
	<rob.parker@keycom.co.uk> to include changes for per-user bandwidth management
32
	via returned RADIUS attributes. This page has been modified to delete any
33
	added rules which may have been created by other per-user code (index.php, etc).
34
	These changes are (c) 2004 Keycom PLC.
35
*/
36 36254e4a Scott Ullrich
37 5b237745 Scott Ullrich
/* include all configuration functions */
38
require_once("functions.inc");
39 9d253549 Scott Ullrich
require_once("globals.inc");
40 3db19cf1 Scott Ullrich
require_once("radius_authentication.inc");
41 0bd34ed6 Scott Ullrich
require_once("radius_accounting.inc");
42 856e58a6 Scott Ullrich
require_once("radius.inc");
43 0bd34ed6 Scott Ullrich
44
$lockfile = "{$g['varrun_path']}/captiveportal.lock";
45 5b237745 Scott Ullrich
46
function captiveportal_configure() {
47
	global $config, $g;
48 36254e4a Scott Ullrich
49 3db19cf1 Scott Ullrich
	if (isset($config['captiveportal']['enable']) &&
50
		(($config['captiveportal']['interface'] == "lan") ||
51
			isset($config['interfaces'][$config['captiveportal']['interface']]['enable']))) {
52 36254e4a Scott Ullrich
53 3db19cf1 Scott Ullrich
		if ($g['booting'])
54
			echo "Starting captive portal... ";
55 36254e4a Scott Ullrich
56 5b237745 Scott Ullrich
		/* kill any running mini_httpd */
57 23a0c341 Scott Ullrich
		killbypid("{$g['varrun_path']}/lighty-CaptivePortal.pid");
58 63fff79b Scott Ullrich
		killbypid("{$g['varrun_path']}/lighty-CaptivePortal-SSL.pid");
59 36254e4a Scott Ullrich
60 c6c92abf Scott Ullrich
		/* kill any running minicron */
61
		killbypid("{$g['varrun_path']}/minicron.pid");
62 36254e4a Scott Ullrich
63 3db19cf1 Scott Ullrich
		/* generate ipfw rules */
64
		$cprules = captiveportal_rules_generate();
65 36254e4a Scott Ullrich
66 3db19cf1 Scott Ullrich
		/* make sure ipfw is loaded */
67
		mwexec("/sbin/kldload ipfw");
68 56735ce3 Ermal Luçi
		
69
		/* 
70
		 * make sure ipfw is the first hook to make CP work correctly on
71
		 * Multi-WAN.
72
		 * Disable the ipfw outer hook it has not value to us.
73
		 */
74
		mwexec("/sbin/sysctl net.inet.ip.pfil.inbound=\"ipfw,pf\"");
75 aadf3add Ermal Luçi
76 697a51eb Ermal Luçi
		/* 
77
		 * TODO: Check if disabling ipfw hook 
78
		 * does not break accounting. 
79
		 */
80
		mwexec("/sbin/sysctl net.inet.ip.pfil.outbound=\"ipfw,pf\"");    
81 36254e4a Scott Ullrich
82 5b237745 Scott Ullrich
		/* stop accounting on all clients */
83 3db19cf1 Scott Ullrich
		captiveportal_radius_stop_all();
84 5b237745 Scott Ullrich
85 0bd34ed6 Scott Ullrich
		/* initialize minicron interval value */
86
		$croninterval = $config['captiveportal']['croninterval'] ? $config['captiveportal']['croninterval'] : 60;
87
88
		/* double check if the $croninterval is numeric and at least 10 seconds. If not we set it to 60 to avoid problems */
89
		if ((!is_numeric($croninterval)) || ($croninterval < 10)) { $croninterval = 60; }
90
91 5b237745 Scott Ullrich
		/* remove old information */
92 d99f7864 Scott Ullrich
		unlink_if_exists("{$g['vardb_path']}/captiveportal.nextrule");
93
		unlink_if_exists("{$g['vardb_path']}/captiveportal.db");
94
		unlink_if_exists("{$g['vardb_path']}/captiveportal_mac.db");
95
		unlink_if_exists("{$g['vardb_path']}/captiveportal_ip.db");
96
		unlink_if_exists("{$g['vardb_path']}/captiveportal_radius.db");
97 36254e4a Scott Ullrich
98 5b237745 Scott Ullrich
		/* write portal page */
99
		if ($config['captiveportal']['page']['htmltext'])
100
			$htmltext = base64_decode($config['captiveportal']['page']['htmltext']);
101
		else {
102
			/* example/template page */
103
			$htmltext = <<<EOD
104
<html>
105
<head>
106 36d0358b Scott Ullrich
<title>{$g['product_name']} captive portal</title>
107 5b237745 Scott Ullrich
</head>
108 3db19cf1 Scott Ullrich
<body>
109 a515d275 Scott Ullrich
<center>
110 36d0358b Scott Ullrich
<h2>{$g['product_name']} captive portal</h2>
111
Welcome to the {$g['product_name']} Captive Portal!  This is the default page since a custom page has not been defined.
112 14d2d21b Scott Ullrich
<p>
113 a515d275 Scott Ullrich
<form method="post" action="\$PORTAL_ACTION\$">
114
<input name="redirurl" type="hidden" value="\$PORTAL_REDIRURL\$">
115 407a29ca Scott Ullrich
<table>
116
   <tr><td>Username:</td><td><input name="auth_user" type="text"></td></tr>
117
   <tr><td>Password:</td><td><input name="auth_pass" type="password"></td></tr>
118 a515d275 Scott Ullrich
   <tr><td>&nbsp;</td></tr>
119 14d2d21b Scott Ullrich
   <tr>
120
     <td colspan="2">
121 0bd34ed6 Scott Ullrich
	<center><input name="accept" type="submit" value="Continue"></center>
122 14d2d21b Scott Ullrich
     </td>
123
   </tr>
124 407a29ca Scott Ullrich
</table>
125 a515d275 Scott Ullrich
</center>
126 407a29ca Scott Ullrich
</form>
127 5b237745 Scott Ullrich
</body>
128
</html>
129
130 0bd34ed6 Scott Ullrich
131
132 5b237745 Scott Ullrich
EOD;
133
		}
134
135
		$fd = @fopen("{$g['varetc_path']}/captiveportal.html", "w");
136
		if ($fd) {
137
			fwrite($fd, $htmltext);
138 36254e4a Scott Ullrich
			fclose($fd);
139 5b237745 Scott Ullrich
		}
140 36254e4a Scott Ullrich
141 5b237745 Scott Ullrich
		/* write error page */
142
		if ($config['captiveportal']['page']['errtext'])
143
			$errtext = base64_decode($config['captiveportal']['page']['errtext']);
144
		else {
145
			/* example page */
146
			$errtext = <<<EOD
147
<html>
148
<head>
149
<title>Authentication error</title>
150
</head>
151
<body>
152
<font color="#cc0000"><h2>Authentication error</h2></font>
153
<b>
154
Username and/or password invalid.
155
<br><br>
156
<a href="javascript:history.back()">Go back</a>
157
</b>
158
</body>
159
</html>
160
161
EOD;
162
		}
163
164
		$fd = @fopen("{$g['varetc_path']}/captiveportal-error.html", "w");
165
		if ($fd) {
166
			fwrite($fd, $errtext);
167 36254e4a Scott Ullrich
			fclose($fd);
168 5b237745 Scott Ullrich
		}
169 36254e4a Scott Ullrich
170 0bd34ed6 Scott Ullrich
		/* write elements */
171
		captiveportal_write_elements();
172 5b237745 Scott Ullrich
173 3db19cf1 Scott Ullrich
		/* load rules */
174
		mwexec("/sbin/ipfw -f delete set 1");
175
		mwexec("/sbin/ipfw -f delete set 2");
176
		mwexec("/sbin/ipfw -f delete set 3");
177 36254e4a Scott Ullrich
178 63fff79b Scott Ullrich
		/* ipfw cannot accept rules directly on stdin,
179 3db19cf1 Scott Ullrich
		   so we have to write them to a temporary file first */
180
		$fd = @fopen("{$g['tmp_path']}/ipfw.cp.rules", "w");
181
		if (!$fd) {
182
			printf("Cannot open ipfw.cp.rules in captiveportal_configure()\n");
183
			return 1;
184
		}
185 36254e4a Scott Ullrich
186 3db19cf1 Scott Ullrich
		fwrite($fd, $cprules);
187
		fclose($fd);
188 36254e4a Scott Ullrich
189 3db19cf1 Scott Ullrich
		mwexec("/sbin/ipfw {$g['tmp_path']}/ipfw.cp.rules");
190 36254e4a Scott Ullrich
191 3db19cf1 Scott Ullrich
		unlink("{$g['tmp_path']}/ipfw.cp.rules");
192 36254e4a Scott Ullrich
193 3db19cf1 Scott Ullrich
		/* filter on layer2 as well so we can check MAC addresses */
194
		mwexec("/sbin/sysctl net.link.ether.ipfw=1");
195 36254e4a Scott Ullrich
196 5b237745 Scott Ullrich
		chdir($g['captiveportal_path']);
197 36254e4a Scott Ullrich
198 9b5a1292 Scott Ullrich
		if ($config['captiveportal']['maxproc'])
199
			$maxproc = $config['captiveportal']['maxproc'];
200
		else
201
			$maxproc = 16;
202 36254e4a Scott Ullrich
203 3e1b0033 Scott Ullrich
		$use_fastcgi = true;
204
205 40b9f8c0 Scott Ullrich
		if(isset($config['captiveportal']['httpslogin'])) {
206
			$cert = base64_decode($config['captiveportal']['certificate']);
207
			$key = base64_decode($config['captiveportal']['private-key']);
208 63fff79b Scott Ullrich
			/* generate lighttpd configuration */
209
			system_generate_lighty_config("{$g['varetc_path']}/lighty-CaptivePortal-SSL.conf",
210
				$cert, $key, "lighty-CaptivePortal-ssl.pid", "8001", "/usr/local/captiveportal/",
211
					"cert-portal.pem", "1", $maxproc, $use_fastcgi, true);
212 40b9f8c0 Scott Ullrich
		}
213 36254e4a Scott Ullrich
214 877ac35d Scott Ullrich
		/* generate lighttpd configuration */
215
		system_generate_lighty_config("{$g['varetc_path']}/lighty-CaptivePortal.conf",
216 63fff79b Scott Ullrich
			"", "", "lighty-CaptivePortal.pid", "8000", "/usr/local/captiveportal/",
217 556d59be Scott Ullrich
				"cert-portal.pem", "1", $maxproc, $use_fastcgi, true);
218 36254e4a Scott Ullrich
219 877ac35d Scott Ullrich
		/* attempt to start lighttpd */
220
		$res = mwexec("/usr/local/sbin/lighttpd -f {$g['varetc_path']}/lighty-CaptivePortal.conf");
221 36254e4a Scott Ullrich
222 63fff79b Scott Ullrich
		/* fire up https instance */
223
		if(isset($config['captiveportal']['httpslogin']))
224
			$res = mwexec("/usr/local/sbin/lighttpd -f {$g['varetc_path']}/lighty-CaptivePortal-SSL.conf");
225 36254e4a Scott Ullrich
226 0bd34ed6 Scott Ullrich
		/* start pruning process (interval defaults to 60 seconds) */
227
		mwexec("/usr/local/bin/minicron $croninterval {$g['varrun_path']}/minicron.pid " .
228 c6c92abf Scott Ullrich
			"/etc/rc.prunecaptiveportal");
229 36254e4a Scott Ullrich
230 5b237745 Scott Ullrich
		/* generate passthru mac database */
231 3db19cf1 Scott Ullrich
		captiveportal_passthrumac_configure();
232
		/* create allowed ip database and insert ipfw rules to make it so */
233
		captiveportal_allowedip_configure();
234 5b237745 Scott Ullrich
235 d99f7864 Scott Ullrich
		/* generate radius server database */
236
		if ($config['captiveportal']['radiusip'] && (!isset($config['captiveportal']['auth_method']) ||
237
				($config['captiveportal']['auth_method'] == "radius"))) {
238
			$radiusip = $config['captiveportal']['radiusip'];
239
			$radiusip2 = ($config['captiveportal']['radiusip2']) ? $config['captiveportal']['radiusip2'] : null;
240
241
			if ($config['captiveportal']['radiusport'])
242
				$radiusport = $config['captiveportal']['radiusport'];
243
			else
244
				$radiusport = 1812;
245
246
			if ($config['captiveportal']['radiusacctport'])
247
				$radiusacctport = $config['captiveportal']['radiusacctport'];
248
			else
249
				$radiusacctport = 1813;
250
251
			if ($config['captiveportal']['radiusport2'])
252
				$radiusport2 = $config['captiveportal']['radiusport2'];
253
			else
254
				$radiusport2 = 1812;
255
256
			$radiuskey = $config['captiveportal']['radiuskey'];
257
			$radiuskey2 = ($config['captiveportal']['radiuskey2']) ? $config['captiveportal']['radiuskey2'] : null;
258
259
			$fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db", "w");
260
			if (!$fd) {
261
				printf("Error: cannot open radius DB file in captiveportal_configure().\n");
262
				return 1;
263
			} else if (isset($radiusip2, $radiuskey2)) {
264
				fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey . "\n"
265
					 . $radiusip2 . "," . $radiusport2 . "," . $radiusacctport . "," . $radiuskey2);
266
			} else {
267
				fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey);
268
			}
269
			fclose($fd);
270
		}
271 36254e4a Scott Ullrich
272 d99f7864 Scott Ullrich
		if ($g['booting'])
273
			echo "done\n";
274 36254e4a Scott Ullrich
275 5b237745 Scott Ullrich
	} else {
276 23a0c341 Scott Ullrich
		killbypid("{$g['varrun_path']}/lighty-CaptivePortal.pid");
277 c6c92abf Scott Ullrich
		killbypid("{$g['varrun_path']}/minicron.pid");
278 12ee8fe4 Scott Ullrich
279 3db19cf1 Scott Ullrich
		captiveportal_radius_stop_all();
280
281
		mwexec("/sbin/sysctl net.link.ether.ipfw=0");
282
283
		if (!isset($config['shaper']['enable'])) {
284
			/* unload ipfw */
285 c5cb3ac2 Scott Ullrich
			$installed_time_based_rules = false;
286
			if($config['schedules']) {
287
				foreach($config['schedules']['schedule'] as $sched) {
288
					$installed_time_based_rules = true;
289
				}
290
			}
291
			if($installed_time_based_rules == false)
292
				mwexec("/sbin/kldunload ipfw");
293 3db19cf1 Scott Ullrich
		} else {
294
			/* shaper is on - just remove our rules */
295
			mwexec("/sbin/ipfw -f delete set 1");
296
			mwexec("/sbin/ipfw -f delete set 2");
297
			mwexec("/sbin/ipfw -f delete set 3");
298
		}
299
	}
300 36254e4a Scott Ullrich
301 c3214b80 Scott Ullrich
    captiveportal_unlock();
302
	
303 5b237745 Scott Ullrich
	return 0;
304
}
305
306 3db19cf1 Scott Ullrich
function captiveportal_rules_generate() {
307
	global $config, $g;
308 36254e4a Scott Ullrich
309 3db19cf1 Scott Ullrich
	$cpifn = $config['captiveportal']['interface'];
310
	$cpif = $config['interfaces'][$cpifn]['if'];
311
	$cpip = $config['interfaces'][$cpifn]['ipaddr'];
312 421f8b5f Scott Ullrich
	$lanip = $config['interfaces']['lan']['ipaddr'];
313
	
314 3db19cf1 Scott Ullrich
	/* note: the captive portal daemon inserts all pass rules for authenticated
315
	   clients as skipto 50000 rules to make traffic shaping work */
316
317 258d082a Scott Ullrich
	$cprules =  "add 500 set 1 allow pfsync from any to any\n";
318
	$cprules .= "add 500 set 1 allow carp from any to any\n";
319 181a843c Scott Ullrich
320
	/*   allow nat redirects to work  see
321
	     http://cvstrac.pfsense.com/tktview?tn=651
322
	 */
323 b16c077d Ermal Luçi
	
324 60089505 Ermal Luçi
	$captive_portal_interface = strtoupper($cpifn);
325
326 b16c077d Ermal Luçi
        /* if list */
327
        $iflist = get_configured_interface_list();
328
329 181a843c Scott Ullrich
	foreach ($iflist as $ifent => $ifname) {
330 d66bb68a Scott Ullrich
		if($captive_portal_interface == strtoupper($ifname))
331 181a843c Scott Ullrich
			continue;
332
		$int = convert_friendly_interface_to_real_interface_name($ifname);
333 657f3f15 Scott Ullrich
		$cprules .= "add 30 set 1 skipto 50000 all from any to any in via {$int} keep-state\n";
334 181a843c Scott Ullrich
	}
335 36254e4a Scott Ullrich
336 3db19cf1 Scott Ullrich
	/* captive portal on LAN interface? */
337
	if ($cpifn == "lan") {
338
		/* add anti-lockout rules */
339
		$cprules .= <<<EOD
340 0bd34ed6 Scott Ullrich
add 500 set 1 pass all from $cpip to any out via $cpif
341 3db19cf1 Scott Ullrich
add 501 set 1 pass all from any to $cpip in via $cpif
342
343
EOD;
344
	}
345
346
	$cprules .= <<<EOD
347
# skip to traffic shaper if not on captive portal interface
348
add 1000 set 1 skipto 50000 all from any to any not layer2 not via $cpif
349
# pass all layer2 traffic on other interfaces
350
add 1001 set 1 pass layer2 not via $cpif
351
352
# layer 2: pass ARP
353
add 1100 set 1 pass layer2 mac-type arp
354 b9d1d810 Scott Ullrich
# pfsense requires for WPA
355
add 1100 set 1 pass layer2 mac-type 0x888e
356 68f34650 Scott Ullrich
add 1100 set 1 pass layer2 mac-type 0x88c7
357 684c787e Scott Ullrich
358 36254e4a Scott Ullrich
# PPP Over Ethernet Discovery Stage
359 684c787e Scott Ullrich
add 1100 set 1 pass layer2 mac-type 0x8863
360
# PPP Over Ethernet Session Stage
361
add 1100 set 1 pass layer2 mac-type 0x8864
362
363 3db19cf1 Scott Ullrich
# layer 2: block anything else non-IP
364
add 1101 set 1 deny layer2 not mac-type ip
365
# layer 2: check if MAC addresses of authenticated clients are correct
366
add 1102 set 1 skipto 20000 layer2
367
368
# allow access to our DHCP server (which needs to be able to ping clients as well)
369
add 1200 set 1 pass udp from any 68 to 255.255.255.255 67 in
370
add 1201 set 1 pass udp from any 68 to $cpip 67 in
371
add 1202 set 1 pass udp from $cpip 67 to any 68 out
372
add 1203 set 1 pass icmp from $cpip to any out icmptype 8
373
add 1204 set 1 pass icmp from any to $cpip in icmptype 0
374
375
# allow access to our DNS forwarder
376
add 1300 set 1 pass udp from any to $cpip 53 in
377
add 1301 set 1 pass udp from $cpip 53 to any out
378
379 421f8b5f Scott Ullrich
# allow access to our DNS forwarder if it incorrectly resolves the hostname to $lanip
380
add 1300 set 1 pass udp from any to $lanip 53 in
381
add 1301 set 1 pass udp from $lanip 53 to any out
382
383 3db19cf1 Scott Ullrich
# allow access to our web server
384
add 1302 set 1 pass tcp from any to $cpip 8000 in
385
add 1303 set 1 pass tcp from $cpip 8000 to any out
386
387 421f8b5f Scott Ullrich
# allow access to lan web server incase the dns name resolves incorrectly to $lanip
388
add 1302 set 1 pass tcp from any to $lanip 8000 in
389
add 1303 set 1 pass tcp from $lanip 8000 to any out
390
391 3db19cf1 Scott Ullrich
EOD;
392
393
	if (isset($config['captiveportal']['httpslogin'])) {
394
		$cprules .= <<<EOD
395
add 1304 set 1 pass tcp from any to $cpip 8001 in
396
add 1305 set 1 pass tcp from $cpip 8001 to any out
397 f56a73f1 Scott Ullrich
add 1306 set 1 pass tcp from any to $lanip 8001 in
398
add 1307 set 1 pass tcp from $lanip 8001 to any out
399 3db19cf1 Scott Ullrich
400
EOD;
401
	}
402 36254e4a Scott Ullrich
403 d44bccc7 Scott Ullrich
        $cprules .= <<<EOD
404
#PPPoE Discovery Stage
405
add 1100 set 1 pass layer2 mac-type 0x8863
406
#PPPoE Session Stage
407
add 1100 set 1 pass layer2 mac-type 0x8864
408 3db19cf1 Scott Ullrich
409 d44bccc7 Scott Ullrich
EOD;
410 3db19cf1 Scott Ullrich
411 d44bccc7 Scott Ullrich
        $cprules .= <<<EOD
412
# Allow WPA
413
add 1100 set 1 pass layer2 mac-type 0x888e
414 5480497a Scott Ullrich
415 d44bccc7 Scott Ullrich
EOD;
416 9a064646 Scott Ullrich
417 5480497a Scott Ullrich
418 d44bccc7 Scott Ullrich
    $cprules .= <<<EOD
419 5480497a Scott Ullrich
420 d44bccc7 Scott Ullrich
# ... 10000-19899: rules per authenticated client go here...
421 5480497a Scott Ullrich
422 d44bccc7 Scott Ullrich
# redirect non-authenticated clients to captive portal
423
add 19902 set 1 fwd 127.0.0.1,8000 tcp from any to any 80 in
424 3db19cf1 Scott Ullrich
# let the responses from the captive portal web server back out
425 5480497a Scott Ullrich
add 19903 set 1 pass tcp from any 80 to any out
426 3db19cf1 Scott Ullrich
# block everything else
427 5480497a Scott Ullrich
add 19904 set 1 deny all from any to any
428 3db19cf1 Scott Ullrich
429
# ... 20000-29899: layer2 block rules per authenticated client go here...
430
431
# pass everything else on layer2
432
add 29900 set 1 pass all from any to any layer2
433
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
    if (!$timeout && !$idletimeout && !isset($config['captiveportal']['reauthenticate']) && !isset($config['captiveportal']['radiussession_timeout']))
460
        return;
461
462
    captiveportal_lock();
463
464
    /* read database */
465
    $cpdb = captiveportal_read_db();
466
467
    $radiusservers = captiveportal_get_radius_servers();
468
469 f56a73f1 Scott Ullrich
 	/*  To make sure we iterate over ALL accounts on every run the count($cpdb) is moved outside of the loop. Otherwise
470
     *  the loop would evalate count() on every iteration and since $i would increase and count() would decrement they
471
     *  would meet before we had a chance to iterate over all accounts.
472
     */
473
    $no_users = count($cpdb);
474
    for ($i = 0; $i < $no_users; $i++) {
475 23c4f978 Scott Ullrich
476
        $timedout = false;
477
        $term_cause = 1;
478
479 5bada54e Scott Ullrich
		/* no pruning for fixed mac address entry */
480
		if (portal_mac_fixed($cpdb[$i][3])) {
481
			continue; // check next value
482
		}
483 23c4f978 Scott Ullrich
        /* hard timeout? */
484
        if ($timeout) {
485
            if ((time() - $cpdb[$i][0]) >= $timeout) {
486
                $timedout = true;
487
                $term_cause = 5; // Session-Timeout
488
            }
489
        }
490
491
        /* Session-Terminate-Time */
492
        if (!$timedout && !empty($cpdb[$i][9])) {
493
            if (time() >= $cpdb[$i][9]) {
494
                $timedout = true;
495
                $term_cause = 5; // Session-Timeout
496
            }
497
        }
498
499
        /* check if the radius idle_timeout attribute has been set and if its set change the idletimeout to this value */
500
        $idletimeout = (is_numeric($cpdb[$i][8])) ? $cpdb[$i][8] : $idletimeout;
501
        /* if an idle timeout is specified, get last activity timestamp from ipfw */
502
        if (!$timedout && $idletimeout) {
503
            $lastact = captiveportal_get_last_activity($cpdb[$i][1]);
504 f56a73f1 Scott Ullrich
			/*  if the user has logged on but not sent any trafic they will never be logged out.
505
			 *  We "fix" this by setting lastact to the login timestamp 
506
			 */
507
			$lastact = $lastact ? $lastact : $cpdb[$i][0];
508 23c4f978 Scott Ullrich
            if ($lastact && ((time() - $lastact) >= $idletimeout)) {
509
                $timedout = true;
510
                $term_cause = 4; // Idle-Timeout
511
                $stop_time = $lastact; // Entry added to comply with WISPr
512
            }
513
        }
514
515
        /* if radius session_timeout is enabled and the session_timeout is not null, then check if the user should be logged out */
516
        if (!$timedout && isset($config['captiveportal']['radiussession_timeout']) && !empty($cpdb[$i][7])) {
517
            if (time() >= ($cpdb[$i][0] + $cpdb[$i][7])) {
518
                $timedout = true;
519
                $term_cause = 5; // Session-Timeout
520
            }
521
        }
522
523
        if ($timedout) {
524
            captiveportal_disconnect($cpdb[$i], $radiusservers,$term_cause,$stop_time);
525
            captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "TIMEOUT");
526
            unset($cpdb[$i]);
527
        }
528
529
        /* do periodic RADIUS reauthentication? */
530
        if (!$timedout && isset($config['captiveportal']['reauthenticate']) &&
531
            ($radiusservers !== false)) {
532
533
            if (isset($config['captiveportal']['radacct_enable'])) {
534
                if ($config['captiveportal']['reauthenticateacct'] == "stopstart") {
535
                    /* stop and restart accounting */
536
                    RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
537
                                           $cpdb[$i][4], // username
538
                                           $cpdb[$i][5], // sessionid
539
                                           $cpdb[$i][0], // start time
540
                                           $radiusservers[0]['ipaddr'],
541
                                           $radiusservers[0]['acctport'],
542
                                           $radiusservers[0]['key'],
543
                                           $cpdb[$i][2], // clientip
544
                                           $cpdb[$i][3], // clientmac
545
                                           10); // NAS Request
546
                    exec("/sbin/ipfw zero {$cpdb[$i][1]}");
547
                    RADIUS_ACCOUNTING_START($cpdb[$i][1], // ruleno
548
                                            $cpdb[$i][4], // username
549
                                            $cpdb[$i][5], // sessionid
550
                                            $radiusservers[0]['ipaddr'],
551
                                            $radiusservers[0]['acctport'],
552
                                            $radiusservers[0]['key'],
553
                                            $cpdb[$i][2], // clientip
554
                                            $cpdb[$i][3]); // clientmac
555
                } else if ($config['captiveportal']['reauthenticateacct'] == "interimupdate") {
556
                    RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
557
                                           $cpdb[$i][4], // username
558
                                           $cpdb[$i][5], // sessionid
559
                                           $cpdb[$i][0], // start time
560
                                           $radiusservers[0]['ipaddr'],
561
                                           $radiusservers[0]['acctport'],
562
                                           $radiusservers[0]['key'],
563
                                           $cpdb[$i][2], // clientip
564
                                           $cpdb[$i][3], // clientmac
565
                                           10, // NAS Request
566
                                           true); // Interim Updates
567
                }
568
            }
569
570
            /* check this user against RADIUS again */
571
            $auth_list = RADIUS_AUTHENTICATION($cpdb[$i][4], // username
572
                                          base64_decode($cpdb[$i][6]), // password
573
                                            $radiusservers,
574
                                          $cpdb[$i][2], // clientip
575
                                          $cpdb[$i][3], // clientmac
576
                                          $cpdb[$i][1]); // ruleno
577
578
            if ($auth_list['auth_val'] == 3) {
579
                captiveportal_disconnect($cpdb[$i], $radiusservers, 17);
580
                captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "RADIUS_DISCONNECT", $auth_list['reply_message']);
581
                unset($cpdb[$i]);
582
            }
583
        }
584
    }
585
586
    /* write database */
587
    captiveportal_write_db($cpdb);
588
589
    captiveportal_unlock();
590 5b237745 Scott Ullrich
}
591
592 3db19cf1 Scott Ullrich
/* remove a single client according to the DB entry */
593 0bd34ed6 Scott Ullrich
function captiveportal_disconnect($dbent, $radiusservers,$term_cause = 1,$stop_time = null) {
594 36254e4a Scott Ullrich
595 d99f7864 Scott Ullrich
	global $g, $config;
596
597
	$stop_time = (empty($stop_time)) ? time() : $stop_time;
598
599
	/* this client needs to be deleted - remove ipfw rules */
600
	if (isset($config['captiveportal']['radacct_enable']) && isset($radiusservers[0])) {
601
		RADIUS_ACCOUNTING_STOP($dbent[1], // ruleno
602
							   $dbent[4], // username
603
							   $dbent[5], // sessionid
604
							   $dbent[0], // start time
605
							   $radiusservers[0]['ipaddr'],
606
							   $radiusservers[0]['acctport'],
607
							   $radiusservers[0]['key'],
608
							   $dbent[2], // clientip
609
							   $dbent[3], // clientmac
610
							   $term_cause, // Acct-Terminate-Cause
611
							   false,
612
							   $stop_time);
613
	}
614
615
	mwexec("/sbin/ipfw delete " . $dbent[1] . " " . ($dbent[1]+10000));
616
617 d44bccc7 Scott Ullrich
    /* We need to delete +40500 and +45500 as well...
618
     * these are the pipe numbers we use to control traffic shaping for each logged in user via captive portal
619
     * We could get an error if the pipe doesn't exist but everything should still be fine
620
     */
621
    if (isset($config['captiveportal']['peruserbw'])) {
622
        mwexec("/sbin/ipfw pipe " . ($dbent[1]+40500) . " delete");
623
        mwexec("/sbin/ipfw pipe " . ($dbent[1]+45500) . " delete");
624
    }
625 7a7abeba Scott Ullrich
626 d44bccc7 Scott Ullrich
	/* pfSense: ensure all pf states are killed (pfSense) */
627 7a7abeba Scott Ullrich
	mwexec("pfctl -k {$dbent[2]}");
628 b1cc2eb2 Ermal Luçi
	mwexec("pfctl -K {$dbent[2]}");
629 7a7abeba Scott Ullrich
630 3db19cf1 Scott Ullrich
}
631 12ee8fe4 Scott Ullrich
632 3db19cf1 Scott Ullrich
/* remove a single client by ipfw rule number */
633 0bd34ed6 Scott Ullrich
function captiveportal_disconnect_client($id,$term_cause = 1) {
634 36254e4a Scott Ullrich
635 d99f7864 Scott Ullrich
	global $g, $config;
636 36254e4a Scott Ullrich
637 d99f7864 Scott Ullrich
	captiveportal_lock();
638 36254e4a Scott Ullrich
639 d99f7864 Scott Ullrich
	/* read database */
640
	$cpdb = captiveportal_read_db();
641
	$radiusservers = captiveportal_get_radius_servers();
642
643
	/* find entry */
644
	for ($i = 0; $i < count($cpdb); $i++) {
645
		if ($cpdb[$i][1] == $id) {
646
			captiveportal_disconnect($cpdb[$i], $radiusservers, $term_cause);
647
			captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "DISCONNECT");
648
			unset($cpdb[$i]);
649
			break;
650
		}
651
	}
652 36254e4a Scott Ullrich
653 d99f7864 Scott Ullrich
	/* write database */
654
	captiveportal_write_db($cpdb);
655 36254e4a Scott Ullrich
656 d99f7864 Scott Ullrich
	captiveportal_unlock();
657 5b237745 Scott Ullrich
}
658
659
/* send RADIUS acct stop for all current clients */
660
function captiveportal_radius_stop_all() {
661 d99f7864 Scott Ullrich
	global $g, $config;
662
663
	if (!isset($config['captiveportal']['radacct_enable']))
664
		return;
665
666
	captiveportal_lock();
667
	$cpdb = captiveportal_read_db();
668
669
	$radiusservers = captiveportal_get_radius_servers();
670
671
	if (isset($radiusservers[0])) {
672
		for ($i = 0; $i < count($cpdb); $i++) {
673
			RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
674
								   $cpdb[$i][4], // username
675
								   $cpdb[$i][5], // sessionid
676
								   $cpdb[$i][0], // start time
677
								   $radiusservers[0]['ipaddr'],
678
								   $radiusservers[0]['acctport'],
679
								   $radiusservers[0]['key'],
680
								   $cpdb[$i][2], // clientip
681
								   $cpdb[$i][3], // clientmac
682
								   7); // Admin Reboot
683
		}
684
	}
685
	captiveportal_unlock();
686 5b237745 Scott Ullrich
}
687
688
function captiveportal_passthrumac_configure() {
689
	global $config, $g;
690 36254e4a Scott Ullrich
691 3db19cf1 Scott Ullrich
	captiveportal_lock();
692 36254e4a Scott Ullrich
693 5b237745 Scott Ullrich
	/* clear out passthru macs, if necessary */
694 3db19cf1 Scott Ullrich
	unlink_if_exists("{$g['vardb_path']}/captiveportal_mac.db");
695 36254e4a Scott Ullrich
696 5b237745 Scott Ullrich
	if (is_array($config['captiveportal']['passthrumac'])) {
697 36254e4a Scott Ullrich
698 5b237745 Scott Ullrich
		$fd = @fopen("{$g['vardb_path']}/captiveportal_mac.db", "w");
699
		if (!$fd) {
700
			printf("Error: cannot open passthru mac DB file in captiveportal_passthrumac_configure().\n");
701 3db19cf1 Scott Ullrich
			captiveportal_unlock();
702 36254e4a Scott Ullrich
			return 1;
703 5b237745 Scott Ullrich
		}
704 36254e4a Scott Ullrich
705 5b237745 Scott Ullrich
		foreach ($config['captiveportal']['passthrumac'] as $macent) {
706
			/* record passthru mac so it can be recognized and let thru */
707
			fwrite($fd, $macent['mac'] . "\n");
708
		}
709 36254e4a Scott Ullrich
710
		fclose($fd);
711 5b237745 Scott Ullrich
	}
712 0bd34ed6 Scott Ullrich
713 d44bccc7 Scott Ullrich
	/*    pfSense:
714
	 * 	  pass through mac entries should always exist.  the reason
715 1de584c9 Scott Ullrich
	 *    for this is because we do not have native mac address filtering
716 d44bccc7 Scott Ullrich
	 *    mechanisms.  this allows us to filter by mac address easily
717 1de584c9 Scott Ullrich
	 *    and get around this limitation.   I consider this a bug in
718 d44bccc7 Scott Ullrich
	 *    m0n0wall and pfSense as m0n0wall does not have native mac
719
	 *    filtering mechanisms as well. -Scott Ullrich
720
	 */
721 1de584c9 Scott Ullrich
	if (is_array($config['captiveportal']['passthrumac'])) {
722
		mwexec("/sbin/ipfw delete 50");
723
		foreach($config['captiveportal']['passthrumac'] as $ptm) {
724
			/* create the pass through mac entry */
725 12249cad Scott Ullrich
			//system("echo /sbin/ipfw add 50 skipto 65535 ip from any to any MAC {$ptm['mac']} any > /tmp/cp");
726 5d61b44e Scott Ullrich
			mwexec("/sbin/ipfw add 50 skipto 29900 ip from any to any MAC {$ptm['mac']} any keep-state");
727
			mwexec("/sbin/ipfw add 50 skipto 29900 ip from any to any MAC any {$ptm['mac']} keep-state");
728 1de584c9 Scott Ullrich
		}
729
	}
730 0bd34ed6 Scott Ullrich
731 3db19cf1 Scott Ullrich
	captiveportal_unlock();
732 36254e4a Scott Ullrich
733 5b237745 Scott Ullrich
	return 0;
734
}
735
736
function captiveportal_allowedip_configure() {
737
	global $config, $g;
738 36254e4a Scott Ullrich
739 3db19cf1 Scott Ullrich
	captiveportal_lock();
740 5b237745 Scott Ullrich
741
	/* clear out existing allowed ips, if necessary */
742
	if (file_exists("{$g['vardb_path']}/captiveportal_ip.db")) {
743
		$fd = @fopen("{$g['vardb_path']}/captiveportal_ip.db", "r");
744
		if ($fd) {
745
			while (!feof($fd)) {
746
				$line = trim(fgets($fd));
747 3db19cf1 Scott Ullrich
				if ($line) {
748 5b237745 Scott Ullrich
					list($ip,$rule) = explode(",",$line);
749 3db19cf1 Scott Ullrich
					mwexec("/sbin/ipfw delete $rule");
750 36254e4a Scott Ullrich
				}
751 5b237745 Scott Ullrich
			}
752
		}
753 3db19cf1 Scott Ullrich
		fclose($fd);
754 5b237745 Scott Ullrich
		unlink("{$g['vardb_path']}/captiveportal_ip.db");
755
	}
756
757 3db19cf1 Scott Ullrich
	/* get next ipfw rule number */
758
	if (file_exists("{$g['vardb_path']}/captiveportal.nextrule"))
759
		$ruleno = trim(file_get_contents("{$g['vardb_path']}/captiveportal.nextrule"));
760
	if (!$ruleno)
761
		$ruleno = 10000;	/* first rule number */
762 36254e4a Scott Ullrich
763 5b237745 Scott Ullrich
	if (is_array($config['captiveportal']['allowedip'])) {
764 36254e4a Scott Ullrich
765 5b237745 Scott Ullrich
		$fd = @fopen("{$g['vardb_path']}/captiveportal_ip.db", "w");
766
		if (!$fd) {
767
			printf("Error: cannot open allowed ip DB file in captiveportal_allowedip_configure().\n");
768 3db19cf1 Scott Ullrich
			captiveportal_unlock();
769 36254e4a Scott Ullrich
			return 1;
770 5b237745 Scott Ullrich
		}
771 36254e4a Scott Ullrich
772 5b237745 Scott Ullrich
		foreach ($config['captiveportal']['allowedip'] as $ipent) {
773 d44bccc7 Scott Ullrich
            /* get next ipfw rule number */
774
            $ruleno = captiveportal_get_next_ipfw_ruleno();
775 36254e4a Scott Ullrich
776 d44bccc7 Scott Ullrich
            /* if the pool is empty, return apprioriate message and fail */
777
            if (is_null($ruleno)) {
778
                printf("Error: system reached maximum login capacity, no free FW rulenos in captiveportal_allowedip_configure().\n");
779
                fclose($fd);
780
                captiveportal_unlock();
781
                return 1;
782
            }
783 36254e4a Scott Ullrich
784 d44bccc7 Scott Ullrich
            /* record allowed ip so it can be recognized and removed later */
785
            fwrite($fd, $ipent['ip'] . "," . $ruleno ."\n");
786 36254e4a Scott Ullrich
787 d44bccc7 Scott Ullrich
            /* insert ipfw rule to allow ip thru */
788
            if ($ipent['dir'] == "from") {
789
                mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from " . $ipent['ip'] . " to any in");
790
                mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from any to " . $ipent['ip'] . " out");
791
            } else {
792
                mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from any to " . $ipent['ip'] . " in");
793
                mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from " . $ipent['ip'] . " to any out");
794
            }
795 36254e4a Scott Ullrich
796 d44bccc7 Scott Ullrich
        }
797 5b237745 Scott Ullrich
798 d44bccc7 Scott Ullrich
        fclose($fd);
799
    }
800 36254e4a Scott Ullrich
801 d44bccc7 Scott Ullrich
    captiveportal_unlock();
802
    return 0;
803 5b237745 Scott Ullrich
}
804
805 3db19cf1 Scott Ullrich
/* get last activity timestamp given ipfw rule number */
806
function captiveportal_get_last_activity($ruleno) {
807 36254e4a Scott Ullrich
808 d99f7864 Scott Ullrich
	$ipfwoutput = "";
809 36254e4a Scott Ullrich
810 d99f7864 Scott Ullrich
	exec("/sbin/ipfw -T list {$ruleno} 2>/dev/null", $ipfwoutput);
811
812
	/* in */
813
	if ($ipfwoutput[0]) {
814
		$ri = explode(" ", $ipfwoutput[0]);
815
		if ($ri[1])
816
			return $ri[1];
817
	}
818 36254e4a Scott Ullrich
819 d99f7864 Scott Ullrich
	return 0;
820 5b237745 Scott Ullrich
}
821
822
/* read RADIUS servers into array */
823
function captiveportal_get_radius_servers() {
824 0bd34ed6 Scott Ullrich
825
        global $g;
826
827
        if (file_exists("{$g['vardb_path']}/captiveportal_radius.db")) {
828
                $fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db","r");
829
                if ($fd) {
830
                        $radiusservers = array();
831
                        while (!feof($fd)) {
832
                                $line = trim(fgets($fd));
833
                                if ($line) {
834
                                        $radsrv = array();
835
                                        list($radsrv['ipaddr'],$radsrv['port'],$radsrv['acctport'],$radsrv['key']) = explode(",",$line);
836
                                        $radiusservers[] = $radsrv;
837
                                }
838
                        }
839
                        fclose($fd);
840
841
                        return $radiusservers;
842
                }
843
        }
844
845
        return false;
846 5b237745 Scott Ullrich
}
847
848 f56a73f1 Scott Ullrich
/* lock captive portal information, decide that the lock file is stale after 
849
   10 minutes and EXIT the process to not risk dataloss, issue warning in syslog every 1 minutes */
850 5b237745 Scott Ullrich
function captiveportal_lock() {
851 0bd34ed6 Scott Ullrich
852
        global $lockfile;
853
854 f56a73f1 Scott Ullrich
        $n = 1;
855
        while ($n) {
856 0bd34ed6 Scott Ullrich
                /* open the lock file in append mode to avoid race condition */
857
                if ($fd = @fopen($lockfile, "x")) {
858
                        /* succeeded */
859
                        fclose($fd);
860 f56a73f1 Scott Ullrich
						if($n > 10) {
861
						    captiveportal_syslog("LOCKINFO: Waiting for lock for $n seconds/s!");
862
						}
863 0bd34ed6 Scott Ullrich
                        return;
864
                } else {
865
                        /* file locked, wait and try again */
866
                        sleep(1);
867 f56a73f1 Scott Ullrich
868
						if(($n % 60) == 0) {
869
						    captiveportal_syslog("LOCKWARNING: waiting for lock for " . $n/60 . " minute/s!");
870
						    if(($n % 600) == 0) {
871
						        captiveportal_syslog("LOCKERROR: waiting for lock for 10 minute/s - EXITING PROCESS!");
872
						        die("Can't get a lock");
873
						    }
874
					    }
875 0bd34ed6 Scott Ullrich
                }
876 f56a73f1 Scott Ullrich
                $n++;
877 0bd34ed6 Scott Ullrich
        }
878 f56a73f1 Scott Ullrich
		/* we never get here */
879 5b237745 Scott Ullrich
}
880
881 0bd34ed6 Scott Ullrich
/* unlock captive portal information file */
882 5b237745 Scott Ullrich
function captiveportal_unlock() {
883 0bd34ed6 Scott Ullrich
884
        global $lockfile;
885
886
        if (file_exists($lockfile))
887
                unlink($lockfile);
888 5b237745 Scott Ullrich
}
889
890 3db19cf1 Scott Ullrich
/* log successful captive portal authentication to syslog */
891
/* part of this code from php.net */
892 0bd34ed6 Scott Ullrich
function captiveportal_logportalauth($user,$mac,$ip,$status, $message = null) {
893 d99f7864 Scott Ullrich
	$message = trim($message);
894
	// Log it
895
	if (!$message)
896 f56a73f1 Scott Ullrich
		$message = "$status: $user, $mac, $ip";
897 d99f7864 Scott Ullrich
	else
898 f56a73f1 Scott Ullrich
		$message = "$status: $user, $mac, $ip, $message";
899
	captiveportal_syslog($message);
900
	closelog();
901
}
902
903
/* log simple messages to syslog */
904
function captiveportal_syslog($message) {
905
	define_syslog_variables();
906
	$message = trim($message);
907
	openlog("logportalauth", LOG_PID, LOG_LOCAL4);
908
	// Log it
909
	syslog(LOG_INFO, $message);
910 d99f7864 Scott Ullrich
	closelog();
911 3db19cf1 Scott Ullrich
}
912
913 0bd34ed6 Scott Ullrich
function radius($username,$password,$clientip,$clientmac,$type) {
914 d44bccc7 Scott Ullrich
    global $g, $config;
915 d99f7864 Scott Ullrich
916 d44bccc7 Scott Ullrich
    /* Start locking from the beginning of an authentication session */
917
    captiveportal_lock();
918 0bd34ed6 Scott Ullrich
919 d44bccc7 Scott Ullrich
    $ruleno = captiveportal_get_next_ipfw_ruleno();
920
921
    /* if the pool is empty, return apprioriate message and fail authentication */
922
    if (is_null($ruleno)) {
923
        $auth_list = array();
924
        $auth_list['auth_val'] = 1;
925
        $auth_list['error'] = "System reached maximum login capacity";
926
        captiveportal_unlock();
927
        return $auth_list;
928
    }
929
930
    $radiusservers = captiveportal_get_radius_servers();
931
932
    $auth_list = RADIUS_AUTHENTICATION($username,
933
                    $password,
934
                    $radiusservers,
935
                    $clientip,
936
                    $clientmac,
937
                    $ruleno);
938
939
    if ($auth_list['auth_val'] == 2) {
940
        captiveportal_logportalauth($username,$clientmac,$clientip,$type);
941
        $sessionid = portal_allow($clientip,
942
                    $clientmac,
943
                    $username,
944
                    $password,
945
                    $auth_list,
946
                    $ruleno);
947
    }
948
    else {
949
        captiveportal_unlock();
950
    }
951
952
    return $auth_list;
953 0bd34ed6 Scott Ullrich
954
}
955
956
/* read captive portal DB into array */
957
function captiveportal_read_db() {
958
959
        global $g;
960
961
        $cpdb = array();
962
        $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r");
963
        if ($fd) {
964
                while (!feof($fd)) {
965
                        $line = trim(fgets($fd));
966
                        if ($line) {
967
                                $cpdb[] = explode(",", $line);
968
                        }
969
                }
970
                fclose($fd);
971
        }
972
        return $cpdb;
973
}
974
975
/* write captive portal DB */
976
function captiveportal_write_db($cpdb) {
977 d44bccc7 Scott Ullrich
                 
978 0bd34ed6 Scott Ullrich
        global $g;
979 d44bccc7 Scott Ullrich
                
980 0bd34ed6 Scott Ullrich
        $fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w");
981 d44bccc7 Scott Ullrich
        if ($fd) { 
982 0bd34ed6 Scott Ullrich
                foreach ($cpdb as $cpent) {
983
                        fwrite($fd, join(",", $cpent) . "\n");
984 d44bccc7 Scott Ullrich
                }       
985 0bd34ed6 Scott Ullrich
                fclose($fd);
986 d44bccc7 Scott Ullrich
        }       
987 0bd34ed6 Scott Ullrich
}
988
989
function captiveportal_write_elements() {
990 d44bccc7 Scott Ullrich
    global $g, $config;
991
    
992
    /* delete any existing elements */
993
    if (is_dir($g['captiveportal_element_path'])) {
994
        $dh = opendir($g['captiveportal_element_path']);
995
        while (($file = readdir($dh)) !== false) {
996
            if ($file != "." && $file != "..")
997
                unlink($g['captiveportal_element_path'] . "/" . $file);
998
        }
999
        closedir($dh);
1000
    } else {
1001
        mkdir($g['captiveportal_element_path']);
1002
    }
1003
    
1004 1fadb31d Scott Ullrich
	if (is_array($config['captiveportal']['element'])) {
1005
		conf_mount_rw();
1006
		foreach ($config['captiveportal']['element'] as $data) {
1007
			$fd = @fopen($g['captiveportal_element_path'] . '/' . $data['name'], "wb");
1008
			if (!$fd) {
1009
				printf("Error: cannot open '{$data['name']}' in captiveportal_write_elements().\n");
1010
				return 1;
1011
			}
1012
			$decoded = base64_decode($data['content']);
1013
			fwrite($fd,$decoded);
1014
			fclose($fd);
1015
			unlink_if_exists("{$g['captiveportal_path']}/{$data['name']}");
1016
			unlink_if_exists("{$g['captiveportal_path']}/{$data['name']}");
1017
			mwexec("cd {$g['captiveportal_path']}/ && ln -s {$g['captiveportal_element_path']}/{$data['name']} {$data['name']}");
1018
		}
1019
		conf_mount_ro();
1020
	}
1021 d44bccc7 Scott Ullrich
    
1022
    return 0;
1023 0bd34ed6 Scott Ullrich
}
1024
1025 920cafaf Scott Ullrich
/*
1026
 * This function will calculate the lowest free firewall ruleno
1027
 * within the range specified based on the actual installed rules
1028
 *
1029
 */
1030
1031
function captiveportal_get_next_ipfw_ruleno($rulenos_start = 10000, $rulenos_range_max = 9899) {
1032
1033 84e5047d Scott Ullrich
	$fwrules = "";
1034
	$matches = "";
1035 920cafaf Scott Ullrich
	exec("/sbin/ipfw show", $fwrules);
1036
	foreach ($fwrules as $fwrule) {
1037
		preg_match("/^(\d+)\s+/", $fwrule, $matches);
1038
		$rulenos_used[] = $matches[1];
1039
	}
1040
	$rulenos_used = array_unique($rulenos_used);
1041
	$rulenos_range = count($rulenos_used);
1042
	if ($rulenos_range > $rulenos_range_max) {
1043
		return NULL;
1044
	}
1045
	$rulenos_pool = range($rulenos_start, ($rulenos_start + $rulenos_range));
1046
	$rulenos_free = array_diff($rulenos_pool, $rulenos_used);
1047
	$ruleno = array_shift($rulenos_free);
1048
1049
	return $ruleno;
1050
}
1051
1052 360d815d Scott Ullrich
/**
1053
 * This function will calculate the traffic produced by a client
1054
 * based on its firewall rule
1055
 *
1056
 * Point of view: NAS
1057
 *
1058
 * Input means: from the client
1059
 * Output means: to the client
1060
 *
1061
 */
1062
1063
function getVolume($ruleno) {
1064
1065
    $volume = array();
1066
1067
    // Initialize vars properly, since we don't want NULL vars
1068
    $volume['input_pkts'] = $volume['input_bytes'] = $volume['output_pkts'] = $volume['output_bytes'] = 0 ;
1069
1070
    // Ingress
1071 84e5047d Scott Ullrich
    $ipfw = "";
1072
    $matches = "";
1073 360d815d Scott Ullrich
    exec("/sbin/ipfw show {$ruleno}", $ipfw);
1074
    preg_match("/(\d+)\s+(\d+)\s+(\d+)\s+.*/", $ipfw[0], $matches);
1075
    $volume['input_pkts'] = $matches[2];
1076
    $volume['input_bytes'] = $matches[3];
1077
1078
    // Flush internal buffer
1079
    unset($matches);
1080
1081
    // Outgress
1082
    preg_match("/(\d+)\s+(\d+)\s+(\d+)\s+.*/", $ipfw[1], $matches);
1083
    $volume['output_pkts'] = $matches[2];
1084
    $volume['output_bytes'] = $matches[3];
1085
1086
    return $volume;
1087
}
1088
1089 856e58a6 Scott Ullrich
/**
1090
 * Get the NAS-Identifier
1091
 *
1092
 * We will use our local hostname to make up the nas_id
1093
 */
1094
function getNasID()
1095
{
1096 84e5047d Scott Ullrich
    $nasId = "";
1097 856e58a6 Scott Ullrich
    exec("/bin/hostname", $nasId);
1098
    if(!$nasId[0])
1099 36d0358b Scott Ullrich
        $nasId[0] = "{$g['product_name']}";
1100 856e58a6 Scott Ullrich
    return $nasId[0];
1101
}
1102
1103
/**
1104
 * Get the NAS-IP-Address based on the current wan address
1105
 *
1106
 * Use functions in interfaces.inc to find this out
1107
 *
1108
 */
1109
1110
function getNasIP()
1111
{
1112
    $nasIp = get_current_wan_address();
1113
    if(!$nasIp)
1114
        $nasIp = "0.0.0.0";
1115
    return $nasIp;
1116
}
1117
1118 5bada54e Scott Ullrich
function portal_mac_fixed($clientmac) {
1119 8abb1030 Scott Ullrich
    global $g ;
1120 5bada54e Scott Ullrich
1121 8abb1030 Scott Ullrich
    /* open captive portal mac db */
1122
    if (file_exists("{$g['vardb_path']}/captiveportal_mac.db")) {
1123
        $fd = @fopen("{$g['vardb_path']}/captiveportal_mac.db","r") ;
1124
        if (!$fd) {
1125
            return FALSE;
1126
        }
1127
        while (!feof($fd)) {
1128
            $mac = trim(fgets($fd)) ;
1129
            if(strcasecmp($clientmac, $mac) == 0) {
1130
                fclose($fd) ;
1131
                return TRUE ;
1132
            }
1133
        }
1134
        fclose($fd) ;
1135
    }
1136
    return FALSE ;
1137 5bada54e Scott Ullrich
}
1138
1139 8abb1030 Scott Ullrich
?>