Project

General

Profile

Download (18.2 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
    $Id$
4
    part of m0n0wall (http://m0n0.ch/wall)
5

    
6
    Copyright (C) 2003-2006 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_once("auth.inc");
32
require_once("functions.inc");
33

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

    
39
$orig_host = $_ENV['HTTP_HOST'];
40
$orig_request = $_GET['redirurl'];
41
$clientip = $_SERVER['REMOTE_ADDR'];
42

    
43
if (!$clientip) {
44
    /* not good - bail out */
45
    echo "An error occured.  Please check the system logs for more information.";
46
    log_error("Captive portal could not deterimine clients ip address.");
47
    exit;
48
}
49

    
50
if (isset($config['captiveportal']['httpslogin']))
51
    $ourhostname = $config['captiveportal']['httpsname'] . ":8001";
52
else {
53
    $ifip = portal_ip_from_client_ip($clientip);
54
    if (!$ifip)
55
    	$ourhostname = $config['system']['hostname'] . ":8000";
56
    else
57
    	$ourhostname = "{$ifip}:8000";
58
}
59

    
60
if ($orig_host != $ourhostname) {
61
    /* the client thinks it's connected to the desired web server, but instead
62
       it's connected to us. Issue a redirect... */
63

    
64
    if (isset($config['captiveportal']['httpslogin']))
65
        header("Location: https://{$ourhostname}/index.php?redirurl=" . urlencode("http://{$orig_host}{$orig_request}"));
66
    else
67
        header("Location: http://{$ourhostname}/index.php?redirurl=" . urlencode("http://{$orig_host}{$orig_request}"));
68

    
69
    exit;
70
}
71

    
72
if (preg_match("/redirurl=(.*)/", $orig_request, $matches))
73
    $redirurl = urldecode($matches[1]);
74
if ($_POST['redirurl'])
75
    $redirurl = $_POST['redirurl'];
76

    
77
$macfilter = !isset($config['captiveportal']['nomacfilter']);
78

    
79
/* find MAC address for client */
80
$clientmac = arp_get_mac_by_ip($clientip);
81
if (!$clientmac && $macfilter) {
82
    /* unable to find MAC address - shouldn't happen! - bail out */
83
    captiveportal_logportalauth("unauthenticated","noclientmac",$clientip,"ERROR");
84
    echo "An error occured.  Please check the system logs for more information.";
85
    log_error("Captive portal could not deterimine clients MAC address.  Disable MAC address filtering in captive portal if you do not needs this functionality.");
86
    exit;
87
}
88

    
89
/* find out if we need RADIUS + RADIUSMAC or not */
90
if (file_exists("{$g['vardb_path']}/captiveportal_radius.db")) {
91
    $radius_enable = TRUE;
92
    if (isset($config['captiveportal']['radmac_enable']))
93
        $radmac_enable = TRUE;
94
}
95

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

    
113
EOD;
114
/* The $macfilter can be removed safely since we first check if the $clientmac is present, if not we fail */
115
} else if ($clientmac && portal_mac_fixed($clientmac)) {
116
    /* punch hole in ipfw for pass thru mac addresses */
117
    portal_allow($clientip, $clientmac, "unauthenticated");
118
    exit;
119

    
120
} else if ($clientmac && $radmac_enable && portal_mac_radius($clientmac,$clientip)) {
121
    /* radius functions handle everything so we exit here since we're done */
122
    exit;
123

    
124
} else if ($_POST['accept'] && $_POST['auth_voucher']) {
125

    
126
    $voucher = trim($_POST['auth_voucher']);
127
    $timecredit = voucher_auth($voucher);
128
    // $timecredit contains either a credit in minutes or an error message
129
    if ($timecredit > 0) {  // voucher is valid. Remaining minutes returned
130
        // if multiple vouchers given, use the first as username
131
        $a_vouchers = split("[\t\n\r ]+",$voucher);
132
        $voucher = $a_vouchers[0];
133
        $attr = array( 'voucher' => 1,
134
                'session_timeout' => $timecredit*60,
135
                'session_terminate_time' => 0);
136
        if (portal_allow($clientip, $clientmac,$voucher,null,$attr)) {
137

    
138
            // YES: user is good for $timecredit minutes.
139
            captiveportal_logportalauth($voucher,$clientmac,$clientip,"VOUCHER LOGIN good for $timecredit min.");
140
        } else {
141
            portal_reply_page($redirurl, "error", $config['voucher']['msgexpired']);
142
        }
143
    } else if (-1 == $timecredit) {  // valid but expired
144
        captiveportal_logportalauth($voucher,$clientmac,$clientip,"FAILURE","voucher expired");
145
        portal_reply_page($redirurl, "error", $config['voucher']['msgexpired']);
146
    } else {
147
        captiveportal_logportalauth($voucher,$clientmac,$clientip,"FAILURE");
148
        portal_reply_page($redirurl, "error", $config['voucher']['msgnoaccess']);
149
    }
150

    
151
} else if ($_POST['accept'] && $radius_enable) {
152

    
153
    if ($_POST['auth_user'] && $_POST['auth_pass']) {
154
        $auth_list = radius($_POST['auth_user'],$_POST['auth_pass'],$clientip,$clientmac,"USER LOGIN");
155

    
156
        if ($auth_list['auth_val'] == 1) {
157
            captiveportal_logportalauth($_POST['auth_user'],$clientmac,$clientip,"ERROR",$auth_list['error']);
158
            portal_reply_page($redirurl, "error", $auth_list['error']);
159
        }
160
        else if ($auth_list['auth_val'] == 3) {
161
            captiveportal_logportalauth($_POST['auth_user'],$clientmac,$clientip,"FAILURE",$auth_list['reply_message']);
162
            portal_reply_page($redirurl, "error", $auth_list['reply_message']);
163
        }
164
    } else {
165
        captiveportal_logportalauth($_POST['auth_user'],$clientmac,$clientip,"ERROR");
166
        portal_reply_page($redirurl, "error");
167
    }
168

    
169
} else if ($_POST['accept'] && $config['captiveportal']['auth_method'] == "local") {
170

    
171
	//check against local user manager
172
	$loginok = local_backed($_POST['auth_user'], $_POST['auth_pass']);
173
    if ($loginok){
174
        captiveportal_logportalauth($_POST['auth_user'],$clientmac,$clientip,"LOGIN");
175
        portal_allow($clientip, $clientmac,$_POST['auth_user']);
176
    } else {
177
        captiveportal_logportalauth($_POST['auth_user'],$clientmac,$clientip,"FAILURE");
178
        portal_reply_page($redirurl, "error");
179
    }
180
} else if ($_POST['accept'] && $clientip) {
181
    captiveportal_logportalauth("unauthenticated",$clientmac,$clientip,"ACCEPT");
182
    portal_allow($clientip, $clientmac, "unauthenticated");
183
} else {
184
    /* display captive portal page */
185
    portal_reply_page($redirurl, "login",null,$clientmac,$clientip);
186
}
187

    
188
exit;
189

    
190
function portal_reply_page($redirurl, $type = null, $message = null, $clientmac = null, $clientip = null) {
191
    global $g, $config;
192

    
193
    /* Get captive portal layout */
194
    if ($type == "login")
195
        $htmltext = file_get_contents("{$g['varetc_path']}/captiveportal.html");
196
    else
197
        $htmltext = file_get_contents("{$g['varetc_path']}/captiveportal-error.html");
198

    
199
    /* substitute other variables */
200
    if (isset($config['captiveportal']['httpslogin']))
201
        $htmltext = str_replace("\$PORTAL_ACTION\$", "https://{$config['captiveportal']['httpsname']}:8001/", $htmltext);
202
    else {
203
	$ifip = portal_ip_from_client_ip($clientip);
204
    	if (!$ifip)
205
        	$ourhostname = $config['system']['hostname'] . ":8000";
206
    	else
207
        	$ourhostname = "{$ifip}:8000";
208
        $htmltext = str_replace("\$PORTAL_ACTION\$", "http://{$ourhostname}/", $htmltext);
209
    }
210

    
211
    $htmltext = str_replace("\$PORTAL_REDIRURL\$", htmlspecialchars($redirurl), $htmltext);
212
    $htmltext = str_replace("\$PORTAL_MESSAGE\$", htmlspecialchars($message), $htmltext);
213
    $htmltext = str_replace("\$CLIENT_MAC\$", htmlspecialchars($clientmac), $htmltext);
214
    $htmltext = str_replace("\$CLIENT_IP\$", htmlspecialchars($clientip), $htmltext);
215

    
216
    echo $htmltext;
217
}
218

    
219
function portal_mac_radius($clientmac,$clientip) {
220
    global $config ;
221

    
222
    $radmac_secret = $config['captiveportal']['radmac_secret'];
223

    
224
    /* authentication against the radius server */
225
    $username = mac_format($clientmac);
226
    $auth_list = radius($username,$radmac_secret,$clientip,$clientmac,"MACHINE LOGIN");
227
    if ($auth_list['auth_val'] == 2) {
228
        return TRUE;
229
    }
230
    return FALSE;
231
}
232

    
233
function portal_allow($clientip,$clientmac,$username,$password = null, $attributes = null, $ruleno = null)  {
234

    
235
    global $redirurl, $g, $config, $url_redirection, $type;
236

    
237
    /* See if a ruleno is passed, if not start locking the sessions because this means there isn't one atm */
238
    $captiveshouldunlock = false;
239
    if ($ruleno == null) {
240
        $cplock = lock('captiveportal');
241
    	$captiveshouldunlock = true;
242
        $ruleno = captiveportal_get_next_ipfw_ruleno();
243
    }
244

    
245
    /* if the pool is empty, return appropriate message and exit */
246
    if (is_null($ruleno)) {
247
        portal_reply_page($redirurl, "error", "System reached maximum login capacity");
248
        log_error("WARNING!  Captive portal has reached maximum login capacity");
249
    	if ($captiveshouldunlock == true)
250
        	unlock($cplock);
251
        exit;
252
    }
253

    
254
    // Ensure we create an array if we are missing attributes
255
    if (!is_array($attributes))
256
        $attributes = array();
257

    
258
    /* read in client database */
259
    $cpdb = captiveportal_read_db();
260

    
261
    $radiusservers = captiveportal_get_radius_servers();
262

    
263
    if ($attributes['voucher'])
264
        $remaining_time = $attributes['session_timeout'];
265

    
266
    /* Find an existing session */
267
    for ($i = 0; $i < count($cpdb); $i++) {
268
        /* on the same ip */
269
        if($cpdb[$i][2] == $clientip) {
270
            captiveportal_logportalauth($cpdb[$i][4],$cpdb[$i][3],$cpdb[$i][2],"CONCURRENT LOGIN - REUSING OLD SESSION");
271
            $sessionid = $cpdb[$i][5];
272
            break;
273
        }
274
	elseif (($attributes['voucher']) && ($username != 'unauthenticated') && ($cpdb[$i][4] == $username)) {
275
            // user logged in with an active voucher. Check for how long and calculate 
276
            // how much time we can give him (voucher credit - used time)
277
            $remaining_time = $cpdb[$i][0] + $cpdb[$i][7] - time();
278
            if ($remaining_time < 0)    // just in case. 
279
                $remaining_time = 0;
280

    
281
            /* This user was already logged in so we disconnect the old one */
282
            captiveportal_disconnect($cpdb[$i],$radiusservers,13);
283
            captiveportal_logportalauth($cpdb[$i][4],$cpdb[$i][3],$cpdb[$i][2],"CONCURRENT LOGIN - TERMINATING OLD SESSION");
284
            unset($cpdb[$i]);
285
            break;
286
        }
287
        elseif ((isset($config['captiveportal']['noconcurrentlogins'])) && ($username != 'unauthenticated')) {
288
            /* on the same username */
289
            if ($cpdb[$i][4] == $username) {
290
                /* This user was already logged in so we disconnect the old one */
291
                captiveportal_disconnect($cpdb[$i],$radiusservers,13);
292
                captiveportal_logportalauth($cpdb[$i][4],$cpdb[$i][3],$cpdb[$i][2],"CONCURRENT LOGIN - TERMINATING OLD SESSION");
293
                unset($cpdb[$i]);
294
                break;
295
            }
296
        }
297
    }
298

    
299
    if ($attributes['voucher'] && $remaining_time <= 0) {
300
	unlock($cplock);
301
        return 0;       // voucher already used and no time left
302
    }
303

    
304
    if (!isset($sessionid)) {
305

    
306
        /* generate unique session ID */
307
        $tod = gettimeofday();
308
        $sessionid = substr(md5(mt_rand() . $tod['sec'] . $tod['usec'] . $clientip . $clientmac), 0, 16);
309

    
310
        /* Add rules for traffic shaping
311
         * We don't need to add extra l3 allow rules since traffic will pass due to the following kernel option
312
         * net.inet.ip.fw.one_pass: 1
313
         */
314
        $peruserbw = isset($config['captiveportal']['peruserbw']);
315

    
316
        $bw_up = isset($attributes['bw_up']) ? trim($attributes['bw_up']) : $config['captiveportal']['bwdefaultup'];
317
        $bw_down = isset($attributes['bw_down']) ? trim($attributes['bw_down']) : $config['captiveportal']['bwdefaultdn'];
318

    
319
        if ($peruserbw && !empty($bw_up) && is_numeric($bw_up)) {
320
            $bw_up_pipeno = $ruleno + 40500;
321
            exec("/sbin/ipfw add $ruleno set 2 pipe $bw_up_pipeno ip from $clientip to any in");
322
            exec("/sbin/ipfw pipe $bw_up_pipeno config bw {$bw_up}Kbit/s queue 100");
323
        } else {
324
            exec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from $clientip to any in");
325
        }
326
        if ($peruserbw && !empty($bw_down) && is_numeric($bw_down)) {
327
            $bw_down_pipeno = $ruleno + 45500;
328
            exec("/sbin/ipfw add $ruleno set 2 pipe $bw_down_pipeno ip from any to $clientip out");
329
            exec("/sbin/ipfw pipe $bw_down_pipeno config bw {$bw_down}Kbit/s queue 100");
330
        } else {
331
            exec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from any to $clientip out");
332
        }
333

    
334
        /* add ipfw rules for layer 2 */
335
        if (!isset($config['captiveportal']['nomacfilter'])) {
336
            $l2ruleno = $ruleno + 10000;
337
            exec("/sbin/ipfw add $l2ruleno set 3 deny all from $clientip to any not MAC any $clientmac layer2 in");
338
            exec("/sbin/ipfw add $l2ruleno set 3 deny all from any to $clientip not MAC $clientmac any layer2 out");
339
        }
340

    
341
	if ($attributes['voucher'])
342
		$attributes['session_timeout'] = $remaining_time;
343

    
344
        /* encode password in Base64 just in case it contains commas */
345
        $bpassword = base64_encode($password);
346
        $cpdb[] = array(time(), $ruleno, $clientip, $clientmac, $username, $sessionid, $bpassword,
347
                $attributes['session_timeout'],
348
                $attributes['idle_timeout'],
349
                $attributes['session_terminate_time']);
350

    
351
        if (isset($config['captiveportal']['radacct_enable']) && isset($radiusservers[0])) {
352
            $acct_val = RADIUS_ACCOUNTING_START($ruleno,
353
                                                            $username,
354
                                                            $sessionid,
355
                                                            $radiusservers[0]['ipaddr'],
356
                                                            $radiusservers[0]['acctport'],
357
                                                            $radiusservers[0]['key'],
358
                                                            $clientip,
359
                                                            $clientmac);
360

    
361
            if ($acct_val == 1)
362
                captiveportal_logportalauth($username,$clientmac,$clientip,$type,"RADIUS ACCOUNTING FAILED");
363
        }
364

    
365

    
366
    }
367

    
368
    /* rewrite information to database */
369
    captiveportal_write_db($cpdb);
370
    if ($captiveshouldunlock == true)
371
	unlock($cplock);
372

    
373
    /* redirect user to desired destination */
374
    if ($url_redirection)
375
        $my_redirurl = $url_redirection;
376
    else if ($config['captiveportal']['redirurl'])
377
        $my_redirurl = $config['captiveportal']['redirurl'];
378
    else
379
        $my_redirurl = $redirurl;
380

    
381
    if(isset($config['captiveportal']['logoutwin_enable'])) {
382

    
383
        if (isset($config['captiveportal']['httpslogin']))
384
            $logouturl = "https://{$config['captiveportal']['httpsname']}:8001/";
385
        else {
386
	    $ifip = portal_ip_from_client_ip($clientip);
387
    	    if (!$ifip)
388
        	$ourhostname = $config['system']['hostname'] . ":8000";
389
            else
390
        	$ourhostname = "{$ifip}:8000";
391
            $logouturl = "http://{$ourhostname}/";
392
	}
393

    
394
        echo <<<EOD
395
<HTML>
396
<HEAD><TITLE>Redirecting...</TITLE></HEAD>
397
<BODY>
398
<SPAN STYLE="font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">
399
<B>Redirecting to <A HREF="{$my_redirurl}">{$my_redirurl}</A>...</B>
400
</SPAN>
401
<SCRIPT LANGUAGE="JavaScript">
402
<!--
403
LogoutWin = window.open('', 'Logout', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=256,height=64');
404
if (LogoutWin) {
405
    LogoutWin.document.write('<HTML>');
406
    LogoutWin.document.write('<HEAD><TITLE>Logout</TITLE></HEAD>') ;
407
    LogoutWin.document.write('<BODY BGCOLOR="#435370">');
408
    LogoutWin.document.write('<DIV ALIGN="center" STYLE="color: #ffffff; font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">') ;
409
    LogoutWin.document.write('<B>Click the button below to disconnect</B><P>');
410
    LogoutWin.document.write('<FORM METHOD="POST" ACTION="{$logouturl}">');
411
    LogoutWin.document.write('<INPUT NAME="logout_id" TYPE="hidden" VALUE="{$sessionid}">');
412
    LogoutWin.document.write('<INPUT NAME="logout" TYPE="submit" VALUE="Logout">');
413
    LogoutWin.document.write('</FORM>');
414
    LogoutWin.document.write('</DIV></BODY>');
415
    LogoutWin.document.write('</HTML>');
416
    LogoutWin.document.close();
417
}
418

    
419
document.location.href="{$my_redirurl}";
420
-->
421
</SCRIPT>
422
</BODY>
423
</HTML>
424

    
425
EOD;
426
    } else {
427
        header("Location: " . $my_redirurl);
428
		return $sessionid;
429
    }
430

    
431
    return $sessionid;
432
}
433

    
434

    
435

    
436
/* remove a single client by session ID
437
   by Dinesh Nair
438
 */
439
function disconnect_client($sessionid, $logoutReason = "LOGOUT", $term_cause = 1) {
440

    
441
    global $g, $config;
442

    
443
    $cplock = lock('captiveportal');
444
    /* read database */
445
    $cpdb = captiveportal_read_db();
446

    
447
    $radiusservers = captiveportal_get_radius_servers();
448

    
449
    /* find entry */
450
    for ($i = 0; $i < count($cpdb); $i++) {
451
        if ($cpdb[$i][5] == $sessionid) {
452
            captiveportal_disconnect($cpdb[$i],$radiusservers, $term_cause);
453
            captiveportal_logportalauth($cpdb[$i][4],$cpdb[$i][3],$cpdb[$i][2],$logoutReason);
454
            unset($cpdb[$i]);
455
            break;
456
        }
457
    }
458

    
459
    /* write database */
460
    captiveportal_write_db($cpdb);
461

    
462
    unlock($cplock);
463
}
464

    
465
?>
(1-1/3)