Project

General

Profile

Download (92.5 KB) Statistics
| Branch: | Tag: | Revision:
1 5b237745 Scott Ullrich
<?php
2
/*
3 ac24dc24 Renato Botelho
 * captiveportal.inc
4
 *
5
 * part of pfSense (https://www.pfsense.org)
6 b8f91b7c Luiz Souza
 * Copyright (c) 2004-2018 Rubicon Communications, LLC (Netgate)
7 ac24dc24 Renato Botelho
 * All rights reserved.
8
 *
9
 * originally part of m0n0wall (http://m0n0.ch/wall)
10 c5d81585 Renato Botelho
 * Copyright (c) 2003-2006 Manuel Kasper <mk@neon1.net>.
11 ac24dc24 Renato Botelho
 * All rights reserved.
12
 *
13 b12ea3fb Renato Botelho
 * Licensed under the Apache License, Version 2.0 (the "License");
14
 * you may not use this file except in compliance with the License.
15
 * You may obtain a copy of the License at
16 ac24dc24 Renato Botelho
 *
17 b12ea3fb Renato Botelho
 * http://www.apache.org/licenses/LICENSE-2.0
18 ac24dc24 Renato Botelho
 *
19 b12ea3fb Renato Botelho
 * Unless required by applicable law or agreed to in writing, software
20
 * distributed under the License is distributed on an "AS IS" BASIS,
21
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
22
 * See the License for the specific language governing permissions and
23
 * limitations under the License.
24 ac24dc24 Renato Botelho
 */
