Project

General

Profile

Download (23.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
	
6 9699028a Scott Ullrich
	Copyright (C) 2003-2005 Manuel Kasper <mk@neon1.net>.
7 5b237745 Scott Ullrich
	All rights reserved.
8 3db19cf1 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 3db19cf1 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 3db19cf1 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 3db19cf1 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 3db19cf1 Scott Ullrich
	
37 5b237745 Scott Ullrich
/* include all configuration functions */
38
require_once("functions.inc");
39 3db19cf1 Scott Ullrich
require_once("radius_authentication.inc");
40 5b237745 Scott Ullrich
require_once("radius_accounting.inc") ;
41
42
function captiveportal_configure() {
43
	global $config, $g;
44 3db19cf1 Scott Ullrich
	
45
	if (isset($config['captiveportal']['enable']) &&
46
		(($config['captiveportal']['interface'] == "lan") ||
47
			isset($config['interfaces'][$config['captiveportal']['interface']]['enable']))) {
48
	
49
		if ($g['booting'])
50
			echo "Starting captive portal... ";
51
		
52 5b237745 Scott Ullrich
		/* kill any running mini_httpd */
53 c6c92abf Scott Ullrich
		killbypid("{$g['varrun_path']}/mini_httpd.cp.pid");
54
		killbypid("{$g['varrun_path']}/mini_httpd.cps.pid");
55
		
56
		/* kill any running minicron */
57
		killbypid("{$g['varrun_path']}/minicron.pid");
58
		
59 3db19cf1 Scott Ullrich
		/* generate ipfw rules */
60
		$cprules = captiveportal_rules_generate();
61
		
62
		/* make sure ipfw is loaded */
63
		mwexec("/sbin/kldload ipfw");
64
		
65 5b237745 Scott Ullrich
		/* stop accounting on all clients */
66 3db19cf1 Scott Ullrich
		captiveportal_radius_stop_all();
67 5b237745 Scott Ullrich
68
		/* remove old information */
69
		unlink_if_exists("{$g['vardb_path']}/captiveportal.nextrule");
70
		unlink_if_exists("{$g['vardb_path']}/captiveportal.db");
71
		unlink_if_exists("{$g['vardb_path']}/captiveportal_mac.db");
72
		unlink_if_exists("{$g['vardb_path']}/captiveportal_ip.db");
73
		unlink_if_exists("{$g['vardb_path']}/captiveportal_radius.db");
74 3db19cf1 Scott Ullrich
		
75 5b237745 Scott Ullrich
		/* write portal page */
76
		if ($config['captiveportal']['page']['htmltext'])
77
			$htmltext = base64_decode($config['captiveportal']['page']['htmltext']);
78
		else {
79
			/* example/template page */
80
			$htmltext = <<<EOD
81
<html>
82
<head>
83 61b040ce Scott Ullrich
<title>pfSense captive portal</title>
84 5b237745 Scott Ullrich
</head>
85 3db19cf1 Scott Ullrich
<body>
86 61b040ce Scott Ullrich
<h2>pfSense captive portal</h2>
87 407a29ca Scott Ullrich
Welcome to the pfSense Captive Portal!  This is the default page since a custom page has not been defined.
88
<form method="post" action="$PORTAL_ACTION$">
89
<table>
90
   <tr><td>Username:</td><td><input name="auth_user" type="text"></td></tr>
91
   <tr><td>Password:</td><td><input name="auth_pass" type="password"></td></tr>
92
   <tr><td colspan="2">
93 656da874 Scott Ullrich
	<form method="post" action="\$PORTAL_ACTION\$">
94 407a29ca Scott Ullrich
	<input name="accept" type="submit" value="Continue">
95 656da874 Scott Ullrich
	<input name="redirurl" type="hidden" value="\$PORTAL_REDIRURL\$">
96 407a29ca Scott Ullrich
   </td></tr>
97
</table>
98
</form>
99 5b237745 Scott Ullrich
</body>
100
</html>
101
102
EOD;
103
		}
104
105
		$fd = @fopen("{$g['varetc_path']}/captiveportal.html", "w");
106
		if ($fd) {
107
			fwrite($fd, $htmltext);
108 3db19cf1 Scott Ullrich
			fclose($fd);	
109 5b237745 Scott Ullrich
		}
110 3db19cf1 Scott Ullrich
		
111 5b237745 Scott Ullrich
		/* write error page */
112
		if ($config['captiveportal']['page']['errtext'])
113
			$errtext = base64_decode($config['captiveportal']['page']['errtext']);
114
		else {
115
			/* example page */
116
			$errtext = <<<EOD
117
<html>
118
<head>
119
<title>Authentication error</title>
120
</head>
121
<body>
122
<font color="#cc0000"><h2>Authentication error</h2></font>
123
<b>
124
Username and/or password invalid.
125
<br><br>
126
<a href="javascript:history.back()">Go back</a>
127
</b>
128
</body>
129
</html>
130
131
EOD;
132
		}
133
134
		$fd = @fopen("{$g['varetc_path']}/captiveportal-error.html", "w");
135
		if ($fd) {
136
			fwrite($fd, $errtext);
137 3db19cf1 Scott Ullrich
			fclose($fd);	
138 5b237745 Scott Ullrich
		}
139
140 3db19cf1 Scott Ullrich
		/* load rules */
141
		mwexec("/sbin/ipfw -f delete set 1");
142
		mwexec("/sbin/ipfw -f delete set 2");
143
		mwexec("/sbin/ipfw -f delete set 3");
144
		
145
		/* XXX - seems like ipfw cannot accept rules directly on stdin,
146
		   so we have to write them to a temporary file first */
147
		$fd = @fopen("{$g['tmp_path']}/ipfw.cp.rules", "w");
148
		if (!$fd) {
149
			printf("Cannot open ipfw.cp.rules in captiveportal_configure()\n");
150
			return 1;
151
		}
152
			
153
		fwrite($fd, $cprules);
154
		fclose($fd);
155
		
156
		mwexec("/sbin/ipfw {$g['tmp_path']}/ipfw.cp.rules");
157
		
158
		unlink("{$g['tmp_path']}/ipfw.cp.rules");
159
		
160
		/* filter on layer2 as well so we can check MAC addresses */
161
		mwexec("/sbin/sysctl net.link.ether.ipfw=1");
162
		
163 5b237745 Scott Ullrich
		chdir($g['captiveportal_path']);
164 cc76d459 Scott Ullrich
165 c6c92abf Scott Ullrich
		$memory = get_memory();
166
		$avail = $memory[0];
167
		if($avail > 0 and $avail < 60) {
168
			$procs = 16;
169
		} else if($avail > 60 and $avail < 120) {
170
			$procs = 24;
171
		} else if($avail > 120 and $avail < 160) {
172
			$procs = 32;
173
		} else if($avail > 160 and $avail < 250) {
174
			$procs = 48;
175
		} else if($avail > 250 and $avail < 380) {
176
			$procs = 56;
177
		} else if($avail > 380 and $avail < 500) {
178
			$procs = 72;
179
		} else if($avail > 500 and $avail < 680) {
180
			$procs = 80;
181
		} else {
182
			$procs = 16;
183
		}	
184
	
185
		/* start web server */
186
		mwexec("/usr/local/sbin/mini_httpd -a -M 0 -u root -maxproc {$procs}" .
187
			" -p 8000 -i {$g['varrun_path']}/mini_httpd.cp.pid");
188
		
189
		/* fire up another one for HTTPS if requested */
190 5b237745 Scott Ullrich
		if (isset($config['captiveportal']['httpslogin']) &&
191
			$config['captiveportal']['certificate'] && $config['captiveportal']['private-key']) {
192 c6c92abf Scott Ullrich
			
193 5b237745 Scott Ullrich
			$cert = base64_decode($config['captiveportal']['certificate']);
194
			$key = base64_decode($config['captiveportal']['private-key']);
195 c6c92abf Scott Ullrich
			
196 5b237745 Scott Ullrich
			$fd = fopen("{$g['varetc_path']}/cert-portal.pem", "w");
197
			if (!$fd) {
198
				printf("Error: cannot open cert-portal.pem in system_webgui_start().\n");
199
				return 1;
200
			}
201
			chmod("{$g['varetc_path']}/cert-portal.pem", 0600);
202
			fwrite($fd, $cert);
203
			fwrite($fd, "\n");
204
			fwrite($fd, $key);
205
			fclose($fd);
206 c6c92abf Scott Ullrich
			
207
			mwexec("/usr/local/sbin/mini_httpd -S -a -M 0 -E {$g['varetc_path']}/cert-portal.pem" .
208
				" -u root -maxproc 16 -p 8001" .
209
				" -i {$g['varrun_path']}/mini_httpd.cps.pid");
210 5b237745 Scott Ullrich
		}
211 c6c92abf Scott Ullrich
			
212
		/* start pruning process (interval = 60 seconds) */
213
		mwexec("/usr/local/bin/minicron 60 {$g['varrun_path']}/minicron.pid " .
214
			"/etc/rc.prunecaptiveportal");
215
		
216 5b237745 Scott Ullrich
		/* generate passthru mac database */
217 3db19cf1 Scott Ullrich
		captiveportal_passthrumac_configure();
218
		/* create allowed ip database and insert ipfw rules to make it so */
219
		captiveportal_allowedip_configure();
220 5b237745 Scott Ullrich
221
		/* generate radius server database */
222 3db19cf1 Scott Ullrich
		if ($config['captiveportal']['radiusip'] && (!isset($config['captiveportal']['auth_method']) ||
223
				($config['captiveportal']['auth_method'] == "radius"))) {
224
			$radiusip = $config['captiveportal']['radiusip'];
225 5b237745 Scott Ullrich
226 3db19cf1 Scott Ullrich
			if ($config['captiveportal']['radiusport'])
227
				$radiusport = $config['captiveportal']['radiusport'];
228 5b237745 Scott Ullrich
			else
229
				$radiusport = 1812;
230
231 3db19cf1 Scott Ullrich
			if ($config['captiveportal']['radiusacctport'])
232
				$radiusacctport = $config['captiveportal']['radiusacctport'];
233 5b237745 Scott Ullrich
			else
234
				$radiusacctport = 1813;
235
236
			$radiuskey = $config['captiveportal']['radiuskey'];
237
238
			$fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db", "w");
239
			if (!$fd) {
240
				printf("Error: cannot open radius DB file in captiveportal_configure().\n");
241
				return 1;
242
			} else {
243 3db19cf1 Scott Ullrich
				fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey);
244 5b237745 Scott Ullrich
			}
245 3db19cf1 Scott Ullrich
			fclose($fd);
246 5b237745 Scott Ullrich
		}
247
248 3db19cf1 Scott Ullrich
		if ($g['booting'])
249
			echo "done\n";
250
		
251 5b237745 Scott Ullrich
	} else {
252 c6c92abf Scott Ullrich
		killbypid("{$g['varrun_path']}/mini_httpd.cp.pid");
253
		killbypid("{$g['varrun_path']}/mini_httpd.cps.pid");
254
		killbypid("{$g['varrun_path']}/minicron.pid");
255 12ee8fe4 Scott Ullrich
256 3db19cf1 Scott Ullrich
		captiveportal_radius_stop_all();
257
258
		mwexec("/sbin/sysctl net.link.ether.ipfw=0");
259
260
		if (!isset($config['shaper']['enable'])) {
261
			/* unload ipfw */
262
			mwexec("/sbin/kldunload ipfw");
263
		} else {
264
			/* shaper is on - just remove our rules */
265
			mwexec("/sbin/ipfw -f delete set 1");
266
			mwexec("/sbin/ipfw -f delete set 2");
267
			mwexec("/sbin/ipfw -f delete set 3");
268
		}
269
	}
270
	
271 5b237745 Scott Ullrich
	return 0;
272
}
273
274 3db19cf1 Scott Ullrich
function captiveportal_rules_generate() {
275
	global $config, $g;
276
	
277
	$cpifn = $config['captiveportal']['interface'];
278
	$cpif = $config['interfaces'][$cpifn]['if'];
279
	$cpip = $config['interfaces'][$cpifn]['ipaddr'];
280
281
	/* note: the captive portal daemon inserts all pass rules for authenticated
282
	   clients as skipto 50000 rules to make traffic shaping work */
283
284
	$cprules = "";
285 181a843c Scott Ullrich
286
	/*   allow nat redirects to work  see
287
	     http://cvstrac.pfsense.com/tktview?tn=651
288
	 */
289
	$iflist = array("lan" => "LAN", "wan" => "WAN");
290 d66bb68a Scott Ullrich
	$captive_portal_interface = strtoupper($config['captiveportal']['interface']);
291 181a843c Scott Ullrich
	for ($i = 1; isset($config['interfaces']['opt' . $i]); $i++) 
292
		$iflist['opt' . $i] = $config['interfaces']['opt' . $i]['descr'];	
293
	foreach ($iflist as $ifent => $ifname) {
294 d66bb68a Scott Ullrich
		//echo "{$ifname} -> {$config['captiveportal']['interface']}\n";
295
		if($captive_portal_interface == strtoupper($ifname))
296 181a843c Scott Ullrich
			continue;
297
		$int = convert_friendly_interface_to_real_interface_name($ifname);
298 657f3f15 Scott Ullrich
		$cprules .= "add 30 set 1 skipto 50000 all from any to any in via {$int} keep-state\n";
299 181a843c Scott Ullrich
	}
300
301 3db19cf1 Scott Ullrich
	/* captive portal on LAN interface? */
302
	if ($cpifn == "lan") {
303
		/* add anti-lockout rules */
304
		$cprules .= <<<EOD
305 657f3f15 Scott Ullrich
add 500 pass all from $cpip to any out via $cpif
306 3db19cf1 Scott Ullrich
add 501 set 1 pass all from any to $cpip in via $cpif
307
308
EOD;
309
	}
310
311
	$cprules .= <<<EOD
312
# skip to traffic shaper if not on captive portal interface
313
add 1000 set 1 skipto 50000 all from any to any not layer2 not via $cpif
314
# pass all layer2 traffic on other interfaces
315
add 1001 set 1 pass layer2 not via $cpif
316
317
# layer 2: pass ARP
318
add 1100 set 1 pass layer2 mac-type arp
319
# layer 2: block anything else non-IP
320
add 1101 set 1 deny layer2 not mac-type ip
321
# layer 2: check if MAC addresses of authenticated clients are correct
322
add 1102 set 1 skipto 20000 layer2
323
324
# allow access to our DHCP server (which needs to be able to ping clients as well)
325
add 1200 set 1 pass udp from any 68 to 255.255.255.255 67 in
326
add 1201 set 1 pass udp from any 68 to $cpip 67 in
327
add 1202 set 1 pass udp from $cpip 67 to any 68 out
328
add 1203 set 1 pass icmp from $cpip to any out icmptype 8
329
add 1204 set 1 pass icmp from any to $cpip in icmptype 0
330
331
# allow access to our DNS forwarder
332
add 1300 set 1 pass udp from any to $cpip 53 in
333
add 1301 set 1 pass udp from $cpip 53 to any out
334
335
# allow access to our web server
336
add 1302 set 1 pass tcp from any to $cpip 8000 in
337
add 1303 set 1 pass tcp from $cpip 8000 to any out
338
339
EOD;
340
341
	if (isset($config['captiveportal']['httpslogin'])) {
342
		$cprules .= <<<EOD
343
add 1304 set 1 pass tcp from any to $cpip 8001 in
344
add 1305 set 1 pass tcp from $cpip 8001 to any out
345
346
EOD;
347
	}
348
	
349
	$cprules .= <<<EOD
350
351
# ... 10000-19899: rules per authenticated client go here...
352
353
# redirect non-authenticated clients to captive portal
354
add 19900 set 1 fwd 127.0.0.1,8000 tcp from any to any 80 in
355
# let the responses from the captive portal web server back out
356
add 19901 set 1 pass tcp from any 80 to any out
357
# block everything else
358
add 19902 set 1 deny all from any to any
359
360
# ... 20000-29899: layer2 block rules per authenticated client go here...
361
362
# pass everything else on layer2
363
add 29900 set 1 pass all from any to any layer2
364
365
EOD;
366
367
	return $cprules;
368
}
369
370 5b237745 Scott Ullrich
/* remove clients that have been around for longer than the specified amount of time */
371 3db19cf1 Scott Ullrich
/* db file structure: timestamp,ipfw_rule_no,clientip,clientmac,username,sessionid,password */
372
/* (password is in Base64 and only saved when reauthentication is enabled) */
373 5b237745 Scott Ullrich
function captiveportal_prune_old() {
374 3db19cf1 Scott Ullrich
	
375 5b237745 Scott Ullrich
	global $g, $config;
376 3db19cf1 Scott Ullrich
	
377 5b237745 Scott Ullrich
	/* check for expired entries */
378
	if ($config['captiveportal']['timeout'])
379
		$timeout = $config['captiveportal']['timeout'] * 60;
380
	else
381
		$timeout = 0;
382 3db19cf1 Scott Ullrich
		
383 5b237745 Scott Ullrich
	if ($config['captiveportal']['idletimeout'])
384
		$idletimeout = $config['captiveportal']['idletimeout'] * 60;
385
	else
386
		$idletimeout = 0;
387 3db19cf1 Scott Ullrich
	
388
	if (!$timeout && !$idletimeout && !isset($config['captiveportal']['reauthenticate']))
389 5b237745 Scott Ullrich
		return;
390 3db19cf1 Scott Ullrich
	
391 5b237745 Scott Ullrich
	captiveportal_lock();
392 3db19cf1 Scott Ullrich
	
393 5b237745 Scott Ullrich
	/* read database */
394
	$cpdb = captiveportal_read_db();
395 3db19cf1 Scott Ullrich
	
396 5b237745 Scott Ullrich
	$radiusservers = captiveportal_get_radius_servers();
397 3db19cf1 Scott Ullrich
	
398 5b237745 Scott Ullrich
	for ($i = 0; $i < count($cpdb); $i++) {
399 3db19cf1 Scott Ullrich
		
400 5b237745 Scott Ullrich
		$timedout = false;
401 a4004399 Scott Ullrich
		
402 3db19cf1 Scott Ullrich
		/* hard timeout? */
403
		if ($timeout) {
404
			if ((time() - $cpdb[$i][0]) >= $timeout)
405
				$timedout = true;	
406
		}
407
		
408
		/* if an idle timeout is specified, get last activity timestamp from ipfw */
409
		if (!$timedout && $idletimeout) {
410
			$lastact = captiveportal_get_last_activity($cpdb[$i][1]);
411
			if ($lastact && ((time() - $lastact) >= $idletimeout))
412
				$timedout = true;
413
		}
414
		
415 5b237745 Scott Ullrich
		if ($timedout) {
416 3db19cf1 Scott Ullrich
			captiveportal_disconnect($cpdb[$i], $radiusservers);
417
			captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "TIMEOUT");
418 5b237745 Scott Ullrich
			unset($cpdb[$i]);
419
		}
420 3db19cf1 Scott Ullrich
		
421
		/* do periodic RADIUS reauthentication? */
422
		if (!$timedout && isset($config['captiveportal']['reauthenticate']) &&
423
			($radiusservers !== false)) {
424
		
425
			if (isset($config['captiveportal']['radacct_enable'])) {
426
				if ($config['captiveportal']['reauthenticateacct'] == "stopstart") {
427
					/* stop and restart accounting */
428
					RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
429
										   $cpdb[$i][4], // username
430
										   $cpdb[$i][5], // sessionid
431
										   $cpdb[$i][0], // start time
432
										   $radiusservers[0]['ipaddr'],
433
										   $radiusservers[0]['acctport'],
434
										   $radiusservers[0]['key'],
435
										   $cpdb[$i][2]); //clientip
436
					exec("/sbin/ipfw zero {$cpdb[$i][1]}");
437
					RADIUS_ACCOUNTING_START($cpdb[$i][4],
438
											$cpdb[$i][5],
439
											$radiusservers[0]['ipaddr'],
440
											$radiusservers[0]['acctport'],
441
											$radiusservers[0]['key'],
442
											$cpdb[$i][2]);
443
				} else if ($config['captiveportal']['reauthenticateacct'] == "interimupdate") {
444
					RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
445
										   $cpdb[$i][4], // username
446
										   $cpdb[$i][5], // sessionid
447
										   $cpdb[$i][0], // start time
448
										   $radiusservers[0]['ipaddr'],
449
										   $radiusservers[0]['acctport'],
450
										   $radiusservers[0]['key'],
451
										   $cpdb[$i][2], //clientip
452
										   true);
453
				}
454
			}
