Project

General

Profile

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