25 36254e4a Scott Ullrich
26 5b237745 Scott Ullrich
/* include all configuration functions */
27 5f1aaed4 Augustin FL
require_once("auth.inc");
28
require_once("PEAR.php"); // required for bcmath
29
require_once("Auth/RADIUS.php"); // required for radius accounting
30 a55cdcc0 Ermal Lu?i
require_once("config.inc");
31
require_once("functions.inc");
32 71fdaecd Ermal
require_once("filter.inc");
33 156487ed Ermal Lu?i
require_once("voucher.inc");
34 0bd34ed6 Scott Ullrich
35 5f1aaed4 Augustin FL
/* Captiveportal Radius Accounting */
36
PEAR::loadExtension('bcmath');
37
// The RADIUS Package doesn't have these vars so we create them ourself
38
define("CUSTOM_RADIUS_ACCT_INPUT_GIGAWORDS", "52");
39
define("CUSTOM_RADIUS_ACCT_OUTPUT_GIGAWORDS", "53");
40
define("GIGAWORDS_RIGHT_OPERAND", '4294967296'); // 2^32
41
42 023aa1f2 Scott Ullrich
function get_default_captive_portal_html() {
43 b4792bf8 Ermal
	global $config, $g, $cpzone;
44 0e296bce Ermal
45 748372bc Stephen Jones
	$translated_text1 = gettext("User");
46
	$translated_text2 = gettext("Password");
47 15064e4f Stephen Jones
	$translated_text3 = gettext("First Authentication Method ");
48
	$translated_text4 = gettext("Second Authentication Method ");
49 748372bc Stephen Jones
	// default images to use.
50
	$logo_src = "captiveportal-default-logo.png";
51
	$bg_src = "linear-gradient(135deg, #1475CF, #2B40B5, #1C1275)";
52
	// Check if customlogo is set and if the element exists
53
	// Check if the image is in the directory
54
	if (isset($config['captiveportal'][$cpzone]['customlogo'])) {
55
		foreach ($config['captiveportal'][$cpzone]['element'] as $element) {
56
			if (strpos($element['name'], "captiveportal-logo.") !== false) {
57
				if (file_exists("{$g['captiveportal_path']}/{$element['name']}")) {
58
					$logo_src = $element['name'];
59
					break;
60
				}
61
			}
62
		}
63
	}
64
	// check if custombg is set and if the element exists
65
	if (isset($config['captiveportal'][$cpzone]['custombg'])) {
66
		foreach ($config['captiveportal'][$cpzone]['element'] as $element) {
67
			if (strpos($element['name'],"captiveportal-background.") !== false) {
68
				if( file_exists("{$g['captiveportal_path']}/{$element['name']}")) {
69
					$bg_src = "url(" . $element['name'] . ")" . "center center no-repeat fixed";
70
					break;
71
				}
72
			}
73
		}
74
75
	}
76
	// bring in terms and conditions
77
	$termsconditions = base64_decode($config['captiveportal'][$cpzone]['termsconditions']);
78
	// if there is no terms and conditions do not require the checkbox to be selected.
79
	$disabled = "";
80
	if ($termsconditions) {
81
		$disabled = "disabled";
82
	}
83 0e296bce Ermal
	$htmltext = <<<EOD
84 748372bc Stephen Jones
<!DOCTYPE html>
85 1e0b1727 Phil Davis
<html>
86 748372bc Stephen Jones
87
<head>
88
89
  <meta charset="UTF-8">
90
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
91
  <meta http-equiv="content-type" content="text/html; charset=UTF-8" />
92
  <title>Captive Portal Login Page</title>
93
  <style>
94 dee5b2b7 Steve Beaver
	  #content,.login,.login-card a,.login-card h1,.login-help{text-align:center}body,html{margin:0;padding:0;width:100%;height:100%;display:table}#content{font-family:'Source Sans Pro',sans-serif;background-color:#1C1275;background:{$bg_src};-webkit-background-size:cover;-moz-background-size:cover;-o-background-size:cover;background-size:cover;display:table-cell;vertical-align:middle}.login-card{padding:40px;width:280px;background-color:#F7F7F7;margin:100px auto 10px;border-radius:2px;box-shadow:0 2px 2px rgba(0,0,0,.3);overflow:hidden}.login-card h1{font-weight:400;font-size:2.3em;color:#1383c6}.login-card h1 span{color:#f26721}.login-card img{width:70%;height:70%}.login-card input[type=submit]{width:100%;display:block;margin-bottom:10px;position:relative}.login-card input[type=text],input[type=password]{height:44px;font-size:16px;width:100%;margin-bottom:10px;-webkit-appearance:none;background:#fff;border:1px solid #d9d9d9;border-top:1px solid silver;padding:0 8px;box-sizing:border-box;-moz-box-sizing:border-box}.login-card input[type=text]:hover,input[type=password]:hover{border:1px solid #b9b9b9;border-top:1px solid #a0a0a0;-moz-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);-webkit-box-shadow:inset 0 1px 2px rgba(0,0,0,.1);box-shadow:inset 0 1px 2px rgba(0,0,0,.1)}.login{font-size:14px;font-family:Arial,sans-serif;font-weight:700;height:36px;padding:0 8px}.login-submit{-webkit-appearance:none;-moz-appearance:none;appearance:none;border:0;color:#fff;text-shadow:0 1px rgba(0,0,0,.1);background-color:#4d90fe}.login-submit:disabled{opacity:.6}.login-submit:hover{border:0;text-shadow:0 1px rgba(0,0,0,.3);background-color:#357ae8}.login-card a{text-decoration:none;color:#222;font-weight:400;display:inline-block;opacity:.6;transition:opacity ease .5s}.login-card a:hover{opacity:1}.login-help{width:100%;font-size:12px}.list{list-style-type:none;padding:0}.list__item{margin:0 0 .7rem;padding:0}label{display:-webkit-box;display:-webkit-flex;display:-ms-flexbox;display:flex;-webkit-box-align:center;-webkit-align-items:center;-ms-flex-align:center;align-items:center;text-align:left;font-size:14px;}input[type=checkbox]{-webkit-box-flex:0;-webkit-flex:none;-ms-flex:none;flex:none;margin-right:10px;float:left}@media screen and (max-width:450px){.login-card{width:70%!important}.login-card img{width:30%;height:30%}}textarea{width:66%;margin:auto;height:120px;max-height:120px;background-color:#f7f7f7;padding:20px}#terms{display:none;padding-top:100px;padding-bottom:300px;}.auth_source{border: 1px solid lightgray; padding:20px 8px 0px 8px; margin-top: -2em; border-radius: 2px; }.auth_head{background-color:#f7f7f7;display:inline-block;}.auth_head_div{text-align:left;}#error-message{text-align:left;color:#ff3e3e;font-style:italic;}
95 748372bc Stephen Jones
  </style>
96
</head>
97
98 1e0b1727 Phil Davis
<body>
99 748372bc Stephen Jones
<div id="content">
100
	<div class="login-card">
101
		<img src="{$logo_src}"/><br>
102
 		<h1></h1>
103 131b297d Stephen Jones
		<div id="error-message">
104
			\$PORTAL_MESSAGE\$
105
		</div>
106 748372bc Stephen Jones
	  <form name="login_form" method="post" action="\$PORTAL_ACTION\$">
107 eb43c5b1 Augustin FL
EOD;
108 748372bc Stephen Jones
	if ($config['captiveportal'][$cpzone]['auth_method'] != "none"){
109 15064e4f Stephen Jones
		if ($config['captiveportal'][$cpzone]['auth_method'] === 'authserver' && !empty($config['captiveportal'][$cpzone]['auth_server2'])) {
110
			$htmltext .= <<<EOD
111
			<div class="auth_head_div">
112
				<h6 class="auth_head">{$translated_text3}</h6>
113
			</div>
114
			<div class="auth_source">
115
116
EOD;
117
		}
118 748372bc Stephen Jones
		$htmltext .=<<<EOD
119
		<input type="text" name="auth_user" placeholder="{$translated_text1}" id="auth_user">
120
		<input type="password" name="auth_pass" placeholder="{$translated_text2}" id="auth_pass">
121 eb43c5b1 Augustin FL
EOD;
122 0e296bce Ermal
123 15064e4f Stephen Jones
		if ($config['captiveportal'][$cpzone]['auth_method'] === 'authserver' && !empty($config['captiveportal'][$cpzone]['auth_server2'])) {
124
			$htmltext .= <<<EOD
125
			</div>
126
			<div class="auth_head_div">
127
				<h6 class="auth_head">{$translated_text4}</h6>
128
			</div>
129
			<div class="auth_source">
130
131
			<input type="text" name="auth_user2" placeholder="{$translated_text1}" id="auth_user2">
132
			<input type="password" name="auth_pass2" placeholder="{$translated_text2}" id="auth_pass2">
133
			</div>
134
EOD;
135
		}
136
137 36254e4a Scott Ullrich
138 748372bc Stephen Jones
		if (isset($config['voucher'][$cpzone]['enable'])) {
139
			$translated_text = gettext("Voucher Code");
140
			$htmltext .= <<<EOD
141 15064e4f Stephen Jones
				<br  /><br  />
142
				<input name="auth_voucher" type="text" placeholder="{$translated_text}">
143 023aa1f2 Scott Ullrich
EOD;
144 748372bc Stephen Jones
		}
145 c2056357 Scott Ullrich
	}
146 0bd34ed6 Scott Ullrich
147 748372bc Stephen Jones
if ($termsconditions) {
148 0e296bce Ermal
	$htmltext .= <<<EOD
149 748372bc Stephen Jones
		  <div class="login-help">
150
			<ul class="list">
151
				<li class="list__item">
152
				  <label class="label--checkbox">
153
					<input type="checkbox" class="checkbox" onchange="document.getElementById('login').disabled = !this.checked;">
154 15064e4f Stephen Jones
					<span>I agree with the <a  rel="noopener" href="#terms" onclick="document.getElementById('terms').style.display = 'block';">terms & conditions</a></span>
155 748372bc Stephen Jones
				  </label>
156
				</li>
157
			</ul>
158
		  </div>
159
EOD;
160
}
161
	$htmltext .= <<<EOD
162
163
		<input name="redirurl" type="hidden" value="\$PORTAL_REDIRURL\$">
164
		<input type="submit" name="accept" class="login login-submit" value="Login" id="login" {$disabled}>
165
	  </form>
166
	  <br  />
167
	  <span> <i>Made with &hearts; by</i> <strong>Netgate</strong></span>
168
	</div>
169
	<div id="terms">
170
		<textarea readonly>{$termsconditions}</textarea>
171
	</div>
172
</div>
173 1e0b1727 Phil Davis
</body>
174 5b237745 Scott Ullrich
</html>
175
176 023aa1f2 Scott Ullrich
EOD;
177 0bd34ed6 Scott Ullrich
178 023aa1f2 Scott Ullrich
	return $htmltext;
179
}
180 0bd34ed6 Scott Ullrich
181 3a4b0147 Ermal
function captiveportal_load_modules() {
182 87e7fdea bcyrill
	global $config;
183 3a4b0147 Ermal
184
	mute_kernel_msgs();
185 87e7fdea bcyrill
	if (!is_module_loaded("ipfw.ko")) {
186
		mwexec("/sbin/kldload ipfw");
187
		/* make sure ipfw is not on pfil hooks */
188 971de1f9 Renato Botelho
		set_sysctl(array(
189 517b893e Renato Botelho
		    "net.inet.ip.pfil.inbound" => "pf",
190
		    "net.inet6.ip6.pfil.inbound" => "pf",
191
		    "net.inet.ip.pfil.outbound" => "pf",
192
		    "net.inet6.ip6.pfil.outbound" => "pf"
193
		));
194 87e7fdea bcyrill
	}
195 2657f21f Ermal
	/* Activate layer2 filtering */
196 517b893e Renato Botelho
	set_sysctl(array(
197
	    "net.link.ether.ipfw" => "1",
198
	    "net.inet.ip.fw.one_pass" => "1",
199
	    "net.inet.ip.fw.tables_max" => "65534"
200
	));
201 c06bdb94 Ermal
202 3a4b0147 Ermal
	/* Always load dummynet now that even allowed ip and mac passthrough use it. */
203
	if (!is_module_loaded("dummynet.ko")) {
204
		mwexec("/sbin/kldload dummynet");
205 517b893e Renato Botelho
		set_sysctl(array(
206
		    "net.inet.ip.dummynet.io_fast" => "1",
207
		    "net.inet.ip.dummynet.hash_size" => "256"
208
		));
209 3a4b0147 Ermal
	}
210
	unmute_kernel_msgs();
211
}
212
213 023aa1f2 Scott Ullrich
function captiveportal_configure() {
214 baec2b00 Ermal
	global $config, $cpzone, $cpzoneid;
215 023aa1f2 Scott Ullrich
216 b4792bf8 Ermal
	if (is_array($config['captiveportal'])) {
217
		foreach ($config['captiveportal'] as $cpkey => $cp) {
218
			$cpzone = $cpkey;
219 baec2b00 Ermal
			$cpzoneid = $cp['zoneid'];
220 b4792bf8 Ermal
			captiveportal_configure_zone($cp);
221
		}
222 4010266a Ermal
	}
223 b4792bf8 Ermal
}
224
225
function captiveportal_configure_zone($cpcfg) {
226 baec2b00 Ermal
	global $config, $g, $cpzone, $cpzoneid;
227 b4792bf8 Ermal
228
	$captiveportallck = lock("captiveportal{$cpzone}", LOCK_EX);
229 1e0b1727 Phil Davis
230 b4792bf8 Ermal
	if (isset($cpcfg['enable'])) {
231 023aa1f2 Scott Ullrich
232 285ef132 Ermal LUÇI
		if (platform_booting()) {
233 b4792bf8 Ermal
			echo "Starting captive portal({$cpcfg['zone']})... ";
234 1e0b1727 Phil Davis
		} else {
235 37e67d04 Ermal
			captiveportal_syslog("Reconfiguring captive portal({$cpcfg['zone']}).");
236 1e0b1727 Phil Davis
		}
237 023aa1f2 Scott Ullrich
238 eb43c5b1 Augustin FL
		/* (re)init ipfw rules. Cause all users to disconnect */
239 37e67d04 Ermal
		captiveportal_init_rules(true);
240 023aa1f2 Scott Ullrich
241
		/* kill any running minicron */
242 b4792bf8 Ermal
		killbypid("{$g['varrun_path']}/cp_prunedb_{$cpzone}.pid");
243
244 023aa1f2 Scott Ullrich
		/* initialize minicron interval value */
245 b4792bf8 Ermal
		$croninterval = $cpcfg['croninterval'] ? $cpcfg['croninterval'] : 60;
246 023aa1f2 Scott Ullrich
247
		/* double check if the $croninterval is numeric and at least 10 seconds. If not we set it to 60 to avoid problems */
248 1e0b1727 Phil Davis
		if ((!is_numeric($croninterval)) || ($croninterval < 10)) {
249 eb7aa263 Ermal
			$croninterval = 60;
250 1e0b1727 Phil Davis
		}
251 023aa1f2 Scott Ullrich
252
		/* write portal page */
253 1e0b1727 Phil Davis
		if (is_array($cpcfg['page']) && $cpcfg['page']['htmltext']) {
254 b4792bf8 Ermal
			$htmltext = base64_decode($cpcfg['page']['htmltext']);
255 1e0b1727 Phil Davis
		} else {
256 023aa1f2 Scott Ullrich
			/* example/template page */
257
			$htmltext = get_default_captive_portal_html();
258 5b237745 Scott Ullrich
		}
259
260 b4792bf8 Ermal
		$fd = @fopen("{$g['varetc_path']}/captiveportal_{$cpzone}.html", "w");
261 5b237745 Scott Ullrich
		if ($fd) {
262 7a7e94a7 Scott Ullrich
			// Special case handling.  Convert so that we can pass this page
263
			// through the PHP interpreter later without clobbering the vars.
264 b4792bf8 Ermal
			$htmltext = str_replace("\$PORTAL_ZONE\$", "#PORTAL_ZONE#", $htmltext);
265 7a7e94a7 Scott Ullrich
			$htmltext = str_replace("\$PORTAL_REDIRURL\$", "#PORTAL_REDIRURL#", $htmltext);
266
			$htmltext = str_replace("\$PORTAL_MESSAGE\$", "#PORTAL_MESSAGE#", $htmltext);
267
			$htmltext = str_replace("\$CLIENT_MAC\$", "#CLIENT_MAC#", $htmltext);
268
			$htmltext = str_replace("\$CLIENT_IP\$", "#CLIENT_IP#", $htmltext);
269
			$htmltext = str_replace("\$PORTAL_ACTION\$", "#PORTAL_ACTION#", $htmltext);
270 1e0b1727 Phil Davis
			if ($cpcfg['preauthurl']) {
271 b4792bf8 Ermal
				$htmltext = str_replace("\$PORTAL_REDIRURL\$", "{$cpcfg['preauthurl']}", $htmltext);
272
				$htmltext = str_replace("#PORTAL_REDIRURL#", "{$cpcfg['preauthurl']}", $htmltext);
273 c4e228f3 Scott Ullrich
			}
274 5b237745 Scott Ullrich
			fwrite($fd, $htmltext);
275 36254e4a Scott Ullrich
			fclose($fd);
276 5b237745 Scott Ullrich
		}
277 2e62a7c4 Ermal
		unset($htmltext);
278 36254e4a Scott Ullrich
279 5b237745 Scott Ullrich
		/* write error page */
280 1e0b1727 Phil Davis
		if (is_array($cpcfg['page']) && $cpcfg['page']['errtext']) {
281 b4792bf8 Ermal
			$errtext = base64_decode($cpcfg['page']['errtext']);
282 1e0b1727 Phil Davis
		} else {
283 a34b8b3b Ermal
			/* example page  */
284
			$errtext = get_default_captive_portal_html();
285 5b237745 Scott Ullrich
		}
286
287 b4792bf8 Ermal
		$fd = @fopen("{$g['varetc_path']}/captiveportal-{$cpzone}-error.html", "w");
288 5b237745 Scott Ullrich
		if ($fd) {
289 7a7e94a7 Scott Ullrich
			// Special case handling.  Convert so that we can pass this page
290
			// through the PHP interpreter later without clobbering the vars.
291 b4792bf8 Ermal
			$errtext = str_replace("\$PORTAL_ZONE\$", "#PORTAL_ZONE#", $errtext);
292 7a7e94a7 Scott Ullrich
			$errtext = str_replace("\$PORTAL_REDIRURL\$", "#PORTAL_REDIRURL#", $errtext);
293
			$errtext = str_replace("\$PORTAL_MESSAGE\$", "#PORTAL_MESSAGE#", $errtext);
294
			$errtext = str_replace("\$CLIENT_MAC\$", "#CLIENT_MAC#", $errtext);
295
			$errtext = str_replace("\$CLIENT_IP\$", "#CLIENT_IP#", $errtext);
296
			$errtext = str_replace("\$PORTAL_ACTION\$", "#PORTAL_ACTION#", $errtext);
297 1e0b1727 Phil Davis
			if ($cpcfg['preauthurl']) {
298 b4792bf8 Ermal
				$errtext = str_replace("\$PORTAL_REDIRURL\$", "{$cpcfg['preauthurl']}", $errtext);
299
				$errtext = str_replace("#PORTAL_REDIRURL#", "{$cpcfg['preauthurl']}", $errtext);
300 c4e228f3 Scott Ullrich
			}
301 5b237745 Scott Ullrich
			fwrite($fd, $errtext);
302 36254e4a Scott Ullrich
			fclose($fd);
303 5b237745 Scott Ullrich
		}
304 2e62a7c4 Ermal
		unset($errtext);
305 36254e4a Scott Ullrich
306 b4792bf8 Ermal
		/* write logout page */
307 1e0b1727 Phil Davis
		if (is_array($cpcfg['page']) && $cpcfg['page']['logouttext']) {
308 b4792bf8 Ermal
			$logouttext = base64_decode($cpcfg['page']['logouttext']);
309 1e0b1727 Phil Davis
		} else {
310 5b87b24e Ermal
			/* example page */
311 d18f3f6e Phil Davis
			$translated_text1 = gettext("Redirecting...");
312
			$translated_text2 = gettext("Redirecting to");
313
			$translated_text3 = gettext("Logout");
314
			$translated_text4 = gettext("Click the button below to disconnect");
315 5b87b24e Ermal
			$logouttext = <<<EOD
316 91f026b0 ayvis
<html>
317 d18f3f6e Phil Davis
<head><title>{$translated_text1}</title></head>
318 91f026b0 ayvis
<body>
319
<span style="font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">
320 d18f3f6e Phil Davis
<b>{$translated_text2} <a href="<?=\$my_redirurl;?>"><?=\$my_redirurl;?></a>...</b>
321 91f026b0 ayvis
</span>
322
<script type="text/javascript">
323 1b244d38 Colin Fleming
//<![CDATA[
324 5b87b24e Ermal
LogoutWin = window.open('', 'Logout', 'toolbar=0,scrollbars=0,location=0,statusbar=0,menubar=0,resizable=0,width=256,height=64');
325
if (LogoutWin) {
326 91f026b0 ayvis
	LogoutWin.document.write('<html>');
327 d18f3f6e Phil Davis
	LogoutWin.document.write('<head><title>{$translated_text3}</title></head>') ;
328 5f18e743 NOYB
	LogoutWin.document.write('<body style="background-color:#435370">');
329
	LogoutWin.document.write('<div class="text-center" style="color: #ffffff; font-family: Tahoma, Verdana, Arial, Helvetica, sans-serif; font-size: 11px;">') ;
330 d18f3f6e Phil Davis
	LogoutWin.document.write('<b>{$translated_text4}</b><p />');
331 91f026b0 ayvis
	LogoutWin.document.write('<form method="POST" action="<?=\$logouturl;?>">');
332
	LogoutWin.document.write('<input name="logout_id" type="hidden" value="<?=\$sessionid;?>" />');
333
	LogoutWin.document.write('<input name="zone" type="hidden" value="<?=\$cpzone;?>" />');
334 d18f3f6e Phil Davis
	LogoutWin.document.write('<input name="logout" type="submit" value="{$translated_text3}" />');
335 91f026b0 ayvis
	LogoutWin.document.write('</form>');
336
	LogoutWin.document.write('</div></body>');
337
	LogoutWin.document.write('</html>');
338 5060dea7 Scott Ullrich
	LogoutWin.document.close();
339 5b87b24e Ermal
}
340
341 6991e1a6 Erik Fonnesbeck
document.location.href="<?=\$my_redirurl;?>";
342 1b244d38 Colin Fleming
//]]>
343 91f026b0 ayvis
</script>
344
</body>
345
</html>
346 5b87b24e Ermal
347
EOD;
348
		}
349
350 b4792bf8 Ermal
		$fd = @fopen("{$g['varetc_path']}/captiveportal-{$cpzone}-logout.html", "w");
351 5b87b24e Ermal
		if ($fd) {
352
			fwrite($fd, $logouttext);
353
			fclose($fd);
354
		}
355 2e62a7c4 Ermal
		unset($logouttext);
356
357 0bd34ed6 Scott Ullrich
		/* write elements */
358
		captiveportal_write_elements();
359 5b237745 Scott Ullrich
360 57cc06af Chris Buechler
		/* kill any running CP nginx instances */
361 4877ae1e Chris Buechler
		killbypid("{$g['varrun_path']}/nginx-{$cpzone}-CaptivePortal.pid");
362
		killbypid("{$g['varrun_path']}/nginx-{$cpzone}-CaptivePortal-SSL.pid");
363 37e67d04 Ermal
364 769e254e Ermal
		/* start up the webserving daemon */
365 e3cf528e bcyrill
		captiveportal_init_webgui_zone($cpcfg);
366 36254e4a Scott Ullrich
367 aa69dbd2 Scott Ullrich
		/* Kill any existing prunecaptiveportal processes */
368 1e0b1727 Phil Davis
		if (file_exists("{$g['varrun_path']}/cp_prunedb_{$cpzone}.pid")) {
369 b4792bf8 Ermal
			killbypid("{$g['varrun_path']}/cp_prunedb_{$cpzone}.pid");
370 1e0b1727 Phil Davis
		}
371 aa69dbd2 Scott Ullrich
372 0bd34ed6 Scott Ullrich
		/* start pruning process (interval defaults to 60 seconds) */
373 b4792bf8 Ermal
		mwexec("/usr/local/bin/minicron $croninterval {$g['varrun_path']}/cp_prunedb_{$cpzone}.pid " .
374
			"/etc/rc.prunecaptiveportal {$cpzone}");
375 36254e4a Scott Ullrich
376 eb43c5b1 Augustin FL
		/* delete outdated radius server database if exist */
377 37e67d04 Ermal
		unlink_if_exists("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db");
378 36254e4a Scott Ullrich
379 285ef132 Ermal LUÇI
		if (platform_booting()) {
380 e35ab948 Michael Newton
			/* send Accounting-On to server */
381 e42ea151 Augustin FL
			captiveportal_send_server_accounting('on');
382 b4792bf8 Ermal
			echo "done\n";
383 e35ab948 Michael Newton
		}
384 36254e4a Scott Ullrich
385 5b237745 Scott Ullrich
	} else {
386 4877ae1e Chris Buechler
		killbypid("{$g['varrun_path']}/nginx-{$cpzone}-CaptivePortal.pid");
387
		killbypid("{$g['varrun_path']}/nginx-{$cpzone}-CaptivePortal-SSL.pid");
388 b4792bf8 Ermal
		killbypid("{$g['varrun_path']}/cp_prunedb_{$cpzone}.pid");
389
		@unlink("{$g['varetc_path']}/captiveportal_{$cpzone}.html");
390
		@unlink("{$g['varetc_path']}/captiveportal-{$cpzone}-error.html");
391
		@unlink("{$g['varetc_path']}/captiveportal-{$cpzone}-logout.html");
392 f8cb8685 Ermal
393 3ece6d54 plumbeo
		captiveportal_radius_stop_all(10); // NAS-Request
394 f8cb8685 Ermal
395 99a537e1 Renato Botelho
		captiveportal_filterdns_configure();
396
397 8b34498c Ermal
		/* remove old information */
398 26ee5aaf Ermal
		unlink_if_exists("{$g['vardb_path']}/captiveportal{$cpzone}.db");
399 8b34498c Ermal
		unlink_if_exists("{$g['vardb_path']}/captiveportal_radius_{$cpzone}.db");
400 0f50d70d Ermal
		unlink_if_exists("{$g['vardb_path']}/captiveportal_{$cpzone}.rules");
401 7fb23399 Ermal
		/* Release allocated pipes for this zone */
402 b2c92623 Renato Botelho
		$pipes_to_remove = captiveportal_free_dnrules();
403 12ee8fe4 Scott Ullrich
404 b2c92623 Renato Botelho
		captiveportal_delete_rules($pipes_to_remove);
405 b4792bf8 Ermal
406 1e0b1727 Phil Davis
		if (empty($config['captiveportal'])) {
407 971de1f9 Renato Botelho
			set_single_sysctl("net.link.ether.ipfw", "0");
408 1e0b1727 Phil Davis
		} else {
409 c06bdb94 Ermal
			/* Deactivate ipfw(4) if not needed */
410
			$cpactive = false;
411 52034432 Renato Botelho
			if (is_array($config['captiveportal'])) {
412
				foreach ($config['captiveportal'] as $cpkey => $cp) {
413
					if (isset($cp['enable'])) {
414
						$cpactive = true;
415
						break;
416
					}
417 c06bdb94 Ermal
				}
418
			}
419 1e0b1727 Phil Davis
			if ($cpactive === false) {
420 971de1f9 Renato Botelho
				set_single_sysctl("net.link.ether.ipfw", "0");
421 1e0b1727 Phil Davis
			}
422 c06bdb94 Ermal
		}
423 3db19cf1 Scott Ullrich
	}
424 36254e4a Scott Ullrich
425 f8b11310 Ermal Lu?i
	unlock($captiveportallck);
426 1e0b1727 Phil Davis
427 5b237745 Scott Ullrich
	return 0;
428
}
429
430 769e254e Ermal
function captiveportal_init_webgui() {
431 b4792bf8 Ermal
	global $config, $cpzone;
432 769e254e Ermal
433 b4792bf8 Ermal
	if (is_array($config['captiveportal'])) {
434 e3cf528e bcyrill
		foreach ($config['captiveportal'] as $cpkey => $cp) {
435 b4792bf8 Ermal
			$cpzone = $cpkey;
436 e3cf528e bcyrill
			captiveportal_init_webgui_zone($cp);
437 b4792bf8 Ermal
		}
438
	}
439
}
440 769e254e Ermal
441 e3cf528e bcyrill
function captiveportal_init_webgui_zonename($zone) {
442
	global $config, $cpzone;
443 1e0b1727 Phil Davis
444 e3cf528e bcyrill
	if (isset($config['captiveportal'][$zone])) {
445
		$cpzone = $zone;
446
		captiveportal_init_webgui_zone($config['captiveportal'][$zone]);
447
	}
448
}
449
450
function captiveportal_init_webgui_zone($cpcfg) {
451 b4792bf8 Ermal
	global $g, $config, $cpzone;
452
453 1e0b1727 Phil Davis
	if (!isset($cpcfg['enable'])) {
454 b4792bf8 Ermal
		return;
455 1e0b1727 Phil Davis
	}
456 b4792bf8 Ermal
457
	if (isset($cpcfg['httpslogin'])) {
458 36f6ed35 bcyrill
		$cert = lookup_cert($cpcfg['certref']);
459 adca02c4 bcyrill
		$crt = base64_decode($cert['crt']);
460
		$key = base64_decode($cert['prv']);
461
		$ca = ca_chain($cert);
462 87e7fdea bcyrill
463 c4a278f2 Chris Buechler
		/* generate nginx configuration */
464 1e0b1727 Phil Davis
		if (!empty($cpcfg['listenporthttps'])) {
465 1c69dbb0 Ermal
			$listenporthttps = $cpcfg['listenporthttps'];
466 1e0b1727 Phil Davis
		} else {
467 1c69dbb0 Ermal
			$listenporthttps = 8001 + $cpcfg['zoneid'];
468 1e0b1727 Phil Davis
		}
469 c4a278f2 Chris Buechler
		system_generate_nginx_config("{$g['varetc_path']}/nginx-{$cpzone}-CaptivePortal-SSL.conf",
470
			$crt, $key, $ca, "nginx-{$cpzone}-CaptivePortal-SSL.pid", $listenporthttps, "/usr/local/captiveportal",
471 3ed3a367 doktornotor
			"cert-{$cpzone}-portal.pem", "ca-{$cpzone}-portal.pem", $cpzone, false);
472 769e254e Ermal
	}
473
474 c4a278f2 Chris Buechler
	/* generate nginx configuration */
475 1e0b1727 Phil Davis
	if (!empty($cpcfg['listenporthttp'])) {
476 1c69dbb0 Ermal
		$listenporthttp = $cpcfg['listenporthttp'];
477 1e0b1727 Phil Davis
	} else {
478 1c69dbb0 Ermal
		$listenporthttp = 8000 + $cpcfg['zoneid'];
479 1e0b1727 Phil Davis
	}
480 c4a278f2 Chris Buechler
	system_generate_nginx_config("{$g['varetc_path']}/nginx-{$cpzone}-CaptivePortal.conf",
481
		"", "", "", "nginx-{$cpzone}-CaptivePortal.pid", $listenporthttp, "/usr/local/captiveportal",
482 3ed3a367 doktornotor
		"", "", $cpzone, false);
483 769e254e Ermal
484 c4a278f2 Chris Buechler
	@unlink("{$g['varrun']}/nginx-{$cpzone}-CaptivePortal.pid");
485
	/* attempt to start nginx */
486 4877ae1e Chris Buechler
	$res = mwexec("/usr/local/sbin/nginx -c {$g['varetc_path']}/nginx-{$cpzone}-CaptivePortal.conf");
487 769e254e Ermal
488
	/* fire up https instance */
489 fe2eb995 Ermal
	if (isset($cpcfg['httpslogin'])) {
490 c4a278f2 Chris Buechler
		@unlink("{$g['varrun']}/nginx-{$cpzone}-CaptivePortal-SSL.pid");
491
		$res = mwexec("/usr/local/sbin/nginx -c {$g['varetc_path']}/nginx-{$cpzone}-CaptivePortal-SSL.conf");
492 fe2eb995 Ermal
	}
493 769e254e Ermal
}
494
495 a08db603 Ermal LUÇI
function captiveportal_init_rules_byinterface($interface) {
496
	global $cpzone, $cpzoneid, $config;
497
498 1e0b1727 Phil Davis
	if (!is_array($config['captiveportal'])) {
499 a08db603 Ermal LUÇI
		return;
500 1e0b1727 Phil Davis
	}
501 a08db603 Ermal LUÇI
502
	foreach ($config['captiveportal'] as $cpkey => $cp) {
503
		$cpzone = $cpkey;
504
		$cpzoneid = $cp['zoneid'];
505
		$cpinterfaces = explode(",", $cp['interface']);
506
		if (in_array($interface, $cpinterfaces)) {
507
			captiveportal_init_rules();
508
			break;
509
		}
510
	}
511
}
512
513 517b893e Renato Botelho
/* Create basic rules used by all zones */
514
function captiveportal_init_general_rules($flush = false) {
515
	global $g;
516
517
	$flush_rule = '';
518
	if ($flush) {
519
		$flush_rule = 'flush';
520
	}
521
522
	/* Already loaded */
523
	if (!$flush && (mwexec("/sbin/ipfw list 1000", true) == 0)) {
524
		return;
525
	}
526
527
	$cprules = <<<EOD
528
{$flush_rule}
529
# Table with interfaces that have CP enabled
530
table cp_ifaces create type iface valtype skipto
531
532
# Redirect each CP interface to its specific rule
533 9945720f Luiz Otavio O Souza
add 1000 skipto tablearg all from any to any via table(cp_ifaces)
534 517b893e Renato Botelho
535
# This interface has no cp zone configured
536
add 1100 allow all from any to any
537
538
# block everything else
539
add 65534 deny all from any to any
540
EOD;
541
542
	/* load rules */
543
	file_put_contents("{$g['tmp_path']}/ipfw.cp.rules", $cprules);
544
	mwexec("/sbin/ipfw -q {$g['tmp_path']}/ipfw.cp.rules", true);
545
	@unlink("{$g['tmp_path']}/ipfw_{$cpzone}.cp.rules");
546
	unset($cprules);
547
}
548
549
/* Create a string with ipfw rule and increase rulenum */
550
function captiveportal_create_ipfw_rule($cmd, &$rulenum, $args) {
551
	$rule = "{$cmd} {$rulenum} {$args}\n";
552
	$rulenum++;
553
554
	return $rule;
555
}
556
557
/* Return first rule number for a cp zone */
558
function captiveportal_ipfw_ruleno($id) {
559
	global $g;
560
561
	return 2000 + $id * $g['captiveportal_rules_interval'];
562
}
563
564 847e5e82 Scott Ullrich
/* reinit will disconnect all users, be careful! */
565 1d9e9cca Ermal
function captiveportal_init_rules($reinit = false) {
566 baec2b00 Ermal
	global $config, $g, $cpzone, $cpzoneid;
567 36254e4a Scott Ullrich
568 1e0b1727 Phil Davis
	if (!isset($config['captiveportal'][$cpzone]['enable'])) {
569 769e254e Ermal
		return;
570 1e0b1727 Phil Davis
	}
571 769e254e Ermal
572 3a4b0147 Ermal
	captiveportal_load_modules();
573 517b893e Renato Botelho
	captiveportal_init_general_rules();
574 f084049d Ermal LUÇI
575
	/* Cleanup so nothing is leaked */
576 fd9e6066 Ermal LUÇI
	captiveportal_free_dnrules();
577 f084049d Ermal LUÇI
	unlink_if_exists("{$g['vardb_path']}/captiveportal_{$cpzone}.rules");
578 3a4b0147 Ermal
579 517b893e Renato Botelho
	$skipto = captiveportal_ipfw_ruleno($cpzoneid);
580
581
	$cprules = '';
582
583 769e254e Ermal
	$cpips = array();
584
	$ifaces = get_configured_interface_list();
585 b4792bf8 Ermal
	$cpinterfaces = explode(",", $config['captiveportal'][$cpzone]['interface']);
586 769e254e Ermal
	$firsttime = 0;
587
	foreach ($cpinterfaces as $cpifgrp) {
588 1e0b1727 Phil Davis
		if (!isset($ifaces[$cpifgrp])) {
589 769e254e Ermal
			continue;
590 1e0b1727 Phil Davis
		}
591 769e254e Ermal
		$tmpif = get_real_interface($cpifgrp);
592 517b893e Renato Botelho
		if (empty($tmpif)) {
593
			continue;
594
		}
595
596
		$cpipm = get_interface_ip($cpifgrp);
597
598
		if (!is_ipaddr($cpipm)) {
599
			continue;
600
		}
601
602
		$cpips[] = $cpipm;
603
		if (is_array($config['virtualip']['vip'])) {
604
			foreach ($config['virtualip']['vip'] as $vip) {
605
				if (($vip['interface'] == $cpifgrp) &&
606
				    (($vip['mode'] == "carp") ||
607
				    ($vip['mode'] == "ipalias"))) {
608
					$cpips[] = $vip['subnet'];
609 769e254e Ermal
				}
610
			}
611
		}
612 517b893e Renato Botelho
613
		$cprules .= "table cp_ifaces add {$tmpif} {$skipto}\n";
614 769e254e Ermal
	}
615
	if (count($cpips) > 0) {
616
		$cpactive = true;
617 1e0b1727 Phil Davis
	} else {
618 769e254e Ermal
		return false;
619 1e0b1727 Phil Davis
	}
620 769e254e Ermal
621 1e0b1727 Phil Davis
	if ($reinit == false) {
622 b4792bf8 Ermal
		$captiveportallck = lock("captiveportal{$cpzone}");
623 1e0b1727 Phil Davis
	}
624 eade409a Ermal
625 517b893e Renato Botelho
	$rulenum = $skipto;
626
	$cprules .= "table {$cpzone}_pipe_mac create type mac valtype pipe\n";
627
	$cprules .= captiveportal_create_ipfw_rule("add", $rulenum,
628
	    "pipe tablearg MAC table({$cpzone}_pipe_mac)");
629
	$cprules .= captiveportal_create_ipfw_rule("add", $rulenum,
630
	    "allow pfsync from any to any");
631
	$cprules .= captiveportal_create_ipfw_rule("add", $rulenum,
632
	    "allow carp from any to any\n");
633
	$cprules .= "# layer 2: pass ARP\n";
634
	$cprules .= captiveportal_create_ipfw_rule("add", $rulenum,
635
	    "pass layer2 mac-type arp,rarp");
636
	$cprules .= "# pfsense requires for WPA\n";
637
	$cprules .= captiveportal_create_ipfw_rule("add", $rulenum,
638
	    "pass layer2 mac-type 0x888e,0x88c7");
639
	$cprules .= "# PPP Over Ethernet Session Stage/Discovery Stage\n";
640
	$cprules .= captiveportal_create_ipfw_rule("add", $rulenum,
641
	    "pass layer2 mac-type 0x8863,0x8864\n");
642
	$cprules .= "# layer 2: block anything else non-IP(v4/v6)\n";
643
	$cprules .= captiveportal_create_ipfw_rule("add", $rulenum,
644
	    "deny layer2 not mac-type ip,ipv6");
645 98bf4991 Ermal LUÇI
646
	/* These tables contain host ips */
647 aab966f2 Renato Botelho
	$cprules .= "table {$cpzone}_host_ips create type addr\n";
648 517b893e Renato Botelho
	$cprules .= captiveportal_create_ipfw_rule("add", $rulenum,
649
	    "pass ip from any to table({$cpzone}_host_ips) in");
650
	$cprules .= captiveportal_create_ipfw_rule("add", $rulenum,
651
	    "pass ip from table({$cpzone}_host_ips) to any out");
652 4e322e2c Phil Davis
	foreach ($cpips as $cpip) {
653 517b893e Renato Botelho
		$cprules .= "table {$cpzone}_host_ips add {$cpip}\n";
654 4e322e2c Phil Davis
	}
655 517b893e Renato Botelho
	$cprules .= captiveportal_create_ipfw_rule("add", $rulenum,
656
	    "pass ip from any to 255.255.255.255 in");
657
	$cprules .= captiveportal_create_ipfw_rule("add", $rulenum,
658
	    "pass ip from 255.255.255.255 to any out");
659 42d59a5d Luiz Otavio O Souza
660 b01792a0 Ermal
	/* Allowed ips */
661 517b893e Renato Botelho
	$cprules .= "table {$cpzone}_allowed_up create type addr valtype pipe\n";
662
	$cprules .= "table {$cpzone}_allowed_down create type addr valtype pipe\n";
663
	$cprules .= captiveportal_create_ipfw_rule("add", $rulenum,
664
	    "pipe tablearg ip from table({$cpzone}_allowed_up) to any in");
665
	$cprules .= captiveportal_create_ipfw_rule("add", $rulenum,
666
	    "pipe tablearg ip from any to table({$cpzone}_allowed_down) in");
667
	$cprules .= captiveportal_create_ipfw_rule("add", $rulenum,
668
	    "pipe tablearg ip from table({$cpzone}_allowed_up) to any out");
669
	$cprules .= captiveportal_create_ipfw_rule("add", $rulenum,
670
	    "pipe tablearg ip from any to table({$cpzone}_allowed_down) out");
671 b01792a0 Ermal
672
	/* Authenticated users rules. */
673 517b893e Renato Botelho
	$cprules .= "table {$cpzone}_auth_up create type addr valtype pipe\n";
674
	$cprules .= "table {$cpzone}_auth_down create type addr valtype pipe\n";
675
	$cprules .= captiveportal_create_ipfw_rule("add", $rulenum,
676 e14c441b Renato Botelho
	    "pipe tablearg ip from table({$cpzone}_auth_up) to any layer2 in");
677 517b893e Renato Botelho
	$cprules .= captiveportal_create_ipfw_rule("add", $rulenum,
678 e14c441b Renato Botelho
	    "pipe tablearg ip from any to table({$cpzone}_auth_down) layer2 out");
679 bb58ed63 Ermal
680 1e0b1727 Phil Davis
	if (!empty($config['captiveportal'][$cpzone]['listenporthttp'])) {
681 391cad9f Renato Botelho
		$listenporthttp = $config['captiveportal'][$cpzone]['listenporthttp'];
682 1e0b1727 Phil Davis
	} else {
683 36de334e Renato Botelho
		$listenporthttp = 8000 + $cpzoneid;
684 1e0b1727 Phil Davis
	}
685 06a45374 Ermal
686 d61cbd50 bcyrill
	if (isset($config['captiveportal'][$cpzone]['httpslogin'])) {
687 1e0b1727 Phil Davis
		if (!empty($config['captiveportal'][$cpzone]['listenporthttps'])) {
688 1c69dbb0 Ermal
			$listenporthttps = $config['captiveportal'][$cpzone]['listenporthttps'];
689 1e0b1727 Phil Davis
		} else {
690 36de334e Renato Botelho
			$listenporthttps = 8001 + $cpzoneid;
691 1e0b1727 Phil Davis
		}
692
		if (!isset($config['captiveportal'][$cpzone]['nohttpsforwards'])) {
693 517b893e Renato Botelho
			$cprules .= captiveportal_create_ipfw_rule("add", $rulenum,
694
			    "fwd 127.0.0.1,{$listenporthttps} tcp from any to any dst-port 443 in");
695 1e0b1727 Phil Davis
		}
696 06a45374 Ermal
	}
697 1e0b1727 Phil Davis
698 517b893e Renato Botelho
	$cprules .= "# redirect non-authenticated clients to captive portal\n";
699
	$cprules .= captiveportal_create_ipfw_rule("add", $rulenum,
700
	    "fwd 127.0.0.1,{$listenporthttp} tcp from any to any dst-port 80 in");
701
	$cprules .= "# let the responses from the captive portal web server back out\n";
702
	$cprules .= captiveportal_create_ipfw_rule("add", $rulenum,
703
	    "pass tcp from any to any out");
704
	$cprules .= "# This CP zone is over, skip to last rule\n";
705
	$cprules .= captiveportal_create_ipfw_rule("add", $rulenum,
706
	    "skipto 65534 all from any to any");
707 3db19cf1 Scott Ullrich
708 769e254e Ermal
	/* generate passthru mac database */
709
	$cprules .= captiveportal_passthrumac_configure(true);
710
	$cprules .= "\n";
711 55c18b30 Scott Ullrich
712 769e254e Ermal
	/* allowed ipfw rules to make allowed ip work */
713
	$cprules .= captiveportal_allowedip_configure();
714
715 55c18b30 Scott Ullrich
	/* allowed ipfw rules to make allowed hostnames work */
716
	$cprules .= captiveportal_allowedhostname_configure();
717 1e0b1727 Phil Davis
718 769e254e Ermal
	/* load rules */
719 517b893e Renato Botelho
	captiveportal_delete_rules();
720 b4792bf8 Ermal
	file_put_contents("{$g['tmp_path']}/ipfw_{$cpzone}.cp.rules", $cprules);
721 517b893e Renato Botelho
	mwexec("/sbin/ipfw -q {$g['tmp_path']}/ipfw_{$cpzone}.cp.rules", true);
722
	@unlink("{$g['tmp_path']}/ipfw_{$cpzone}.cp.rules");
723 98bf4991 Ermal LUÇI
	unset($cprules);
724 eade409a Ermal
725 517b893e Renato Botelho
	captiveportal_filterdns_configure();
726
727 1e0b1727 Phil Davis
	if ($reinit == false) {
728 eade409a Ermal
		unlock($captiveportallck);
729 1e0b1727 Phil Davis
	}
730 3db19cf1 Scott Ullrich
}
731
732 517b893e Renato Botelho
/* Delete all rules related to specific cpzone */
733 b2c92623 Renato Botelho
function captiveportal_delete_rules($pipes_to_remove = array()) {
734 fbfbc6bd Renato Botelho
	global $g, $cpzoneid, $cpzone;
735 517b893e Renato Botelho
736
	$skipto1 = captiveportal_ipfw_ruleno($cpzoneid);
737
	$skipto2 = $skipto1 + $g['captiveportal_rules_interval'];
738
739
	$cp_ifaces = pfSense_ipfw_table_list("cp_ifaces");
740
	if (is_array($cp_ifaces)) {
741
		foreach ($cp_ifaces as $cp_iface) {
742
			if (empty($cp_iface['skipto']) ||
743
			    $cp_iface['skipto'] != $skipto1) {
744
				continue;
745
			}
746
747
			pfSense_ipfw_table("cp_ifaces", IP_FW_TABLE_XDEL,
748
			    $cp_iface['iface']);
749
		}
750
	}
751
752
	mwexec("/sbin/ipfw delete {$skipto1}-{$skipto2}", true);
753
754
	$tables = captiveportal_get_ipfw_table_names();
755
756
	$delrules = "";
757
	foreach ($tables as $table) {
758
		$delrules .= "table {$table} destroy\n";
759
	}
760
761 b2c92623 Renato Botelho
	foreach ($pipes_to_remove as $pipeno) {
762
		$delrules .= "pipe delete {$pipeno}\n";
763
	}
764
765 517b893e Renato Botelho
	if (empty($delrules)) {
766
		return;
767
	}
768
769
	file_put_contents("{$g['tmp_path']}/ipfw_{$cpzone}.deltable.rules", $delrules);
770
	mwexec("/sbin/ipfw -q {$g['tmp_path']}/ipfw_{$cpzone}.deltable.rules", true);
771 5c7fead1 Renato Botelho
	@unlink("{$g['tmp_path']}/ipfw_{$cpzone}.deltable.rules");
772 517b893e Renato Botelho
}
773
774 1e0b1727 Phil Davis
/*
775 f1f58a6f Ermal
 * Remove clients that have been around for longer than the specified amount of time
776 eb7aa263 Ermal
 * db file structure:
777 eb43c5b1 Augustin FL
 * timestamp,ipfw_rule_no,clientip,clientmac,username,sessionid,password,session_timeout,idle_timeout,session_terminate_time,interim_interval,traffic_quota,auth_method,context
778 eb7aa263 Ermal
 * (password is in Base64 and only saved when reauthentication is enabled)
779
 */
