Project

General

Profile

Download (16.1 KB) Statistics
| Branch: | Tag: | Revision:
1 8c1ce6c7 Scott Ullrich
<?php
2 5b237745 Scott Ullrich
/*
3 65fbb388 Scott Ullrich
    $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 5b237745 Scott Ullrich
*/
30
31 65fbb388 Scott Ullrich
require_once("functions.inc");
32
33
header("Expires: 0");
34
header("Cache-Control: no-store, no-cache, must-revalidate");
35
header("Cache-Control: post-check=0, pre-check=0", false);
36
header("Pragma: no-cache");
37 5b237745 Scott Ullrich
38
$orig_host = $_ENV['HTTP_HOST'];
39 eb0f441c Scott Ullrich
$orig_request = $_GET['redirurl'];
40 6fa4bdc6 Scott Ullrich
$clientip = $_SERVER['REMOTE_ADDR'];
41 5b237745 Scott Ullrich
42
if (!$clientip) {
43 65fbb388 Scott Ullrich
    /* not good - bail out */
44 8c1ce6c7 Scott Ullrich
    echo "An error occured.  Please check the system logs for more information.";
45
    log_error("Captive portal could not deterimine clients ip address.");
46 65fbb388 Scott Ullrich
    exit;
47
}
48
49
if (isset($config['captiveportal']['httpslogin']))
50
    $ourhostname = $config['captiveportal']['httpsname'] . ":8001";
51
else
52
    $ourhostname = $config['interfaces'][$config['captiveportal']['interface']]['ipaddr'] . ":8000";
53
54
if ($orig_host != $ourhostname) {
55
    /* the client thinks it's connected to the desired web server, but instead
56
       it's connected to us. Issue a redirect... */
57
58
    if (isset($config['captiveportal']['httpslogin']))
59
        header("Location: https://{$ourhostname}/index.php?redirurl=" . urlencode("http://{$orig_host}{$orig_request}"));
60
    else
61
        header("Location: http://{$ourhostname}/index.php?redirurl=" . urlencode("http://{$orig_host}{$orig_request}"));
62
63
    exit;
64 5b237745 Scott Ullrich
}
65
66 65fbb388 Scott Ullrich
if (preg_match("/redirurl=(.*)/", $orig_request, $matches))
67
    $redirurl = urldecode($matches[1]);
68
if ($_POST['redirurl'])
69
    $redirurl = $_POST['redirurl'];
