Project

General

Profile

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