780 5b237745 Scott Ullrich
function captiveportal_prune_old() {
781 baec2b00 Ermal
	global $g, $config, $cpzone, $cpzoneid;
782 b4792bf8 Ermal
783 1e0b1727 Phil Davis
	if (empty($cpzone)) {
784 b4792bf8 Ermal
		return;
785 1e0b1727 Phil Davis
	}
786 23c4f978 Scott Ullrich
787 f1f58a6f Ermal
	$cpcfg = $config['captiveportal'][$cpzone];
788
	$vcpcfg = $config['voucher'][$cpzone];
789
790 5060dea7 Scott Ullrich
	/* check for expired entries */
791 f1f58a6f Ermal
	$idletimeout = 0;
792
	$timeout = 0;
793 1e0b1727 Phil Davis
	if (!empty($cpcfg['timeout']) && is_numeric($cpcfg['timeout'])) {
794 f1f58a6f Ermal
		$timeout = $cpcfg['timeout'] * 60;
795 1e0b1727 Phil Davis
	}
796 f1f58a6f Ermal
797 1e0b1727 Phil Davis
	if (!empty($cpcfg['idletimeout']) && is_numeric($cpcfg['idletimeout'])) {
798 f1f58a6f Ermal
		$idletimeout = $cpcfg['idletimeout'] * 60;
799 1e0b1727 Phil Davis
	}
800 f1f58a6f Ermal
801 acbd943d plumbeo
	/* check for entries exceeding their traffic quota */
802
	$trafficquota = 0;
803
	if (!empty($cpcfg['trafficquota']) && is_numeric($cpcfg['trafficquota'])) {
804
		$trafficquota = $cpcfg['trafficquota'] * 1048576;
805
	}
806
807 f1f58a6f Ermal
	/* Is there any job to do? */
808 acbd943d plumbeo
	if (!$timeout && !$idletimeout && !$trafficquota && !isset($cpcfg['reauthenticate']) &&
809 f3e403d5 plumbeo
	    !isset($cpcfg['radiussession_timeout']) && !isset($cpcfg['radiustraffic_quota']) &&
810 e42ea151 Augustin FL
	    !isset($vcpcfg['enable']) && !isset($cpcfg['radacct_enable'])) {
811 5060dea7 Scott Ullrich
		return;
812 1e0b1727 Phil Davis
	}
813 5060dea7 Scott Ullrich
814 006802ab Ermal
815 26ee5aaf Ermal
	/* Read database */
816
	/* NOTE: while this can be simplified in non radius case keep as is for now */
817 5060dea7 Scott Ullrich
	$cpdb = captiveportal_read_db();
818 0bd34ed6 Scott Ullrich
819 5060dea7 Scott Ullrich
	$unsetindexes = array();
820 5ebe85e9 Ermal
	$voucher_needs_sync = false;
821 1e0b1727 Phil Davis
	/*
822 b09c2d86 Ermal
	 * Snapshot the time here to use for calculation to speed up the process.
823
	 * If something is missed next run will catch it!
824
	 */
825
	$pruning_time = time();
826 3e5c0ab7 Ermal
	foreach ($cpdb as $cpentry) {
827 b5ccfb0b Renato Botelho
		$stop_time = $pruning_time;
828 5060dea7 Scott Ullrich
829
		$timedout = false;
830
		$term_cause = 1;
831 643315be plumbeo
		/* hard timeout or session_timeout from radius if enabled */
832
		if (isset($cpcfg['radiussession_timeout'])) {
833
			$timeout = (is_numeric($cpentry[7])) ? $cpentry[7] : $timeout;
834
		}
835 5060dea7 Scott Ullrich
		if ($timeout) {
836 5705c60a Renato Botelho
			if (($pruning_time - $cpentry[0]) >= $timeout) {
837 5060dea7 Scott Ullrich
				$timedout = true;
838 5705c60a Renato Botelho
				$term_cause = 5; // Session-Timeout
839 e4c34f17 plumbeo
				$logout_cause = 'SESSION TIMEOUT';
840 5060dea7 Scott Ullrich
			}
841 eb7aa263 Ermal
		}
842 23c4f978 Scott Ullrich
843 5060dea7 Scott Ullrich
		/* Session-Terminate-Time */
844 5705c60a Renato Botelho
		if (!$timedout && !empty($cpentry[9])) {
845
			if ($pruning_time >= $cpentry[9]) {
846 5060dea7 Scott Ullrich
				$timedout = true;
847 5705c60a Renato Botelho
				$term_cause = 5; // Session-Timeout
848 e4c34f17 plumbeo
				$logout_cause = 'SESSION TIMEOUT';
849 5060dea7 Scott Ullrich
			}
850
		}
851
852 eb43c5b1 Augustin FL
		/* check if an idle_timeout has been set and if its set change the idletimeout to this value */
853 5705c60a Renato Botelho
		$uidletimeout = (is_numeric($cpentry[8])) ? $cpentry[8] : $idletimeout;
854 5060dea7 Scott Ullrich
		/* if an idle timeout is specified, get last activity timestamp from ipfw */
855 1a1967d6 Ermal
		if (!$timedout && $uidletimeout > 0) {
856 f4c867e0 Renato Botelho
			$lastact = captiveportal_get_last_activity($cpentry[2]);
857 5060dea7 Scott Ullrich
			/*	If the user has logged on but not sent any traffic they will never be logged out.
858 1e0b1727 Phil Davis
			 *	We "fix" this by setting lastact to the login timestamp.
859 f56a73f1 Scott Ullrich
			 */
860 5705c60a Renato Botelho
			$lastact = $lastact ? $lastact : $cpentry[0];
861 b09c2d86 Ermal
			if ($lastact && (($pruning_time - $lastact) >= $uidletimeout)) {
862 5060dea7 Scott Ullrich
				$timedout = true;
863 5705c60a Renato Botelho
				$term_cause = 4; // Idle-Timeout
864 e4c34f17 plumbeo
				$logout_cause = 'IDLE TIMEOUT';
865 1878e1c9 plumbeo
				if (!isset($config['captiveportal'][$cpzone]['includeidletime'])) {
866
					$stop_time = $lastact;
867
				}
868 5060dea7 Scott Ullrich
			}
869 336e3c1c Charlie
		}
870
871 5060dea7 Scott Ullrich
		/* if vouchers are configured, activate session timeouts */
872 5705c60a Renato Botelho
		if (!$timedout && isset($vcpcfg['enable']) && !empty($cpentry[7])) {
873
			if ($pruning_time >= ($cpentry[0] + $cpentry[7])) {
874 5060dea7 Scott Ullrich
				$timedout = true;
875 5705c60a Renato Botelho
				$term_cause = 5; // Session-Timeout
876 e4c34f17 plumbeo
				$logout_cause = 'SESSION TIMEOUT';
877 5ebe85e9 Ermal
				$voucher_needs_sync = true;
878 5060dea7 Scott Ullrich
			}
879
		}
880
881 f3e403d5 plumbeo
		/* traffic quota, value retrieved from the radius attribute if the option is enabled */
882
		if (isset($cpcfg['radiustraffic_quota'])) {
883
			$trafficquota = (is_numeric($cpentry[11])) ? $cpentry[11] : $trafficquota;
884
		}
885 acbd943d plumbeo
		if (!$timedout && $trafficquota > 0) {
886
			$volume = getVolume($cpentry[2], $cpentry[3]);
887
			if (($volume['input_bytes'] + $volume['output_bytes']) > $trafficquota) {
888
				$timedout = true;
889
				$term_cause = 10; // NAS-Request
890 e4c34f17 plumbeo
				$logout_cause = 'QUOTA EXCEEDED';
891 acbd943d plumbeo
			}
892
		}
893
894 5060dea7 Scott Ullrich
		if ($timedout) {
895 eb43c5b1 Augustin FL
			captiveportal_disconnect($cpentry, $term_cause, $stop_time);
896 e4c34f17 plumbeo
			captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], $logout_cause);
897 5705c60a Renato Botelho
			$unsetindexes[] = $cpentry[5];
898 5060dea7 Scott Ullrich
		}
899
900 e42ea151 Augustin FL
		/* do periodic reauthentication? For Radius servers, send accounting updates? */
901 eb43c5b1 Augustin FL
		if (!$timedout) {
902 e42ea151 Augustin FL
			//Radius servers : send accounting
903
			if (isset($cpcfg['radacct_enable']) && $cpentry[12] === 'radius') {
904
				if (substr($cpcfg['reauthenticateacct'], 0, 9) === "stopstart") {
905 5060dea7 Scott Ullrich
					/* stop and restart accounting */
906 e42ea151 Augustin FL
					if ($cpcfg['reauthenticateacct'] === "stopstartfreeradius") {
907 ab225849 jim-p
						$rastart_time = 0;
908
						$rastop_time = 60;
909
					} else {
910
						$rastart_time = $cpentry[0];
911 e42ea151 Augustin FL
						$rastop_time = time();
912 ab225849 jim-p
					}
913 e42ea151 Augustin FL
					captiveportal_send_server_accounting('stop',
914
						$cpentry[1], // ruleno
915 5705c60a Renato Botelho
						$cpentry[4], // username
916
						$cpentry[2], // clientip
917
						$cpentry[3], // clientmac
918 e42ea151 Augustin FL
						$cpentry[5], // sessionid
919
						$rastart_time, // start time
920
						$rastop_time, // Stop Time
921
						10); // NAS Request
922 0cd7c91a Renato Botelho
					$clientsn = (is_ipaddrv6($cpentry[2])) ? 128 : 32;
923 f6e6ff31 Renato Botelho
					pfSense_ipfw_table_zerocnt("{$cpzone}_auth_up", "{$cpentry[2]}/{$clientsn}");
924
					pfSense_ipfw_table_zerocnt("{$cpzone}_auth_down", "{$cpentry[2]}/{$clientsn}");
925 ab225849 jim-p
					if ($cpcfg['reauthenticateacct'] == "stopstartfreeradius") {
926
						/* Need to pause here or the FreeRADIUS server gets confused about packet ordering. */
927
						sleep(1);
928
					}
929 e42ea151 Augustin FL
					captiveportal_send_server_accounting('start',
930
						$cpentry[1], // ruleno
931 5705c60a Renato Botelho
						$cpentry[4], // username
932
						$cpentry[2], // clientip
933 e42ea151 Augustin FL
						$cpentry[3], // clientmac
934
						$cpentry[5]); // sessionid
935 f1f58a6f Ermal
				} else if ($cpcfg['reauthenticateacct'] == "interimupdate") {
936 5705c60a Renato Botelho
					$session_time = $pruning_time - $cpentry[0];
937 1e0b1727 Phil Davis
					if (!empty($cpentry[10]) && $cpentry[10] > 60) {
938 5705c60a Renato Botelho
						$interval = $cpentry[10];
939 1e0b1727 Phil Davis
					} else {
940 338c0941 Ermal
						$interval = 0;
941 1e0b1727 Phil Davis
					}
942 338c0941 Ermal
					$past_interval_min = ($session_time > $interval);
943 1e0b1727 Phil Davis
					if ($interval != 0) {
944 40a8f669 Renato Botelho
						$within_interval = ($session_time % $interval >= 0 && $session_time % $interval <= 59);
945 1e0b1727 Phil Davis
					}
946 40a8f669 Renato Botelho
					if ($interval === 0 || ($interval > 0 && $past_interval_min && $within_interval)) {
947 e42ea151 Augustin FL
					captiveportal_send_server_accounting('update',
948
						$cpentry[1], // ruleno
949
						$cpentry[4], // username
950
						$cpentry[2], // clientip
951
						$cpentry[3], // clientmac
952
						$cpentry[5], // sessionid
953
						$cpentry[0]); // start time
954 338c0941 Ermal
					}
955 5060dea7 Scott Ullrich
				}
956
			}
957 23c4f978 Scott Ullrich
958 eb43c5b1 Augustin FL
			/* check this user again */
959
			if (isset($cpcfg['reauthenticate']) && $cpentry[13] !== 'voucher') {
960
				$auth_result = captiveportal_authenticate_user(
961
					$cpentry[4], // username
962 5705c60a Renato Botelho
					base64_decode($cpentry[6]), // password
963
					$cpentry[3], // clientmac
964 748372bc Stephen Jones
					$cpentry[2], // clientip
965 eb43c5b1 Augustin FL
					$cpentry[1], // ruleno
966
					$cpentry[13]); // context
967
				if ($auth_result['result'] === false) {
968
					captiveportal_disconnect($cpentry, 17);
969
					captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "DISCONNECT - REAUTHENTICATION FAILED", $auth_list['reply_message']);
970 5705c60a Renato Botelho
					$unsetindexes[] = $cpentry[5];
971 eb43c5b1 Augustin FL
				} else if ($auth_result['result'] === true) {
972
					if ($cpentry[12] !== $auth_result['auth_method']) {
973
						// if the user got authenticated against another server type:  we update the database
974
						if (!empty($cpentry[5])) {
975
							captiveportal_write_db("UPDATE captiveportal SET authmethod = '{$auth_result['auth_method']}' WHERE sessionid = '{$cpentry[5]}'");
976
							captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CHANGED AUTHENTICATION SERVER", $auth_list['reply_message']);
977
						}
978
						// User was logged on a RADIUS server, but is now logged in by another server type : we send an accounting Stop
979
						if (isset($config['captiveportal'][$cpzone]['radacct_enable']) && $cpentry[12] =='radius') {
980
							if ($cpcfg['reauthenticateacct'] === "stopstartfreeradius") {
981
								$rastart_time = 0;
982
								$rastop_time = 60;
983
							} else {
984
								$rastart_time = $cpentry[0];
985
								$rastop_time = time();
986
							}
987
							captiveportal_send_server_accounting('stop',
988
								$cpentry[1], // ruleno
989
								$cpentry[4], // username
990
								$cpentry[2], // clientip
991
								$cpentry[3], // clientmac
992
								$cpentry[5], // sessionid
993
								$rastart_time, // start time
994
								$rastop_time, // Stop Time
995
								3); // Lost Service
996
						// User was logged on a non-RADIUS Server but is now logged in by a RADIUS server : we send an accounting Start
997
						} else if(isset($config['captiveportal'][$cpzone]['radacct_enable']) && $auth_result['auth_method'] === 'radius') {
998
							captiveportal_send_server_accounting('start',
999
								$cpentry[1], // ruleno
1000
								$cpentry[4], // username
1001
								$cpentry[2], // clientip
1002
								$cpentry[3], // clientmac
1003
								$cpentry[5], // sessionid
1004
								$cpentry[0]); // start_time
1005
						}
1006
					}
1007
					captiveportal_reapply_attributes($cpentry, $auth_result['attributes']);
1008 1e0b1727 Phil Davis
				}
1009 5060dea7 Scott Ullrich
			}
