Project

General

Profile

Download (11.9 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

    
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
			$sessionid = portal_allow($clientip, $clientmac, $_POST['auth_user']);
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
			}
84
		} else {
85
			readfile("{$g['varetc_path']}/captiveportal-error.html");
86
		}
87
	} else {
88
		readfile("{$g['varetc_path']}/captiveportal-error.html");
89
	}
90

    
91
} else if ($_POST['accept'] && $clientip) {
92
	portal_allow($clientip, $clientmac, "unauthenticated");
93
} else if ($_POST['logout_id']) {
94
	disconnect_client($_POST['logout_id']);
95
	echo <<<EOD
96
<HTML>
97
<HEAD><TITLE>Disconnecting...</TITLE></HEAD>
98
<BODY BGCOLOR="#435370">
99
<SPAN STYLE="color: #ffffff; font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">
100
<B>You've been disconnected.</B>
101
</SPAN>
102
<SCRIPT LANGUAGE="JavaScript">
103
<!--
104
setTimeout('window.close();',5000) ;
105
-->
106
</SCRIPT>
107
</BODY>
108
</HTML>
109

    
110
EOD;
111
} else if (($_ENV['SERVER_PORT'] != 8001) && isset($config['captiveportal']['httpslogin'])) {
112
	/* redirect to HTTPS login page */
113
	header("Location: https://{$config['captiveportal']['httpsname']}:8001/?redirurl=" . urlencode("http://{$orig_host}{$orig_request}"));
114
} else {
115
	/* display captive portal page */
116
	$htmltext = file_get_contents("{$g['varetc_path']}/captiveportal.html");
117
	
118
	/* substitute variables */
119
	if (isset($config['captiveportal']['httpslogin']))
120
		$htmltext = str_replace("\$PORTAL_ACTION\$", "https://{$config['captiveportal']['httpsname']}:8001/", $htmltext);
121
	else
122
		$htmltext = str_replace("\$PORTAL_ACTION\$", "", $htmltext);
123
	
124
	if (preg_match("/redirurl=(.*)/", $orig_request, $matches))
125
		$redirurl = urldecode($matches[1]);
126
	else
127
		$redirurl = "http://{$orig_host}{$orig_request}";
128
	$htmltext = str_replace("\$PORTAL_REDIRURL\$", htmlspecialchars($redirurl), $htmltext);
129
	
130
	echo $htmltext;
131
}
132

    
133
exit;
134

    
135
function portal_mac_fixed($clientmac) {
136
	global $g ;
137
	
138
	/* open captive portal mac db */
139
	if (file_exists("{$g['vardb_path']}/captiveportal_mac.db")) {
140
		$fd = @fopen("{$g['vardb_path']}/captiveportal_mac.db","r") ;
141
		if (!$fd) {
142
			return FALSE;
143
		}
144
		while (!feof($fd)) {
145
			$mac = trim(fgets($fd)) ;
146
			if(strcasecmp($clientmac, $mac) == 0) {
147
				fclose($fd) ;
148
				return TRUE ;
149
			}
150
		}
151
		fclose($fd) ;
152
	}
153
	return FALSE ;
154
}	
155

    
156
function portal_allow($clientip,$clientmac,$clientuser) {
157

    
158
	global $orig_host, $orig_request, $g, $config;
159

    
160
	/* user has accepted AUP - let him in */
161
	portal_lock();
162
	
163
	/* get next ipfw rule number */
164
	if (file_exists("{$g['vardb_path']}/captiveportal.nextrule"))
165
		$ruleno = trim(file_get_contents("{$g['vardb_path']}/captiveportal.nextrule"));
166
	if (!$ruleno)
167
		$ruleno = 10000;	/* first rule number */
168

    
169
	$saved_ruleno = $ruleno;
170
	
171
	/* generate unique session ID */
172
	$tod = gettimeofday();
173
	$sessionid = substr(md5(mt_rand() . $tod['sec'] . $tod['usec'] . $clientip . $clientmac), 0, 16);
174
	
175
	/* add ipfw rules for layer 3 */
176
	exec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from $clientip to any in");
177
	exec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from any to $clientip out");
178
	
179
	/* add ipfw rules for layer 2 */
180
	if (!isset($config['captiveportal']['nomacfilter'])) {
181
		$l2ruleno = $ruleno + 10000;
182
		exec("/sbin/ipfw add $l2ruleno set 3 deny all from $clientip to any not MAC any $clientmac layer2 in");
183
		exec("/sbin/ipfw add $l2ruleno set 3 deny all from any to $clientip not MAC $clientmac any layer2 out");
184
	}
185
	
186
	/* read in client database */
187
	$cpdb = array();
188

    
189
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r");
190
	if ($fd) {
191
		while (!feof($fd)) {
192
			$line = trim(fgets($fd)) ;
193
			if($line) {
194
				$cpdb[] = explode(",",$line);
195
			}	
196
		}
197
		fclose($fd);
198
	}
199
	
200
	$radiusservers = captiveportal_get_radius_servers();
201

    
202
	/* find an existing entry and delete it */
203
	for ($i = 0; $i < count($cpdb); $i++) {
204
		if(!strcasecmp($cpdb[$i][2],$clientip)) {
205
			if(isset($config['captiveportal']['radacct_enable']) && isset($radiusservers[0])) {
206
				RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
207
									   $cpdb[$i][4], // username
208
									   $cpdb[$i][5], // sessionid
209
									   $cpdb[$i][0], // start time
210
									   $radiusservers[0]['ipaddr'],
211
									   $radiusservers[0]['acctport'],
212
									   $radiusservers[0]['key']);
213
			}
214
			mwexec("/sbin/ipfw delete " . $cpdb[$i][1] . " " . ($cpdb[$i][1]+10000));
215
			unset($cpdb[$i]);
216
			break;
217
		}
218
	}	
