Project

General

Profile

Download (15.5 KB) Statistics
| Branch: | Tag: | Revision:
1
#!/usr/local/bin/php
2
<?php 
3
/*
4
	index.php
5
	part of m0n0wall (http://m0n0.ch/wall)
6
	
7
	Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
8
	All rights reserved.
9
	
10
	Redistribution and use in source and binary forms, with or without
11
	modification, are permitted provided that the following conditions are met:
12
	
13
	1. Redistributions of source code must retain the above copyright notice,
14
	   this list of conditions and the following disclaimer.
15
	
16
	2. Redistributions in binary form must reproduce the above copyright
17
	   notice, this list of conditions and the following disclaimer in the
18
	   documentation and/or other materials provided with the distribution.
19
	
20
	THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
21
	INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
22
	AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
23
	AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
24
	OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
	SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
	INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
	CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
	POSSIBILITY OF SUCH DAMAGE.
30

    
31
	This version of index.php has been modified by Rob Parker
32
	<rob.parker@keycom.co.uk>. Changes made are in relation to Per-User Bandwidth
33
	Management based on returned RADIUS attributes, and are (c) 2004 Keycom PLC.
34
*/
35

    
36
require("globals.inc");
37
require("util.inc");
38
require("config.inc");
39
require("radius_authentication.inc") ;
40
require("radius_accounting.inc") ;
41

    
42
header("Expires: 0");
43
header("Cache-Control: no-store, no-cache, must-revalidate");
44
header("Cache-Control: post-check=0, pre-check=0", false);
45
header("Pragma: no-cache");
46

    
47
$orig_host = $_ENV['HTTP_HOST'];
48
$orig_request = $_ENV['CAPTIVE_REQPATH'];
49
$lockfile = "{$g['varrun_path']}/captiveportal.lock";
50
$clientip = $_ENV['REMOTE_ADDR'];
51

    
52
if (!$clientip) {
53
	/* not good - bail out */
54
	exit;
55
}
56

    
57
/* find MAC address for client */
58
$clientmac = arp_get_mac_by_ip($clientip);
59
if (!$clientmac && !isset($config['captiveportal']['nomacfilter'])) {
60
	/* unable to find MAC address - shouldn't happen! - bail out */
61
	exit;
62
}
63

    
64
if ($clientmac && portal_mac_fixed($clientmac)) {
65
	/* punch hole in ipfw for pass thru mac addresses */
66
	// KEYCOM: passthru mac bandwidth control]
67
	if (isset($config['captiveportal']['peruserbw'])) {
68
		portal_allow($clientip, $clientmac, "unauthenticated",$config['captiveportal']['bwauthmacup'],$config['captiveportal']['bwauthmacdn']);
69
	} else {
70
		portal_allow($clientip, $clientmac, "unauthenticated",0,0);
71
	}
72

    
73
} else if ($_POST['accept'] && file_exists("{$g['vardb_path']}/captiveportal_radius.db")) {
74

    
75
	/* authenticate against radius server */
76
	$radiusservers = captiveportal_get_radius_servers();
77
	
78
	if ($_POST['auth_user'] && $_POST['auth_pass']) {
79
		$auth_val = RADIUS_AUTHENTICATION($_POST['auth_user'],
80
										  $_POST['auth_pass'],
81
							  			  $radiusservers[0]['ipaddr'],
82
							  			  $radiusservers[0]['port'],
83
							  			  $radiusservers[0]['key']);
84
		$auth_returns = explode("/", $auth_val);
85
		$auth_val = $auth_returns[0];
86
		$bw_up = $auth_returns[1];
87
		$bw_down = $auth_returns[2];
88
		if ($auth_val == 2) {
89
			if (isset($config['captiveportal']['peruserbw'])) {
90
				$sessionid = portal_allow($clientip, $clientmac, $_POST['auth_user'],$bw_up,$bw_down);
91
			} else {
92
				$sessionid = portal_allow($clientip, $clientmac, $_POST['auth_user'],0,0);
93
			}
94
			if (isset($config['captiveportal']['radacct_enable']) && isset($radiusservers[0])) {
95
				$auth_val = RADIUS_ACCOUNTING_START($_POST['auth_user'],
96
													$sessionid,
97
													$radiusservers[0]['ipaddr'],
98
													$radiusservers[0]['acctport'],
99
													$radiusservers[0]['key'],
100
													$clientip);
101
			}
102
		} else {
103
			readfile("{$g['varetc_path']}/captiveportal-error.html");
104
		}
105
	} else {
106
		readfile("{$g['varetc_path']}/captiveportal-error.html");
107
	}
108

    
109
} else if ($_POST['accept'] && $config['captiveportal']['auth_method']=="local") {
110
	//check against local usermanager
111
	
112
	//erase expired accounts
113
	if(trim($config['users'][$_POST['auth_user']]['expirationdate'])!="" && strtotime("-1 day")>strtotime($config['users'][$_POST['auth_user']]['expirationdate'])){
114
		unset($config['users'][$_POST['auth_user']]);
115
		write_config();
116
	}
117
	if($config['users'][$_POST['auth_user']]['password']==md5($_POST['auth_pass'])){
118
		portal_allow($clientip, $clientmac,$_POST['auth_user'],0,0);
119
	} else {
120
		readfile("{$g['varetc_path']}/captiveportal-error.html");
121
	}
122
} else if ($_POST['accept'] && $clientip) {
123
	//KEYCOM: authorised up and down bandwidth defaults (set from webgui). If not set, use 128/128
124
	if (isset($config['captiveportal']['peruserbw'])) {
125
		$bw_up=$config['captiveportal']['bwauthipup'];
126
		$bw_down=$config['captiveportal']['bwauthipdn'];
127
		if(!isset($bw_up)) {
128
			$bw_up=128;
129
		}
130
		if(!isset($bw_down)) {
131
			$bw_down=128;
132
		}
133
		portal_allow($clientip, $clientmac, "unauthenticated",$bw_up,$bw_down);
134
	} else {
135
		portal_allow($clientip, $clientmac, "unauthenticated",0,0);
136
	}
137
} else if ($_POST['logout_id']) {
138
	disconnect_client($_POST['logout_id']);
139
	echo <<<EOD
140
<HTML>
141
<HEAD><TITLE>Disconnecting...</TITLE></HEAD>
142
<BODY BGCOLOR="#435370">
143
<SPAN STYLE="color: #ffffff; font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">
144
<B>You've been disconnected.</B>
145
</SPAN>
146
<SCRIPT LANGUAGE="JavaScript">
147
<!--
148
setTimeout('window.close();',5000) ;
149
-->
150
</SCRIPT>
151
</BODY>
152
</HTML>
153

    
154
EOD;
155
} else if (($_ENV['SERVER_PORT'] != 8001) && isset($config['captiveportal']['httpslogin'])) {
156
	/* redirect to HTTPS login page */
157
	header("Location: https://{$config['captiveportal']['httpsname']}:8001/?redirurl=" . urlencode("http://{$orig_host}{$orig_request}"));
158
} else {
159
	/* display captive portal page */
160
	$htmltext = file_get_contents("{$g['varetc_path']}/captiveportal.html");
161
	
162
	/* substitute variables */
163
	if (isset($config['captiveportal']['httpslogin']))
164
		$htmltext = str_replace("\$PORTAL_ACTION\$", "https://{$config['captiveportal']['httpsname']}:8001/", $htmltext);
165
	else
166
		$htmltext = str_replace("\$PORTAL_ACTION\$", "", $htmltext);
167
	
168
	if (preg_match("/redirurl=(.*)/", $orig_request, $matches))
169
		$redirurl = urldecode($matches[1]);
170
	else
171
		$redirurl = "http://{$orig_host}{$orig_request}";
172
	$htmltext = str_replace("\$PORTAL_REDIRURL\$", htmlspecialchars($redirurl), $htmltext);
173
	
174
	echo $htmltext;
175
}
176

    
177
exit;
178

    
179
function portal_mac_fixed($clientmac) {
180
	global $g ;
181
	
182
	/* open captive portal mac db */
183
	if (file_exists("{$g['vardb_path']}/captiveportal_mac.db")) {
184
		$fd = @fopen("{$g['vardb_path']}/captiveportal_mac.db","r") ;
185
		if (!$fd) {
186
			return FALSE;
187
		}
188
		while (!feof($fd)) {
189
			$mac = trim(fgets($fd)) ;
190
			if(strcasecmp($clientmac, $mac) == 0) {
191
				fclose($fd) ;
192
				return TRUE ;
193
			}
194
		}
195
		fclose($fd) ;
196
	}
197
	return FALSE ;
198
}	
199

    
200
function portal_allow($clientip,$clientmac,$clientuser,$bw_up,$bw_down) {
201

    
202
	global $orig_host, $orig_request, $g, $config;
203

    
204
	/* user has accepted AUP - let him in */
205
	portal_lock();
206
	
207
	/* get next ipfw rule number */
208
	if (file_exists("{$g['vardb_path']}/captiveportal.nextrule"))
209
		$ruleno = trim(file_get_contents("{$g['vardb_path']}/captiveportal.nextrule"));
210
	if (!$ruleno)
211
		$ruleno = 10000;	/* first rule number */
212

    
213
	$saved_ruleno = $ruleno;
214
	
215
	/* generate unique session ID */
216
	$tod = gettimeofday();
217
	$sessionid = substr(md5(mt_rand() . $tod['sec'] . $tod['usec'] . $clientip . $clientmac), 0, 16);
218
	
219
	/* add ipfw rules for layer 3 */
220
	exec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from $clientip to any in");
221
	exec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from any to $clientip out");
222

    
223
	/* KEYCOM: add ipfw rules for dummynet based on bw_up and bw_down */
224
	//we're just copying them by adding on some and hoping no collision will occur
225
	//2000 users would be expecting a bit much from a WAP ;)
226

    
227
	//we're using fixed rule numbers which are 'a step above' the m0n0 ones
228
	//this makes sure we always know where our rules are, and taht they are deleted when m0n0's are
229
	//they're set so they shouldn't hit anything important, and also so they are in roughly the right position in the fw.
230

    
231
	//of course, we only need to do this if it's enabled in the config
232
	if (isset($config['captiveportal']['peruserbw'])) {
233
		$up_rule_number = $ruleno + 40500;
234
		$down_rule_number = $ruleno + 45500;
235
		$lanif = $config['interfaces']['lan']['if'];
236
		exec("/sbin/ipfw add $up_rule_number set 4 pipe $up_rule_number ip from $clientip to any via $lanif");
237
		exec("/sbin/ipfw add $down_rule_number set 4 pipe $down_rule_number ip from any to $clientip via $lanif");
238
		exec("/sbin/ipfw pipe $up_rule_number config bw " . trim($bw_up) . "Kbit/s queue 10");
239
		exec("/sbin/ipfw pipe $down_rule_number config bw " . trim($bw_down) . "Kbit/s queue 10");
240
	}
241
	/* done */
242
	
243
	/* add ipfw rules for layer 2 */
244
	if (!isset($config['captiveportal']['nomacfilter'])) {
245
		$l2ruleno = $ruleno + 10000;
246
		exec("/sbin/ipfw add $l2ruleno set 3 deny all from $clientip to any not MAC any $clientmac layer2 in");
247
		exec("/sbin/ipfw add $l2ruleno set 3 deny all from any to $clientip not MAC $clientmac any layer2 out");
248
	}
249
	
250
	/* read in client database */
251
	$cpdb = array();
252

    
253
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r");
254
	if ($fd) {
255
		while (!feof($fd)) {
256
			$line = trim(fgets($fd)) ;
257
			if($line) {
258
				$cpdb[] = explode(",",$line);
259
			}	
260
		}
261
		fclose($fd);
262
	}
263
	
264
	$radiusservers = captiveportal_get_radius_servers();
265

    
266
	/* find an existing entry and delete it */
267
	for ($i = 0; $i < count($cpdb); $i++) {
268
		if(!strcasecmp($cpdb[$i][2],$clientip)) {
269
			if(isset($config['captiveportal']['radacct_enable']) && isset($radiusservers[0])) {
270
				RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
271
									   $cpdb[$i][4], // username
272
									   $cpdb[$i][5], // sessionid
273
									   $cpdb[$i][0], // start time
274
									   $radiusservers[0]['ipaddr'],
275
									   $radiusservers[0]['acctport'],
276
									   $radiusservers[0]['key'],
277
									   $clientip);
278
			}
279
			//KEYCOM: we need to delete +40500 and +45500 as well...
280
			//these are the rule numbers we use to control traffic shaping for each logged in user via captive portal
281
			mwexec("/sbin/ipfw delete " . $cpdb[$i][1]);
282
			//we only need to remove our rules if peruserbw is turned on.
283
			if(isset($config['captiveportal']['peruserbw'])) {
284
				mwexec("/sbin/ipfw delete " . ($cpdb[$i][1]+40500));
285
				mwexec("/sbin/ipfw delete " . ($cpdb[$i][1]+45500));
286
			}
287
			unset($cpdb[$i]);
288
			break;
289
		}
290
	}	