1010
		}
1011
	}
1012 f32eae2d Ermal
	unset($cpdb);
1013 23c4f978 Scott Ullrich
1014 522f1cc7 Ermal
	captiveportal_prune_old_automac();
1015
1016 1e0b1727 Phil Davis
	if ($voucher_needs_sync == true) {
1017 f416763b Phil Davis
		/* Trigger a sync of the vouchers on config */
1018 5ebe85e9 Ermal
		send_event("service sync vouchers");
1019 1e0b1727 Phil Davis
	}
1020 5ebe85e9 Ermal
1021 5060dea7 Scott Ullrich
	/* write database */
1022 1e0b1727 Phil Davis
	if (!empty($unsetindexes)) {
1023 26ee5aaf Ermal
		captiveportal_remove_entries($unsetindexes);
1024 1e0b1727 Phil Davis
	}
1025 5b237745 Scott Ullrich
}
1026
1027 522f1cc7 Ermal
function captiveportal_prune_old_automac() {
1028 baec2b00 Ermal
	global $g, $config, $cpzone, $cpzoneid;
1029 522f1cc7 Ermal
1030 517b893e Renato Botelho
	if (is_array($config['captiveportal'][$cpzone]['passthrumac']) &&
1031 20588aac Augustin-FL
	    isset($config['captiveportal'][$cpzone]['passthrumacadd'])) {
1032 522f1cc7 Ermal
		$tmpvoucherdb = array();
1033
		$macrules = "";
1034
		$writecfg = false;
1035
		foreach ($config['captiveportal'][$cpzone]['passthrumac'] as $eid => $emac) {
1036 517b893e Renato Botelho
			if ($emac['logintype'] != "voucher") {
1037
				continue;
1038
			}
1039
			if (isset($config['captiveportal'][$cpzone]['noconcurrentlogins'])) {
1040
				if (isset($tmpvoucherdb[$emac['username']])) {
1041
					$temac = $config['captiveportal'][$cpzone]['passthrumac'][$tmpvoucherdb[$emac['username']]];
1042
					$pipeno = captiveportal_get_dn_passthru_ruleno($temac['mac']);
1043 aea56408 Ermal
					if ($pipeno) {
1044
						captiveportal_free_dn_ruleno($pipeno);
1045 517b893e Renato Botelho
						$macrules .= "table {$cpzone}_pipe_mac delete any,{$temac['mac']}\n";
1046
						$macrules .= "table {$cpzone}_pipe_mac delete {$temac['mac']},any\n";
1047 aea56408 Ermal
						$macrules .= "pipe delete {$pipeno}\n";
1048
						++$pipeno;
1049
						$macrules .= "pipe delete {$pipeno}\n";
1050
					}
1051 522f1cc7 Ermal
					$writecfg = true;
1052 517b893e Renato Botelho
					captiveportal_logportalauth($temac['username'], $temac['mac'],
1053
					    $temac['ip'], "DUPLICATE {$temac['username']} LOGIN - TERMINATING OLD SESSION");
1054
					unset($config['captiveportal'][$cpzone]['passthrumac'][$tmpvoucherdb[$emac['username']]]);
1055 522f1cc7 Ermal
				}
1056 517b893e Renato Botelho
				$tmpvoucherdb[$emac['username']] = $eid;
1057
			}
1058
			if (voucher_auth($emac['username']) <= 0) {
1059
				$pipeno = captiveportal_get_dn_passthru_ruleno($emac['mac']);
1060
				if ($pipeno) {
1061
					captiveportal_free_dn_ruleno($pipeno);
1062
					$macrules .= "table {$cpzone}_pipe_mac delete any,{$emac['mac']}\n";
1063
					$macrules .= "table {$cpzone}_pipe_mac delete {$emac['mac']},any\n";
1064
					$macrules .= "pipe delete {$pipeno}\n";
1065
					++$pipeno;
1066
					$macrules .= "pipe delete {$pipeno}\n";
1067
				}
1068
				$writecfg = true;
1069
				captiveportal_logportalauth($emac['username'], $emac['mac'],
1070
				    $emac['ip'], "EXPIRED {$emac['username']} LOGIN - TERMINATING SESSION");
1071
				unset($config['captiveportal'][$cpzone]['passthrumac'][$eid]);
1072 522f1cc7 Ermal
			}
1073
		}
1074 bae729da Ermal
		unset($tmpvoucherdb);
1075 522f1cc7 Ermal
		if (!empty($macrules)) {
1076
			@file_put_contents("{$g['tmp_path']}/macentry.prunerules.tmp", $macrules);
1077 2e62a7c4 Ermal
			unset($macrules);
1078 517b893e Renato Botelho
			mwexec("/sbin/ipfw -q {$g['tmp_path']}/macentry.prunerules.tmp");
1079 522f1cc7 Ermal
		}
1080 1e0b1727 Phil Davis
		if ($writecfg === true) {
1081 522f1cc7 Ermal
			write_config("Prune session for auto-added macs");
1082 1e0b1727 Phil Davis
		}
1083 522f1cc7 Ermal
	}
1084
}
1085
1086 3db19cf1 Scott Ullrich
/* remove a single client according to the DB entry */
1087 eb43c5b1 Augustin FL
function captiveportal_disconnect($dbent, $term_cause = 1, $stop_time = null) {
1088 cbe38717 Ermal
	global $g, $config, $cpzone, $cpzoneid;
1089 d99f7864 Scott Ullrich
1090
	$stop_time = (empty($stop_time)) ? time() : $stop_time;
1091
1092
	/* this client needs to be deleted - remove ipfw rules */
1093 e42ea151 Augustin FL
	if (isset($config['captiveportal'][$cpzone]['radacct_enable']) && $dbent[12] =='radius') {
1094
		if ($config['captiveportal'][$cpzone]['reauthenticateacct'] == "stopstartfreeradius") {
1095 9d706ff8 Renato Botelho
			/*
1096
			 * Interim updates are on so the session time must be
1097
			 * reported as the elapsed time since the previous
1098
			 * interim update.
1099
			 */
1100 00de7de6 jim-p
			$session_time = ($stop_time - $dbent[0]) % 60;
1101
			$start_time = $stop_time - $session_time;
1102
		} else {
1103
			$start_time = $dbent[0];
1104
		}
1105 748372bc Stephen Jones
		captiveportal_send_server_accounting('stop',
1106 e42ea151 Augustin FL
			$dbent[1], // ruleno
1107 5705c60a Renato Botelho
			$dbent[4], // username
1108
			$dbent[2], // clientip
1109
			$dbent[3], // clientmac
1110 e42ea151 Augustin FL
			$dbent[5], // sessionid
1111
			$start_time, // start time
1112
			$stop_time, // stop time
1113
			$term_cause); // Acct-Terminate-Cause
1114 d99f7864 Scott Ullrich
	}
1115 1e0b1727 Phil Davis
1116 5705c60a Renato Botelho
	if (is_ipaddr($dbent[2])) {
1117 3c4fcd5b Renato Botelho
		/*
1118
		 * Delete client's ip entry from tables auth_up and auth_down.
1119
		 * It's not necessary to explicit specify mac address here
1120
		 */
1121 356f29a0 Renato Botelho
		$cpsession = captiveportal_isip_logged($dbent[2]);
1122
		if (!empty($cpsession)) {
1123
			$clientsn = (is_ipaddrv6($dbent[2])) ? 128 : 32;
1124
			pfSense_ipfw_table("{$cpzone}_auth_up",
1125
			    IP_FW_TABLE_XDEL, "{$dbent[2]}/{$clientsn}");
1126
			pfSense_ipfw_table("{$cpzone}_auth_down",
1127
			    IP_FW_TABLE_XDEL, "{$dbent[2]}/{$clientsn}");
1128
		}
1129 32c392aa Ermal
		/* XXX: Redundant?! Ensure all pf(4) states are killed. */
1130 c2e2d133 Ermal
		$_gb = @pfSense_kill_states($dbent[2]);
1131
		$_gb = @pfSense_kill_srcstates($dbent[2]);
1132 32c392aa Ermal
	}
1133 f9f71ad3 Ermal Lu?i
1134 1e0b1727 Phil Davis
	/*
1135 9d706ff8 Renato Botelho
	 * These are the pipe numbers we use to control traffic shaping for
1136
	 * each logged in user via captive portal
1137
	 * We could get an error if the pipe doesn't exist but everything
1138
	 * should still be fine
1139
	 */
1140 5705c60a Renato Botelho
	if (!empty($dbent[1])) {
1141 356f29a0 Renato Botelho
		/*
1142
		 * Call captiveportal_free_dnrules() in dry_run mode to verify
1143
		 * if there are pipes to be removed and prevent the attempt to
1144
		 * delete invalid pipes
1145
		 */
1146
		$removed_pipes = captiveportal_free_dnrules($dbent[1],
1147
		    $dbent[1]+1, true);
1148 7a7abeba Scott Ullrich
1149 356f29a0 Renato Botelho
		if (!empty($removed_pipes)) {
1150
			$_gb = @pfSense_ipfw_pipe("pipe delete {$dbent[1]}");
1151
			$_gb = @pfSense_ipfw_pipe("pipe delete " .
1152
			    ($dbent[1]+1));
1153
1154
			/*
1155
			 * Release the ruleno so it can be reallocated to new
1156
			 * clients
1157
			 */
1158
			captiveportal_free_dn_ruleno($dbent[1]);
1159
		}
1160 6cbda317 Ermal
	}
1161 d322e3b3 Scott Ullrich
1162
	// XMLRPC Call over to the master Voucher node
1163 b8963db6 Renato Botelho
	if (xmlrpc_sync_voucher_details($syncip, $syncport,
1164
	    $vouchersyncusername, $syncpass)) {
1165 9d706ff8 Renato Botelho
		$remote_status = xmlrpc_sync_voucher_disconnect($dbent, $syncip,
1166
		    $syncport, $syncpass, $vouchersyncusername, $term_cause,
1167
		    $stop_time);
1168 d322e3b3 Scott Ullrich
	}
1169
1170 3db19cf1 Scott Ullrich
}
1171 12ee8fe4 Scott Ullrich
1172 006802ab Ermal
/* remove a single client by sessionid */
1173
function captiveportal_disconnect_client($sessionid, $term_cause = 1, $logoutReason = "LOGOUT") {
1174 26ee5aaf Ermal
	global $g, $config;
1175 36254e4a Scott Ullrich
1176 69c97c32 Chris Buechler
	$sessionid = SQLite3::escapeString($sessionid);
1177 006802ab Ermal
	/* read database */
1178 5038fb53 bcyrill
	$result = captiveportal_read_db("WHERE sessionid = '{$sessionid}'");
1179 d99f7864 Scott Ullrich
1180
	/* find entry */
1181 5038fb53 bcyrill
	if (!empty($result)) {
1182 26ee5aaf Ermal
1183 5038fb53 bcyrill
		foreach ($result as $cpentry) {
1184 eb43c5b1 Augustin FL
			captiveportal_disconnect($cpentry, $term_cause);
1185 5705c60a Renato Botelho
			captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "DISCONNECT");
1186 5038fb53 bcyrill
		}
1187 eb43c5b1 Augustin FL
		captiveportal_remove_entries(array($sessionid));
1188 5038fb53 bcyrill
		unset($result);
1189 26ee5aaf Ermal
	}
1190 5b237745 Scott Ullrich
}
1191
1192 feab4e54 plumbeo
/* remove all clients */
1193
function captiveportal_disconnect_all($term_cause = 6, $logoutReason = "DISCONNECT") {
1194
	global $g, $config, $cpzone, $cpzoneid;
1195
1196 d793617e plumbeo
	/* check if we're pruning old entries and eventually wait */
1197 47f96785 plumbeo
	$rcprunelock = try_lock("rcprunecaptiveportal{$cpzone}", 15);
1198 d793617e plumbeo
1199
	/* if we still don't have the lock, unlock forcefully and take it */
1200
	if (!$rcprunelock) {
1201 47f96785 plumbeo
		log_error("CP zone ${cpzone}: could not obtain the lock for more than 15 seconds, lock taken forcefully to disconnect all users");
1202 d793617e plumbeo
		unlock_force("rcprunecaptiveportal{$cpzone}");
1203
		$rcprunelock = lock("rcprunecaptiveportal{$cpzone}", LOCK_EX);
1204
	}
1205
1206 47f96785 plumbeo
	/* take a lock so new users won't be able to log in */
1207
	$cpdblck = lock("captiveportaldb{$cpzone}", LOCK_EX);
1208
1209
	captiveportal_radius_stop_all($term_cause, $logoutReason);
1210 feab4e54 plumbeo
1211 47f96785 plumbeo
	/* remove users from the database */
1212
	$cpdb = captiveportal_read_db();
1213 025ec94a plumbeo
	$unsetindexes = array_column($cpdb,5);
1214
	if (!empty($unsetindexes)) {
1215
		captiveportal_remove_entries($unsetindexes);
1216
	}
1217 feab4e54 plumbeo
1218 47f96785 plumbeo
	/* reinit ipfw rules */
1219
	captiveportal_init_rules(true);
1220 d793617e plumbeo
1221 47f96785 plumbeo
	unlock($cpdblck);
1222 d793617e plumbeo
	unlock($rcprunelock);
1223 feab4e54 plumbeo
}
1224
1225 e42ea151 Augustin FL
/* send RADIUS acct stop for all current clients connected with RADIUS servers */
1226 3ece6d54 plumbeo
function captiveportal_radius_stop_all($term_cause = 6, $logoutReason = "DISCONNECT") {
1227
	global $g, $config, $cpzone, $cpzoneid;
1228 d99f7864 Scott Ullrich
1229 3ece6d54 plumbeo
	$cpdb = captiveportal_read_db();
1230
1231
	$radacct = isset($config['captiveportal'][$cpzone]['radacct_enable']) ? true : false;
1232
	foreach ($cpdb as $cpentry) {
1233 e42ea151 Augustin FL
		if ($cpentry[12] === 'radius' && $radacct) {
1234
			if ($config['captiveportal'][$cpzone]['reauthenticateacct'] == "stopstartfreeradius") {
1235
				$session_time = (time() - $cpentry[0]) % 60;
1236
				$start_time = time() - $session_time;
1237
			} else {
1238
				$start_time = $cpentry[0];
1239 ebc0e4b6 Ermal
			}
1240 748372bc Stephen Jones
			captiveportal_send_server_accounting('stop',
1241 e42ea151 Augustin FL
				$cpentry[1], // ruleno
1242
				$cpentry[4], // username
1243
				$cpentry[2], // clientip
1244
				$cpentry[3], // clientmac
1245
				$cpentry[5], // sessionid
1246
				$start_time, // start time
1247
				$stop_time, // stop time
1248
				$term_cause); // Acct-Terminate-Cause
1249 d99f7864 Scott Ullrich
		}
1250 3ece6d54 plumbeo
		captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], $logoutReason);
1251 d99f7864 Scott Ullrich
	}
1252 3ece6d54 plumbeo
	unset($cpdb);
1253 5b237745 Scott Ullrich
}
1254
1255 621fed0e Ermal
function captiveportal_passthrumac_configure_entry($macent, $pipeinrule = false) {
1256 a413e743 Renato Botelho
	global $config, $g, $cpzone;
1257 aea56408 Ermal
1258 7519cc29 Ermal
	$bwUp = 0;
1259 1e0b1727 Phil Davis
	if (!empty($macent['bw_up'])) {
1260 7519cc29 Ermal
		$bwUp = $macent['bw_up'];
1261 1e0b1727 Phil Davis
	} else if (!empty($config['captiveportal'][$cpzone]['bwdefaultup'])) {
1262 7519cc29 Ermal
		$bwUp = $config['captiveportal'][$cpzone]['bwdefaultup'];
1263 1e0b1727 Phil Davis
	}
1264 7519cc29 Ermal
	$bwDown = 0;
1265 1e0b1727 Phil Davis
	if (!empty($macent['bw_down'])) {
1266 7519cc29 Ermal
		$bwDown = $macent['bw_down'];
1267 1e0b1727 Phil Davis
	} else if (!empty($config['captiveportal'][$cpzone]['bwdefaultdn'])) {
1268 7519cc29 Ermal
		$bwDown = $config['captiveportal'][$cpzone]['bwdefaultdn'];
1269 1e0b1727 Phil Davis
	}
1270 d5ae560d Ermal
1271 666f88e0 Renato Botelho
	if ($macent['action'] == 'pass') {
1272 621fed0e Ermal
		$rules = "";
1273 666f88e0 Renato Botelho
1274 393c1317 Renato Botelho
		$pipeno = captiveportal_get_next_dn_ruleno();
1275 1e0b1727 Phil Davis
1276 393c1317 Renato Botelho
		$pipeup = $pipeno;
1277
		if ($pipeinrule == true) {
1278
			$_gb = @pfSense_ipfw_pipe("pipe {$pipeno} config bw {$bwUp}Kbit/s queue 100 buckets 16");
1279
		} else {
1280
			$rules .= "pipe {$pipeno} config bw {$bwUp}Kbit/s queue 100 buckets 16\n";
1281
		}
1282 517b893e Renato Botelho
1283 393c1317 Renato Botelho
		$pipedown = $pipeno + 1;
1284
		if ($pipeinrule == true) {
1285
			$_gb = @pfSense_ipfw_pipe("pipe {$pipedown} config bw {$bwDown}Kbit/s queue 100 buckets 16");
1286
		} else {
1287
			$rules .= "pipe {$pipedown} config bw {$bwDown}Kbit/s queue 100 buckets 16\n";
1288 1e0b1727 Phil Davis
		}
1289 666f88e0 Renato Botelho
1290 517b893e Renato Botelho
		$rules .= "table {$cpzone}_pipe_mac add any,{$macent['mac']} {$pipeup}\n";
1291
		$rules .= "table {$cpzone}_pipe_mac add {$macent['mac']},any {$pipedown}\n";
1292 0d33f1fc Renato Botelho
	}
1293 d5ae560d Ermal
1294
	return $rules;
1295
}
1296
1297 666f88e0 Renato Botelho
function captiveportal_passthrumac_delete_entry($macent) {
1298 918ef12c jim-p
	global $cpzone;
1299 5eee3755 Renato Botelho
	$rules = "";
1300 666f88e0 Renato Botelho
1301 a413e743 Renato Botelho
	if ($macent['action'] == 'pass') {
1302 666f88e0 Renato Botelho
		$pipeno = captiveportal_get_dn_passthru_ruleno($macent['mac']);
1303
1304
		if (!empty($pipeno)) {
1305
			captiveportal_free_dn_ruleno($pipeno);
1306 517b893e Renato Botelho
			$rules .= "table {$cpzone}_pipe_mac delete any,{$macent['mac']}\n";
1307
			$rules .= "table {$cpzone}_pipe_mac delete {$macent['mac']},any\n";
1308 5eee3755 Renato Botelho
			$rules .= "pipe delete " . $pipeno . "\n";
1309
			$rules .= "pipe delete " . ++$pipeno . "\n";
1310 666f88e0 Renato Botelho
		}
1311
	}
1312
1313 5eee3755 Renato Botelho
	return $rules;
1314 666f88e0 Renato Botelho
}
1315
1316 41196b69 Ermal LUÇI
function captiveportal_passthrumac_configure($filename = false, $startindex = 0, $stopindex = 0) {
1317 b4792bf8 Ermal
	global $config, $g, $cpzone;
1318 36254e4a Scott Ullrich
1319 d5ae560d Ermal
	$rules = "";
1320 36254e4a Scott Ullrich
1321 621fed0e Ermal
	if (is_array($config['captiveportal'][$cpzone]['passthrumac'])) {
1322 41196b69 Ermal LUÇI
		if ($stopindex > 0) {
1323
			$fd = fopen($filename, "w");
1324
			for ($idx = $startindex; $idx <= $stopindex; $idx++) {
1325
				if (isset($config['captiveportal'][$cpzone]['passthrumac'][$idx])) {
1326
					$rules = captiveportal_passthrumac_configure_entry($config['captiveportal'][$cpzone]['passthrumac'][$idx]);
1327
					fwrite($fd, $rules);
1328
				}
1329
			}
1330
			fclose($fd);
1331
1332
			return;
1333
		} else {
1334
			$nentries = count($config['captiveportal'][$cpzone]['passthrumac']);
1335
			if ($nentries > 2000) {
1336
				$nloops = $nentries / 1000;
1337
				$remainder= $nentries % 1000;
1338 1e0b1727 Phil Davis
				for ($i = 0; $i < $nloops; $i++) {
1339 41196b69 Ermal LUÇI
					mwexec_bg("/usr/local/sbin/fcgicli -f /etc/rc.captiveportal_configure_mac -d \"cpzone={$cpzone}&startidx=" . ($i * 1000) . "&stopidx=" . ((($i+1) * 1000) - 1) . "\"");
1340 1e0b1727 Phil Davis
				}
1341
				if ($remainder > 0) {
1342 41196b69 Ermal LUÇI
					mwexec_bg("/usr/local/sbin/fcgicli -f /etc/rc.captiveportal_configure_mac -d \"cpzone={$cpzone}&startidx=" . ($i * 1000) . "&stopidx=" . (($i* 1000) + $remainder) ."\"");
1343 1e0b1727 Phil Davis
				}
1344 41196b69 Ermal LUÇI
			} else {
1345 1e0b1727 Phil Davis
				foreach ($config['captiveportal'][$cpzone]['passthrumac'] as $macent) {
1346 41196b69 Ermal LUÇI
					$rules .= captiveportal_passthrumac_configure_entry($macent, true);
1347 1e0b1727 Phil Davis
				}
1348 41196b69 Ermal LUÇI
			}
1349 fe9ec12b Ermal LUÇI
		}
1350 621fed0e Ermal
	}
1351 0bd34ed6 Scott Ullrich
1352 d5ae560d Ermal
	return $rules;
1353 5b237745 Scott Ullrich
}
1354
1355 fac13a5e Ermal
function captiveportal_passthrumac_findbyname($username) {
1356 b4792bf8 Ermal
	global $config, $cpzone;
1357 fac13a5e Ermal
1358 b4792bf8 Ermal
	if (is_array($config['captiveportal'][$cpzone]['passthrumac'])) {
1359
		foreach ($config['captiveportal'][$cpzone]['passthrumac'] as $macent) {
1360 1e0b1727 Phil Davis
			if ($macent['username'] == $username) {
1361 fac13a5e Ermal
				return $macent;
1362 1e0b1727 Phil Davis
			}
1363 fac13a5e Ermal
		}
1364
	}
1365
	return NULL;
1366
}
1367
1368 1e0b1727 Phil Davis
/*
1369 b01792a0 Ermal
 * table (3=IN)/(4=OUT) hold allowed ip's without bw limits
1370
 */
