Project

General

Profile

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