455
		
456
			/* check this user against RADIUS again */
457
			$auth_val = RADIUS_AUTHENTICATION($cpdb[$i][4],
458
										  base64_decode($cpdb[$i][6]),
459
							  			  $radiusservers[0]['ipaddr'],
460
							  			  $radiusservers[0]['port'],
461
							  			  $radiusservers[0]['key']);
462
			
463
			if ($auth_val == 3) {
464
				captiveportal_disconnect($cpdb[$i], $radiusservers);
465
				captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "RADIUS_DISCONNECT");
466
				unset($cpdb[$i]);
467
			}
468
		}
469 5b237745 Scott Ullrich
	}
470 3db19cf1 Scott Ullrich
	
471 5b237745 Scott Ullrich
	/* write database */
472
	captiveportal_write_db($cpdb);
473 3db19cf1 Scott Ullrich
	
474 5b237745 Scott Ullrich
	captiveportal_unlock();
475
}
476
477 3db19cf1 Scott Ullrich
/* remove a single client according to the DB entry */
478
function captiveportal_disconnect($dbent, $radiusservers) {
479
	
480 5b237745 Scott Ullrich
	global $g, $config;
481 3db19cf1 Scott Ullrich
	
482
	/* this client needs to be deleted - remove ipfw rules */
483
	if (isset($config['captiveportal']['radacct_enable']) && isset($radiusservers[0])) {
484
		RADIUS_ACCOUNTING_STOP($dbent[1], // ruleno
485
							   $dbent[4], // username
486
							   $dbent[5], // sessionid
487
							   $dbent[0], // start time
488
							   $radiusservers[0]['ipaddr'],
489
							   $radiusservers[0]['acctport'],
490
							   $radiusservers[0]['key'],
491
							   $dbent[2]); //clientip
492
	}
493
	
494
	mwexec("/sbin/ipfw delete " . $dbent[1] . " " . ($dbent[1]+10000));
495
	
496
	//KEYCOM: we need to delete +40500 and +45500 as well...
497
	//these are the rule numbers we use to control traffic shaping for each logged in user via captive portal
498
	//we only need to remove our rules if peruserbw is turned on.
499
	if (isset($config['captiveportal']['peruserbw'])) {
500
		mwexec("/sbin/ipfw delete " . ($dbent[1]+40500));
501
		mwexec("/sbin/ipfw delete " . ($dbent[1]+45500));
502
	}
503
}
504 12ee8fe4 Scott Ullrich
505 3db19cf1 Scott Ullrich
/* remove a single client by ipfw rule number */
506
function captiveportal_disconnect_client($id) {
507
	
508
	global $g, $config;
509
	
510 5b237745 Scott Ullrich
	captiveportal_lock();
511 3db19cf1 Scott Ullrich
	
512 5b237745 Scott Ullrich
	/* read database */
513
	$cpdb = captiveportal_read_db();
514
	$radiusservers = captiveportal_get_radius_servers();
515 3db19cf1 Scott Ullrich
	
516
	/* find entry */	
517 5b237745 Scott Ullrich
	for ($i = 0; $i < count($cpdb); $i++) {
518
		if ($cpdb[$i][1] == $id) {
519 3db19cf1 Scott Ullrich
			captiveportal_disconnect($cpdb[$i], $radiusservers);
520
			captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "DISCONNECT");
521 5b237745 Scott Ullrich
			unset($cpdb[$i]);
522
			break;
523
		}
524
	}
