Project

General

Profile

Download (13.8 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php 
2
/*
3
	index.php
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

    
31
require("globals.inc");
32
require("util.inc");
33
require("config.inc");
34
require("radius_authentication.inc");
35
require("radius_accounting.inc");
36

    
37
header("Expires: 0");
38
header("Cache-Control: no-store, no-cache, must-revalidate");
39
header("Cache-Control: post-check=0, pre-check=0", false);
40
header("Pragma: no-cache");
41

    
42
$orig_host = $_ENV['HTTP_HOST'];
43
$orig_request = $_GET['redirurl'];
44
$lockfile = "{$g['varrun_path']}/captiveportal.lock";
45
$clientip = $_SERVER['REMOTE_ADDR'];
46

    
47
if (!$clientip) {
48
	/* not good - bail out */
49
	exit;
50
}
51

    
52
/* find MAC address for client */
53
$clientmac = arp_get_mac_by_ip($clientip);
54
if (!$clientmac && !isset($config['captiveportal']['nomacfilter'])) {
55
	/* unable to find MAC address - shouldn't happen! - bail out */
56
	exit;
57
}
58

    
59
if ($clientmac && portal_mac_fixed($clientmac)) {
60
	/* punch hole in ipfw for pass thru mac addresses */
61
	portal_allow($clientip, $clientmac, "unauthenticated");
62

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

    
65
	/* authenticate against radius server */
66
	$radiusservers = captiveportal_get_radius_servers();
67
	
68
	if ($_POST['auth_user'] && $_POST['auth_pass']) {
69
		$auth_val = RADIUS_AUTHENTICATION($_POST['auth_user'],
70
										  $_POST['auth_pass'],
71
							  			  $radiusservers[0]['ipaddr'],
72
							  			  $radiusservers[0]['port'],
73
							  			  $radiusservers[0]['key']);
74
		if ($auth_val == 2) {
75
			captiveportal_logportalauth($_POST['auth_user'],$clientmac,$clientip,"LOGIN");
76
			$sessionid = portal_allow($clientip, $clientmac, $_POST['auth_user'], $_POST['auth_pass']);
77
			if (isset($config['captiveportal']['radacct_enable']) && isset($radiusservers[0])) {
78
				$auth_val = RADIUS_ACCOUNTING_START($_POST['auth_user'],
79
													$sessionid,
80
													$radiusservers[0]['ipaddr'],
81
													$radiusservers[0]['acctport'],
82
													$radiusservers[0]['key'],
83
													$clientip);
84
			}
85
		} else {
86
			captiveportal_logportalauth($_POST['auth_user'],$clientmac,$clientip,"FAILURE");
87
			readfile("{$g['varetc_path']}/captiveportal-error.html");
88
		}
89
	} else {
90
		readfile("{$g['varetc_path']}/captiveportal-error.html");
91
	}
92
	
93
} else if ($_POST['accept'] && $config['captiveportal']['auth_method'] == "local") {
94

    
95
	//check against local usermanager
96
	$userdb = &$config['captiveportal']['user'];
97

    
98
	$loginok = false;
99

    
100
	//erase expired accounts
101
	if (is_array($userdb)) {
102
		$moddb = false;
103
		for ($i = 0; $i < count($userdb); $i++) {
104
			if ($userdb[$i]['expirationdate'] && (strtotime("-1 day") > strtotime($userdb[$i]['expirationdate']))) {
105
				unset($userdb[$i]);
106
				$moddb = true;
107
			}
108
		}
109
		if ($moddb)
110
			write_config();
111
			
112
		$userdb = &$config['captiveportal']['user'];
113
		
114
		for ($i = 0; $i < count($userdb); $i++) {
115
			if (($userdb[$i]['name'] == $_POST['auth_user']) && ($userdb[$i]['password'] == md5($_POST['auth_pass']))) {
116
				$loginok = true;
117
				break;
118
			}
119
		}
120
	}
121

    
122
	if ($loginok){
123
		captiveportal_logportalauth($_POST['auth_user'],$clientmac,$clientip,"LOGIN");
124
		portal_allow($clientip, $clientmac,$_POST['auth_user'],0,0);
125
	} else {
126
		captiveportal_logportalauth($_POST['auth_user'],$clientmac,$clientip,"FAILURE");
127
		readfile("{$g['varetc_path']}/captiveportal-error.html");
128
	}
129
} else if ($_POST['accept'] && $clientip) {
130
	portal_allow($clientip, $clientmac, "unauthenticated");
131
} else if ($_POST['logout_id']) {
132
	disconnect_client($_POST['logout_id']);
133
	echo <<<EOD
134
<HTML>
135
<HEAD><TITLE>Disconnecting...</TITLE></HEAD>
136
<BODY BGCOLOR="#435370">
137
<SPAN STYLE="color: #ffffff; font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">
138
<B>You've been disconnected.</B>
139
</SPAN>
140
<SCRIPT LANGUAGE="JavaScript">
141
<!--
142
setTimeout('window.close();',5000) ;
143
-->
144
</SCRIPT>
145
</BODY>
146
</HTML>
147

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

    
171

    
172
exit;
173

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

    
195
function portal_allow($clientip,$clientmac,$clientuser,$password = "") {
196

    
197
	global $orig_host, $orig_request, $g, $config;
198

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

    
208
	$saved_ruleno = $ruleno;
209
	
210
	/* generate unique session ID */
211
	$tod = gettimeofday();
212
	$sessionid = substr(md5(mt_rand() . $tod['sec'] . $tod['usec'] . $clientip . $clientmac), 0, 16);
213
	
214
	/* add ipfw rules for layer 3 */
215
	exec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from $clientip to any in");
216
	exec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from any to $clientip out");
217
	
218
	/* add ipfw rules for layer 2 */
219
	if (!isset($config['captiveportal']['nomacfilter'])) {
220
		$l2ruleno = $ruleno + 10000;
221
		exec("/sbin/ipfw add $l2ruleno set 3 deny all from $clientip to any not MAC any $clientmac layer2 in");
222
		exec("/sbin/ipfw add $l2ruleno set 3 deny all from any to $clientip not MAC $clientmac any layer2 out");
223
	}
224
	
225
	/* read in client database */
226
	$cpdb = array();
227

    
228
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r");
229
	if ($fd) {
230
		while (!feof($fd)) {
231
			$line = trim(fgets($fd)) ;
232
			if($line) {
233
				$cpdb[] = explode(",",$line);
234
			}	
235
		}
236
		fclose($fd);
237
	}
238
	
239
	$radiusservers = captiveportal_get_radius_servers();
240

    
241
	/* find an existing entry and delete it */
242
	for ($i = 0; $i < count($cpdb); $i++) {
243
		if(!strcasecmp($cpdb[$i][2],$clientip)) {
244
			if(isset($config['captiveportal']['radacct_enable']) && isset($radiusservers[0])) {
245
				RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
246
									   $cpdb[$i][4], // username
247
									   $cpdb[$i][5], // sessionid
248
									   $cpdb[$i][0], // start time
249
									   $radiusservers[0]['ipaddr'],
250
									   $radiusservers[0]['acctport'],
251
									   $radiusservers[0]['key'],
252
									   $clientip);
253
			}
254
			mwexec("/sbin/ipfw delete " . $cpdb[$i][1] . " " . ($cpdb[$i][1]+10000));
255
			unset($cpdb[$i]);
256
			break;
257
		}
258
	}	
259

    
260
	/* rewrite information to database */
261
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w");
262
	if ($fd) {
263
		foreach ($cpdb as $cpent) {
264
			fwrite($fd, join(",", $cpent) . "\n");
265
		}
266
		/* write in this new entry */
267
		/* encode password in Base64 just in case it contains commas */
268
		$bpassword = base64_encode($password);
269
		fwrite($fd, time().",{$ruleno},{$clientip},{$clientmac},{$clientuser},{$sessionid},{$bpassword}\n") ;
270
		fclose($fd);
271
	}
272
	
273
	/* write next rule number */
274
	$fd = @fopen("{$g['vardb_path']}/captiveportal.nextrule", "w");
275
	if ($fd) {
276
		$ruleno++;
277
		if ($ruleno > 19899)
278
			$ruleno = 10000;	/* wrap around */
279
		fwrite($fd, $ruleno);
280
		fclose($fd);
281
	}
282
	
283
	portal_unlock();
284
	
285
	/* redirect user to desired destination */
286
	if ($config['captiveportal']['redirurl'])
287
		$redirurl = $config['captiveportal']['redirurl'];
288
	else if ($_POST['redirurl'])
289
		$redirurl = $_POST['redirurl'];
290
	else
291
		$redirurl = "http://{$orig_host}{$orig_request}";
292
	
293
	if(isset($config['captiveportal']['logoutwin_enable'])) {
294
		
295
		if (isset($config['captiveportal']['httpslogin']))
296
			$logouturl = "https://{$config['captiveportal']['httpsname']}:8001/";
297
		else
298
			$logouturl = "http://{$config['interfaces'][$config['captiveportal']['interface']]['ipaddr']}:8000/";
299
		
300
		echo <<<EOD
301
<HTML>
302
<HEAD><TITLE>Redirecting...</TITLE></HEAD>
303
<BODY>
304
<SPAN STYLE="font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">
305
<B>Redirecting to <A HREF="{$redirurl}">{$redirurl}</A>...</B>
306
</SPAN>
307
<SCRIPT LANGUAGE="JavaScript">
308
<!--
309
LogoutWin = window.open('', 'Logout', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=256,height=64');
310
if (LogoutWin) {
311
	LogoutWin.document.write('<HTML>');
312
	LogoutWin.document.write('<HEAD><TITLE>Logout</TITLE></HEAD>') ;
313
	LogoutWin.document.write('<BODY BGCOLOR="#435370">');
314
	LogoutWin.document.write('<DIV ALIGN="center" STYLE="color: #ffffff; font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">') ;
315
	LogoutWin.document.write('<B>Click the button below to disconnect</B><P>');
316
	LogoutWin.document.write('<FORM METHOD="POST" ACTION="{$logouturl}">');
317
	LogoutWin.document.write('<INPUT NAME="logout_id" TYPE="hidden" VALUE="{$sessionid}">');
318
	LogoutWin.document.write('<INPUT NAME="logout" TYPE="submit" VALUE="Logout">');
319
	LogoutWin.document.write('</FORM>');
320
	LogoutWin.document.write('</DIV></BODY>');
321
	LogoutWin.document.write('</HTML>');
322
	LogoutWin.document.close();
323
}
324

    
325
document.location.href="{$redirurl}";
326
-->
327
</SCRIPT>
328
</BODY>
329
</HTML>
330

    
331
EOD;
332
	} else {
333
		header("Location: " . $redirurl); 
334
	}
335
	
336
	return $sessionid;
337
}
338

    
339
/* read RADIUS servers into array */
340
function captiveportal_get_radius_servers() {
341
	
342
	global $g;
343
	
344
	if (file_exists("{$g['vardb_path']}/captiveportal_radius.db")) {
345
	   	$fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db","r");
346
		if ($fd) {
347
			$radiusservers = array();
348
			while (!feof($fd)) {
349
				$line = trim(fgets($fd));
350
				if ($line) {
351
					$radsrv = array();
352
					list($radsrv['ipaddr'],$radsrv['port'],$radsrv['acctport'],$radsrv['key']) = explode(",",$line);
353
					$radiusservers[] = $radsrv;
354
				}
355
			}
356
			fclose($fd);
357
			
358
			return $radiusservers;
359
		}
360
	}
361
	
362
	return false;
363
}
364

    
365
/* lock captive portal information, decide that the lock file is stale after
366
   10 seconds */
367
function portal_lock() {
368
	
369
	global $lockfile;
370
	
371
	$n = 0;
372
	while ($n < 10) {
373
		/* open the lock file in append mode to avoid race condition */
374
		if ($fd = @fopen($lockfile, "x")) {
375
			/* succeeded */
376
			fclose($fd);
377
			return;
378
		} else {
379
			/* file locked, wait and try again */
380
			sleep(1);
381
			$n++;
382
		}
383
	}
384
}
385

    
386
/* unlock captive portal information file */
387
function portal_unlock() {
388
	
389
	global $lockfile;
390
	
391
	if (file_exists($lockfile))
392
		unlink($lockfile);
393
}
394

    
395
/* remove a single client by session ID
396
   by Dinesh Nair
397
 */
398
function disconnect_client($sessionid) {
399
	
400
	global $g, $config;
401
	
402
	portal_lock();
403
	
404
	/* read database */
405
	$cpdb = array() ;
406
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r");
407
	if ($fd) {
408
		while (!feof($fd)) {
409
			$line = trim(fgets($fd)) ;
410
			if($line) {
411
				$cpdb[] = explode(",",$line);
412
			}	
413
		}
414
		fclose($fd);
415
	}
416
	
417
	$radiusservers = captiveportal_get_radius_servers();
418
	
419
	/* find entry */	
420
	for ($i = 0; $i < count($cpdb); $i++) {
421
		if ($cpdb[$i][5] == $sessionid) {
422
			/* this client needs to be deleted - remove ipfw rules */
423
			if(isset($config['captiveportal']['radacct_enable']) && isset($radiusservers[0])) {
424
				RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
425
									   $cpdb[$i][4], // username
426
									   $cpdb[$i][5], // sessionid
427
									   $cpdb[$i][0], // start time
428
									   $radiusservers[0]['ipaddr'],
429
									   $radiusservers[0]['acctport'],
430
									   $radiusservers[0]['key'],
431
									   $cpdb[$i][2]);
432
			}
433
			mwexec("/sbin/ipfw delete " . $cpdb[$i][1] . " " . ($cpdb[$i][1]+10000));
434
			captiveportal_logportalauth($cpdb[$i][4],$cpdb[$i][3],$cpdb[$i][2],"LOGOUT");
435
			unset($cpdb[$i]);
436
			break;
437
		}
438
	}
439
	
440
	/* rewrite information to database */
441
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w");
442
	if ($fd) {
443
		foreach ($cpdb as $cpent) {
444
			fwrite($fd, join(",", $cpent) . "\n");
445
		}
446
		fclose($fd);
447
	}
448
	
449
	portal_unlock();
450
}
451

    
452
/* log successful captive portal authentication to syslog */
453
/* part of this code from php.net */
454
function captiveportal_logportalauth($user,$mac,$ip,$status) {
455
	define_syslog_variables();
456
	openlog("logportalauth", LOG_PID, LOG_LOCAL4);
457
	// Log it
458
	syslog(LOG_INFO, "$status: $user, $mac, $ip");
459
	closelog();
460
}
461

    
462
?>
(1-1/3)