Project

General

Profile

Download (21.3 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
	captiveportal.inc
4
	part of m0n0wall (http://m0n0.ch/wall)
5
	
6
	Copyright (C) 2003-2005 Manuel Kasper <mk@neon1.net>.
7
	All rights reserved.
8
	
9
	Redistribution and use in source and binary forms, with or without
10
	modification, are permitted provided that the following conditions are met:
11
	
12
	1. Redistributions of source code must retain the above copyright notice,
13
	   this list of conditions and the following disclaimer.
14
	
15
	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
	
19
	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

    
30
	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
	
37
/* include all configuration functions */
38
require_once("functions.inc");
39
require_once("radius_authentication.inc");
40
require_once("radius_accounting.inc") ;
41

    
42
function captiveportal_configure() {
43
	global $config, $g;
44
	
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
		/* kill any running mini_httpd */
53
		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
		/* generate ipfw rules */
60
		$cprules = captiveportal_rules_generate();
61
		
62
		/* make sure ipfw is loaded */
63
		mwexec("/sbin/kldload ipfw");
64
		
65
		/* stop accounting on all clients */
66
		captiveportal_radius_stop_all();
67

    
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
		
75
		/* 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
<title>pfSense captive portal</title>
84
</head>
85
<body>
86
<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
<form method="post" action="\$PORTAL_ACTION\$">
89
  <input name="accept" type="submit" value="Continue">
90
  <input name="redirurl" type="hidden" value="\$PORTAL_REDIRURL\$">
91
</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
			fclose($fd);	
102
		}
103
		
104
		/* 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
			fclose($fd);	
131
		}
132

    
133
		/* 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
		chdir($g['captiveportal_path']);
157

    
158
		$memory = get_memory();
159
		$avail = $memory[0];
160
		if($avail > 0 and $avail < 60) {
161
			$procs = 16;
162
		} else if($avail > 60 and $avail < 120) {
163
			$procs = 24;
164
		} else if($avail > 120 and $avail < 160) {
165
			$procs = 32;
166
		} else if($avail > 160 and $avail < 250) {
167
			$procs = 48;
168
		} else if($avail > 250 and $avail < 380) {
169
			$procs = 56;
170
		} else if($avail > 380 and $avail < 500) {
171
			$procs = 72;
172
		} else if($avail > 500 and $avail < 680) {
173
			$procs = 80;
174
		} else {
175
			$procs = 16;
176
		}	
177
	
178
		/* start web server */
179
		mwexec("/usr/local/sbin/mini_httpd -a -M 0 -u root -maxproc {$procs}" .
180
			" -p 8000 -i {$g['varrun_path']}/mini_httpd.cp.pid");
181
		
182
		/* fire up another one for HTTPS if requested */
183
		if (isset($config['captiveportal']['httpslogin']) &&
184
			$config['captiveportal']['certificate'] && $config['captiveportal']['private-key']) {
185
			
186
			$cert = base64_decode($config['captiveportal']['certificate']);
187
			$key = base64_decode($config['captiveportal']['private-key']);
188
			
189
			$fd = fopen("{$g['varetc_path']}/cert-portal.pem", "w");
190
			if (!$fd) {
191
				printf("Error: cannot open cert-portal.pem in system_webgui_start().\n");
192
				return 1;
193
			}
194
			chmod("{$g['varetc_path']}/cert-portal.pem", 0600);
195
			fwrite($fd, $cert);
196
			fwrite($fd, "\n");
197
			fwrite($fd, $key);
198
			fclose($fd);
199
			
200
			mwexec("/usr/local/sbin/mini_httpd -S -a -M 0 -E {$g['varetc_path']}/cert-portal.pem" .
201
				" -u root -maxproc 16 -p 8001" .
202
				" -i {$g['varrun_path']}/mini_httpd.cps.pid");
203
		}
204
			
205
		/* start pruning process (interval = 60 seconds) */
206
		mwexec("/usr/local/bin/minicron 60 {$g['varrun_path']}/minicron.pid " .
207
			"/etc/rc.prunecaptiveportal");
208
		
209
		/* generate passthru mac database */
210
		captiveportal_passthrumac_configure();
211
		/* create allowed ip database and insert ipfw rules to make it so */
212
		captiveportal_allowedip_configure();
213

    
214
		/* generate radius server database */
215
		if ($config['captiveportal']['radiusip'] && (!isset($config['captiveportal']['auth_method']) ||
216
				($config['captiveportal']['auth_method'] == "radius"))) {
217
			$radiusip = $config['captiveportal']['radiusip'];
218

    
219
			if ($config['captiveportal']['radiusport'])
220
				$radiusport = $config['captiveportal']['radiusport'];
221
			else
222
				$radiusport = 1812;
223

    
224
			if ($config['captiveportal']['radiusacctport'])
225
				$radiusacctport = $config['captiveportal']['radiusacctport'];
226
			else
227
				$radiusacctport = 1813;
228

    
229
			$radiuskey = $config['captiveportal']['radiuskey'];
230

    
231
			$fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db", "w");
232
			if (!$fd) {
233
				printf("Error: cannot open radius DB file in captiveportal_configure().\n");
234
				return 1;
235
			} else {
236
				fwrite($fd,$radiusip . "," . $radiusport . "," . $radiusacctport . "," . $radiuskey);
237
			}
238
			fclose($fd);
239
		}
240

    
241
		if ($g['booting'])
242
			echo "done\n";
243
		
244
	} else {
245
		killbypid("{$g['varrun_path']}/mini_httpd.cp.pid");
246
		killbypid("{$g['varrun_path']}/mini_httpd.cps.pid");
247
		killbypid("{$g['varrun_path']}/minicron.pid");
248

    
249
		captiveportal_radius_stop_all();
250

    
251
		mwexec("/sbin/sysctl net.link.ether.ipfw=0");
252

    
253
		if (!isset($config['shaper']['enable'])) {
254
			/* unload ipfw */
255
			mwexec("/sbin/kldunload ipfw");
256
		} else {
257
			/* shaper is on - just remove our rules */
258
			mwexec("/sbin/ipfw -f delete set 1");
259
			mwexec("/sbin/ipfw -f delete set 2");
260
			mwexec("/sbin/ipfw -f delete set 3");
261
		}
262
	}
263
	
264
	return 0;
265
}
266

    
267
function captiveportal_rules_generate() {
268
	global $config, $g;
269
	
270
	$cpifn = $config['captiveportal']['interface'];
271
	$cpif = $config['interfaces'][$cpifn]['if'];
272
	$cpip = $config['interfaces'][$cpifn]['ipaddr'];
273

    
274
	/* note: the captive portal daemon inserts all pass rules for authenticated
275
	   clients as skipto 50000 rules to make traffic shaping work */
276

    
277
	$cprules = "";
278
	
279
	/* captive portal on LAN interface? */
280
	if ($cpifn == "lan") {
281
		/* add anti-lockout rules */
282
		$cprules .= <<<EOD
283
add 500 set 1 pass all from $cpip to any out via $cpif
284
add 501 set 1 pass all from any to $cpip in via $cpif
285

    
286
EOD;
287
	}
288

    
289
	$cprules .= <<<EOD
290
# skip to traffic shaper if not on captive portal interface
291
add 1000 set 1 skipto 50000 all from any to any not layer2 not via $cpif
292
# pass all layer2 traffic on other interfaces
293
add 1001 set 1 pass layer2 not via $cpif
294

    
295
# layer 2: pass ARP
296
add 1100 set 1 pass layer2 mac-type arp
297
# layer 2: block anything else non-IP
298
add 1101 set 1 deny layer2 not mac-type ip
299
# layer 2: check if MAC addresses of authenticated clients are correct
300
add 1102 set 1 skipto 20000 layer2
301

    
302
# allow access to our DHCP server (which needs to be able to ping clients as well)
303
add 1200 set 1 pass udp from any 68 to 255.255.255.255 67 in
304
add 1201 set 1 pass udp from any 68 to $cpip 67 in
305
add 1202 set 1 pass udp from $cpip 67 to any 68 out
306
add 1203 set 1 pass icmp from $cpip to any out icmptype 8
307
add 1204 set 1 pass icmp from any to $cpip in icmptype 0
308

    
309
# allow access to our DNS forwarder
310
add 1300 set 1 pass udp from any to $cpip 53 in
311
add 1301 set 1 pass udp from $cpip 53 to any out
312

    
313
# allow access to our web server
314
add 1302 set 1 pass tcp from any to $cpip 8000 in
315
add 1303 set 1 pass tcp from $cpip 8000 to any out
316

    
317
EOD;
318

    
319
	if (isset($config['captiveportal']['httpslogin'])) {
320
		$cprules .= <<<EOD
321
add 1304 set 1 pass tcp from any to $cpip 8001 in
322
add 1305 set 1 pass tcp from $cpip 8001 to any out
323

    
324
EOD;
325
	}
326
	
327
	$cprules .= <<<EOD
328

    
329
# ... 10000-19899: rules per authenticated client go here...
330

    
331
# redirect non-authenticated clients to captive portal
332
add 19900 set 1 fwd 127.0.0.1,8000 tcp from any to any 80 in
333
# let the responses from the captive portal web server back out
334
add 19901 set 1 pass tcp from any 80 to any out
335
# block everything else
336
add 19902 set 1 deny all from any to any
337

    
338
# ... 20000-29899: layer2 block rules per authenticated client go here...
339

    
340
# pass everything else on layer2
341
add 29900 set 1 pass all from any to any layer2
342

    
343
EOD;
344

    
345
	return $cprules;
346
}
347

    
348
/* remove clients that have been around for longer than the specified amount of time */
349
/* db file structure: timestamp,ipfw_rule_no,clientip,clientmac,username,sessionid,password */
350
/* (password is in Base64 and only saved when reauthentication is enabled) */
351
function captiveportal_prune_old() {
352
	
353
	global $g, $config;
354
	
355
	/* check for expired entries */
356
	if ($config['captiveportal']['timeout'])
357
		$timeout = $config['captiveportal']['timeout'] * 60;
358
	else
359
		$timeout = 0;
360
		
361
	if ($config['captiveportal']['idletimeout'])
362
		$idletimeout = $config['captiveportal']['idletimeout'] * 60;
363
	else
364
		$idletimeout = 0;
365
	
366
	if (!$timeout && !$idletimeout && !isset($config['captiveportal']['reauthenticate']))
367
		return;
368
	
369
	captiveportal_lock();
370
	
371
	/* read database */
372
	$cpdb = captiveportal_read_db();
373
	
374
	$radiusservers = captiveportal_get_radius_servers();
375
	
376
	for ($i = 0; $i < count($cpdb); $i++) {
377
		
378
		$timedout = false;
379
		
380
		/* hard timeout? */
381
		if ($timeout) {
382
			if ((time() - $cpdb[$i][0]) >= $timeout)
383
				$timedout = true;	
384
		}
385
		
386
		/* if an idle timeout is specified, get last activity timestamp from ipfw */
387
		if (!$timedout && $idletimeout) {
388
			$lastact = captiveportal_get_last_activity($cpdb[$i][1]);
389
			if ($lastact && ((time() - $lastact) >= $idletimeout))
390
				$timedout = true;
391
		}
392
		
393
		if ($timedout) {
394
			captiveportal_disconnect($cpdb[$i], $radiusservers);
395
			captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "TIMEOUT");
396
			unset($cpdb[$i]);
397
		}
398
		
399
		/* do periodic RADIUS reauthentication? */
400
		if (!$timedout && isset($config['captiveportal']['reauthenticate']) &&
401
			($radiusservers !== false)) {
402
		
403
			if (isset($config['captiveportal']['radacct_enable'])) {
404
				if ($config['captiveportal']['reauthenticateacct'] == "stopstart") {
405
					/* stop and restart accounting */
406
					RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
407
										   $cpdb[$i][4], // username
408
										   $cpdb[$i][5], // sessionid
409
										   $cpdb[$i][0], // start time
410
										   $radiusservers[0]['ipaddr'],
411
										   $radiusservers[0]['acctport'],
412
										   $radiusservers[0]['key'],
413
										   $cpdb[$i][2]); //clientip
414
					exec("/sbin/ipfw zero {$cpdb[$i][1]}");
415
					RADIUS_ACCOUNTING_START($cpdb[$i][4],
416
											$cpdb[$i][5],
417
											$radiusservers[0]['ipaddr'],
418
											$radiusservers[0]['acctport'],
419
											$radiusservers[0]['key'],
420
											$cpdb[$i][2]);
421
				} else if ($config['captiveportal']['reauthenticateacct'] == "interimupdate") {
422
					RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
423
										   $cpdb[$i][4], // username
424
										   $cpdb[$i][5], // sessionid
425
										   $cpdb[$i][0], // start time
426
										   $radiusservers[0]['ipaddr'],
427
										   $radiusservers[0]['acctport'],
428
										   $radiusservers[0]['key'],
429
										   $cpdb[$i][2], //clientip
430
										   true);
431
				}
432
			}
433
		
434
			/* check this user against RADIUS again */
435
			$auth_val = RADIUS_AUTHENTICATION($cpdb[$i][4],
436
										  base64_decode($cpdb[$i][6]),
437
							  			  $radiusservers[0]['ipaddr'],
438
							  			  $radiusservers[0]['port'],
439
							  			  $radiusservers[0]['key']);
440
			
441
			if ($auth_val == 3) {
442
				captiveportal_disconnect($cpdb[$i], $radiusservers);
443
				captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "RADIUS_DISCONNECT");
444
				unset($cpdb[$i]);
445
			}
