Project

General

Profile

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

    
33
*/
34

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

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

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

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

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

    
63
/*   loop through allowed ip list.  if user is on the list then allow
64
 *   them access w/o authenticating
65
 */
66
if($config['captiveportal']['allowedip'] <> "")
67
	foreach($config['captiveportal']['allowedip'] as $ip) {
68
		if($clientip == $ip['ip']) {
69
			$allowed_ip = true;
70
		}
71
	}
72

    
73
if ($clientmac && portal_mac_fixed($clientmac) or $allowed_ip == true) {
74
	/* punch hole in pf table and allow thru ip for the mac addresses */
75
	portal_allow($clientip, $clientmac, "unauthenticated");
76

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

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

    
107
} else if ($_POST['accept'] && $config['captiveportal']['auth_method']=="local") {
108
	//check against local usermanager
109
	
110
	//erase expired accounts
111
	if(trim($config['users'][$_POST['auth_user']]['expirationdate'])!="" && strtotime("-1 day")>strtotime($config['users'][$_POST['auth_user']]['expirationdate'])){
112
		unset($config['users'][$_POST['auth_user']]);
113
		write_config();
114
	}
115
	if($config['users'][$_POST['auth_user']]['password']==md5($_POST['auth_pass'])){
116
		portal_allow($clientip, $clientmac,$_POST['auth_user']);
117
	} else {
118
		readfile("{$g['varetc_path']}/captiveportal-error.html");
119
	}
120
} else if ($_POST['accept'] && $clientip) {
121
	portal_allow($clientip, $clientmac, "unauthenticated");
122
} else if ($_POST['logout_id']) {
123
	disconnect_client($_POST['logout_id']);
124
	echo <<<EOD
125
<HTML>
126
<HEAD><TITLE>Disconnecting...</TITLE></HEAD>
127
<BODY BGCOLOR="#435370">
128
<SPAN STYLE="color: #ffffff; font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">
129
<B>You've been disconnected.</B>
130
</SPAN>
131
<SCRIPT LANGUAGE="JavaScript">
132
<!--
133
setTimeout('window.close();',5000) ;
134
-->
135
</SCRIPT>
136
</BODY>
137
</HTML>
138

    
139
EOD;
140
} else if (($_ENV['SERVER_PORT'] != 8001) && isset($config['captiveportal']['httpslogin'])) {
141
	/* redirect to HTTPS login page */
142
	header("Location: https://{$config['captiveportal']['httpsname']}:8001/?redirurl=" . urlencode("http://{$orig_host}{$orig_request}"));
143
} else {
144
	/* display captive portal page */
145
	$htmltext = file_get_contents("{$g['varetc_path']}/captiveportal.html");
146
	
147
	/* substitute variables */
148
	if (isset($config['captiveportal']['httpslogin']))
149
		$htmltext = str_replace("\$PORTAL_ACTION\$", "https://{$config['captiveportal']['httpsname']}:8001/", $htmltext);
150
	else
151
		$htmltext = str_replace("\$PORTAL_ACTION\$", "", $htmltext);
152
	
153
	if (preg_match("/redirurl=(.*)/", $orig_request, $matches))
154
		$redirurl = urldecode($matches[1]);
155
	else
156
		$redirurl = "http://{$orig_host}{$orig_request}";
157
	$htmltext = str_replace("\$PORTAL_REDIRURL\$", htmlspecialchars($redirurl), $htmltext);
158
	
159
	echo $htmltext;
160
}
161

    
162
exit;
163

    
164
function portal_mac_fixed($clientmac) {
165
	global $g ;
166
	
167
	/* open captive portal mac db */
168
	if (file_exists("{$g['vardb_path']}/captiveportal_mac.db")) {
169
		$fd = @fopen("{$g['vardb_path']}/captiveportal_mac.db","r") ;
170
		if (!$fd) {
171
			return FALSE;
172
		}
173
		while (!feof($fd)) {
174
			$mac = trim(fgets($fd)) ;
175
			if(strcasecmp($clientmac, $mac) == 0) {
176
				fclose($fd) ;
177
				return TRUE ;
178
			}
179
		}
180
		fclose($fd) ;
181
	}
182
	return FALSE ;
183
}	
184

    
185
function portal_allow($clientip,$clientmac,$clientuser) {
186

    
187
	global $orig_host, $orig_request, $g, $config;
188

    
189
	/* user has accepted AUP - let him in */
190
	portal_lock();
191

    
192
	$saved_ruleno = $clientip;
193
	
194
	/* generate unique session ID */
195
	$tod = gettimeofday();
196
	$sessionid = substr(md5(mt_rand() . $tod['sec'] . $tod['usec'] . $clientip . $clientmac), 0, 16);
197
		
198
	/* read in client database */
199
	$cpdb = array();
200

    
201
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r");
202
	if ($fd) {
203
		while (!feof($fd)) {
204
			$line = trim(fgets($fd)) ;
205
			if($line) {
206
				$cpdb[] = explode(",",$line);
207
			}	
208
		}
209
		fclose($fd);
210
	}
211
	
212
	$radiusservers = captiveportal_get_radius_servers();
213

    
214
	/* find an existing entry and delete it */
215
	for ($i = 0; $i < count($cpdb); $i++) {
216
		if(!strcasecmp($cpdb[$i][2],$clientip)) {
217
			if(isset($config['captiveportal']['radacct_enable']) && isset($radiusservers[0])) {
218
				RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
219
									   $cpdb[$i][4], // username
220
									   $cpdb[$i][5], // sessionid
221
									   $cpdb[$i][0], // start time
222
									   $radiusservers[0]['ipaddr'],
223
									   $radiusservers[0]['acctport'],
224
									   $radiusservers[0]['key'],
225
									   $clientip);
226
			}
227
			mwexec("/sbin/pfctl -t captiveportal -T delete {$clientip}");
228
			unset($cpdb[$i]);
229
			break;
230
		}
231
	}	