70
71
$macfilter = !isset($config['captiveportal']['nomacfilter']);
72
73 5b237745 Scott Ullrich
/* find MAC address for client */
74
$clientmac = arp_get_mac_by_ip($clientip);
75 65fbb388 Scott Ullrich
if (!$clientmac && $macfilter) {
76
    /* unable to find MAC address - shouldn't happen! - bail out */
77
    captiveportal_logportalauth("unauthenticated","noclientmac",$clientip,"ERROR");
78 8c1ce6c7 Scott Ullrich
    echo "An error occured.  Please check the system logs for more information.";
79
    log_error("Captive portal could not deterimine clients MAC address.  Disable MAC address filtering in captive portal if you do not needs this functionality.");
80 65fbb388 Scott Ullrich
    exit;
81 5b237745 Scott Ullrich
}
82
83 65fbb388 Scott Ullrich
/* find out if we need RADIUS + RADIUSMAC or not */
84
if (file_exists("{$g['vardb_path']}/captiveportal_radius.db")) {
85
    $radius_enable = TRUE;
86
    if ($radius_enable && isset($config['captiveportal']['radmac_enable']))
87
        $radmac_enable = TRUE;
88
}
89 6e865a74 Scott Ullrich
90 65fbb388 Scott Ullrich
if ($_POST['logout_id']) {
91
    disconnect_client($_POST['logout_id']);
92
    echo <<<EOD
93 5b237745 Scott Ullrich
<HTML>
94
<HEAD><TITLE>Disconnecting...</TITLE></HEAD>
95
<BODY BGCOLOR="#435370">
96
<SPAN STYLE="color: #ffffff; font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">
97 b2ce71ff Scott Ullrich
<B>You have been disconnected.</B>
98 5b237745 Scott Ullrich
</SPAN>
99
<SCRIPT LANGUAGE="JavaScript">
100
<!--
101
setTimeout('window.close();',5000) ;
102
-->
103
</SCRIPT>
104
</BODY>
105
</HTML>
106
107
EOD;
108 65fbb388 Scott Ullrich
/* The $macfilter can be removed safely since we first check if the $clientmac is present, if not we fail */
109
} else if ($clientmac && portal_mac_fixed($clientmac)) {
110
    /* punch hole in ipfw for pass thru mac addresses */
111
    portal_allow($clientip, $clientmac, "unauthenticated");
112
    exit;
113
114
} else if ($clientmac && $radmac_enable && portal_mac_radius($clientmac,$clientip)) {
115
    /* radius functions handle everything so we exit here since we're done */
116
    exit;
117
118
} else if ($_POST['accept'] && $radius_enable) {
119
120
    if ($_POST['auth_user'] && $_POST['auth_pass']) {
121
        $auth_list = radius($_POST['auth_user'],$_POST['auth_pass'],$clientip,$clientmac,"USER LOGIN");
122
123
        if ($auth_list['auth_val'] == 1) {
124
            captiveportal_logportalauth($_POST['auth_user'],$clientmac,$clientip,"ERROR",$auth_list['error']);
125
            portal_reply_page($redirurl, "error", $auth_list['error']);
126
        }
127
        else if ($auth_list['auth_val'] == 3) {
128
            captiveportal_logportalauth($_POST['auth_user'],$clientmac,$clientip,"FAILURE",$auth_list['reply_message']);
129
            portal_reply_page($redirurl, "error", $auth_list['reply_message']);
130
        }
131
    } else {
132
        captiveportal_logportalauth($_POST['auth_user'],$clientmac,$clientip,"ERROR");
133
        portal_reply_page($redirurl, "error");
134
    }
135 8c1ce6c7 Scott Ullrich
136 65fbb388 Scott Ullrich
} else if ($_POST['accept'] && $config['captiveportal']['auth_method'] == "local") {
137 8c1ce6c7 Scott Ullrich
138 65fbb388 Scott Ullrich
    //check against local usermanager
139
    $userdb = &$config['captiveportal']['user'];
140
141
    $loginok = false;
142
143
    //erase expired accounts
144
    if (is_array($userdb)) {
145
        $moddb = false;
146
        for ($i = 0; $i < count($userdb); $i++) {
147
            if ($userdb[$i]['expirationdate'] && (strtotime("-1 day") > strtotime($userdb[$i]['expirationdate']))) {
148
                unset($userdb[$i]);
149
                $moddb = true;
150
            }
151
        }
152
        if ($moddb)
153
            write_config();
154
155
        $userdb = &$config['captiveportal']['user'];
156
157
        for ($i = 0; $i < count($userdb); $i++) {
158
            if (($userdb[$i]['name'] == $_POST['auth_user']) && ($userdb[$i]['password'] == md5($_POST['auth_pass']))) {
159
                $loginok = true;
160
                break;
161
            }
162
        }
163
    }
164
165
    if ($loginok){
166
        captiveportal_logportalauth($_POST['auth_user'],$clientmac,$clientip,"LOGIN");
167
        portal_allow($clientip, $clientmac,$_POST['auth_user']);
168
    } else {
169
        captiveportal_logportalauth($_POST['auth_user'],$clientmac,$clientip,"FAILURE");
170
        portal_reply_page($redirurl, "error");
171
    }
172
} else if ($_POST['accept'] && $clientip) {
173
    captiveportal_logportalauth("unauthenticated",$clientmac,$clientip,"ACCEPT");
174
    portal_allow($clientip, $clientmac, "unauthenticated");
175
} else {
176
    /* display captive portal page */
177 4993f81c Chris Buechler
    portal_reply_page($redirurl, "login",null,$clientmac,$clientip);
178 5b237745 Scott Ullrich
}
179
180
exit;
181
182 4993f81c Chris Buechler
function portal_reply_page($redirurl, $type = null, $message = null, $clientmac = null, $clientip = null) {
183 65fbb388 Scott Ullrich
    global $g, $config;
184 5b237745 Scott Ullrich
185 65fbb388 Scott Ullrich
    /* Get captive portal layout */
186
    if ($type == "login")
187
        $htmltext = file_get_contents("{$g['varetc_path']}/captiveportal.html");
188
    else
189
        $htmltext = file_get_contents("{$g['varetc_path']}/captiveportal-error.html");
190 5b237745 Scott Ullrich
191 65fbb388 Scott Ullrich
    /* substitute other variables */
192
    if (isset($config['captiveportal']['httpslogin']))
193
        $htmltext = str_replace("\$PORTAL_ACTION\$", "https://{$config['captiveportal']['httpsname']}:8001/", $htmltext);
194
    else
195
        $htmltext = str_replace("\$PORTAL_ACTION\$", "http://{$config['interfaces'][$config['captiveportal']['interface']]['ipaddr']}:8000/", $htmltext);
196 8c1ce6c7 Scott Ullrich
197 65fbb388 Scott Ullrich
    $htmltext = str_replace("\$PORTAL_REDIRURL\$", htmlspecialchars($redirurl), $htmltext);
198
    $htmltext = str_replace("\$PORTAL_MESSAGE\$", htmlspecialchars($message), $htmltext);
199 4993f81c Chris Buechler
    $htmltext = str_replace("\$CLIENT_MAC\$", htmlspecialchars($clientmac), $htmltext);
200
    $htmltext = str_replace("\$CLIENT_IP\$", htmlspecialchars($clientip), $htmltext);
201 5b237745 Scott Ullrich
202 65fbb388 Scott Ullrich
    echo $htmltext;
203
}
204 8c1ce6c7 Scott Ullrich
205 65fbb388 Scott Ullrich
function portal_mac_radius($clientmac,$clientip) {
206
    global $config ;
207 8c1ce6c7 Scott Ullrich
208 65fbb388 Scott Ullrich
    $radmac_secret = $config['captiveportal']['radmac_secret'];
209 8c1ce6c7 Scott Ullrich
210 65fbb388 Scott Ullrich
    /* authentication against the radius server */
211
    $username = mac_format($clientmac);
212
    $auth_list = radius($username,$radmac_secret,$clientip,$clientmac,"MACHINE LOGIN");
213
    if ($auth_list['auth_val'] == 2) {
214
        return TRUE;
215
    }
216
    return FALSE;
217
}
218 8c1ce6c7 Scott Ullrich
219 65fbb388 Scott Ullrich
function portal_allow($clientip,$clientmac,$username,$password = null, $attributes = null, $ruleno = null)  {
220
221 56ea4f47 Scott Ullrich
    global $redirurl, $g, $config, $url_redirection, $type;
222 65fbb388 Scott Ullrich
223
    /* See if a ruleno is passed, if not start locking the sessions because this means there isn't one atm */
224
    if ($ruleno == null) {
225
        captiveportal_lock();
226
        $ruleno = captiveportal_get_next_ipfw_ruleno();
227
    }
228
229
    /* if the pool is empty, return appropriate message and exit */
230
    if (is_null($ruleno)) {
231
        portal_reply_page($redirurl, "error", "System reached maximum login capacity");
232 6a92a6e1 Scott Ullrich
        log_error("WARNING!  Captive portal has reached maximum login capacity");
233 65fbb388 Scott Ullrich
        captiveportal_unlock();
234
        exit;
235
    }
236
237
    // Ensure we create an array if we are missing attributes
238
    if (!is_array($attributes))
239
        $attributes = array();
240
241
    /* read in client database */
242
    $cpdb = captiveportal_read_db();
243
244
    $radiusservers = captiveportal_get_radius_servers();
245
246
    /* Find an existing session */
247
    for ($i = 0; $i < count($cpdb); $i++) {
248
        /* on the same ip */
249
        if($cpdb[$i][2] == $clientip) {
250
            captiveportal_logportalauth($cpdb[$i][4],$cpdb[$i][3],$cpdb[$i][2],"CONCURRENT LOGIN - REUSING OLD SESSION");
251
            $sessionid = $cpdb[$i][5];
252
            break;
253
        }
254
        elseif ((isset($config['captiveportal']['noconcurrentlogins'])) && ($username != 'unauthenticated')) {
255
            /* on the same username */
256
            if ($cpdb[$i][4] == $username) {
257
                /* This user was already logged in so we disconnect the old one */
258
                captiveportal_disconnect($cpdb[$i],$radiusservers,13);
259
                captiveportal_logportalauth($cpdb[$i][4],$cpdb[$i][3],$cpdb[$i][2],"CONCURRENT LOGIN - TERMINATING OLD SESSION");
260
                unset($cpdb[$i]);
261
                break;
262
            }
263
        }
264
    }
265
266
    if (!isset($sessionid)) {
267
268
        /* generate unique session ID */
269
        $tod = gettimeofday();
270
        $sessionid = substr(md5(mt_rand() . $tod['sec'] . $tod['usec'] . $clientip . $clientmac), 0, 16);
271
272
        /* Add rules for traffic shaping
273
         * We don't need to add extra l3 allow rules since traffic will pass due to the following kernel option
274
         * net.inet.ip.fw.one_pass: 1
275
         */
276
        $peruserbw = isset($config['captiveportal']['peruserbw']);
277
278
        $bw_up = isset($attributes['bw_up']) ? trim($attributes['bw_up']) : $config['captiveportal']['bwdefaultup'];
279
        $bw_down = isset($attributes['bw_down']) ? trim($attributes['bw_down']) : $config['captiveportal']['bwdefaultdn'];
280
281
        if ($peruserbw && !empty($bw_up) && is_numeric($bw_up)) {
282
            $bw_up_pipeno = $ruleno + 40500;
283
            exec("/sbin/ipfw add $ruleno set 2 pipe $bw_up_pipeno ip from $clientip to any in");
284
            exec("/sbin/ipfw pipe $bw_up_pipeno config bw {$bw_up}Kbit/s queue 100");
285
        } else {
286
            exec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from $clientip to any in");
287
        }
288
        if ($peruserbw && !empty($bw_down) && is_numeric($bw_down)) {
289
            $bw_down_pipeno = $ruleno + 45500;
290
            exec("/sbin/ipfw add $ruleno set 2 pipe $bw_down_pipeno ip from any to $clientip out");
291
            exec("/sbin/ipfw pipe $bw_down_pipeno config bw {$bw_down}Kbit/s queue 100");
292
        } else {
293
            exec("/sbin/ipfw add $ruleno set 2 skipto 50000 ip from any to $clientip out");
294
        }
295
296
        /* add ipfw rules for layer 2 */
297
        if (!isset($config['captiveportal']['nomacfilter'])) {
298
            $l2ruleno = $ruleno + 10000;
299
            exec("/sbin/ipfw add $l2ruleno set 3 deny all from $clientip to any not MAC any $clientmac layer2 in");
300
            exec("/sbin/ipfw add $l2ruleno set 3 deny all from any to $clientip not MAC $clientmac any layer2 out");
301
        }
302
303
        /* encode password in Base64 just in case it contains commas */
304
        $bpassword = base64_encode($password);
305
        $cpdb[] = array(time(), $ruleno, $clientip, $clientmac, $username, $sessionid, $bpassword,
306
                $attributes['session_timeout'],
307
                $attributes['idle_timeout'],
308
                $attributes['session_terminate_time']);
309
310
        if (isset($config['captiveportal']['radacct_enable']) && isset($radiusservers[0])) {
311
            $acct_val = RADIUS_ACCOUNTING_START($ruleno,
312
                                                            $username,
313
                                                            $sessionid,
314
                                                            $radiusservers[0]['ipaddr'],
315
                                                            $radiusservers[0]['acctport'],
316
                                                            $radiusservers[0]['key'],
317
                                                            $clientip,
318
                                                            $clientmac);
319 2653f750 Scott Ullrich
320 65fbb388 Scott Ullrich
            if ($acct_val == 1)
321 56ea4f47 Scott Ullrich
                captiveportal_logportalauth($username,$clientmac,$clientip,$type,"RADIUS ACCOUNTING FAILED");
322 65fbb388 Scott Ullrich
        }
323
324
325
    }
326
327
    /* rewrite information to database */
328
    captiveportal_write_db($cpdb);
329
330
    /* redirect user to desired destination */
331
    if ($url_redirection)
332
        $my_redirurl = $url_redirection;
333
    else if ($config['captiveportal']['redirurl'])
334
        $my_redirurl = $config['captiveportal']['redirurl'];
335
    else
336
        $my_redirurl = $redirurl;
337
338
    if(isset($config['captiveportal']['logoutwin_enable'])) {
339
340
        if (isset($config['captiveportal']['httpslogin']))
341
            $logouturl = "https://{$config['captiveportal']['httpsname']}:8001/";
342
        else
343
            $logouturl = "http://{$config['interfaces'][$config['captiveportal']['interface']]['ipaddr']}:8000/";
344
345
        echo <<<EOD
346 5b237745 Scott Ullrich
<HTML>
347
<HEAD><TITLE>Redirecting...</TITLE></HEAD>
348
<BODY>
349
<SPAN STYLE="font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">
350 65fbb388 Scott Ullrich
<B>Redirecting to <A HREF="{$my_redirurl}">{$my_redirurl}</A>...</B>
351 5b237745 Scott Ullrich
</SPAN>
352
<SCRIPT LANGUAGE="JavaScript">
353
<!--
354
LogoutWin = window.open('', 'Logout', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=256,height=64');
355
if (LogoutWin) {
356 65fbb388 Scott Ullrich
    LogoutWin.document.write('<HTML>');
357
    LogoutWin.document.write('<HEAD><TITLE>Logout</TITLE></HEAD>') ;
358
    LogoutWin.document.write('<BODY BGCOLOR="#435370">');
359
    LogoutWin.document.write('<DIV ALIGN="center" STYLE="color: #ffffff; font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">') ;
360
    LogoutWin.document.write('<B>Click the button below to disconnect</B><P>');
361
    LogoutWin.document.write('<FORM METHOD="POST" ACTION="{$logouturl}">');
362
    LogoutWin.document.write('<INPUT NAME="logout_id" TYPE="hidden" VALUE="{$sessionid}">');
363
    LogoutWin.document.write('<INPUT NAME="logout" TYPE="submit" VALUE="Logout">');
364
    LogoutWin.document.write('</FORM>');
365
    LogoutWin.document.write('</DIV></BODY>');
366
    LogoutWin.document.write('</HTML>');
367
    LogoutWin.document.close();
368 5b237745 Scott Ullrich
}
369
370 65fbb388 Scott Ullrich
document.location.href="{$my_redirurl}";
371 5b237745 Scott Ullrich
-->
372
</SCRIPT>
373
</BODY>
374
</HTML>
375
376
EOD;
377 65fbb388 Scott Ullrich
    } else {
378 40657838 Scott Ullrich
		captiveportal_unlock();	
379 65fbb388 Scott Ullrich
        header("Location: " . $my_redirurl);
380 6e41522c Scott Ullrich
		return $sessionid;
381 65fbb388 Scott Ullrich
    }
382 8c1ce6c7 Scott Ullrich
383 65fbb388 Scott Ullrich
    captiveportal_unlock();
384
    return $sessionid;
385 5b237745 Scott Ullrich
}
386
387 8c1ce6c7 Scott Ullrich
388 5b237745 Scott Ullrich
389 65fbb388 Scott Ullrich
/* remove a single client by session ID
390
   by Dinesh Nair
391
 */
392
function disconnect_client($sessionid, $logoutReason = "LOGOUT", $term_cause = 1) {
393 5b237745 Scott Ullrich
394 65fbb388 Scott Ullrich
    global $g, $config;
395 8c1ce6c7 Scott Ullrich
396 65fbb388 Scott Ullrich
    captiveportal_lock();
397
    /* read database */
398
    $cpdb = captiveportal_read_db();
399 8c1ce6c7 Scott Ullrich
400 65fbb388 Scott Ullrich
    $radiusservers = captiveportal_get_radius_servers();
401
402
    /* find entry */
403
    for ($i = 0; $i < count($cpdb); $i++) {
404
        if ($cpdb[$i][5] == $sessionid) {
405
            captiveportal_disconnect($cpdb[$i],$radiusservers, $term_cause);
406
            captiveportal_logportalauth($cpdb[$i][4],$cpdb[$i][3],$cpdb[$i][2],$logoutReason);
407
            unset($cpdb[$i]);
408
            break;
409
        }
410
    }
411
412
    /* write database */
413
    captiveportal_write_db($cpdb);
414
415
    captiveportal_unlock();
416 5b237745 Scott Ullrich
}
417
418 65fbb388 Scott Ullrich
?>