Project

General

Profile

Download (13.8 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-2005 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

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

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

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

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

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

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

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

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

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

    
99
	$loginok = false;
100

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

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

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