232

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

    
298
document.location.href="{$redirurl}";
299
-->
300
</SCRIPT>
301
</BODY>
302
</HTML>
303

    
304
EOD;
305
	} else {
306
		header("Location: " . $redirurl); 
307
	}
308
	
309
	return $sessionid;
310
}
311

    
312
/* read RADIUS servers into array */
313
function captiveportal_get_radius_servers() {
314
	
315
	global $g;
316
	
317
	if (file_exists("{$g['vardb_path']}/captiveportal_radius.db")) {
318
	   	$fd = @fopen("{$g['vardb_path']}/captiveportal_radius.db","r");
319
		if ($fd) {
320
			$radiusservers = array();
321
			while (!feof($fd)) {
322
				$line = trim(fgets($fd));
323
				if ($line) {
324
					$radsrv = array();
325
					list($radsrv['ipaddr'],$radsrv['port'],$radsrv['acctport'],$radsrv['key']) = explode(",",$line);
326
					$radiusservers[] = $radsrv;
327
				}
328
			}
329
			fclose($fd);
330
			
331
			return $radiusservers;
332
		}
333
	}
334
	
335
	return false;
336
}
337

    
338
/* lock captive portal information, decide that the lock file is stale after
339
   10 seconds */
340
function portal_lock() {
341
	
342
	global $lockfile;
343
	
344
	$n = 0;
345
	while ($n < 10) {
346
		/* open the lock file in append mode to avoid race condition */
347
		if ($fd = @fopen($lockfile, "x")) {
348
			/* succeeded */
349
			fclose($fd);
350
			return;
351
		} else {
352
			/* file locked, wait and try again */
353
			sleep(1);
354
			$n++;
355
		}
356
	}
357
}
358

    
359
/* unlock captive portal information file */
360
function portal_unlock() {
361
	
362
	global $lockfile;
363
	
364
	if (file_exists($lockfile))
365
		unlink($lockfile);
366
}
367

    
368
/* remove a single client by session ID
369
   by Dinesh Nair
370
 */
371
function disconnect_client($sessionid) {
372
	
373
	global $g, $config;
374
	
375
	portal_lock();
376
	
377
	/* read database */
378
	$cpdb = array() ;
379
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "r");
380
	if ($fd) {
381
		while (!feof($fd)) {
382
			$line = trim(fgets($fd)) ;
383
			if($line) {
384
				$cpdb[] = explode(",",$line);
385
			}	
386
		}
387
		fclose($fd);
388
	}
389
	
390
	$radiusservers = captiveportal_get_radius_servers();
391
	
392
	/* find entry */	
393
	for ($i = 0; $i < count($cpdb); $i++) {
394
		if ($cpdb[$i][5] == $sessionid) {
395
			/* this client needs to be deleted - remove pf table item */
396
			if(isset($config['captiveportal']['radacct_enable']) && isset($radiusservers[0])) {
397
				RADIUS_ACCOUNTING_STOP($cpdb[$i][1], // ruleno
398
									   $cpdb[$i][4], // username
399
									   $cpdb[$i][5], // sessionid
400
									   $cpdb[$i][0], // start time
401
									   $radiusservers[0]['ipaddr'],
402
									   $radiusservers[0]['acctport'],
403
									   $radiusservers[0]['key'],
404
									   $clientip);
405
			}
406
			mwexec("/sbin/pfctl -t captiveportal -T delete {$cpdb[$i][2]}");
407
			unset($cpdb[$i]);
408
			break;
409
		}
410
	}
411
	
412
	/* rewrite information to database */
413
	$fd = @fopen("{$g['vardb_path']}/captiveportal.db", "w");
414
	if ($fd) {
415
		foreach ($cpdb as $cpent) {
416
			fwrite($fd, join(",", $cpent) . "\n");
417
		}
418
		fclose($fd);
419
	}
420
	
421
	portal_unlock();
422
}
423
?>
(1-1/3)