446
		}
447
	}
448
	
449
	/* write database */
450
	captiveportal_write_db($cpdb);
451
	
452
	captiveportal_unlock();
453
}
454

    
455
/* remove a single client according to the DB entry */
456
function captiveportal_disconnect($dbent, $radiusservers) {
457
	
458
	global $g, $config;
459
	
460
	/* this client needs to be deleted - remove ipfw rules */
461
	if (isset($config['captiveportal']['radacct_enable']) && isset($radiusservers[0])) {
462
		RADIUS_ACCOUNTING_STOP($dbent[1], // ruleno
463
							   $dbent[4], // username
464
							   $dbent[5], // sessionid
465
							   $dbent[0], // start time
466
							   $radiusservers[0]['ipaddr'],
467
							   $radiusservers[0]['acctport'],
468
							   $radiusservers[0]['key'],
469
							   $dbent[2]); //clientip
470
	}
471
	
472
	mwexec("/sbin/ipfw delete " . $dbent[1] . " " . ($dbent[1]+10000));
473
	
474
	//KEYCOM: we need to delete +40500 and +45500 as well...
475
	//these are the rule numbers we use to control traffic shaping for each logged in user via captive portal
476
	//we only need to remove our rules if peruserbw is turned on.
477
	if (isset($config['captiveportal']['peruserbw'])) {
478
		mwexec("/sbin/ipfw delete " . ($dbent[1]+40500));
479
		mwexec("/sbin/ipfw delete " . ($dbent[1]+45500));
480
	}
481
}
482

    
483
/* remove a single client by ipfw rule number */
484
function captiveportal_disconnect_client($id) {
485
	
486
	global $g, $config;
487
	
488
	captiveportal_lock();
489
	
490
	/* read database */
491
	$cpdb = captiveportal_read_db();
492
	$radiusservers = captiveportal_get_radius_servers();
493
	
494
	/* find entry */	
495
	for ($i = 0; $i < count($cpdb); $i++) {
496
		if ($cpdb[$i][1] == $id) {
497
			captiveportal_disconnect($cpdb[$i], $radiusservers);
498
			captiveportal_logportalauth($cpdb[$i][4], $cpdb[$i][3], $cpdb[$i][2], "DISCONNECT");
499
			unset($cpdb[$i]);
500
			break;
501
		}
502
	}
503
	
504
	/* write database */
505
	captiveportal_write_db($cpdb);
506
	
507
	captiveportal_unlock();
508
}
509

    
510
/* send RADIUS acct stop for all current clients */
511
function captiveportal_radius_stop_all() {
512
	global $g, $config;
513

    
514
	captiveportal_lock();
515
	$cpdb = captiveportal_read_db();
516
	
517
	$radiusservers = captiveportal_get_radius_servers();
518
	
519
	if (isset($radiusservers[0])) {
520
		for ($i = 0; $i < count($cpdb); $i++) {
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
		}
530
	}
531
	captiveportal_unlock();
532
}
533

    
534
function captiveportal_passthrumac_configure() {
535
	global $config, $g;
536
	
537
	captiveportal_lock();
538
	
539
	/* clear out passthru macs, if necessary */
540
	unlink_if_exists("{$g['vardb_path']}/captiveportal_mac.db");
541
	
542
	if (is_array($config['captiveportal']['passthrumac'])) {
543
		
544
		$fd = @fopen("{$g['vardb_path']}/captiveportal_mac.db", "w");
545
		if (!$fd) {
546
			printf("Error: cannot open passthru mac DB file in captiveportal_passthrumac_configure().\n");
547
			captiveportal_unlock();
548
			return 1;		
549
		}
550
		
551
		foreach ($config['captiveportal']['passthrumac'] as $macent) {
552
			/* record passthru mac so it can be recognized and let thru */
553
			fwrite($fd, $macent['mac'] . "\n");
554
		}
555
		
556
		fclose($fd); 
557
	}
558
	
559
	captiveportal_unlock();
560
	
561
	return 0;
562
}
563

    
564
function captiveportal_allowedip_configure() {
565
	global $config, $g;
566
	
567
	captiveportal_lock();
568

    
569
	/* clear out existing allowed ips, if necessary */
570
	if (file_exists("{$g['vardb_path']}/captiveportal_ip.db")) {
571
		$fd = @fopen("{$g['vardb_path']}/captiveportal_ip.db", "r");
572
		if ($fd) {
573
			while (!feof($fd)) {
574
				$line = trim(fgets($fd));
575
				if ($line) {
576
					list($ip,$rule) = explode(",",$line);
577
					mwexec("/sbin/ipfw delete $rule");
578
				}	
579
			}
580
		}
581
		fclose($fd);
582
		unlink("{$g['vardb_path']}/captiveportal_ip.db");
583
	}
584

    
585
	/* get next ipfw rule number */
586
	if (file_exists("{$g['vardb_path']}/captiveportal.nextrule"))
587
		$ruleno = trim(file_get_contents("{$g['vardb_path']}/captiveportal.nextrule"));
588
	if (!$ruleno)
589
		$ruleno = 10000;	/* first rule number */
590
	
591
	if (is_array($config['captiveportal']['allowedip'])) {
592
		
593
		$fd = @fopen("{$g['vardb_path']}/captiveportal_ip.db", "w");
594
		if (!$fd) {
595
			printf("Error: cannot open allowed ip DB file in captiveportal_allowedip_configure().\n");
596
			captiveportal_unlock();
597
			return 1;		
598
		}
599
		
600
		foreach ($config['captiveportal']['allowedip'] as $ipent) {
601
		
602
			/* record allowed ip so it can be recognized and removed later */
603
			fwrite($fd, $ipent['ip'] . "," . $ruleno ."\n");
604
			
605
			/* insert ipfw rule to allow ip thru */
606
			if ($ipent['dir'] == "from") {
607
				mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from " . $ipent['ip'] . " to any in");
608
				mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from any to " . $ipent['ip'] . " out");
609
			} else {
610
				mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from any to " . $ipent['ip'] . " in");
611
				mwexec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from " . $ipent['ip'] . " to any out");
612
			}
613
			
614
			$ruleno++;
615
			if ($ruleno > 19899)
616
				$ruleno = 10000;
617
		}
618
		
619
		fclose($fd); 
620

    
621
		/* write next rule number */
622
		$fd = @fopen("{$g['vardb_path']}/captiveportal.nextrule", "w");
623
		if ($fd) {
624
			fwrite($fd, $ruleno);
625
			fclose($fd);
626
		}
627
	}
628
	
629
	captiveportal_unlock();
630
	return 0;
631
}
632

    
633
/* get last activity timestamp given ipfw rule number */
634
function captiveportal_get_last_activity($ruleno) {
635
	
636
	exec("/sbin/ipfw -T list {$ruleno} 2>/dev/null", $ipfwoutput);
637
	
638
	/* in */
639
	if ($ipfwoutput[0]) {
640
		$ri = explode(" ", $ipfwoutput[0]);
641
		if ($ri[1])
642
			return $ri[1];
643
	}
644
	
645
	return 0;
646
}
647

    
648
/* read captive portal DB into array */
649
function captiveportal_read_db() {
650
	
651
	global $g;
652
	
653
	$cpdb = array();
654
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r");
655
	if ($fd) {
656
		while (!feof($fd)) {
657
			$line = trim(fgets($fd));
658
			if ($line) {
659
				$cpdb[] = explode(",", $line);
660
			}	
661
		}
662
		fclose($fd);
663
	}
664
	return $cpdb;
665
}
666

    
667
/* write captive portal DB */
668
function captiveportal_write_db($cpdb) {
669
	
670
	global $g;
671
	
672
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w");
673
	if ($fd) {
674
		foreach ($cpdb as $cpent) {
675
			fwrite($fd, join(",", $cpent) . "\n");
676
		}
677
		fclose($fd);
678
	}
679
}
680

    
681
/* read RADIUS servers into array */
682
function captiveportal_get_radius_servers() {
683
	
684
	global $g;
685
	
686
	if (file_exists("{$g['vardb_path']}/captiveportal_radius.db")) {
687
	   	$fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db","r");
688
		if ($fd) {
689
			$radiusservers = array();
690
			while (!feof($fd)) {
691
				$line = trim(fgets($fd));
692
				if ($line) {
693
					$radsrv = array();
694
					list($radsrv['ipaddr'],$radsrv['port'],$radsrv['acctport'],$radsrv['key']) = explode(",",$line);
695
					$radiusservers[] = $radsrv;
696
				}
697
			}
698
			fclose($fd);
699
			
700
			return $radiusservers;
701
		}
702
	}
703
	
704
	return false;
705
}
706

    
707
/* lock captive portal information, decide that the lock file is stale after
708
   10 seconds */