219

    
220
	/* rewrite information to database */
221
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w");
222
	if ($fd) {
223
		foreach ($cpdb as $cpent) {
224
			fwrite($fd, join(",", $cpent) . "\n");
225
		}
226
		/* write in this new entry */
227
		fwrite($fd, time().",{$ruleno},{$clientip},{$clientmac},{$clientuser},{$sessionid}\n") ;
228
		fclose($fd);
229
	}
230
	
231
	/* write next rule number */
232
	$fd = @fopen("{$g['vardb_path']}/captiveportal.nextrule", "w");
233
	if ($fd) {
234
		$ruleno++;
235
		if ($ruleno > 19899)
236
			$ruleno = 10000;	/* wrap around */
237
		fwrite($fd, $ruleno);
238
		fclose($fd);
239
	}
240
	
241
	portal_unlock();
242
	
243
	/* redirect user to desired destination */
244
	if ($config['captiveportal']['redirurl'])
245
		$redirurl = $config['captiveportal']['redirurl'];
246
	else if ($_POST['redirurl'])
247
		$redirurl = $_POST['redirurl'];
248
	else
249
		$redirurl = "http://{$orig_host}{$orig_request}";
250
	
251
	if(isset($config['captiveportal']['logoutwin_enable'])) {
252
		
253
		if (isset($config['captiveportal']['httpslogin']))
254
			$logouturl = "https://{$config['captiveportal']['httpsname']}:8001/";
255
		else
256
			$logouturl = "http://{$config['interfaces'][$config['captiveportal']['interface']]['ipaddr']}:8000/";
257
		
258
		echo <<<EOD
259
<HTML>
260
<HEAD><TITLE>Redirecting...</TITLE></HEAD>
261
<BODY>
262
<SPAN STYLE="font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">
263
<B>Redirecting to <A HREF="{$redirurl}">{$redirurl}</A>...</B>
264
</SPAN>
265
<SCRIPT LANGUAGE="JavaScript">
266
<!--
267
LogoutWin = window.open('', 'Logout', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=256,height=64');
268
if (LogoutWin) {
269
	LogoutWin.document.write('<HTML>');
270
	LogoutWin.document.write('<HEAD><TITLE>Logout</TITLE></HEAD>') ;
271
	LogoutWin.document.write('<BODY BGCOLOR="#435370">');
272
	LogoutWin.document.write('<DIV ALIGN="center" STYLE="color: #ffffff; font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">') ;
273
	LogoutWin.document.write('<B>Click the button below to disconnect</B><P>');
274
	LogoutWin.document.write('<FORM METHOD="POST" ACTION="{$logouturl}">');
275
	LogoutWin.document.write('<INPUT NAME="logout_id" TYPE="hidden" VALUE="{$sessionid}">');
276
	LogoutWin.document.write('<INPUT NAME="logout" TYPE="submit" VALUE="Logout">');
277
	LogoutWin.document.write('</FORM>');
278
	LogoutWin.document.write('</DIV></BODY>');
279
	LogoutWin.document.write('</HTML>');
280
	LogoutWin.document.close();
281
}
282

    
283
document.location.href="{$redirurl}";
284
-->
285
</SCRIPT>
286
</BODY>
287
</HTML>
288

    
289
EOD;
290
	} else {
291
		header("Location: " . $redirurl); 
292
	}
293
	
294
	return $sessionid;
295
}
296

    
297
/* read RADIUS servers into array */
298
function captiveportal_get_radius_servers() {
299
	
300
	global $g;
301
	
302
	if (file_exists("{$g['vardb_path']}/captiveportal_radius.db")) {
303
	   	$fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db","r");
304
		if ($fd) {
305
			$radiusservers = array();
306
			while (!feof($fd)) {
307
				$line = trim(fgets($fd));
308
				if ($line) {
309
					$radsrv = array();
310
					list($radsrv['ipaddr'],$radsrv['port'],$radsrv['acctport'],$radsrv['key']) = explode(",",$line);
311
					$radiusservers[] = $radsrv;
312
				}
313
			}
314
			fclose($fd);
315
			
316
			return $radiusservers;
317
		}
318
	}
319
	
320
	return false;
321
}
322

    
323
/* lock captive portal information, decide that the lock file is stale after
324
   10 seconds */