1371 1272429c Ermal
function captiveportal_allowedip_configure_entry($ipent, $ishostname = false) {
1372 517b893e Renato Botelho
	global $g, $config, $cpzone;
1373 b01792a0 Ermal
1374 8b73cc7e Scott Ullrich
	/*  Instead of copying this entire function for something
1375
	 *  easy such as hostname vs ip address add this check
1376
	 */
1377 bb58ed63 Ermal
	if ($ishostname === true) {
1378 285ef132 Ermal LUÇI
		if (!platform_booting()) {
1379 9e875e0c Renato Botelho
			$ipaddress = gethostbyname($ipent['hostname']);
1380 1e0b1727 Phil Davis
			if (!is_ipaddr($ipaddress)) {
1381 1b584e3f Ermal
				return;
1382 1e0b1727 Phil Davis
			}
1383
		} else {
1384 9e875e0c Renato Botelho
			$ipaddress = "";
1385 1e0b1727 Phil Davis
		}
1386
	} else {
1387 9e875e0c Renato Botelho
		$ipaddress = $ipent['ip'];
1388 1e0b1727 Phil Davis
	}
1389 0b108eda Scott Ullrich
1390 b01792a0 Ermal
	$rules = "";
1391 1272429c Ermal
	$cp_filterdns_conf = "";
1392 2e080989 Ermal
	$enBwup = 0;
1393 1e0b1727 Phil Davis
	if (!empty($ipent['bw_up'])) {
1394 ca321bfd Ermal
		$enBwup = intval($ipent['bw_up']);
1395 1e0b1727 Phil Davis
	} else if (!empty($config['captiveportal'][$cpzone]['bwdefaultup'])) {
1396 2e080989 Ermal
		$enBwup = $config['captiveportal'][$cpzone]['bwdefaultup'];
1397 1e0b1727 Phil Davis
	}
1398 2e080989 Ermal
	$enBwdown = 0;
1399 1e0b1727 Phil Davis
	if (!empty($ipent['bw_down'])) {
1400 2e080989 Ermal
		$enBwdown = intval($ipent['bw_down']);
1401 1e0b1727 Phil Davis
	} else if (!empty($config['captiveportal'][$cpzone]['bwdefaultdn'])) {
1402 2e080989 Ermal
		$enBwdown = $config['captiveportal'][$cpzone]['bwdefaultdn'];
1403 1e0b1727 Phil Davis
	}
1404 b01792a0 Ermal
1405 393c1317 Renato Botelho
	$pipeup = captiveportal_get_next_dn_ruleno();
1406
	$_gb = @pfSense_ipfw_pipe("pipe {$pipeup} config bw {$enBwup}Kbit/s queue 100 buckets 16");
1407
	$pipedown = $pipeup + 1;
1408
	$_gb = @pfSense_ipfw_pipe("pipe {$pipedown} config bw {$enBwdown}Kbit/s queue 100 buckets 16");
1409
1410 1272429c Ermal
	if ($ishostname === true) {
1411 517b893e Renato Botelho
		$cp_filterdns_conf .= "ipfw {$ipent['hostname']} {$cpzone}_allowed_up pipe {$pipeup}\n";
1412
		$cp_filterdns_conf .= "ipfw {$ipent['hostname']} {$cpzone}_allowed_down pipe {$pipedown}\n";
1413 1e0b1727 Phil Davis
		if (!is_ipaddr($ipaddress)) {
1414 1b584e3f Ermal
			return array("", $cp_filterdns_conf);
1415 1e0b1727 Phil Davis
		}
1416 1272429c Ermal
	}
1417 393c1317 Renato Botelho
1418 d6a0379d Ermal
	$subnet = "";
1419 1e0b1727 Phil Davis
	if (!empty($ipent['sn'])) {
1420 d6a0379d Ermal
		$subnet = "/{$ipent['sn']}";
1421 1e0b1727 Phil Davis
	}
1422 517b893e Renato Botelho
	$rules .= "table {$cpzone}_allowed_up add {$ipaddress}{$subnet} {$pipeup}\n";
1423
	$rules .= "table {$cpzone}_allowed_down add {$ipaddress}{$subnet} {$pipedown}\n";
1424 9e875e0c Renato Botelho
1425 1e0b1727 Phil Davis
	if ($ishostname === true) {
1426 1272429c Ermal
		return array($rules, $cp_filterdns_conf);
1427 1e0b1727 Phil Davis
	} else {
1428 1272429c Ermal
		return $rules;
1429 1e0b1727 Phil Davis
	}
1430 f23a6091 Scott Ullrich
}
1431
1432
function captiveportal_allowedhostname_configure() {
1433 3378289a Ermal LUÇI
	global $config, $g, $cpzone, $cpzoneid;
1434 f23a6091 Scott Ullrich
1435 1272429c Ermal
	$rules = "";
1436 517b893e Renato Botelho
	if (!is_array($config['captiveportal'][$cpzone]['allowedhostname'])) {
1437
		return $rules;
1438
	}
1439
1440
	$rules = "\n# captiveportal_allowedhostname_configure()\n";
1441
	$cp_filterdns_conf = "";
1442
	foreach ($config['captiveportal'][$cpzone]['allowedhostname'] as $hostnameent) {
1443
		$tmprules = captiveportal_allowedip_configure_entry($hostnameent, true);
1444
		$rules .= $tmprules[0];
1445
		$cp_filterdns_conf .= $tmprules[1];
1446
	}
1447
	$cp_filterdns_filename = "{$g['varetc_path']}/filterdns-{$cpzone}-captiveportal.conf";
1448
	@file_put_contents($cp_filterdns_filename, $cp_filterdns_conf);
1449
	unset($cp_filterdns_conf);
1450
1451
	return $rules;
1452
}
1453
1454
function captiveportal_filterdns_configure() {
1455
	global $config, $g, $cpzone, $cpzoneid;
1456
1457
	$cp_filterdns_filename = $g['varetc_path'] .
1458
	    "/filterdns-{$cpzone}-captiveportal.conf";
1459
1460 99a537e1 Renato Botelho
	if (isset($config['captiveportal'][$cpzone]['enable']) &&
1461
	    is_array($config['captiveportal'][$cpzone]['allowedhostname']) &&
1462 517b893e Renato Botelho
	    file_exists($cp_filterdns_filename)) {
1463
		if (isvalidpid($g['varrun_path'] .
1464
		    "/filterdns-{$cpzone}-cpah.pid")) {
1465
			sigkillbypid($g['varrun_path'] .
1466
			    "/filterdns-{$cpzone}-cpah.pid", "HUP");
1467 1e0b1727 Phil Davis
		} else {
1468 517b893e Renato Botelho
			mwexec("/usr/local/sbin/filterdns -p " .
1469
			    "{$g['varrun_path']}/filterdns-{$cpzone}-cpah.pid" .
1470 a4aebf44 Renato Botelho
			    " -i 300 -c {$cp_filterdns_filename} -d 1");
1471 1e0b1727 Phil Davis
		}
1472 7b5eab84 bcyrill
	} else {
1473
		killbypid("{$g['varrun_path']}/filterdns-{$cpzone}-cpah.pid");
1474
		@unlink("{$g['varrun_path']}/filterdns-{$cpzone}-cpah.pid");
1475 55c18b30 Scott Ullrich
	}
1476 1272429c Ermal
1477 f23a6091 Scott Ullrich
	return $rules;
1478
}
1479
1480 cb0a2913 Ermal Lu?i
function captiveportal_allowedip_configure() {
1481 b4792bf8 Ermal
	global $config, $g, $cpzone;
1482 36254e4a Scott Ullrich
1483 6ce61a8f Ermal
	$rules = "";
1484 b4792bf8 Ermal
	if (is_array($config['captiveportal'][$cpzone]['allowedip'])) {
1485 1e0b1727 Phil Davis
		foreach ($config['captiveportal'][$cpzone]['allowedip'] as $ipent) {
1486 b01792a0 Ermal
			$rules .= captiveportal_allowedip_configure_entry($ipent);
1487 1e0b1727 Phil Davis
		}
1488 cb0a2913 Ermal Lu?i
	}
1489 36254e4a Scott Ullrich
1490 6ce61a8f Ermal
	return $rules;
1491 5b237745 Scott Ullrich
}
1492
1493 2d53158f stompro
/* get last activity timestamp given client IP address */
1494 f4c867e0 Renato Botelho
function captiveportal_get_last_activity($ip) {
1495 b40b4a3e Renato Botelho
	global $cpzone;
1496 36254e4a Scott Ullrich
1497 f9f71ad3 Ermal Lu?i
	/* Reading only from one of the tables is enough of approximation. */
1498 b40b4a3e Renato Botelho
	$tables = array("{$cpzone}_allowed_up", "{$cpzone}_auth_up");
1499
1500
	foreach ($tables as $table) {
1501
		$ipfw = pfSense_ipfw_table_lookup($table, $ip);
1502
		if (is_array($ipfw)) {
1503
			/* Workaround for #46652 */
1504
			if ($ipfw['packets'] > 0) {
1505
				return $ipfw['timestamp'];
1506
			} else {
1507
				return 0;
1508
			}
1509 2842c8d4 Ermal LUÇI
		}
1510 d99f7864 Scott Ullrich
	}
1511 36254e4a Scott Ullrich
1512 d99f7864 Scott Ullrich
	return 0;
1513 5b237745 Scott Ullrich
}
1514
1515
1516 3db19cf1 Scott Ullrich
/* log successful captive portal authentication to syslog */
1517
/* part of this code from php.net */
1518 086cf944 Phil Davis
function captiveportal_logportalauth($user, $mac, $ip, $status, $message = null) {
1519 d99f7864 Scott Ullrich
	// Log it
1520 1e0b1727 Phil Davis
	if (!$message) {
1521 12feed15 Ermal
		$message = "{$status}: {$user}, {$mac}, {$ip}";
1522 1e0b1727 Phil Davis
	} else {
1523 d31bc32a Ermal
		$message = trim($message);
1524 12feed15 Ermal
		$message = "{$status}: {$user}, {$mac}, {$ip}, {$message}";
1525 d31bc32a Ermal
	}
1526 f56a73f1 Scott Ullrich
	captiveportal_syslog($message);
1527
}
1528
1529
/* log simple messages to syslog */
1530
function captiveportal_syslog($message) {
1531 12feed15 Ermal
	global $cpzone;
1532
1533 f56a73f1 Scott Ullrich
	$message = trim($message);
1534 9aec47b7 Chris Buechler
	$message = "Zone: {$cpzone} - {$message}";
1535 f56a73f1 Scott Ullrich
	openlog("logportalauth", LOG_PID, LOG_LOCAL4);
1536
	// Log it
1537
	syslog(LOG_INFO, $message);
1538 d99f7864 Scott Ullrich
	closelog();
1539 3db19cf1 Scott Ullrich
}
1540
1541 eb43c5b1 Augustin FL
/* Authenticate users using Authentication Backend */
1542
function captiveportal_authenticate_user(&$login = '', &$password = '', $clientmac = '', $clientip = '', $pipeno = 'null', $context = 'first') {
1543
	global $g, $config, $cpzone;
1544
	$cpcfg = $config['captiveportal'][$cpzone];
1545 748372bc Stephen Jones
1546 eb43c5b1 Augustin FL
	$login_status = 'FAILURE';
1547
	$login_msg = gettext('Invalid credentials specified');
1548
	$reply_attributes = array();
1549
	$auth_method = '';
1550
	$auth_result = null;
1551 748372bc Stephen Jones
1552
	/*
1553
	Management of the reply Message (reason why the authentication failed) :
1554 eb43c5b1 Augustin FL
	multiple authentication servers can be used, so multiple reply messages could theoretically be returned.
1555
	But only one message is returned (the most important one).
1556
	The return value of authenticate_user() define how important messages are :
1557 748372bc Stephen Jones
		- Reply message of a successful auth is more important than reply message of
1558 eb43c5b1 Augustin FL
		a user failed auth(invalid credentials/authorization)
1559 748372bc Stephen Jones
1560
		- Reply message of a user failed auth is more important than reply message of
1561 eb43c5b1 Augustin FL
		a server failed auth (unable to contact server)
1562 748372bc Stephen Jones
1563 eb43c5b1 Augustin FL
		- When multiple user failed auth are encountered, messages returned by remote servers
1564
		(eg. reply in RADIUS Access-Reject) are more important than pfSense error messages.
1565 748372bc Stephen Jones
1566
	The $authlevel variable is a flag indicating the status of authentication
1567 eb43c5b1 Augustin FL
	0 = failed server auth
1568
	1 = failed user auth
1569
	2 = failed user auth with custom server reply recieved
1570
	3 = successful auth
1571
	*/
1572
	$authlevel = 0;
1573
1574
	/* Getting authentication servers from captiveportal configuration */
1575
	$auth_servers = array();
1576 748372bc Stephen Jones
1577 eb43c5b1 Augustin FL
	if ($cpcfg['auth_method'] === 'none') {
1578
		$auth_servers[] = array('type' => 'none');
1579
	} else {
1580
		if ($context === 'second') {
1581
			$fullauthservers = explode(",", $cpcfg['auth_server2']);
1582
		} else {
1583
			$fullauthservers = explode(",", $cpcfg['auth_server']);
1584
		}
1585 d44bccc7 Scott Ullrich
1586 eb43c5b1 Augustin FL
		foreach ($fullauthservers as $authserver) {
1587
			if (strpos($authserver, ' - ') !== false) {
1588
				$authserver = explode(' - ', $authserver);
1589
				array_shift($authserver);
1590
				$authserver = implode(' - ', $authserver);
1591 748372bc Stephen Jones
1592 eb43c5b1 Augustin FL
				if (auth_get_authserver($authserver) !== null) {
1593
					$auth_servers[] = auth_get_authserver($authserver);
1594
				} else {
1595
					log_error("Zone: {$cpzone} - Captive portal was unable to find the settings of the server '{$authserver}' used for authentication !");
1596
				}
1597
			}
1598
		}
1599 1e0b1727 Phil Davis
	}
1600 ebc0e4b6 Ermal
1601 eb43c5b1 Augustin FL
	/* Unable to find the any authentication server config - shouldn't happen! - bail out */
1602
	if (count($auth_servers) === 0) {
1603
		log_error("Zone: {$cpzone} - No valid server could be used for authentication.");
1604
		$login_msg = gettext("Internal Error");
1605 d2c98878 falbertopl
	} else {
1606 eb43c5b1 Augustin FL
		foreach ($auth_servers as $authcfg) {
1607
			if ($authlevel < 3) {
1608
				$radmac_error = false;
1609
				$attributes = array("nas_identifier" => "CaptivePortal",
1610
					"nas_port_type" => RADIUS_ETHERNET,
1611
					"nas_port" => $pipeno,
1612
					"framed_ip" => $clientip);
1613
				if (mac_format($clientmac) !== null) {
1614
					$attributes["calling_station_id"] = mac_format($clientmac);
1615
				}
1616 748372bc Stephen Jones
1617 eb43c5b1 Augustin FL
				$result = null;
1618
				$status = null;
1619
				$msg = null;
1620 748372bc Stephen Jones
1621 eb43c5b1 Augustin FL
				/* Radius MAC authentication */
1622
				if ($cpcfg['auth_method'] === 'radmac' && $clientmac) {
1623
					if ($authcfg['type'] === 'radius') {
1624
						$login = mac_format($clientmac);
1625
						$password = $cpcfg['radmac_secret'];
1626
						$status = "MACHINE LOGIN";
1627
					} else {
1628
						/* Trying to perform a Radius MAC authentication on a non-radius server - shouldn't happen! - bail out */
1629
						$msg = gettext("Internal Error");
1630
						log_error("Zone: {$cpzone} - Trying to perform RADIUS MAC authentication on a non-RADIUS server !");
1631
						$radmac_error = true;
1632
						$result = null;
1633
					}
1634
				}
1635 748372bc Stephen Jones
1636 eb43c5b1 Augustin FL
				if (!$radmac_error) {
1637
					if ($authcfg['type'] === 'none') {
1638
						$result = true;
1639
					} else {
1640
						$result = authenticate_user($login, $password, $authcfg, $attributes);
1641
					}
1642 748372bc Stephen Jones
1643 eb43c5b1 Augustin FL
					if (!empty($attributes['error_message'])) {
1644
						$msg = $attributes['error_message'];
1645
					}
1646 748372bc Stephen Jones
1647 eb43c5b1 Augustin FL
					if ($authcfg['type'] == 'Local Auth' && $result && isset($cpcfg['localauth_priv'])) {
1648
						if (!userHasPrivilege(getUserEntry($login), "user-services-captiveportal-login")) {
1649
							$result = false;
1650
							$msg = gettext("Access Denied");
1651
						}
1652
					}
1653 748372bc Stephen Jones
1654 eb43c5b1 Augustin FL
					if (empty($status)) {
1655
						if ($result === true) {
1656
							$status = "ACCEPT";
1657
						} elseif ($result === null) {
1658
							$status = "ERROR";
1659
						} else {
1660
							$status = "FAILURE";
1661
						}
1662
					}
1663 748372bc Stephen Jones
1664 eb43c5b1 Augustin FL
					if ($cpcfg['auth_method'] === 'radmac' && $login == mac_format($clientmac) || $authcfg['type'] === 'none' && empty($login)) {
1665
						$login = "unauthenticated";
1666 748372bc Stephen Jones
					}
1667 eb43c5b1 Augustin FL
					// We determine a flag
1668
					if ($result === true) {
1669
						$val = 3;
1670
					} elseif ($result === false && !empty($attributes['reply_message'])) {
1671
						$val = 2;
1672
						$msg = $attributes['reply_message'];
1673
					} elseif ($result === false) {
1674
						$val = 1;
1675
					} elseif ($result === null) {
1676
						$val = 0;
1677
					}
1678
				}
1679 748372bc Stephen Jones
1680 eb43c5b1 Augustin FL
				if ($val >= $auth_val) {
1681
					$auth_val = $val;
1682
					$auth_method = $authcfg['type'];
1683
					$login_status = $status;
1684
					$login_msg = $msg;
1685
					$reply_attributes = $attributes;
1686
					$auth_result = $result;
1687
				}
1688
			}
1689
		}
1690 1e0b1727 Phil Davis
	}
1691 0bd34ed6 Scott Ullrich
1692 eb43c5b1 Augustin FL
	return array('result'=>$auth_result, 'attributes'=>$reply_attributes, 'auth_method' =>$auth_method, 'login_status'=> $login_status, 'login_message' => $login_msg);
1693 0bd34ed6 Scott Ullrich
}
1694
1695 26ee5aaf Ermal
function captiveportal_opendb() {
1696 5e71234e Chris Buechler
	global $g, $config, $cpzone, $cpzoneid;
1697 5060dea7 Scott Ullrich
1698 79e46ebd jim-p
	$db_path = "{$g['vardb_path']}/captiveportal{$cpzone}.db";
1699
	$createquery = "CREATE TABLE IF NOT EXISTS captiveportal (" .
1700
				"allow_time INTEGER, pipeno INTEGER, ip TEXT, mac TEXT, username TEXT, " .
1701
				"sessionid TEXT, bpassword TEXT, session_timeout INTEGER, idle_timeout INTEGER, " .
1702 f3e403d5 plumbeo
				"session_terminate_time INTEGER, interim_interval INTEGER, traffic_quota INTEGER, " .
1703 eb43c5b1 Augustin FL
				"authmethod TEXT, context TEXT); " .
1704 79e46ebd jim-p
			"CREATE UNIQUE INDEX IF NOT EXISTS idx_active ON captiveportal (sessionid, username); " .
1705
			"CREATE INDEX IF NOT EXISTS user ON captiveportal (username); " .
1706
			"CREATE INDEX IF NOT EXISTS ip ON captiveportal (ip); " .
1707
			"CREATE INDEX IF NOT EXISTS starttime ON captiveportal (allow_time)";
1708
1709 6b8ad2da jim-p
	try {
1710
		$DB = new SQLite3($db_path);
1711 18ca572b Chris Buechler
		$DB->busyTimeout(60000);
1712 6b8ad2da jim-p
	} catch (Exception $e) {
1713
		captiveportal_syslog("Could not open {$db_path} as an sqlite database for {$cpzone}. Error message: " . $e->getMessage() . " -- Trying again.");
1714
		unlink_if_exists($db_path);
1715
		try {
1716
			$DB = new SQLite3($db_path);
1717 18ca572b Chris Buechler
			$DB->busyTimeout(60000);
1718 6b8ad2da jim-p
		} catch (Exception $e) {
1719
			captiveportal_syslog("Still could not open {$db_path} as an sqlite database for {$cpzone}. Error message: " . $e->getMessage() . " -- Remove the database file manually and ensure there is enough free space.");
1720
			return;
1721
		}
1722
	}
1723
1724 7c38032f jim-p
	if (!$DB) {
1725
		captiveportal_syslog("Could not open {$db_path} as an sqlite database for {$cpzone}. Error message: {$DB->lastErrorMsg()}. Trying again.");
1726
		unlink_if_exists($db_path);
1727
		$DB = new SQLite3($db_path);
1728 18ca572b Chris Buechler
		$DB->busyTimeout(60000);
1729 7c38032f jim-p
		if (!$DB) {
1730
			captiveportal_syslog("Still could not open {$db_path} as an sqlite database for {$cpzone}. Error message: {$DB->lastErrorMsg()}. Remove the database file manually and ensure there is enough free space.");
1731
			return;
1732
		}
1733
	}
1734
1735 79e46ebd jim-p
	if (! $DB->exec($createquery)) {
1736
		captiveportal_syslog("Error during table {$cpzone} creation. Error message: {$DB->lastErrorMsg()}. Resetting and trying again.");
1737
1738
		/* If unable to initialize the database, reset and try again. */
1739
		$DB->close();
1740
		unset($DB);
1741
		unlink_if_exists($db_path);
1742
		$DB = new SQLite3($db_path);
1743 18ca572b Chris Buechler
		$DB->busyTimeout(60000);
1744 79e46ebd jim-p
		if ($DB->exec($createquery)) {
1745
			captiveportal_syslog("Successfully reinitialized tables for {$cpzone} -- database has been reset.");
1746 5e71234e Chris Buechler
			if (!is_numericint($cpzoneid)) {
1747
				if (is_array($config['captiveportal'])) {
1748
					foreach ($config['captiveportal'] as $cpkey => $cp) {
1749 0c388fef Renato Botelho
						if ($cpzone == $cpkey) {
1750 5e71234e Chris Buechler
							$cpzoneid = $cp['zoneid'];
1751
						}
1752
					}
1753
				}
1754
			}
1755
			if (is_numericint($cpzoneid)) {
1756 517b893e Renato Botelho
				$table_names = captiveportal_get_ipfw_table_names();
1757
				foreach ($table_names as $table_name) {
1758
					mwexec("/sbin/ipfw table {$table_name} flush");
1759
				}
1760 5e71234e Chris Buechler
				captiveportal_syslog("Flushed tables for {$cpzone} after database reset.");
1761
			}
1762 79e46ebd jim-p
		} else {
1763
			captiveportal_syslog("Still unable to create tables for {$cpzone}. Error message: {$DB->lastErrorMsg()}. Remove the database file manually and try again.");
1764
		}
1765 1e0b1727 Phil Davis
	}
1766 26ee5aaf Ermal
1767
	return $DB;
1768 0bd34ed6 Scott Ullrich
}
1769
1770 517b893e Renato Botelho
/* Get all tables for specific cpzone */
1771
function captiveportal_get_ipfw_table_names() {
1772
	global $cpzone;
1773
1774
	$result = array();
1775
	$tables = pfSense_ipfw_tables_list();
1776
1777
	if (!is_array($tables)) {
1778
		return $result;
1779
	}
1780
1781
	$len = strlen($cpzone) + 1;
1782
	foreach ($tables as $table) {
1783
		if (substr($table['name'], 0, $len) != $cpzone . '_') {
1784
			continue;
1785
		}
1786
1787
		$result[] = $table['name'];
1788
	}
1789
1790
	return $result;
1791
}
1792
1793 26ee5aaf Ermal
/* read captive portal DB into array */
1794
function captiveportal_read_db($query = "") {
1795 5cf91315 Renato Botelho
	$cpdb = array();
1796 5060dea7 Scott Ullrich
1797 26ee5aaf Ermal
	$DB = captiveportal_opendb();
1798
	if ($DB) {
1799 5cf91315 Renato Botelho
		$response = $DB->query("SELECT * FROM captiveportal {$query}");
1800 d338018f Ermal
		if ($response != FALSE) {
1801 1e0b1727 Phil Davis
			while ($row = $response->fetchArray()) {
1802 d338018f Ermal
				$cpdb[] = $row;
1803 1e0b1727 Phil Davis
			}
1804 d338018f Ermal
		}
1805 5cf91315 Renato Botelho
		$DB->close();
1806 006802ab Ermal
	}
1807 26ee5aaf Ermal
1808
	return $cpdb;
1809
}
1810
1811
function captiveportal_remove_entries($remove) {
1812
1813 1e0b1727 Phil Davis
	if (!is_array($remove) || empty($remove)) {
1814 26ee5aaf Ermal
		return;
1815 1e0b1727 Phil Davis
	}
1816 26ee5aaf Ermal
1817 1974c2d6 bcyrill
	$query = "DELETE FROM captiveportal WHERE sessionid in (";
1818 1e0b1727 Phil Davis
	foreach ($remove as $idx => $unindex) {
1819 26ee5aaf Ermal
		$query .= "'{$unindex}'";
1820 1e0b1727 Phil Davis
		if ($idx < (count($remove) - 1)) {
1821 26ee5aaf Ermal
			$query .= ",";
1822 1e0b1727 Phil Davis
		}
1823 006802ab Ermal
	}
1824 26ee5aaf Ermal
	$query .= ")";
1825
	captiveportal_write_db($query);
1826
}
1827
1828
/* write captive portal DB */
1829
function captiveportal_write_db($queries) {
1830
	global $g;
1831
1832 1e0b1727 Phil Davis
	if (is_array($queries)) {
1833 26ee5aaf Ermal
		$query = implode(";", $queries);
1834 1e0b1727 Phil Davis
	} else {
1835 26ee5aaf Ermal
		$query = $queries;
1836 1e0b1727 Phil Davis
	}
1837 26ee5aaf Ermal
1838
	$DB = captiveportal_opendb();
1839
	if ($DB) {
1840 5cf91315 Renato Botelho
		$DB->exec("BEGIN TRANSACTION");
1841
		$result = $DB->exec($query);
1842 1e0b1727 Phil Davis
		if (!$result) {
1843 5cf91315 Renato Botelho
			captiveportal_syslog("Trying to modify DB returned error: {$DB->lastErrorMsg()}");
1844 1e0b1727 Phil Davis
		} else {
1845 5cf91315 Renato Botelho
			$DB->exec("END TRANSACTION");
1846 1e0b1727 Phil Davis
		}
1847 5cf91315 Renato Botelho
		$DB->close();
1848 26ee5aaf Ermal
		return $result;
1849 1e0b1727 Phil Davis
	} else {
1850 26ee5aaf Ermal
		return true;
1851 1e0b1727 Phil Davis
	}
1852 0bd34ed6 Scott Ullrich
}
1853
1854
function captiveportal_write_elements() {
1855 b4792bf8 Ermal
	global $g, $config, $cpzone;
1856 1e0b1727 Phil Davis
1857 b4792bf8 Ermal
	$cpcfg = $config['captiveportal'][$cpzone];
1858
1859 1e0b1727 Phil Davis
	if (!is_dir($g['captiveportal_element_path'])) {
1860 769e254e Ermal
		@mkdir($g['captiveportal_element_path']);
1861 1e0b1727 Phil Davis
	}
1862 769e254e Ermal
1863 b4792bf8 Ermal
	if (is_array($cpcfg['element'])) {
1864
		foreach ($cpcfg['element'] as $data) {
1865 84b128b6 jim-p
			/* Do not attempt to decode or write out empty files. */
1866 748372bc Stephen Jones
			if (isset($data['nocontent'])) {
1867
					continue;
1868
			}
1869 84b128b6 jim-p
			if (empty($data['content']) || empty(base64_decode($data['content']))) {
1870
				unlink_if_exists("{$g['captiveportal_element_path']}/{$data['name']}");
1871
				touch("{$g['captiveportal_element_path']}/{$data['name']}");
1872
			} elseif (!@file_put_contents("{$g['captiveportal_element_path']}/{$data['name']}", base64_decode($data['content']))) {
1873 1579e70f Phil Davis
				printf(gettext('Error: cannot open \'%1$s\' in captiveportal_write_elements()%2$s'), $data['name'], "\n");
1874 1fadb31d Scott Ullrich
				return 1;
1875
			}
1876 1e0b1727 Phil Davis
			if (!file_exists("{$g['captiveportal_path']}/{$data['name']}")) {
1877 bdba4fa7 Ermal
				@symlink("{$g['captiveportal_element_path']}/{$data['name']}", "{$g['captiveportal_path']}/{$data['name']}");
1878 1e0b1727 Phil Davis
			}
1879 1fadb31d Scott Ullrich
		}
1880
	}
1881 1e0b1727 Phil Davis
1882 769e254e Ermal
	return 0;
1883 0bd34ed6 Scott Ullrich
}
1884
1885 b27df7cf Renato Botelho
function captiveportal_free_dnrules($rulenos_start = 2000,
1886
    $rulenos_range_max = 64500, $dry_run = false) {
1887 fbfbc6bd Renato Botelho
	global $g, $cpzone;
1888 7fb23399 Ermal
1889 b2c92623 Renato Botelho
	$removed_pipes = array();
1890
1891 ff8b4019 Renato Botelho
	if (!file_exists("{$g['vardb_path']}/captiveportaldn.rules")) {
1892
		return $removed_pipes;
1893
	}
1894
1895 b27df7cf Renato Botelho
	if (!$dry_run) {
1896
		$cpruleslck = lock("captiveportalrulesdn", LOCK_EX);
1897
	}
1898 ff8b4019 Renato Botelho
1899
	$rules = unserialize(file_get_contents(
1900
	    "{$g['vardb_path']}/captiveportaldn.rules"));
1901
	$ridx = $rulenos_start;
1902
	while ($ridx < $rulenos_range_max) {
1903
		if ($rules[$ridx] == $cpzone) {
1904
			if (!$dry_run) {
1905
				$rules[$ridx] = false;
1906 1e0b1727 Phil Davis
			}
1907 ff8b4019 Renato Botelho
			$removed_pipes[] = $ridx;
1908
			$ridx++;
1909
			if (!$dry_run) {
1910
				$rules[$ridx] = false;
1911
			}
1912
			$removed_pipes[] = $ridx;
1913
			$ridx++;
1914
		} else {
1915
			$ridx += 2;
1916 7fb23399 Ermal
		}
1917
	}
1918 ff8b4019 Renato Botelho
1919 b27df7cf Renato Botelho
	if (!$dry_run) {
1920 ff8b4019 Renato Botelho
		file_put_contents("{$g['vardb_path']}/captiveportaldn.rules",
1921
		    serialize($rules));
1922 b27df7cf Renato Botelho
		unlock($cpruleslck);
1923
	}
1924 b2c92623 Renato Botelho
1925 ff8b4019 Renato Botelho
	unset($rules);
1926
1927 b2c92623 Renato Botelho
	return $removed_pipes;
1928 7fb23399 Ermal
}
1929
1930 aea56408 Ermal
function captiveportal_get_next_dn_ruleno($rulenos_start = 2000, $rulenos_range_max = 64500) {
1931 7fb23399 Ermal
	global $config, $g, $cpzone;
1932 6ce61a8f Ermal
1933 aea56408 Ermal
	$cpruleslck = lock("captiveportalrulesdn", LOCK_EX);
1934
	$ruleno = 0;
1935
	if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) {
1936
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules"));
1937 1f965b69 Ermal
		$ridx = $rulenos_start;
1938
		while ($ridx < $rulenos_range_max) {
1939 f38b383b Ermal
			if (empty($rules[$ridx])) {
1940 1f965b69 Ermal
				$ruleno = $ridx;
1941
				$rules[$ridx] = $cpzone;
1942 aea56408 Ermal
				$ridx++;
1943 1f965b69 Ermal
				$rules[$ridx] = $cpzone;
1944
				break;
1945 21f82ab6 Ermal
			} else {
1946
				$ridx += 2;
1947 aea56408 Ermal
			}
1948
		}
1949
	} else {
1950 fe3693cb Ermal
		$rules = array_pad(array(), $rulenos_range_max, false);
1951 d2c98878 falbertopl
		$ruleno = $rulenos_start;
1952 7fb23399 Ermal
		$rules[$rulenos_start] = $cpzone;
1953 1f965b69 Ermal
		$rulenos_start++;
1954
		$rules[$rulenos_start] = $cpzone;
1955 aea56408 Ermal
	}