709
function captiveportal_lock() {
710
	
711
	global $g;
712
	
713
	$lockfile = "{$g['varrun_path']}/captiveportal.lock";
714
	
715
	$n = 0;
716
	while ($n < 10) {
717
		/* open the lock file in append mode to avoid race condition */
718
		if ($fd = @fopen($lockfile, "x")) {
719
			/* succeeded */
720
			fclose($fd);
721
			return;
722
		} else {
723
			/* file locked, wait and try again */
724
			sleep(1);
725
			$n++;
726
		}
727
	}
728
}
729

    
730
/* unlock configuration file */
731
function captiveportal_unlock() {
732
	
733
	global $g;
734
	
735
	$lockfile = "{$g['varrun_path']}/captiveportal.lock";
736
	
737
	if (file_exists($lockfile))
738
		unlink($lockfile);
739
}
740

    
741
/* log successful captive portal authentication to syslog */
742
/* part of this code from php.net */
743
function captiveportal_logportalauth($user,$mac,$ip,$status) {
744
	define_syslog_variables();
745
	openlog("logportalauth", LOG_PID, LOG_LOCAL4);
746
	// Log it
747
	syslog(LOG_INFO, "$status: $user, $mac, $ip");
748
	closelog();
749
}
750

    
751
?>
(2-2/23)