291

    
292
	/* rewrite information to database */
293
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w");
294
	if ($fd) {
295
		foreach ($cpdb as $cpent) {
296
			fwrite($fd, join(",", $cpent) . "\n");
297
		}
298
		/* write in this new entry */
299
		fwrite($fd, time().",{$ruleno},{$clientip},{$clientmac},{$clientuser},{$sessionid}\n") ;
300
		fclose($fd);
301
	}
302
	
303
	/* write next rule number */
304
	$fd = @fopen("{$g['vardb_path']}/captiveportal.nextrule", "w");
305
	if ($fd) {
306
		$ruleno++;
307
		if ($ruleno > 19899)
308
			$ruleno = 10000;	/* wrap around */
309
		fwrite($fd, $ruleno);
310
		fclose($fd);
311
	}
312
	
313
	portal_unlock();
314
	
315
	/* redirect user to desired destination */
316
	if ($config['captiveportal']['redirurl'])
317
		$redirurl = $config['captiveportal']['redirurl'];
318
	else if ($_POST['redirurl'])
319
		$redirurl = $_POST['redirurl'];
320
	else
321
		$redirurl = "http://{$orig_host}{$orig_request}";
322
	
323
	if(isset($config['captiveportal']['logoutwin_enable'])) {
324
		
325
		if (isset($config['captiveportal']['httpslogin']))
326
			$logouturl = "https://{$config['captiveportal']['httpsname']}:8001/";
327
		else
328
			$logouturl = "http://{$config['interfaces'][$config['captiveportal']['interface']]['ipaddr']}:8000/";
329
		
330
		echo <<<EOD
331
<HTML>
332
<HEAD><TITLE>Redirecting...</TITLE></HEAD>
333
<BODY>
334
<SPAN STYLE="font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">
335
<B>Redirecting to <A HREF="{$redirurl}">{$redirurl}</A>...</B>
336
</SPAN>
337
<SCRIPT LANGUAGE="JavaScript">
338
<!--
339
LogoutWin = window.open('', 'Logout', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=256,height=64');
340
if (LogoutWin) {
341
	LogoutWin.document.write('<HTML>');
342
	LogoutWin.document.write('<HEAD><TITLE>Logout</TITLE></HEAD>') ;
343
	LogoutWin.document.write('<BODY BGCOLOR="#435370">');
344
	LogoutWin.document.write('<DIV ALIGN="center" STYLE="color: #ffffff; font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">') ;
345
	LogoutWin.document.write('<B>Click the button below to disconnect</B><P>');
346
	LogoutWin.document.write('<FORM METHOD="POST" ACTION="{$logouturl}">');
347
	LogoutWin.document.write('<INPUT NAME="logout_id" TYPE="hidden" VALUE="{$sessionid}">');
348
	LogoutWin.document.write('<INPUT NAME="logout" TYPE="submit" VALUE="Logout">');
349
	LogoutWin.document.write('</FORM>');
350
	LogoutWin.document.write('</DIV></BODY>');
351
	LogoutWin.document.write('</HTML>');
352
	LogoutWin.document.close();
353
}
354

    
355
document.location.href="{$redirurl}";
356
-->
357
</SCRIPT>
358
</BODY>
359
</HTML>
360

    
361
EOD;
362
	} else {
363
		header("Location: " . $redirurl); 
364
	}
365
	
366
	return $sessionid;
367
}
368

    
369
/* read RADIUS servers into array */
370
function captiveportal_get_radius_servers() {
371
	
372
	global $g;
373
	
374
	if (file_exists("{$g['vardb_path']}/captiveportal_radius.db")) {
375
	   	$fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db","r");
376
		if ($fd) {
377
			$radiusservers = array();
378
			while (!feof($fd)) {
379
				$line = trim(fgets($fd));
380
				if ($line) {
381
					$radsrv = array();
382
					list($radsrv['ipaddr'],$radsrv['port'],$radsrv['acctport'],$radsrv['key']) = explode(",",$line);
383
					$radiusservers[] = $radsrv;
384
				}
385
			}
386
			fclose($fd);
387
			
388
			return $radiusservers;
389
		}
390
	}
391
	
392
	return false;
393
}
394

    
395
/* lock captive portal information, decide that the lock file is stale after
396
   10 seconds */