1956
	file_put_contents("{$g['vardb_path']}/captiveportaldn.rules", serialize($rules));
1957
	unlock($cpruleslck);
1958 1f965b69 Ermal
	unset($rules);
1959 aea56408 Ermal
1960
	return $ruleno;
1961
}
1962
1963
function captiveportal_free_dn_ruleno($ruleno) {
1964 87e7fdea bcyrill
	global $config, $g;
1965
1966
	$cpruleslck = lock("captiveportalrulesdn", LOCK_EX);
1967
	if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) {
1968
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules"));
1969
		$rules[$ruleno] = false;
1970 a2a42c72 Ermal
		$ruleno++;
1971
		$rules[$ruleno] = false;
1972 87e7fdea bcyrill
		file_put_contents("{$g['vardb_path']}/captiveportaldn.rules", serialize($rules));
1973 1f965b69 Ermal
		unset($rules);
1974 87e7fdea bcyrill
	}
1975
	unlock($cpruleslck);
1976 aea56408 Ermal
}
1977
1978
function captiveportal_get_dn_passthru_ruleno($value) {
1979 baec2b00 Ermal
	global $config, $g, $cpzone, $cpzoneid;
1980 b273dd26 Ermal
1981
	$cpcfg = $config['captiveportal'][$cpzone];
1982 1e0b1727 Phil Davis
	if (!isset($cpcfg['enable'])) {
1983 b273dd26 Ermal
		return NULL;
1984 1e0b1727 Phil Davis
	}
1985 aea56408 Ermal
1986 fe7e987e Ermal
	$cpruleslck = lock("captiveportalrulesdn", LOCK_EX);
1987 1f965b69 Ermal
	$ruleno = NULL;
1988 fe7e987e Ermal
	if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) {
1989 1f965b69 Ermal
		unset($output);
1990 517b893e Renato Botelho
		$item = pfSense_ipfw_table_lookup("{$cpzone}_pipe_mac",
1991
		    "any,{$value}");
1992
		if (!is_array($item) || empty($item['pipe'])) {
1993
			unlock($cpruleslck);
1994
			return NULL;
1995 6ce61a8f Ermal
		}
1996
1997 517b893e Renato Botelho
		$ruleno = intval($item['pipe']);
1998
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules"));
1999 1e0b1727 Phil Davis
		if (!$rules[$ruleno]) {
2000 1f965b69 Ermal
			$ruleno = NULL;
2001 1e0b1727 Phil Davis
		}
2002 1f965b69 Ermal
		unset($rules);
2003 5060dea7 Scott Ullrich
	}
2004 d31bc32a Ermal
	unlock($cpruleslck);
2005 1f965b69 Ermal
2006
	return $ruleno;
2007 920cafaf Scott Ullrich
}
2008
2009 360d815d Scott Ullrich
/**
2010
 * This function will calculate the traffic produced by a client
2011
 * based on its firewall rule
2012
 *
2013
 * Point of view: NAS
2014
 *
2015
 * Input means: from the client
2016
 * Output means: to the client
2017
 *
2018
 */