525 3db19cf1 Scott Ullrich
	
526 5b237745 Scott Ullrich
	/* write database */
527
	captiveportal_write_db($cpdb);
528 3db19cf1 Scott Ullrich
	
529 5b237745 Scott Ullrich
	captiveportal_unlock();
530
}
531
532
/* send RADIUS acct stop for all current clients */
533
function captiveportal_radius_stop_all() {
534
	global $g, $config;
535
536 3db19cf1 Scott Ullrich
	captiveportal_lock();
537
	$cpdb = captiveportal_read_db();
538
	
539 5b237745 Scott Ullrich
	$radiusservers = captiveportal_get_radius_servers();
540 3db19cf1 Scott Ullrich
	
541 5b237745 Scott Ullrich
	if (isset($radiusservers[0])) {
542
		for ($i = 0; $i < count($cpdb); $i++) {
543
			RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
544
								   $cpdb[$i][4], // username
545
								   $cpdb[$i][5], // sessionid
546
								   $cpdb[$i][0], // start time
547
								   $radiusservers[0]['ipaddr'],
548
								   $radiusservers[0]['acctport'],
549 9699028a Scott Ullrich
								   $radiusservers[0]['key'],
550
								   $cpdb[$i][2]); //clientip
551 5b237745 Scott Ullrich
		}
552
	}