325
function portal_lock() {
326
	
327
	global $lockfile;
328
	
329
	$n = 0;
330
	while ($n < 10) {
331
		/* open the lock file in append mode to avoid race condition */
332
		if ($fd = @fopen($lockfile, "x")) {
333
			/* succeeded */
334
			fclose($fd);
335
			return;
336
		} else {
337
			/* file locked, wait and try again */
338
			sleep(1);
339
			$n++;
340
		}
341
	}
342
}
343

    
344
/* unlock captive portal information file */
345
function portal_unlock() {
346
	
347
	global $lockfile;
348
	
349
	if (file_exists($lockfile))
350
		unlink($lockfile);
351
}
352

    
353
/* remove a single client by session ID
354
   by Dinesh Nair
355
 */
356
function disconnect_client($sessionid) {
357
	
358
	global $g, $config;
359
	
360
	portal_lock();
361
	
362
	/* read database */
363
	$cpdb = array() ;
364
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r");
365
	if ($fd) {
366
		while (!feof($fd)) {
367
			$line = trim(fgets($fd)) ;
368
			if($line) {
369
				$cpdb[] = explode(",",$line);
370
			}	
371
		}
372
		fclose($fd);
373
	}
374
	
375
	$radiusservers = captiveportal_get_radius_servers();
376
	
377
	/* find entry */	
378
	for ($i = 0; $i < count($cpdb); $i++) {
379
		if ($cpdb[$i][5] == $sessionid) {
380
			/* this client needs to be deleted - remove ipfw rules */
381
			if(isset($config['captiveportal']['radacct_enable']) && isset($radiusservers[0])) {
382
				RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
383
									   $cpdb[$i][4], // username
384
									   $cpdb[$i][5], // sessionid
385
									   $cpdb[$i][0], // start time
386
									   $radiusservers[0]['ipaddr'],
387
									   $radiusservers[0]['acctport'],
388
									   $radiusservers[0]['key']);
389
			}
390
			mwexec("/sbin/ipfw delete " . $cpdb[$i][1] . " " . ($cpdb[$i][1]+10000));
391
			unset($cpdb[$i]);
392
			break;
393
		}
394
	}
395
	
396
	/* rewrite information to database */
397
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w");
398
	if ($fd) {
399
		foreach ($cpdb as $cpent) {
400
			fwrite($fd, join(",", $cpent) . "\n");
401
		}
402
		fclose($fd);
403
	}
404
	
405
	portal_unlock();
406
}
407
?>
(1-1/3)