2019
2020 f4c867e0 Renato Botelho
function getVolume($ip) {
2021 b40b4a3e Renato Botelho
	global $config, $cpzone;
2022 360d815d Scott Ullrich
2023 b40b4a3e Renato Botelho
	$reverse = isset($config['captiveportal'][$cpzone]['reverseacct'])
2024
	    ? true : false;
2025 5060dea7 Scott Ullrich
	$volume = array();
2026
	// Initialize vars properly, since we don't want NULL vars
2027 b40b4a3e Renato Botelho
	$volume['input_pkts'] = $volume['input_bytes'] = 0;
2028
	$volume['output_pkts'] = $volume['output_bytes'] = 0;
2029 360d815d Scott Ullrich
2030 b40b4a3e Renato Botelho
	$tables = array("allowed", "auth");
2031
2032
	foreach($tables as $table) {
2033
		$ipfw = pfSense_ipfw_table_lookup("{$cpzone}_{$table}_up", $ip);
2034
		if (!is_array($ipfw)) {
2035
			continue;
2036
		}
2037 f48abba2 Michael Newton
		if ($reverse) {
2038
			$volume['output_pkts'] = $ipfw['packets'];
2039
			$volume['output_bytes'] = $ipfw['bytes'];
2040 b40b4a3e Renato Botelho
		} else {
2041 f48abba2 Michael Newton
			$volume['input_pkts'] = $ipfw['packets'];
2042
			$volume['input_bytes'] = $ipfw['bytes'];
2043
		}
2044 5060dea7 Scott Ullrich
	}
2045 f9f71ad3 Ermal Lu?i
2046 b40b4a3e Renato Botelho
	foreach($tables as $table) {
2047
		$ipfw = pfSense_ipfw_table_lookup("{$cpzone}_{$table}_down",
2048
		    $ip);
2049
		if (!is_array($ipfw)) {
2050
			continue;
2051
		}
2052 f48abba2 Michael Newton
		if ($reverse) {
2053
			$volume['input_pkts'] = $ipfw['packets'];
2054
			$volume['input_bytes'] = $ipfw['bytes'];
2055 b40b4a3e Renato Botelho
		} else {
2056 f48abba2 Michael Newton
			$volume['output_pkts'] = $ipfw['packets'];
2057
			$volume['output_bytes'] = $ipfw['bytes'];
2058
		}
2059 5060dea7 Scott Ullrich
	}
2060 360d815d Scott Ullrich
2061 5060dea7 Scott Ullrich
	return $volume;
2062 360d815d Scott Ullrich
}
2063
2064 f8b11310 Ermal Lu?i
function portal_ip_from_client_ip($cliip) {
2065 b4792bf8 Ermal
	global $config, $cpzone;
2066 f8b11310 Ermal Lu?i
2067 45bef774 bcyrill
	$isipv6 = is_ipaddrv6($cliip);
2068 b4792bf8 Ermal
	$interfaces = explode(",", $config['captiveportal'][$cpzone]['interface']);
2069 f8b11310 Ermal Lu?i
	foreach ($interfaces as $cpif) {
2070 45bef774 bcyrill
		if ($isipv6) {
2071
			$ip = get_interface_ipv6($cpif);
2072
			$sn = get_interface_subnetv6($cpif);
2073
		} else {
2074
			$ip = get_interface_ip($cpif);
2075
			$sn = get_interface_subnet($cpif);
2076
		}
2077 1e0b1727 Phil Davis
		if (ip_in_subnet($cliip, "{$ip}/{$sn}")) {
2078 f8b11310 Ermal Lu?i
			return $ip;
2079 1e0b1727 Phil Davis
		}
2080 f8b11310 Ermal Lu?i
	}
2081
2082 45bef774 bcyrill
	$inet = ($isipv6) ? '-inet6' : '-inet';
2083
	$iface = exec_command("/sbin/route -n get {$inet} {$cliip} | /usr/bin/awk '/interface/ { print \$2; };'");
2084 f86fa91c jim-p
	$iface = trim($iface, "\n");
2085
	if (!empty($iface)) {
2086 45bef774 bcyrill
		$ip = ($isipv6) ? find_interface_ipv6($iface) : find_interface_ip($iface);
2087 1e0b1727 Phil Davis
		if (is_ipaddr($ip)) {
2088 f86fa91c jim-p
			return $ip;
2089 1e0b1727 Phil Davis
		}
2090 f86fa91c jim-p
	}
2091
2092 cc125e13 Chris Buechler
	// doesn't match up to any particular interface
2093 1e0b1727 Phil Davis
	// so let's set the portal IP to what PHP says
2094
	// the server IP issuing the request is.
2095
	// allows same behavior as 1.2.x where IP isn't
2096 cc125e13 Chris Buechler
	// in the subnet of any CP interface (static routes, etc.)
2097
	// rather than forcing to DNS hostname resolution
2098
	$ip = $_SERVER['SERVER_ADDR'];
2099 1e0b1727 Phil Davis
	if (is_ipaddr($ip)) {
2100 cc125e13 Chris Buechler
		return $ip;
2101 1e0b1727 Phil Davis
	}
2102 cc125e13 Chris Buechler
2103 f8b11310 Ermal Lu?i
	return false;
2104
}
2105
2106 de132ae3 bcyrill
function portal_hostname_from_client_ip($cliip) {
2107
	global $config, $cpzone;
2108
2109
	$cpcfg = $config['captiveportal'][$cpzone];
2110
2111
	if (isset($cpcfg['httpslogin'])) {
2112 4320083f Renato Botelho
		$listenporthttps = $cpcfg['listenporthttps'] ? $cpcfg['listenporthttps'] : ($cpcfg['zoneid'] + 8001);
2113 de132ae3 bcyrill
		$ourhostname = $cpcfg['httpsname'];
2114 1e0b1727 Phil Davis
2115
		if ($listenporthttps != 443) {
2116 de132ae3 bcyrill
			$ourhostname .= ":" . $listenporthttps;
2117 1e0b1727 Phil Davis
		}
2118 de132ae3 bcyrill
	} else {
2119 6c07db48 Phil Davis
		$listenporthttp = $cpcfg['listenporthttp'] ? $cpcfg['listenporthttp'] : ($cpcfg['zoneid'] + 8000);
2120 de132ae3 bcyrill
		$ifip = portal_ip_from_client_ip($cliip);
2121 1e0b1727 Phil Davis
		if (!$ifip) {
2122 de132ae3 bcyrill
			$ourhostname = "{$config['system']['hostname']}.{$config['system']['domain']}";
2123 1e0b1727 Phil Davis
		} else {
2124 de132ae3 bcyrill
			$ourhostname = (is_ipaddrv6($ifip)) ? "[{$ifip}]" : "{$ifip}";
2125 1e0b1727 Phil Davis
		}
2126
2127
		if ($listenporthttp != 80) {
2128 de132ae3 bcyrill
			$ourhostname .= ":" . $listenporthttp;
2129 1e0b1727 Phil Davis
		}
2130 de132ae3 bcyrill
	}
2131 1e0b1727 Phil Davis
2132 d5ac388b bcyrill
	return $ourhostname;
2133 de132ae3 bcyrill
}
2134
2135 ac631bba lgcosta
/* functions move from index.php */
2136
2137
function portal_reply_page($redirurl, $type = null, $message = null, $clientmac = null, $clientip = null, $username = null, $password = null) {
2138 b4792bf8 Ermal
	global $g, $config, $cpzone;
2139 ac631bba lgcosta
2140
	/* Get captive portal layout */
2141
	if ($type == "redir") {
2142
		header("Location: {$redirurl}");
2143
		return;
2144 1e0b1727 Phil Davis
	} else if ($type == "login") {
2145 b4792bf8 Ermal
		$htmltext = get_include_contents("{$g['varetc_path']}/captiveportal_{$cpzone}.html");
2146 1e0b1727 Phil Davis
	} else {
2147 b4792bf8 Ermal
		$htmltext = get_include_contents("{$g['varetc_path']}/captiveportal-{$cpzone}-error.html");
2148 1e0b1727 Phil Davis
	}
2149 b4792bf8 Ermal
2150
	$cpcfg = $config['captiveportal'][$cpzone];
2151 ac631bba lgcosta
2152
	/* substitute the PORTAL_REDIRURL variable */
2153 de132ae3 bcyrill
	if ($cpcfg['preauthurl']) {
2154
		$htmltext = str_replace("\$PORTAL_REDIRURL\$", "{$cpcfg['preauthurl']}", $htmltext);
2155
		$htmltext = str_replace("#PORTAL_REDIRURL#", "{$cpcfg['preauthurl']}", $htmltext);
2156 ac631bba lgcosta
	}
2157
2158
	/* substitute other variables */
2159 de132ae3 bcyrill
	$ourhostname = portal_hostname_from_client_ip($clientip);
2160
	$protocol = (isset($cpcfg['httpslogin'])) ? 'https://' : 'http://';
2161 0c388fef Renato Botelho
	$htmltext = str_replace("\$PORTAL_ACTION\$", "{$protocol}{$ourhostname}/index.php?zone={$cpzone}", $htmltext);
2162
	$htmltext = str_replace("#PORTAL_ACTION#", "{$protocol}{$ourhostname}/index.php?zone={$cpzone}", $htmltext);
2163 ac631bba lgcosta
2164 b4792bf8 Ermal
	$htmltext = str_replace("\$PORTAL_ZONE\$", htmlspecialchars($cpzone), $htmltext);
2165 ac631bba lgcosta
	$htmltext = str_replace("\$PORTAL_REDIRURL\$", htmlspecialchars($redirurl), $htmltext);
2166
	$htmltext = str_replace("\$PORTAL_MESSAGE\$", htmlspecialchars($message), $htmltext);
2167
	$htmltext = str_replace("\$CLIENT_MAC\$", htmlspecialchars($clientmac), $htmltext);
2168
	$htmltext = str_replace("\$CLIENT_IP\$", htmlspecialchars($clientip), $htmltext);
2169
2170 1e0b1727 Phil Davis
	// Special handling case for captive portal master page so that it can be ran
2171 ac631bba lgcosta
	// through the PHP interpreter using the include method above.  We convert the
2172
	// $VARIABLE$ case to #VARIABLE# in /etc/inc/captiveportal.inc before writing out.
2173 b4792bf8 Ermal
	$htmltext = str_replace("#PORTAL_ZONE#", htmlspecialchars($cpzone), $htmltext);
2174 ac631bba lgcosta
	$htmltext = str_replace("#PORTAL_REDIRURL#", htmlspecialchars($redirurl), $htmltext);
2175
	$htmltext = str_replace("#PORTAL_MESSAGE#", htmlspecialchars($message), $htmltext);
2176
	$htmltext = str_replace("#CLIENT_MAC#", htmlspecialchars($clientmac), $htmltext);
2177
	$htmltext = str_replace("#CLIENT_IP#", htmlspecialchars($clientip), $htmltext);
2178
	$htmltext = str_replace("#USERNAME#", htmlspecialchars($username), $htmltext);
2179
	$htmltext = str_replace("#PASSWORD#", htmlspecialchars($password), $htmltext);
2180
2181 87e7fdea bcyrill
	echo $htmltext;
2182 ac631bba lgcosta
}
2183
2184 aec0f2fd Ermal
function captiveportal_reapply_attributes($cpentry, $attributes) {
2185 2ec063f9 Warren Baker
	global $config, $cpzone, $g;
2186 87e7fdea bcyrill
2187 ce90c89a Ermal LUÇI
	if (isset($config['captiveportal'][$cpzone]['peruserbw'])) {
2188 384deecb Ermal LUÇI
		$dwfaultbw_up = !empty($config['captiveportal'][$cpzone]['bwdefaultup']) ? $config['captiveportal'][$cpzone]['bwdefaultup'] : 0;
2189
		$dwfaultbw_down = !empty($config['captiveportal'][$cpzone]['bwdefaultdn']) ? $config['captiveportal'][$cpzone]['bwdefaultdn'] : 0;
2190 1e0b1727 Phil Davis
	} else {
2191 ce90c89a Ermal LUÇI
		$dwfaultbw_up = $dwfaultbw_down = 0;
2192 1e0b1727 Phil Davis
	}
2193 7c4e07c6 jim-p
	/* pipe throughputs must always be an integer, enforce that restriction again here. */
2194 f87ddb3b plumbeo
	if (isset($config['captiveportal'][$cpzone]['radiusperuserbw'])) {
2195
		$bw_up = round(!empty($attributes['bw_up']) ? intval($attributes['bw_up'])/1000 : $dwfaultbw_up, 0);
2196
		$bw_down = round(!empty($attributes['bw_down']) ? intval($attributes['bw_down'])/1000 : $dwfaultbw_down, 0);
2197
	} else {
2198
		$bw_up = round($dwfaultbw_up,0);
2199
		$bw_down = round($dwfaultbw_down,0);
2200
	}
2201
2202 5705c60a Renato Botelho
	$bw_up_pipeno = $cpentry[1];
2203
	$bw_down_pipeno = $cpentry[1]+1;
2204 aec0f2fd Ermal
2205 517b893e Renato Botelho
	$_gb = @pfSense_ipfw_pipe("pipe {$bw_up_pipeno} config bw {$bw_up}Kbit/s queue 100 buckets 16");
2206
	$_gb = @pfSense_ipfw_pipe("pipe {$bw_down_pipeno} config bw {$bw_down}Kbit/s queue 100 buckets 16");
2207 10b9dfcf Ermal
	//captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "RADIUS_BANDWIDTH_REAPPLY", "{$bw_up}/{$bw_down}");
2208 aec0f2fd Ermal
2209 87e7fdea bcyrill
	unset($bw_up_pipeno, $bw_down_pipeno, $bw_up, $bw_down);
2210 aec0f2fd Ermal
}
2211
2212 eb43c5b1 Augustin FL
function portal_allow($clientip, $clientmac, $username, $password = null, $attributes = null, $pipeno = null, $authmethod = null, $context = 'first') {
2213 20588aac Augustin-FL
	global $redirurl, $g, $config, $type, $_POST, $cpzone, $cpzoneid;
2214 ac631bba lgcosta
2215
	// Ensure we create an array if we are missing attributes
2216 1e0b1727 Phil Davis
	if (!is_array($attributes)) {
2217 ac631bba lgcosta
		$attributes = array();
2218 1e0b1727 Phil Davis
	}
2219 ac631bba lgcosta
2220 26ee5aaf Ermal
	unset($sessionid);
2221 ac631bba lgcosta
2222 006802ab Ermal
	/* Do not allow concurrent login execution. */
2223 b4792bf8 Ermal
	$cpdblck = lock("captiveportaldb{$cpzone}", LOCK_EX);
2224 006802ab Ermal
2225 1e0b1727 Phil Davis
	if ($attributes['voucher']) {
2226 ac631bba lgcosta
		$remaining_time = $attributes['session_timeout'];
2227 eb43c5b1 Augustin FL
		$authmethod = "voucher"; // Set RADIUS-Attribute to Voucher to prevent ReAuth-Reqeuest for Vouchers Bug: #2155
2228
		$context = "voucher";
2229 1e0b1727 Phil Davis
	}
2230 ac631bba lgcosta
2231
	$writecfg = false;
2232 a8cf8a30 Augustin-FL
	/* If both "Add MAC addresses of connected users as pass-through MAC" and "Disable concurrent logins" are checked, 
2233
	then we need to check if the user was already authenticated using another MAC Address, and if so remove the previous Pass-Through MAC. */	
2234 20588aac Augustin-FL
	if ((isset($config['captiveportal'][$cpzone]['noconcurrentlogins'])) && ($username != 'unauthenticated') && isset($config['captiveportal'][$cpzone]['passthrumacadd'])) {
2235 517b893e Renato Botelho
		$mac = captiveportal_passthrumac_findbyname($username);
2236 a8cf8a30 Augustin-FL
		if (!empty($mac)) {
2237 517b893e Renato Botelho
			foreach ($config['captiveportal'][$cpzone]['passthrumac'] as $idx => $macent) {
2238
				if ($macent['mac'] != $mac['mac']) {
2239
					continue;
2240 ac631bba lgcosta
				}
2241 517b893e Renato Botelho
2242
				$pipeno = captiveportal_get_dn_passthru_ruleno($mac['mac']);
2243
				if ($pipeno) {
2244
					captiveportal_free_dn_ruleno($pipeno);
2245 a8cf8a30 Augustin-FL
					@pfSense_ipfw_table("{$cpzone}_pipe_mac", IP_FW_TABLE_XDEL, "any,{$mac['mac']}");
2246
					@pfSense_ipfw_table("{$cpzone}_pipe_mac", IP_FW_TABLE_XDEL, "{$mac['mac']},any");
2247 bd79529b Renato Botelho
					@pfSense_ipfw_pipe("pipe delete " . $pipeno+1);
2248
					@pfSense_ipfw_pipe("pipe delete " . $pipeno);
2249 517b893e Renato Botelho
				}
2250
				unset($config['captiveportal'][$cpzone]['passthrumac'][$idx]);
2251 ac631bba lgcosta
			}
2252
		}
2253
	}
2254
2255 26ee5aaf Ermal
	/* read in client database */
2256
	$query = "WHERE ip = '{$clientip}'";
2257 0a02fc5e Phil Davis
	$tmpusername = SQLite3::escapeString(strtolower($username));
2258 1e0b1727 Phil Davis
	if (isset($config['captiveportal'][$cpzone]['noconcurrentlogins'])) {
2259 26ee5aaf Ermal
		$query .= " OR (username != 'unauthenticated' AND lower(username) = '{$tmpusername}')";
2260 1e0b1727 Phil Davis
	}
2261 26ee5aaf Ermal
	$cpdb = captiveportal_read_db($query);
2262
2263 ebc0e4b6 Ermal
	/* Snapshot the timestamp */
2264 b09c2d86 Ermal
	$allow_time = time();
2265 26ee5aaf Ermal
	$unsetindexes = array();
2266
2267
	foreach ($cpdb as $cpentry) {
2268 ac631bba lgcosta
		/* on the same ip */
2269 5705c60a Renato Botelho
		if ($cpentry[2] == $clientip) {
2270 1e0b1727 Phil Davis
			if (isset($config['captiveportal'][$cpzone]['nomacfilter']) || $cpentry[3] == $clientmac) {
2271 086cf944 Phil Davis
				captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CONCURRENT LOGIN - REUSING OLD SESSION");
2272 1e0b1727 Phil Davis
			} else {
2273 086cf944 Phil Davis
				captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CONCURRENT LOGIN - REUSING IP {$cpentry[2]} WITH DIFFERENT MAC ADDRESS {$cpentry[3]}");
2274 1e0b1727 Phil Davis
			}
2275 5705c60a Renato Botelho
			$sessionid = $cpentry[5];
2276 ac631bba lgcosta
			break;
2277 1e0b1727 Phil Davis
		} elseif (($attributes['voucher']) && ($username != 'unauthenticated') && ($cpentry[4] == $username)) {
2278
			// user logged in with an active voucher. Check for how long and calculate
2279 ac631bba lgcosta
			// how much time we can give him (voucher credit - used time)
2280 5705c60a Renato Botelho
			$remaining_time = $cpentry[0] + $cpentry[7] - $allow_time;
2281 1e0b1727 Phil Davis
			if ($remaining_time < 0) { // just in case.
2282 ac631bba lgcosta
				$remaining_time = 0;
2283 1e0b1727 Phil Davis
			}
2284 ac631bba lgcosta
2285
			/* This user was already logged in so we disconnect the old one */
2286 eb43c5b1 Augustin FL
			captiveportal_disconnect($cpentry, 13);
2287 086cf944 Phil Davis
			captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CONCURRENT LOGIN - TERMINATING OLD SESSION");
2288 5705c60a Renato Botelho
			$unsetindexes[] = $cpentry[5];
2289 ac631bba lgcosta
			break;
2290 1e0b1727 Phil Davis
		} elseif ((isset($config['captiveportal'][$cpzone]['noconcurrentlogins'])) && ($username != 'unauthenticated')) {
2291 ac631bba lgcosta
			/* on the same username */
2292 5705c60a Renato Botelho
			if (strcasecmp($cpentry[4], $username) == 0) {
2293 ac631bba lgcosta
				/* This user was already logged in so we disconnect the old one */
2294 eb43c5b1 Augustin FL
				captiveportal_disconnect($cpentry, 13);
2295 086cf944 Phil Davis
				captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CONCURRENT LOGIN - TERMINATING OLD SESSION");
2296 5705c60a Renato Botelho
				$unsetindexes[] = $cpentry[5];
2297 ac631bba lgcosta
				break;
2298
			}
2299
		}
2300
	}
2301 f32eae2d Ermal
	unset($cpdb);
2302 ac631bba lgcosta
2303 1e0b1727 Phil Davis
	if (!empty($unsetindexes)) {
2304 26ee5aaf Ermal
		captiveportal_remove_entries($unsetindexes);
2305 1e0b1727 Phil Davis
	}
2306 26ee5aaf Ermal
2307 1e0b1727 Phil Davis
	if ($attributes['voucher'] && $remaining_time <= 0) {
2308 ac631bba lgcosta
		return 0;       // voucher already used and no time left
2309 1e0b1727 Phil Davis
	}
2310 ac631bba lgcosta
2311
	if (!isset($sessionid)) {
2312
		/* generate unique session ID */
2313
		$tod = gettimeofday();
2314
		$sessionid = substr(md5(mt_rand() . $tod['sec'] . $tod['usec'] . $clientip . $clientmac), 0, 16);
2315
2316 aa1c6774 Jonatan Ramos
		if (isset($config['captiveportal'][$cpzone]['peruserbw'])) {
2317
			$dwfaultbw_up = !empty($config['captiveportal'][$cpzone]['bwdefaultup']) ? $config['captiveportal'][$cpzone]['bwdefaultup'] : 0;
2318
			$dwfaultbw_down = !empty($config['captiveportal'][$cpzone]['bwdefaultdn']) ? $config['captiveportal'][$cpzone]['bwdefaultdn'] : 0;
2319
		} else {
2320
			$dwfaultbw_up = $dwfaultbw_down = 0;
2321
		}
2322 7c4e07c6 jim-p
		/* pipe throughputs must always be an integer, enforce that restriction again here. */
2323 f87ddb3b plumbeo
		if (isset($config['captiveportal'][$cpzone]['radiusperuserbw'])) {
2324
			$bw_up = round(!empty($attributes['bw_up']) ? intval($attributes['bw_up'])/1000 : $dwfaultbw_up, 0);
2325
			$bw_down = round(!empty($attributes['bw_down']) ? intval($attributes['bw_down'])/1000 : $dwfaultbw_down, 0);
2326
		} else {
2327
			$bw_up = round($dwfaultbw_up,0);
2328
			$bw_down = round($dwfaultbw_down,0);
2329
		}
2330 aa1c6774 Jonatan Ramos
2331 20588aac Augustin-FL
		if (isset($config['captiveportal'][$cpzone]['passthrumacadd'])) {
2332 a464eaf7 Stephen Jones
2333 ac631bba lgcosta
			$mac = array();
2334 6ffb064f Renato Botelho
			$mac['action'] = 'pass';
2335 ac631bba lgcosta
			$mac['mac'] = $clientmac;
2336 522f1cc7 Ermal
			$mac['ip'] = $clientip; /* Used only for logging */
2337 20588aac Augustin-FL
			$mac['username'] = $username;
2338
			if ($attributes['voucher']) {
2339
				$mac['logintype'] = "voucher";
2340 522f1cc7 Ermal
			}
2341 cc229ee9 Phil Davis
			if ($username == "unauthenticated") {
2342 6c07db48 Phil Davis
				$mac['descr'] = "Auto-added";
2343 20588aac Augustin-FL
			} else if ($authmethod == "voucher") {
2344
				$mac['descr'] = "Auto-added for voucher {$username}";
2345 1e0b1727 Phil Davis
			} else {
2346 6c07db48 Phil Davis
				$mac['descr'] = "Auto-added for user {$username}";
2347 1e0b1727 Phil Davis
			}
2348
			if (!empty($bw_up)) {
2349 ac631bba lgcosta
				$mac['bw_up'] = $bw_up;
2350 1e0b1727 Phil Davis
			}
2351
			if (!empty($bw_down)) {
2352 ac631bba lgcosta
				$mac['bw_down'] = $bw_down;
2353 1e0b1727 Phil Davis
			}
2354
			if (!is_array($config['captiveportal'][$cpzone]['passthrumac'])) {
2355 b4792bf8 Ermal
				$config['captiveportal'][$cpzone]['passthrumac'] = array();
2356 1e0b1727 Phil Davis
			}
2357 a464eaf7 Stephen Jones
			//check for mac duplicates before adding it to config.
2358
			$mac_duplicate = false;
2359
			foreach($config['captiveportal'][$cpzone]['passthrumac'] as $mac_check){
2360
				if($mac_check['mac'] == $mac['mac']){
2361
					$mac_duplicate = true;
2362
				}
2363
			}
2364
			if(!$mac_duplicate){
2365
				$config['captiveportal'][$cpzone]['passthrumac'][] = $mac;
2366
			}
2367 006802ab Ermal
			unlock($cpdblck);
2368 ac631bba lgcosta
			$macrules = captiveportal_passthrumac_configure_entry($mac);
2369 b4792bf8 Ermal
			file_put_contents("{$g['tmp_path']}/macentry_{$cpzone}.rules.tmp", $macrules);
2370 517b893e Renato Botelho
			mwexec("/sbin/ipfw -q {$g['tmp_path']}/macentry_{$cpzone}.rules.tmp");
2371 ac631bba lgcosta
			$writecfg = true;
2372
		} else {
2373 aea56408 Ermal
			/* See if a pipeno is passed, if not start sessions because this means there isn't one atm */
2374 1e0b1727 Phil Davis
			if (is_null($pipeno)) {
2375 aea56408 Ermal
				$pipeno = captiveportal_get_next_dn_ruleno();
2376 1e0b1727 Phil Davis
			}
2377 aea56408 Ermal
2378
			/* if the pool is empty, return appropriate message and exit */
2379
			if (is_null($pipeno)) {
2380
				portal_reply_page($redirurl, "error", "System reached maximum login capacity");
2381 12feed15 Ermal
				log_error("Zone: {$cpzone} - WARNING!  Captive portal has reached maximum login capacity");
2382 aea56408 Ermal
				unlock($cpdblck);
2383
				return;
2384
			}
2385
2386
			$bw_up_pipeno = $pipeno;
2387
			$bw_down_pipeno = $pipeno + 1;
2388 10b9dfcf Ermal
			//$bw_up /= 1000; // Scale to Kbit/s
2389 517b893e Renato Botelho
			$_gb = @pfSense_ipfw_pipe("pipe {$bw_up_pipeno} config bw {$bw_up}Kbit/s queue 100 buckets 16");
2390
			$_gb = @pfSense_ipfw_pipe("pipe {$bw_down_pipeno} config bw {$bw_down}Kbit/s queue 100 buckets 16");
2391 ac631bba lgcosta
2392 75395abf Renato Botelho
			$rule_entry = "{$clientip}/" . (is_ipaddrv6($clientip) ? "128" : "32");
2393 3c4fcd5b Renato Botelho
			if (!isset($config['captiveportal'][$cpzone]['nomacfilter'])) {
2394
				$rule_entry .= ",{$clientmac}";
2395
			}
2396
			$_gb = @pfSense_ipfw_table("{$cpzone}_auth_up", IP_FW_TABLE_XADD, "{$rule_entry}", $bw_up_pipeno);
2397 bd068df3 Luiz Souza
			$_gb = @pfSense_ipfw_table("{$cpzone}_auth_down", IP_FW_TABLE_XADD, "{$rule_entry}", $bw_down_pipeno);
2398 ac631bba lgcosta
2399 1e0b1727 Phil Davis
			if ($attributes['voucher']) {
2400 ac631bba lgcosta
				$attributes['session_timeout'] = $remaining_time;
2401 1e0b1727 Phil Davis
			}
2402
2403 1974c2d6 bcyrill
			/* handle empty attributes */
2404
			$session_timeout = (!empty($attributes['session_timeout'])) ? $attributes['session_timeout'] : 'NULL';
2405
			$idle_timeout = (!empty($attributes['idle_timeout'])) ? $attributes['idle_timeout'] : 'NULL';
2406
			$session_terminate_time = (!empty($attributes['session_terminate_time'])) ? $attributes['session_terminate_time'] : 'NULL';
2407 338c0941 Ermal
			$interim_interval = (!empty($attributes['interim_interval'])) ? $attributes['interim_interval'] : 'NULL';
2408 f3e403d5 plumbeo
			$traffic_quota = (!empty($attributes['maxbytes'])) ? $attributes['maxbytes'] : 'NULL';
2409 1974c2d6 bcyrill
2410
			/* escape username */
2411 5cf91315 Renato Botelho
			$safe_username = SQLite3::escapeString($username);
2412 ac631bba lgcosta
2413
			/* encode password in Base64 just in case it contains commas */
2414 d4e42c54 jim-p
			$bpassword = (isset($config['captiveportal'][$cpzone]['reauthenticate'])) ? base64_encode($password) : '';
2415 eb43c5b1 Augustin FL
			$insertquery = "INSERT INTO captiveportal (allow_time, pipeno, ip, mac, username, sessionid, bpassword, session_timeout, idle_timeout, session_terminate_time, interim_interval, traffic_quota, authmethod, context) ";
2416 1974c2d6 bcyrill
			$insertquery .= "VALUES ({$allow_time}, {$pipeno}, '{$clientip}', '{$clientmac}', '{$safe_username}', '{$sessionid}', '{$bpassword}', ";
2417 eb43c5b1 Augustin FL
			$insertquery .= "{$session_timeout}, {$idle_timeout}, {$session_terminate_time}, {$interim_interval}, {$traffic_quota}, '{$authmethod}', '{$context}')";
2418 ac631bba lgcosta
2419 26ee5aaf Ermal
			/* store information to database */
2420
			captiveportal_write_db($insertquery);
2421 006802ab Ermal
			unlock($cpdblck);
2422 1f965b69 Ermal
			unset($insertquery, $bpassword);
2423 006802ab Ermal
2424 e42ea151 Augustin FL
			$radacct = isset($config['captiveportal'][$cpzone]['radacct_enable']) ? true : false;
2425
			if ($authmethod === 'radius' && $radacct) {
2426
				captiveportal_send_server_accounting('start',
2427
					$pipeno, // ruleno
2428
					$username, // username
2429
					$clientip, // clientip
2430
					$clientmac, // clientmac
2431
					$sessionid, // sessionid
2432
					time());  // start time
2433 ac631bba lgcosta
			}
2434
		}
2435 a7ee038b Ermal
	} else {
2436 eb43c5b1 Augustin FL
		/* NOTE: #3062-11 If the pipeno has been allocated free it to not DoS the CP */
2437 1e0b1727 Phil Davis
		if (!is_null($pipeno)) {
2438 a7ee038b Ermal
			captiveportal_free_dn_ruleno($pipeno);
2439 1e0b1727 Phil Davis
		}
2440 a7ee038b Ermal
2441 006802ab Ermal
		unlock($cpdblck);
2442 a7ee038b Ermal
	}
2443 ac631bba lgcosta
2444 1e0b1727 Phil Davis
	if ($writecfg == true) {
2445 b05c860c doktornotor
		write_config(gettext("Captive Portal allowed users configuration changed"));
2446 1e0b1727 Phil Davis
	}
2447 ac631bba lgcosta
2448
	/* redirect user to desired destination */
2449 1e0b1727 Phil Davis
	if (!empty($attributes['url_redirection'])) {
2450 ac631bba lgcosta
		$my_redirurl = $attributes['url_redirection'];
2451 1e0b1727 Phil Davis
	} else if (!empty($redirurl)) {
2452 ac420abd Ermal
		$my_redirurl = $redirurl;
2453 1e0b1727 Phil Davis
	} else if (!empty($config['captiveportal'][$cpzone]['redirurl'])) {
2454 b4792bf8 Ermal
		$my_redirurl = $config['captiveportal'][$cpzone]['redirurl'];
2455 1e0b1727 Phil Davis
	}
2456 ac631bba lgcosta
2457 20588aac Augustin-FL
	if (isset($config['captiveportal'][$cpzone]['logoutwin_enable']) && !isset($config['captiveportal'][$cpzone]['passthrumacadd'])) {
2458 de132ae3 bcyrill
		$ourhostname = portal_hostname_from_client_ip($clientip);
2459
		$protocol = (isset($config['captiveportal'][$cpzone]['httpslogin'])) ? 'https://' : 'http://';
2460
		$logouturl = "{$protocol}{$ourhostname}/";
2461 ac631bba lgcosta
2462 1e0b1727 Phil Davis
		if (isset($attributes['reply_message'])) {
2463 ac631bba lgcosta
			$message = $attributes['reply_message'];
2464 1e0b1727 Phil Davis
		} else {
2465 ac631bba lgcosta
			$message = 0;
2466 1e0b1727 Phil Davis
		}
2467 ac631bba lgcosta
2468 86573bb9 Phil Davis
		include_once("{$g['varetc_path']}/captiveportal-{$cpzone}-logout.html");
2469 ac631bba lgcosta
2470
	} else {
2471 fb0c2bd6 Ermal
		portal_reply_page($my_redirurl, "redir", "Just redirect the user.");
2472 ac631bba lgcosta
	}
2473
2474
	return $sessionid;
2475
}
2476
2477
2478
/*
2479
 * Used for when pass-through credits are enabled.
2480
 * Returns true when there was at least one free login to deduct for the MAC.
2481
 * Expired entries are removed as they are seen.
2482
 * Active entries are updated according to the configuration.
2483
 */