553 3db19cf1 Scott Ullrich
	captiveportal_unlock();
554 5b237745 Scott Ullrich
}
555
556
function captiveportal_passthrumac_configure() {
557
	global $config, $g;
558 3db19cf1 Scott Ullrich
	
559
	captiveportal_lock();
560
	
561 5b237745 Scott Ullrich
	/* clear out passthru macs, if necessary */
562 3db19cf1 Scott Ullrich
	unlink_if_exists("{$g['vardb_path']}/captiveportal_mac.db");
563
	
564 5b237745 Scott Ullrich
	if (is_array($config['captiveportal']['passthrumac'])) {
565 3db19cf1 Scott Ullrich
		
566 5b237745 Scott Ullrich
		$fd = @fopen("{$g['vardb_path']}/captiveportal_mac.db", "w");
567
		if (!$fd) {
568
			printf("Error: cannot open passthru mac DB file in captiveportal_passthrumac_configure().\n");
569 3db19cf1 Scott Ullrich
			captiveportal_unlock();
570
			return 1;		
571 5b237745 Scott Ullrich
		}
572 3db19cf1 Scott Ullrich
		
573 5b237745 Scott Ullrich
		foreach ($config['captiveportal']['passthrumac'] as $macent) {
574
			/* record passthru mac so it can be recognized and let thru */
575
			fwrite($fd, $macent['mac'] . "\n");
576
		}
577 3db19cf1 Scott Ullrich
		
578
		fclose($fd); 
579 5b237745 Scott Ullrich
	}
580 3db19cf1 Scott Ullrich
	
581 1de584c9 Scott Ullrich
	/*    pass through mac entries should always exist.  the reason
582
	 *    for this is because we do not have native mac address filtering
583
         *    mechanisms.  this allows us to filter by mac address easily
584
	 *    and get around this limitation.   I consider this a bug in
585
         *    m0n0wall and pfSense as m0n0wall does not have native mac 
586
         *    filtering mechanisms as well. -Scott Ullrich
587
         */
588
	if (is_array($config['captiveportal']['passthrumac'])) {
589
		mwexec("/sbin/ipfw delete 50");
590
		foreach($config['captiveportal']['passthrumac'] as $ptm) {
591
			/* create the pass through mac entry */
592 12249cad Scott Ullrich
			//system("echo /sbin/ipfw add 50 skipto 65535 ip from any to any MAC {$ptm['mac']} any > /tmp/cp");
593 5d61b44e Scott Ullrich
			mwexec("/sbin/ipfw add 50 skipto 29900 ip from any to any MAC {$ptm['mac']} any keep-state");
594
			mwexec("/sbin/ipfw add 50 skipto 29900 ip from any to any MAC any {$ptm['mac']} keep-state");
595 1de584c9 Scott Ullrich
		}
596
	}
597
	
598 3db19cf1 Scott Ullrich
	captiveportal_unlock();
599
	
600 5b237745 Scott Ullrich
	return 0;
601
}
602
603
function captiveportal_allowedip_configure() {
604
	global $config, $g;
605 3db19cf1 Scott Ullrich
	
606
	captiveportal_lock();
607 5b237745 Scott Ullrich
608
	/* clear out existing allowed ips, if necessary */
609
	if (file_exists("{$g['vardb_path']}/captiveportal_ip.db")) {
610
		$fd = @fopen("{$g['vardb_path']}/captiveportal_ip.db", "r");
611
		if ($fd) {
612
			while (!feof($fd)) {
613
				$line = trim(fgets($fd));
614 3db19cf1 Scott Ullrich
				if ($line) {
615 5b237745 Scott Ullrich
					list($ip,$rule) = explode(",",$line);
616 3db19cf1 Scott Ullrich
					mwexec("/sbin/ipfw delete $rule");
617
				}	
618 5b237745 Scott Ullrich
			}
619
		}
620 3db19cf1 Scott Ullrich
		fclose($fd);
621 5b237745 Scott Ullrich
		unlink("{$g['vardb_path']}/captiveportal_ip.db");
622
	}
623
624 3db19cf1 Scott Ullrich
	/* get next ipfw rule number */
625
	if (file_exists("{$g['vardb_path']}/captiveportal.nextrule"))
626
		$ruleno = trim(file_get_contents("{$g['vardb_path']}/captiveportal.nextrule"));
627
	if (!$ruleno)
628
		$ruleno = 10000;	/* first rule number */
629
	
630 5b237745 Scott Ullrich
	if (is_array($config['captiveportal']['allowedip'])) {
631 3db19cf1 Scott Ullrich
		
632 5b237745 Scott Ullrich
		$fd = @fopen("{$g['vardb_path']}/captiveportal_ip.db", "w");
633
		if (!$fd) {
634
			printf("Error: cannot open allowed ip DB file in captiveportal_allowedip_configure().\n");
635 3db19cf1 Scott Ullrich
			captiveportal_unlock();
636
			return 1;		
637 5b237745 Scott Ullrich
		}
638 3db19cf1 Scott Ullrich
		
639 5b237745 Scott Ullrich
		foreach ($config['captiveportal']['allowedip'] as $ipent) {
640 3db19cf1 Scott Ullrich
		
641 5b237745 Scott Ullrich
			/* record allowed ip so it can be recognized and removed later */
642 3db19cf1 Scott Ullrich
			fwrite($fd, $ipent['ip'] . "," . $ruleno ."\n");
643
			
644
			/* insert ipfw rule to allow ip thru */
645
			if ($ipent['dir'] == "from") {
646
				mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from " . $ipent['ip'] . " to any in");
647
				mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from any to " . $ipent['ip'] . " out");
648
			} else {
649
				mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from any to " . $ipent['ip'] . " in");
650
				mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from " . $ipent['ip'] . " to any out");
651
			}
652
			
653
			$ruleno++;
654
			if ($ruleno > 19899)
655
				$ruleno = 10000;
656 5b237745 Scott Ullrich
		}
657 3db19cf1 Scott Ullrich
		
658
		fclose($fd); 
659 5b237745 Scott Ullrich
660
		/* write next rule number */
661
		$fd = @fopen("{$g['vardb_path']}/captiveportal.nextrule", "w");
662
		if ($fd) {
663
			fwrite($fd, $ruleno);
664
			fclose($fd);
665
		}
666
	}
667 3db19cf1 Scott Ullrich
	
668
	captiveportal_unlock();
669 5b237745 Scott Ullrich
	return 0;
670
}
671
672 3db19cf1 Scott Ullrich
/* get last activity timestamp given ipfw rule number */
673
function captiveportal_get_last_activity($ruleno) {
674
	
675
	exec("/sbin/ipfw -T list {$ruleno} 2>/dev/null", $ipfwoutput);
676
	
677
	/* in */
678
	if ($ipfwoutput[0]) {
679
		$ri = explode(" ", $ipfwoutput[0]);
680
		if ($ri[1])
681
			return $ri[1];
682
	}
683
	
684 5b237745 Scott Ullrich
	return 0;
685
}
686
687
/* read captive portal DB into array */
688
function captiveportal_read_db() {
689 3db19cf1 Scott Ullrich
	
690 5b237745 Scott Ullrich
	global $g;
691 3db19cf1 Scott Ullrich
	
692 5b237745 Scott Ullrich
	$cpdb = array();
693
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r");
694
	if ($fd) {
695
		while (!feof($fd)) {
696
			$line = trim(fgets($fd));
697
			if ($line) {
698
				$cpdb[] = explode(",", $line);
699 3db19cf1 Scott Ullrich
			}	
700 5b237745 Scott Ullrich
		}
701
		fclose($fd);
702
	}
703
	return $cpdb;
704
}
705
706
/* write captive portal DB */
707
function captiveportal_write_db($cpdb) {
708 3db19cf1 Scott Ullrich
	
709 5b237745 Scott Ullrich
	global $g;
710 3db19cf1 Scott Ullrich
	
711 5b237745 Scott Ullrich
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w");
712
	if ($fd) {
713
		foreach ($cpdb as $cpent) {
714
			fwrite($fd, join(",", $cpent) . "\n");
715
		}
716
		fclose($fd);
717
	}
718
}
719
720
/* read RADIUS servers into array */
721
function captiveportal_get_radius_servers() {
722 3db19cf1 Scott Ullrich
	
723 5b237745 Scott Ullrich
	global $g;
724 3db19cf1 Scott Ullrich
	
725 5b237745 Scott Ullrich
	if (file_exists("{$g['vardb_path']}/captiveportal_radius.db")) {
726
	   	$fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db","r");
727
		if ($fd) {
728
			$radiusservers = array();
729
			while (!feof($fd)) {
730
				$line = trim(fgets($fd));
731
				if ($line) {
732
					$radsrv = array();
733
					list($radsrv['ipaddr'],$radsrv['port'],$radsrv['acctport'],$radsrv['key']) = explode(",",$line);
734
					$radiusservers[] = $radsrv;
735
				}
736
			}
737
			fclose($fd);
738 3db19cf1 Scott Ullrich
			
739 5b237745 Scott Ullrich
			return $radiusservers;
740
		}
741
	}
742 3db19cf1 Scott Ullrich
	
743 5b237745 Scott Ullrich
	return false;
744
}
745
746
/* lock captive portal information, decide that the lock file is stale after
747
   10 seconds */