397
function portal_lock() {
398
	
399
	global $lockfile;
400
	
401
	$n = 0;
402
	while ($n < 10) {
403
		/* open the lock file in append mode to avoid race condition */
404
		if ($fd = @fopen($lockfile, "x")) {
405
			/* succeeded */
406
			fclose($fd);
407
			return;
408
		} else {
409
			/* file locked, wait and try again */
410
			sleep(1);
411
			$n++;
412
		}
413
	}
414
}
415

    
416
/* unlock captive portal information file */
417
function portal_unlock() {
418
	
419
	global $lockfile;
420
	
421
	if (file_exists($lockfile))
422
		unlink($lockfile);
423
}
424

    
425
/* remove a single client by session ID
426
   by Dinesh Nair
427
 */
428
function disconnect_client($sessionid) {
429
	
430
	global $g, $config;
431
	
432
	portal_lock();
433
	
434
	/* read database */
435
	$cpdb = array() ;
436
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r");
437
	if ($fd) {
438
		while (!feof($fd)) {
439
			$line = trim(fgets($fd)) ;
440
			if($line) {
441
				$cpdb[] = explode(",",$line);
442
			}	
443
		}
444
		fclose($fd);
445
	}
446
	
447
	$radiusservers = captiveportal_get_radius_servers();
448
	
449
	/* find entry */	
450
	for ($i = 0; $i < count($cpdb); $i++) {
451
		if ($cpdb[$i][5] == $sessionid) {
452
			/* this client needs to be deleted - remove ipfw rules */
453
			if(isset($config['captiveportal']['radacct_enable']) && isset($radiusservers[0])) {
454
				RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
455
									   $cpdb[$i][4], // username
456
									   $cpdb[$i][5], // sessionid
457
									   $cpdb[$i][0], // start time
458
									   $radiusservers[0]['ipaddr'],
459
									   $radiusservers[0]['acctport'],
460
									   $radiusservers[0]['key'],
461
									   $clientip);
462
			}
463
			//again we need to remve +40500 and +45500 as well, if they exist
464
			mwexec("/sbin/ipfw delete " . $cpdb[$i][1] . " " . ($cpdb[$i][1]+10000));
465
			if(isset($config['captiveportal']['peruserbw'])) {
466
				mwexec("/sbin/ipfw delete " . ($cpdb[$i][1]+40500));
467
				mwexec("/sbin/ipfw delete " . ($cpdb[$i][1]+45500));
468
			}
469
			unset($cpdb[$i]);
470
			break;
471
		}
472
	}
473
	
474
	/* rewrite information to database */
475
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w");
476
	if ($fd) {
477
		foreach ($cpdb as $cpent) {
478
			fwrite($fd, join(",", $cpent) . "\n");
479
		}
480
		fclose($fd);
481
	}
482
	
483
	portal_unlock();
484
}
485
?>
(1-1/3)