2484
function portal_consume_passthrough_credit($clientmac) {
2485 b4792bf8 Ermal
	global $config, $cpzone;
2486 ac631bba lgcosta
2487 1e0b1727 Phil Davis
	if (!empty($config['captiveportal'][$cpzone]['freelogins_count']) && is_numeric($config['captiveportal'][$cpzone]['freelogins_count'])) {
2488 b4792bf8 Ermal
		$freeloginscount = $config['captiveportal'][$cpzone]['freelogins_count'];
2489 1e0b1727 Phil Davis
	} else {
2490 ac631bba lgcosta
		return false;
2491 1e0b1727 Phil Davis
	}
2492 ac631bba lgcosta
2493 1e0b1727 Phil Davis
	if (!empty($config['captiveportal'][$cpzone]['freelogins_resettimeout']) && is_numeric($config['captiveportal'][$cpzone]['freelogins_resettimeout'])) {
2494 b4792bf8 Ermal
		$resettimeout = $config['captiveportal'][$cpzone]['freelogins_resettimeout'];
2495 1e0b1727 Phil Davis
	} else {
2496 ac631bba lgcosta
		return false;
2497 1e0b1727 Phil Davis
	}
2498 ac631bba lgcosta
2499 1e0b1727 Phil Davis
	if ($freeloginscount < 1 || $resettimeout <= 0 || !$clientmac) {
2500 ac631bba lgcosta
		return false;
2501 1e0b1727 Phil Davis
	}
2502 ac631bba lgcosta
2503 b4792bf8 Ermal
	$updatetimeouts = isset($config['captiveportal'][$cpzone]['freelogins_updatetimeouts']);
2504 ac631bba lgcosta
2505
	/*
2506
	 * Read database of used MACs.  Lines are a comma-separated list
2507
	 * of the time, MAC, then the count of pass-through credits remaining.
2508
	 */
2509
	$usedmacs = captiveportal_read_usedmacs_db();
2510
2511
	$currenttime = time();
2512
	$found = false;
2513
	foreach ($usedmacs as $key => $usedmac) {
2514
		$usedmac = explode(",", $usedmac);
2515
2516
		if ($usedmac[1] == $clientmac) {
2517
			if ($usedmac[0] + ($resettimeout * 3600) > $currenttime) {
2518
				if ($usedmac[2] < 1) {
2519
					if ($updatetimeouts) {
2520
						$usedmac[0] = $currenttime;
2521
						unset($usedmacs[$key]);
2522
						$usedmacs[] = implode(",", $usedmac);
2523
						captiveportal_write_usedmacs_db($usedmacs);
2524
					}
2525
2526
					return false;
2527
				} else {
2528
					$usedmac[2] -= 1;
2529
					$usedmacs[$key] = implode(",", $usedmac);
2530
				}
2531
2532
				$found = true;
2533 1e0b1727 Phil Davis
			} else {
2534 ac631bba lgcosta
				unset($usedmacs[$key]);
2535 1e0b1727 Phil Davis
			}
2536 ac631bba lgcosta
2537
			break;
2538 1e0b1727 Phil Davis
		} else if ($usedmac[0] + ($resettimeout * 3600) <= $currenttime) {
2539
			unset($usedmacs[$key]);
2540
		}
2541 ac631bba lgcosta
	}
2542
2543
	if (!$found) {
2544
		$usedmac = array($currenttime, $clientmac, $freeloginscount - 1);
2545
		$usedmacs[] = implode(",", $usedmac);
2546
	}
2547
2548
	captiveportal_write_usedmacs_db($usedmacs);
2549
	return true;
2550
}
2551
2552
function captiveportal_read_usedmacs_db() {
2553 b4792bf8 Ermal
	global $g, $cpzone;
2554 ac631bba lgcosta
2555 b4792bf8 Ermal
	$cpumaclck = lock("captiveusedmacs{$cpzone}");
2556
	if (file_exists("{$g['vardb_path']}/captiveportal_usedmacs_{$cpzone}.db")) {
2557
		$usedmacs = file("{$g['vardb_path']}/captiveportal_usedmacs_{$cpzone}.db", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2558 1e0b1727 Phil Davis
		if (!$usedmacs) {
2559 ac631bba lgcosta
			$usedmacs = array();
2560 1e0b1727 Phil Davis
		}
2561
	} else {
2562 ac631bba lgcosta
		$usedmacs = array();
2563 1e0b1727 Phil Davis
	}
2564 ac631bba lgcosta
2565
	unlock($cpumaclck);
2566
	return $usedmacs;
2567
}
2568
2569
function captiveportal_write_usedmacs_db($usedmacs) {
2570 b4792bf8 Ermal
	global $g, $cpzone;
2571 ac631bba lgcosta
2572 b4792bf8 Ermal
	$cpumaclck = lock("captiveusedmacs{$cpzone}", LOCK_EX);
2573
	@file_put_contents("{$g['vardb_path']}/captiveportal_usedmacs_{$cpzone}.db", implode("\n", $usedmacs));
2574 ac631bba lgcosta
	unlock($cpumaclck);
2575
}
2576
2577 81ce28d8 Renato Botelho
function captiveportal_blocked_mac($mac) {
2578
	global $config, $g, $cpzone;
2579 0d33f1fc Renato Botelho
2580 1e0b1727 Phil Davis
	if (empty($mac) || !is_macaddr($mac)) {
2581 81ce28d8 Renato Botelho
		return false;
2582 1e0b1727 Phil Davis
	}
2583 0d33f1fc Renato Botelho
2584 1e0b1727 Phil Davis
	if (!is_array($config['captiveportal'][$cpzone]['passthrumac'])) {
2585 81ce28d8 Renato Botelho
		return false;
2586 1e0b1727 Phil Davis
	}
2587 0d33f1fc Renato Botelho
2588 1e0b1727 Phil Davis
	foreach ($config['captiveportal'][$cpzone]['passthrumac'] as $passthrumac) {
2589 81ce28d8 Renato Botelho
		if (($passthrumac['action'] == 'block') &&
2590 ae52d165 Renato Botelho
		    ($passthrumac['mac'] == strtolower($mac))) {
2591 81ce28d8 Renato Botelho
			return true;
2592 1e0b1727 Phil Davis
		}
2593
	}
2594 0d33f1fc Renato Botelho
2595 81ce28d8 Renato Botelho
	return false;
2596 3b2769be Renato Botelho
2597
}
2598
2599 e42ea151 Augustin FL
/* Captiveportal Radius Accounting */
2600
2601
function gigawords($bytes) {
2602
2603
	/*
2604
	 * RFC2866 Specifies a 32bit unsigned integer, which is a max of 4294967295
2605
	 * Currently there is a fault in the PECL radius_put_int function which can handle only 32bit signed integer.
2606
	 */
2607
2608
	// We use BCMath functions since normal integers don't work with so large numbers
2609
	$gigawords = bcdiv( bcsub( $bytes, remainder($bytes) ) , GIGAWORDS_RIGHT_OPERAND) ;
2610
2611
	// We need to manually set this to a zero instead of NULL for put_int() safety
2612
	if (is_null($gigawords)) {
2613
		$gigawords = 0;
2614
	}
2615
2616
	return $gigawords;
2617
}
2618
2619
function remainder($bytes) {
2620
	// Calculate the bytes we are going to send to the radius
2621
	$bytes = bcmod($bytes, GIGAWORDS_RIGHT_OPERAND);
2622 748372bc Stephen Jones
2623 e42ea151 Augustin FL
	if (is_null($bytes)) {
2624
		$bytes = 0;
2625
	}
2626
2627
    return $bytes;
2628
}
2629
2630 5f1aaed4 Augustin FL
function captiveportal_send_server_accounting($type = 'on', $ruleno = null, $username = null, $clientip = null, $clientmac = null, $sessionid = null, $start_time = null, $stop_time = null, $term_cause = null) {
2631 62f20eab Michael Newton
	global $cpzone, $config;
2632
2633 5f1aaed4 Augustin FL
	$cpcfg = $config['captiveportal'][$cpzone];
2634
	$acctcfg = auth_get_authserver($cpcfg['radacct_server']);
2635 748372bc Stephen Jones
2636 5f1aaed4 Augustin FL
	if (!isset($cpcfg['radacct_enable']) || empty($acctcfg)) {
2637
		return null;
2638 62f20eab Michael Newton
	}
2639 748372bc Stephen Jones
2640 5f1aaed4 Augustin FL
	if ($type === 'on') {
2641
		$racct = new Auth_RADIUS_Acct_On;
2642
	} elseif ($type === 'off') {
2643 62f20eab Michael Newton
		$racct = new Auth_RADIUS_Acct_Off;
2644 5f1aaed4 Augustin FL
	} elseif ($type === 'start') {
2645
		$racct = new Auth_RADIUS_Acct_Start;
2646
		if (!is_int($start_time)) {
2647
			$start_time = time();
2648
		}
2649
	} elseif ($type === 'stop') {
2650
		$racct = new Auth_RADIUS_Acct_Stop;
2651
		if (!is_int($stop_time)) {
2652
			$stop_time = time();
2653
		}
2654
	} elseif ($type === 'update') {
2655
        $racct = new Auth_RADIUS_Acct_Update;
2656
		if (!is_int($stop_time)) {
2657
			$stop_time = time(); // "top time" here will be used only for calcuating session time.
2658
		}
2659 62f20eab Michael Newton
	} else {
2660 5f1aaed4 Augustin FL
		return null;
2661 62f20eab Michael Newton
	}
2662 5f1aaed4 Augustin FL
2663
	$racct->addServer($acctcfg['host'], $acctcfg['radius_acct_port'],
2664
		$acctcfg['radius_secret'], $acctcfg['radius_timeout']);
2665 748372bc Stephen Jones
2666 5f1aaed4 Augustin FL
	$racct->authentic = RADIUS_AUTH_RADIUS;
2667
	if (!empty($username)) {
2668
		$racct->username = $username;
2669 62f20eab Michael Newton
	}
2670
2671 5f1aaed4 Augustin FL
	if (PEAR::isError($racct->start())) {
2672
		captiveportal_syslog('RADIUS ACCOUNTING FAILED : '.$racct->getError());
2673 62f20eab Michael Newton
		$racct->close();
2674 5f1aaed4 Augustin FL
		return null;
2675
	}
2676 748372bc Stephen Jones
2677 5f1aaed4 Augustin FL
	$nasip = $acctcfg['radius_nasip_attribute'];
2678
	if (!is_ipaddr($nasip)) {
2679 748372bc Stephen Jones
		$nasip = get_interface_ip($nasip);
2680 5f1aaed4 Augustin FL
		if (!is_ipaddr($nasip)) {
2681
			$nasip = get_interface_ip();//We use WAN interface IP as fallback for NAS-IP-Address
2682
		}
2683
	}
2684
	$nasmac = get_interface_mac(find_ip_interface($nasip));
2685
	$racct->putAttribute(RADIUS_NAS_IP_ADDRESS, $nasip, "addr");
2686
	$racct->putAttribute(RADIUS_NAS_IDENTIFIER, 'CaptivePortal');
2687 748372bc Stephen Jones
2688 5f1aaed4 Augustin FL
	if (is_int($ruleno)) {
2689
		$racct->putAttribute(RADIUS_NAS_PORT_TYPE, RADIUS_ETHERNET);
2690
		$racct->putAttribute(RADIUS_NAS_PORT, intval($ruleno), 'integer');
2691
	}
2692 748372bc Stephen Jones
2693 5f1aaed4 Augustin FL
	if (!empty($sessionid)) {
2694
		$racct->putAttribute(RADIUS_ACCT_SESSION_ID, $sessionid);
2695
	}
2696
2697
	if (!empty($clientip) && is_ipaddr($clientip)) {
2698
		$racct->putAttribute(RADIUS_FRAMED_IP_ADDRESS, $clientip, "addr");
2699
	}
2700
	if (!empty($clientmac)) {
2701
		$racct->putAttribute(RADIUS_CALLING_STATION_ID, mac_format($clientmac));
2702
	}
2703
	if (!empty($nasmac)) {
2704
		$racct->putAttribute(RADIUS_CALLED_STATION_ID, mac_format($nasmac).':'.gethostname());
2705
	}
2706 748372bc Stephen Jones
2707 5f1aaed4 Augustin FL
	// Accounting request Stop and Update : send the current data volume
2708
	if (($type === 'stop' || $type === 'update') && is_int($start_time)) {
2709
		$volume = getVolume($clientip);
2710
		$session_time = $stop_time - $start_time;
2711
		$volume['input_bytes_radius'] = remainder($volume['input_bytes']);
2712
		$volume['input_gigawords'] = gigawords($volume['input_bytes']);
2713
		$volume['output_bytes_radius'] = remainder($volume['output_bytes']);
2714
		$volume['output_gigawords'] = gigawords($volume['output_bytes']);
2715
2716
		// Volume stuff: Ingress
2717
		$racct->putAttribute(RADIUS_ACCT_INPUT_PACKETS, intval($volume['input_pkts']), "integer");
2718
		$racct->putAttribute(RADIUS_ACCT_INPUT_OCTETS, intval($volume['input_bytes_radius']), "integer");
2719
		// Volume stuff: Outgress
2720
		$racct->putAttribute(RADIUS_ACCT_OUTPUT_PACKETS, intval($volume['output_pkts']), "integer");
2721
		$racct->putAttribute(RADIUS_ACCT_OUTPUT_OCTETS, intval($volume['output_bytes_radius']), "integer");
2722
		$racct->putAttribute(RADIUS_ACCT_SESSION_TIME, intval($session_time), "integer");
2723 748372bc Stephen Jones
2724 5f1aaed4 Augustin FL
		$racct->putAttribute(CUSTOM_RADIUS_ACCT_OUTPUT_GIGAWORDS, intval($volume['output_gigawords']), "integer");
2725
		$racct->putAttribute(CUSTOM_RADIUS_ACCT_INPUT_GIGAWORDS, intval($volume['input_gigawords']), "integer");
2726
		// Set session_time
2727
		$racct->session_time = $session_time;
2728
	}
2729 748372bc Stephen Jones
2730 5f1aaed4 Augustin FL
	if ($type === 'stop') {
2731
		if (empty($term_cause)) {
2732
			$term_cause = 1;
2733
		}
2734
		$racct->putAttribute(RADIUS_ACCT_TERMINATE_CAUSE, $term_cause);
2735
	}
2736 748372bc Stephen Jones
2737 62f20eab Michael Newton
	// Send request
2738
	$result = $racct->send();
2739 5f1aaed4 Augustin FL
2740 62f20eab Michael Newton
	if (PEAR::isError($result)) {
2741 5f1aaed4 Augustin FL
		 captiveportal_syslog('RADIUS ACCOUNTING FAILED : '.$racct->getError());
2742
		 $result = null;
2743
	} elseif ($result !== true) {
2744
		$result = false;
2745 62f20eab Michael Newton
	}
2746
2747
	$racct->close();
2748 5f1aaed4 Augustin FL
	return $result;
2749 62f20eab Michael Newton
}
2750 d2ecbddc jim-p
2751
function captiveportal_isip_logged($clientip) {
2752
	global $g, $cpzone;
2753
2754
	/* read in client database */
2755
	$query = "WHERE ip = '{$clientip}'";
2756
	$cpdb = captiveportal_read_db($query);
2757
	foreach ($cpdb as $cpentry) {
2758
		return $cpentry;
2759
	}
2760
}
2761 64c0462b Ermal
?>