748
function captiveportal_lock() {
749 3db19cf1 Scott Ullrich
	
750 5b237745 Scott Ullrich
	global $g;
751 3db19cf1 Scott Ullrich
	
752 5b237745 Scott Ullrich
	$lockfile = "{$g['varrun_path']}/captiveportal.lock";
753 3db19cf1 Scott Ullrich
	
754 5b237745 Scott Ullrich
	$n = 0;
755
	while ($n < 10) {
756
		/* open the lock file in append mode to avoid race condition */
757
		if ($fd = @fopen($lockfile, "x")) {
758
			/* succeeded */
759
			fclose($fd);
760
			return;
761
		} else {
762
			/* file locked, wait and try again */
763
			sleep(1);
764
			$n++;
765
		}
766
	}
767
}
768
769
/* unlock configuration file */
770
function captiveportal_unlock() {
771 3db19cf1 Scott Ullrich
	
772 5b237745 Scott Ullrich
	global $g;
773 3db19cf1 Scott Ullrich
	
774 5b237745 Scott Ullrich
	$lockfile = "{$g['varrun_path']}/captiveportal.lock";
775 3db19cf1 Scott Ullrich
	
776 5b237745 Scott Ullrich
	if (file_exists($lockfile))
777
		unlink($lockfile);
778
}
779
780 3db19cf1 Scott Ullrich
/* log successful captive portal authentication to syslog */
781
/* part of this code from php.net */
782
function captiveportal_logportalauth($user,$mac,$ip,$status) {
783
	define_syslog_variables();
784
	openlog("logportalauth", LOG_PID, LOG_LOCAL4);
785
	// Log it
786
	syslog(LOG_INFO, "$status: $user, $mac, $ip");
787
	closelog();
788
}
789
790 c6c92abf Scott Ullrich
?>