Project

General

Profile

Download (12.3 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)
69
			$allowed_ip = true;
70
	}
71

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

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

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

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

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

    
161
exit;
162

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

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

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

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

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

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

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

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

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

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

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

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

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

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