Project

General

Profile

Download (92.8 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 b1862963 jim-p
				$attributes = array("nas_identifier" => empty($cpcfg["radiusnasid"]) ? "CaptivePortal-{$cpzone}" : $cpcfg["radiusnasid"],
1610 eb43c5b1 Augustin FL
					"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 bb90e3c5 Augustin-FL
				if ($context === 'radmac' && $clientmac) {
1623 eb43c5b1 Augustin FL
					if ($authcfg['type'] === 'radius') {
1624
						$login = mac_format($clientmac);
1625
						$status = "MACHINE LOGIN";
1626
					} else {
1627
						/* Trying to perform a Radius MAC authentication on a non-radius server - shouldn't happen! - bail out */
1628
						$msg = gettext("Internal Error");
1629
						log_error("Zone: {$cpzone} - Trying to perform RADIUS MAC authentication on a non-RADIUS server !");
1630
						$radmac_error = true;
1631
						$result = null;
1632
					}
1633
				}
1634 748372bc Stephen Jones
1635 eb43c5b1 Augustin FL
				if (!$radmac_error) {
1636
					if ($authcfg['type'] === 'none') {
1637
						$result = true;
1638
					} else {
1639
						$result = authenticate_user($login, $password, $authcfg, $attributes);
1640
					}
1641 748372bc Stephen Jones
1642 eb43c5b1 Augustin FL
					if (!empty($attributes['error_message'])) {
1643
						$msg = $attributes['error_message'];
1644
					}
1645 748372bc Stephen Jones
1646 eb43c5b1 Augustin FL
					if ($authcfg['type'] == 'Local Auth' && $result && isset($cpcfg['localauth_priv'])) {
1647
						if (!userHasPrivilege(getUserEntry($login), "user-services-captiveportal-login")) {
1648
							$result = false;
1649
							$msg = gettext("Access Denied");
1650
						}
1651
					}
1652 bb90e3c5 Augustin-FL
					if ($context === 'radmac' && $result === null && empty($attributes['reply_message'])) {
1653
						$msg = gettext("RADIUS MAC Authentication Failed.");
1654
					}
1655 748372bc Stephen Jones
1656 eb43c5b1 Augustin FL
					if (empty($status)) {
1657
						if ($result === true) {
1658
							$status = "ACCEPT";
1659
						} elseif ($result === null) {
1660
							$status = "ERROR";
1661
						} else {
1662
							$status = "FAILURE";
1663
						}
1664
					}
1665 748372bc Stephen Jones
1666 bb90e3c5 Augustin-FL
					if ($context === 'radmac' && $login == mac_format($clientmac) || $authcfg['type'] === 'none' && empty($login)) {
1667 eb43c5b1 Augustin FL
						$login = "unauthenticated";
1668 748372bc Stephen Jones
					}
1669 eb43c5b1 Augustin FL
					// We determine a flag
1670
					if ($result === true) {
1671
						$val = 3;
1672
					} elseif ($result === false && !empty($attributes['reply_message'])) {
1673
						$val = 2;
1674
						$msg = $attributes['reply_message'];
1675
					} elseif ($result === false) {
1676
						$val = 1;
1677
					} elseif ($result === null) {
1678
						$val = 0;
1679
					}
1680
				}
1681 748372bc Stephen Jones
1682 eb43c5b1 Augustin FL
				if ($val >= $auth_val) {
1683
					$auth_val = $val;
1684
					$auth_method = $authcfg['type'];
1685
					$login_status = $status;
1686
					$login_msg = $msg;
1687
					$reply_attributes = $attributes;
1688
					$auth_result = $result;
1689
				}
1690
			}
1691
		}
1692 1e0b1727 Phil Davis
	}
1693 0bd34ed6 Scott Ullrich
1694 eb43c5b1 Augustin FL
	return array('result'=>$auth_result, 'attributes'=>$reply_attributes, 'auth_method' =>$auth_method, 'login_status'=> $login_status, 'login_message' => $login_msg);
1695 0bd34ed6 Scott Ullrich
}
1696
1697 26ee5aaf Ermal
function captiveportal_opendb() {
1698 5e71234e Chris Buechler
	global $g, $config, $cpzone, $cpzoneid;
1699 5060dea7 Scott Ullrich
1700 79e46ebd jim-p
	$db_path = "{$g['vardb_path']}/captiveportal{$cpzone}.db";
1701
	$createquery = "CREATE TABLE IF NOT EXISTS captiveportal (" .
1702
				"allow_time INTEGER, pipeno INTEGER, ip TEXT, mac TEXT, username TEXT, " .
1703
				"sessionid TEXT, bpassword TEXT, session_timeout INTEGER, idle_timeout INTEGER, " .
1704 f3e403d5 plumbeo
				"session_terminate_time INTEGER, interim_interval INTEGER, traffic_quota INTEGER, " .
1705 eb43c5b1 Augustin FL
				"authmethod TEXT, context TEXT); " .
1706 79e46ebd jim-p
			"CREATE UNIQUE INDEX IF NOT EXISTS idx_active ON captiveportal (sessionid, username); " .
1707
			"CREATE INDEX IF NOT EXISTS user ON captiveportal (username); " .
1708
			"CREATE INDEX IF NOT EXISTS ip ON captiveportal (ip); " .
1709
			"CREATE INDEX IF NOT EXISTS starttime ON captiveportal (allow_time)";
1710
1711 6b8ad2da jim-p
	try {
1712
		$DB = new SQLite3($db_path);
1713 18ca572b Chris Buechler
		$DB->busyTimeout(60000);
1714 6b8ad2da jim-p
	} catch (Exception $e) {
1715
		captiveportal_syslog("Could not open {$db_path} as an sqlite database for {$cpzone}. Error message: " . $e->getMessage() . " -- Trying again.");
1716
		unlink_if_exists($db_path);
1717
		try {
1718
			$DB = new SQLite3($db_path);
1719 18ca572b Chris Buechler
			$DB->busyTimeout(60000);
1720 6b8ad2da jim-p
		} catch (Exception $e) {
1721
			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.");
1722
			return;
1723
		}
1724
	}
1725
1726 7c38032f jim-p
	if (!$DB) {
1727
		captiveportal_syslog("Could not open {$db_path} as an sqlite database for {$cpzone}. Error message: {$DB->lastErrorMsg()}. Trying again.");
1728
		unlink_if_exists($db_path);
1729
		$DB = new SQLite3($db_path);
1730 18ca572b Chris Buechler
		$DB->busyTimeout(60000);
1731 7c38032f jim-p
		if (!$DB) {
1732
			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.");
1733
			return;
1734
		}
1735
	}
1736
1737 79e46ebd jim-p
	if (! $DB->exec($createquery)) {
1738
		captiveportal_syslog("Error during table {$cpzone} creation. Error message: {$DB->lastErrorMsg()}. Resetting and trying again.");
1739
1740
		/* If unable to initialize the database, reset and try again. */
1741
		$DB->close();
1742
		unset($DB);
1743
		unlink_if_exists($db_path);
1744
		$DB = new SQLite3($db_path);
1745 18ca572b Chris Buechler
		$DB->busyTimeout(60000);
1746 79e46ebd jim-p
		if ($DB->exec($createquery)) {
1747
			captiveportal_syslog("Successfully reinitialized tables for {$cpzone} -- database has been reset.");
1748 5e71234e Chris Buechler
			if (!is_numericint($cpzoneid)) {
1749
				if (is_array($config['captiveportal'])) {
1750
					foreach ($config['captiveportal'] as $cpkey => $cp) {
1751 0c388fef Renato Botelho
						if ($cpzone == $cpkey) {
1752 5e71234e Chris Buechler
							$cpzoneid = $cp['zoneid'];
1753
						}
1754
					}
1755
				}
1756
			}
1757
			if (is_numericint($cpzoneid)) {
1758 517b893e Renato Botelho
				$table_names = captiveportal_get_ipfw_table_names();
1759
				foreach ($table_names as $table_name) {
1760
					mwexec("/sbin/ipfw table {$table_name} flush");
1761
				}
1762 5e71234e Chris Buechler
				captiveportal_syslog("Flushed tables for {$cpzone} after database reset.");
1763
			}
1764 79e46ebd jim-p
		} else {
1765
			captiveportal_syslog("Still unable to create tables for {$cpzone}. Error message: {$DB->lastErrorMsg()}. Remove the database file manually and try again.");
1766
		}
1767 1e0b1727 Phil Davis
	}
1768 26ee5aaf Ermal
1769
	return $DB;
1770 0bd34ed6 Scott Ullrich
}
1771
1772 517b893e Renato Botelho
/* Get all tables for specific cpzone */
1773
function captiveportal_get_ipfw_table_names() {
1774
	global $cpzone;
1775
1776
	$result = array();
1777
	$tables = pfSense_ipfw_tables_list();
1778
1779
	if (!is_array($tables)) {
1780
		return $result;
1781
	}
1782
1783
	$len = strlen($cpzone) + 1;
1784
	foreach ($tables as $table) {
1785
		if (substr($table['name'], 0, $len) != $cpzone . '_') {
1786
			continue;
1787
		}
1788
1789
		$result[] = $table['name'];
1790
	}
1791
1792
	return $result;
1793
}
1794
1795 26ee5aaf Ermal
/* read captive portal DB into array */
1796
function captiveportal_read_db($query = "") {
1797 5cf91315 Renato Botelho
	$cpdb = array();
1798 5060dea7 Scott Ullrich
1799 26ee5aaf Ermal
	$DB = captiveportal_opendb();
1800
	if ($DB) {
1801 5cf91315 Renato Botelho
		$response = $DB->query("SELECT * FROM captiveportal {$query}");
1802 d338018f Ermal
		if ($response != FALSE) {
1803 1e0b1727 Phil Davis
			while ($row = $response->fetchArray()) {
1804 d338018f Ermal
				$cpdb[] = $row;
1805 1e0b1727 Phil Davis
			}
1806 d338018f Ermal
		}
1807 5cf91315 Renato Botelho
		$DB->close();
1808 006802ab Ermal
	}
1809 26ee5aaf Ermal
1810
	return $cpdb;
1811
}
1812
1813
function captiveportal_remove_entries($remove) {
1814
1815 1e0b1727 Phil Davis
	if (!is_array($remove) || empty($remove)) {
1816 26ee5aaf Ermal
		return;
1817 1e0b1727 Phil Davis
	}
1818 26ee5aaf Ermal
1819 1974c2d6 bcyrill
	$query = "DELETE FROM captiveportal WHERE sessionid in (";
1820 1e0b1727 Phil Davis
	foreach ($remove as $idx => $unindex) {
1821 26ee5aaf Ermal
		$query .= "'{$unindex}'";
1822 1e0b1727 Phil Davis
		if ($idx < (count($remove) - 1)) {
1823 26ee5aaf Ermal
			$query .= ",";
1824 1e0b1727 Phil Davis
		}
1825 006802ab Ermal
	}
1826 26ee5aaf Ermal
	$query .= ")";
1827
	captiveportal_write_db($query);
1828
}
1829
1830
/* write captive portal DB */
1831
function captiveportal_write_db($queries) {
1832
	global $g;
1833
1834 1e0b1727 Phil Davis
	if (is_array($queries)) {
1835 26ee5aaf Ermal
		$query = implode(";", $queries);
1836 1e0b1727 Phil Davis
	} else {
1837 26ee5aaf Ermal
		$query = $queries;
1838 1e0b1727 Phil Davis
	}
1839 26ee5aaf Ermal
1840
	$DB = captiveportal_opendb();
1841
	if ($DB) {
1842 5cf91315 Renato Botelho
		$DB->exec("BEGIN TRANSACTION");
1843
		$result = $DB->exec($query);
1844 1e0b1727 Phil Davis
		if (!$result) {
1845 5cf91315 Renato Botelho
			captiveportal_syslog("Trying to modify DB returned error: {$DB->lastErrorMsg()}");
1846 1e0b1727 Phil Davis
		} else {
1847 5cf91315 Renato Botelho
			$DB->exec("END TRANSACTION");
1848 1e0b1727 Phil Davis
		}
1849 5cf91315 Renato Botelho
		$DB->close();
1850 26ee5aaf Ermal
		return $result;
1851 1e0b1727 Phil Davis
	} else {
1852 26ee5aaf Ermal
		return true;
1853 1e0b1727 Phil Davis
	}
1854 0bd34ed6 Scott Ullrich
}
1855
1856
function captiveportal_write_elements() {
1857 b4792bf8 Ermal
	global $g, $config, $cpzone;
1858 1e0b1727 Phil Davis
1859 b4792bf8 Ermal
	$cpcfg = $config['captiveportal'][$cpzone];
1860
1861 1e0b1727 Phil Davis
	if (!is_dir($g['captiveportal_element_path'])) {
1862 769e254e Ermal
		@mkdir($g['captiveportal_element_path']);
1863 1e0b1727 Phil Davis
	}
1864 769e254e Ermal
1865 b4792bf8 Ermal
	if (is_array($cpcfg['element'])) {
1866
		foreach ($cpcfg['element'] as $data) {
1867 84b128b6 jim-p
			/* Do not attempt to decode or write out empty files. */
1868 748372bc Stephen Jones
			if (isset($data['nocontent'])) {
1869
					continue;
1870
			}
1871 84b128b6 jim-p
			if (empty($data['content']) || empty(base64_decode($data['content']))) {
1872
				unlink_if_exists("{$g['captiveportal_element_path']}/{$data['name']}");
1873
				touch("{$g['captiveportal_element_path']}/{$data['name']}");
1874
			} elseif (!@file_put_contents("{$g['captiveportal_element_path']}/{$data['name']}", base64_decode($data['content']))) {
1875 1579e70f Phil Davis
				printf(gettext('Error: cannot open \'%1$s\' in captiveportal_write_elements()%2$s'), $data['name'], "\n");
1876 1fadb31d Scott Ullrich
				return 1;
1877
			}
1878 1e0b1727 Phil Davis
			if (!file_exists("{$g['captiveportal_path']}/{$data['name']}")) {
1879 bdba4fa7 Ermal
				@symlink("{$g['captiveportal_element_path']}/{$data['name']}", "{$g['captiveportal_path']}/{$data['name']}");
1880 1e0b1727 Phil Davis
			}
1881 1fadb31d Scott Ullrich
		}
1882
	}
1883 1e0b1727 Phil Davis
1884 769e254e Ermal
	return 0;
1885 0bd34ed6 Scott Ullrich
}
1886
1887 b27df7cf Renato Botelho
function captiveportal_free_dnrules($rulenos_start = 2000,
1888
    $rulenos_range_max = 64500, $dry_run = false) {
1889 fbfbc6bd Renato Botelho
	global $g, $cpzone;
1890 7fb23399 Ermal
1891 b2c92623 Renato Botelho
	$removed_pipes = array();
1892
1893 ff8b4019 Renato Botelho
	if (!file_exists("{$g['vardb_path']}/captiveportaldn.rules")) {
1894
		return $removed_pipes;
1895
	}
1896
1897 b27df7cf Renato Botelho
	if (!$dry_run) {
1898
		$cpruleslck = lock("captiveportalrulesdn", LOCK_EX);
1899
	}
1900 ff8b4019 Renato Botelho
1901
	$rules = unserialize(file_get_contents(
1902
	    "{$g['vardb_path']}/captiveportaldn.rules"));
1903
	$ridx = $rulenos_start;
1904
	while ($ridx < $rulenos_range_max) {
1905
		if ($rules[$ridx] == $cpzone) {
1906
			if (!$dry_run) {
1907
				$rules[$ridx] = false;
1908 1e0b1727 Phil Davis
			}
1909 ff8b4019 Renato Botelho
			$removed_pipes[] = $ridx;
1910
			$ridx++;
1911
			if (!$dry_run) {
1912
				$rules[$ridx] = false;
1913
			}
1914
			$removed_pipes[] = $ridx;
1915
			$ridx++;
1916
		} else {
1917
			$ridx += 2;
1918 7fb23399 Ermal
		}
1919
	}
1920 ff8b4019 Renato Botelho
1921 b27df7cf Renato Botelho
	if (!$dry_run) {
1922 ff8b4019 Renato Botelho
		file_put_contents("{$g['vardb_path']}/captiveportaldn.rules",
1923
		    serialize($rules));
1924 b27df7cf Renato Botelho
		unlock($cpruleslck);
1925
	}
1926 b2c92623 Renato Botelho
1927 ff8b4019 Renato Botelho
	unset($rules);
1928
1929 b2c92623 Renato Botelho
	return $removed_pipes;
1930 7fb23399 Ermal
}
1931
1932 aea56408 Ermal
function captiveportal_get_next_dn_ruleno($rulenos_start = 2000, $rulenos_range_max = 64500) {
1933 7fb23399 Ermal
	global $config, $g, $cpzone;
1934 6ce61a8f Ermal
1935 aea56408 Ermal
	$cpruleslck = lock("captiveportalrulesdn", LOCK_EX);
1936
	$ruleno = 0;
1937
	if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) {
1938
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules"));
1939 1f965b69 Ermal
		$ridx = $rulenos_start;
1940
		while ($ridx < $rulenos_range_max) {
1941 f38b383b Ermal
			if (empty($rules[$ridx])) {
1942 1f965b69 Ermal
				$ruleno = $ridx;
1943
				$rules[$ridx] = $cpzone;
1944 aea56408 Ermal
				$ridx++;
1945 1f965b69 Ermal
				$rules[$ridx] = $cpzone;
1946
				break;
1947 21f82ab6 Ermal
			} else {
1948
				$ridx += 2;
1949 aea56408 Ermal
			}
1950
		}
1951
	} else {
1952 fe3693cb Ermal
		$rules = array_pad(array(), $rulenos_range_max, false);
1953 d2c98878 falbertopl
		$ruleno = $rulenos_start;
1954 7fb23399 Ermal
		$rules[$rulenos_start] = $cpzone;
1955 1f965b69 Ermal
		$rulenos_start++;
1956
		$rules[$rulenos_start] = $cpzone;
1957 aea56408 Ermal
	}
1958
	file_put_contents("{$g['vardb_path']}/captiveportaldn.rules", serialize($rules));
1959
	unlock($cpruleslck);
1960 1f965b69 Ermal
	unset($rules);
1961 aea56408 Ermal
1962
	return $ruleno;
1963
}
1964
1965
function captiveportal_free_dn_ruleno($ruleno) {
1966 87e7fdea bcyrill
	global $config, $g;
1967
1968
	$cpruleslck = lock("captiveportalrulesdn", LOCK_EX);
1969
	if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) {
1970
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules"));
1971
		$rules[$ruleno] = false;
1972 a2a42c72 Ermal
		$ruleno++;
1973
		$rules[$ruleno] = false;
1974 87e7fdea bcyrill
		file_put_contents("{$g['vardb_path']}/captiveportaldn.rules", serialize($rules));
1975 1f965b69 Ermal
		unset($rules);
1976 87e7fdea bcyrill
	}
1977
	unlock($cpruleslck);
1978 aea56408 Ermal
}
1979
1980
function captiveportal_get_dn_passthru_ruleno($value) {
1981 baec2b00 Ermal
	global $config, $g, $cpzone, $cpzoneid;
1982 b273dd26 Ermal
1983
	$cpcfg = $config['captiveportal'][$cpzone];
1984 1e0b1727 Phil Davis
	if (!isset($cpcfg['enable'])) {
1985 b273dd26 Ermal
		return NULL;
1986 1e0b1727 Phil Davis
	}
1987 aea56408 Ermal
1988 fe7e987e Ermal
	$cpruleslck = lock("captiveportalrulesdn", LOCK_EX);
1989 1f965b69 Ermal
	$ruleno = NULL;
1990 fe7e987e Ermal
	if (file_exists("{$g['vardb_path']}/captiveportaldn.rules")) {
1991 1f965b69 Ermal
		unset($output);
1992 517b893e Renato Botelho
		$item = pfSense_ipfw_table_lookup("{$cpzone}_pipe_mac",
1993
		    "any,{$value}");
1994
		if (!is_array($item) || empty($item['pipe'])) {
1995
			unlock($cpruleslck);
1996
			return NULL;
1997 6ce61a8f Ermal
		}
1998
1999 517b893e Renato Botelho
		$ruleno = intval($item['pipe']);
2000
		$rules = unserialize(file_get_contents("{$g['vardb_path']}/captiveportaldn.rules"));
2001 1e0b1727 Phil Davis
		if (!$rules[$ruleno]) {
2002 1f965b69 Ermal
			$ruleno = NULL;
2003 1e0b1727 Phil Davis
		}
2004 1f965b69 Ermal
		unset($rules);
2005 5060dea7 Scott Ullrich
	}
2006 d31bc32a Ermal
	unlock($cpruleslck);
2007 1f965b69 Ermal
2008
	return $ruleno;
2009 920cafaf Scott Ullrich
}
2010
2011 360d815d Scott Ullrich
/**
2012
 * This function will calculate the traffic produced by a client
2013
 * based on its firewall rule
2014
 *
2015
 * Point of view: NAS
2016
 *
2017
 * Input means: from the client
2018
 * Output means: to the client
2019
 *
2020
 */
2021
2022 f4c867e0 Renato Botelho
function getVolume($ip) {
2023 b40b4a3e Renato Botelho
	global $config, $cpzone;
2024 360d815d Scott Ullrich
2025 b40b4a3e Renato Botelho
	$reverse = isset($config['captiveportal'][$cpzone]['reverseacct'])
2026
	    ? true : false;
2027 5060dea7 Scott Ullrich
	$volume = array();
2028
	// Initialize vars properly, since we don't want NULL vars
2029 b40b4a3e Renato Botelho
	$volume['input_pkts'] = $volume['input_bytes'] = 0;
2030
	$volume['output_pkts'] = $volume['output_bytes'] = 0;
2031 360d815d Scott Ullrich
2032 b40b4a3e Renato Botelho
	$tables = array("allowed", "auth");
2033
2034
	foreach($tables as $table) {
2035
		$ipfw = pfSense_ipfw_table_lookup("{$cpzone}_{$table}_up", $ip);
2036
		if (!is_array($ipfw)) {
2037
			continue;
2038
		}
2039 f48abba2 Michael Newton
		if ($reverse) {
2040
			$volume['output_pkts'] = $ipfw['packets'];
2041
			$volume['output_bytes'] = $ipfw['bytes'];
2042 b40b4a3e Renato Botelho
		} else {
2043 f48abba2 Michael Newton
			$volume['input_pkts'] = $ipfw['packets'];
2044
			$volume['input_bytes'] = $ipfw['bytes'];
2045
		}
2046 5060dea7 Scott Ullrich
	}
2047 f9f71ad3 Ermal Lu?i
2048 b40b4a3e Renato Botelho
	foreach($tables as $table) {
2049
		$ipfw = pfSense_ipfw_table_lookup("{$cpzone}_{$table}_down",
2050
		    $ip);
2051
		if (!is_array($ipfw)) {
2052
			continue;
2053
		}
2054 f48abba2 Michael Newton
		if ($reverse) {
2055
			$volume['input_pkts'] = $ipfw['packets'];
2056
			$volume['input_bytes'] = $ipfw['bytes'];
2057 b40b4a3e Renato Botelho
		} else {
2058 f48abba2 Michael Newton
			$volume['output_pkts'] = $ipfw['packets'];
2059
			$volume['output_bytes'] = $ipfw['bytes'];
2060
		}
2061 5060dea7 Scott Ullrich
	}
2062 360d815d Scott Ullrich
2063 5060dea7 Scott Ullrich
	return $volume;
2064 360d815d Scott Ullrich
}
2065
2066 f8b11310 Ermal Lu?i
function portal_ip_from_client_ip($cliip) {
2067 b4792bf8 Ermal
	global $config, $cpzone;
2068 f8b11310 Ermal Lu?i
2069 45bef774 bcyrill
	$isipv6 = is_ipaddrv6($cliip);
2070 b4792bf8 Ermal
	$interfaces = explode(",", $config['captiveportal'][$cpzone]['interface']);
2071 f8b11310 Ermal Lu?i
	foreach ($interfaces as $cpif) {
2072 45bef774 bcyrill
		if ($isipv6) {
2073
			$ip = get_interface_ipv6($cpif);
2074
			$sn = get_interface_subnetv6($cpif);
2075
		} else {
2076
			$ip = get_interface_ip($cpif);
2077
			$sn = get_interface_subnet($cpif);
2078
		}
2079 1e0b1727 Phil Davis
		if (ip_in_subnet($cliip, "{$ip}/{$sn}")) {
2080 f8b11310 Ermal Lu?i
			return $ip;
2081 1e0b1727 Phil Davis
		}
2082 f8b11310 Ermal Lu?i
	}
2083
2084 45bef774 bcyrill
	$inet = ($isipv6) ? '-inet6' : '-inet';
2085
	$iface = exec_command("/sbin/route -n get {$inet} {$cliip} | /usr/bin/awk '/interface/ { print \$2; };'");
2086 f86fa91c jim-p
	$iface = trim($iface, "\n");
2087
	if (!empty($iface)) {
2088 45bef774 bcyrill
		$ip = ($isipv6) ? find_interface_ipv6($iface) : find_interface_ip($iface);
2089 1e0b1727 Phil Davis
		if (is_ipaddr($ip)) {
2090 f86fa91c jim-p
			return $ip;
2091 1e0b1727 Phil Davis
		}
2092 f86fa91c jim-p
	}
2093
2094 cc125e13 Chris Buechler
	// doesn't match up to any particular interface
2095 1e0b1727 Phil Davis
	// so let's set the portal IP to what PHP says
2096
	// the server IP issuing the request is.
2097
	// allows same behavior as 1.2.x where IP isn't
2098 cc125e13 Chris Buechler
	// in the subnet of any CP interface (static routes, etc.)
2099
	// rather than forcing to DNS hostname resolution
2100
	$ip = $_SERVER['SERVER_ADDR'];
2101 1e0b1727 Phil Davis
	if (is_ipaddr($ip)) {
2102 cc125e13 Chris Buechler
		return $ip;
2103 1e0b1727 Phil Davis
	}
2104 cc125e13 Chris Buechler
2105 f8b11310 Ermal Lu?i
	return false;
2106
}
2107
2108 de132ae3 bcyrill
function portal_hostname_from_client_ip($cliip) {
2109
	global $config, $cpzone;
2110
2111
	$cpcfg = $config['captiveportal'][$cpzone];
2112
2113
	if (isset($cpcfg['httpslogin'])) {
2114 4320083f Renato Botelho
		$listenporthttps = $cpcfg['listenporthttps'] ? $cpcfg['listenporthttps'] : ($cpcfg['zoneid'] + 8001);
2115 de132ae3 bcyrill
		$ourhostname = $cpcfg['httpsname'];
2116 1e0b1727 Phil Davis
2117
		if ($listenporthttps != 443) {
2118 de132ae3 bcyrill
			$ourhostname .= ":" . $listenporthttps;
2119 1e0b1727 Phil Davis
		}
2120 de132ae3 bcyrill
	} else {
2121 6c07db48 Phil Davis
		$listenporthttp = $cpcfg['listenporthttp'] ? $cpcfg['listenporthttp'] : ($cpcfg['zoneid'] + 8000);
2122 de132ae3 bcyrill
		$ifip = portal_ip_from_client_ip($cliip);
2123 1e0b1727 Phil Davis
		if (!$ifip) {
2124 de132ae3 bcyrill
			$ourhostname = "{$config['system']['hostname']}.{$config['system']['domain']}";
2125 1e0b1727 Phil Davis
		} else {
2126 de132ae3 bcyrill
			$ourhostname = (is_ipaddrv6($ifip)) ? "[{$ifip}]" : "{$ifip}";
2127 1e0b1727 Phil Davis
		}
2128
2129
		if ($listenporthttp != 80) {
2130 de132ae3 bcyrill
			$ourhostname .= ":" . $listenporthttp;
2131 1e0b1727 Phil Davis
		}
2132 de132ae3 bcyrill
	}
2133 1e0b1727 Phil Davis
2134 d5ac388b bcyrill
	return $ourhostname;
2135 de132ae3 bcyrill
}
2136
2137 ac631bba lgcosta
/* functions move from index.php */
2138
2139
function portal_reply_page($redirurl, $type = null, $message = null, $clientmac = null, $clientip = null, $username = null, $password = null) {
2140 b4792bf8 Ermal
	global $g, $config, $cpzone;
2141 ac631bba lgcosta
2142
	/* Get captive portal layout */
2143
	if ($type == "redir") {
2144
		header("Location: {$redirurl}");
2145
		return;
2146 1e0b1727 Phil Davis
	} else if ($type == "login") {
2147 b4792bf8 Ermal
		$htmltext = get_include_contents("{$g['varetc_path']}/captiveportal_{$cpzone}.html");
2148 1e0b1727 Phil Davis
	} else {
2149 b4792bf8 Ermal
		$htmltext = get_include_contents("{$g['varetc_path']}/captiveportal-{$cpzone}-error.html");
2150 1e0b1727 Phil Davis
	}
2151 b4792bf8 Ermal
2152
	$cpcfg = $config['captiveportal'][$cpzone];
2153 ac631bba lgcosta
2154
	/* substitute the PORTAL_REDIRURL variable */
2155 de132ae3 bcyrill
	if ($cpcfg['preauthurl']) {
2156
		$htmltext = str_replace("\$PORTAL_REDIRURL\$", "{$cpcfg['preauthurl']}", $htmltext);
2157
		$htmltext = str_replace("#PORTAL_REDIRURL#", "{$cpcfg['preauthurl']}", $htmltext);
2158 ac631bba lgcosta
	}
2159
2160
	/* substitute other variables */
2161 de132ae3 bcyrill
	$ourhostname = portal_hostname_from_client_ip($clientip);
2162
	$protocol = (isset($cpcfg['httpslogin'])) ? 'https://' : 'http://';
2163 0c388fef Renato Botelho
	$htmltext = str_replace("\$PORTAL_ACTION\$", "{$protocol}{$ourhostname}/index.php?zone={$cpzone}", $htmltext);
2164
	$htmltext = str_replace("#PORTAL_ACTION#", "{$protocol}{$ourhostname}/index.php?zone={$cpzone}", $htmltext);
2165 ac631bba lgcosta
2166 b4792bf8 Ermal
	$htmltext = str_replace("\$PORTAL_ZONE\$", htmlspecialchars($cpzone), $htmltext);
2167 ac631bba lgcosta
	$htmltext = str_replace("\$PORTAL_REDIRURL\$", htmlspecialchars($redirurl), $htmltext);
2168
	$htmltext = str_replace("\$PORTAL_MESSAGE\$", htmlspecialchars($message), $htmltext);
2169
	$htmltext = str_replace("\$CLIENT_MAC\$", htmlspecialchars($clientmac), $htmltext);
2170
	$htmltext = str_replace("\$CLIENT_IP\$", htmlspecialchars($clientip), $htmltext);
2171
2172 1e0b1727 Phil Davis
	// Special handling case for captive portal master page so that it can be ran
2173 ac631bba lgcosta
	// through the PHP interpreter using the include method above.  We convert the
2174
	// $VARIABLE$ case to #VARIABLE# in /etc/inc/captiveportal.inc before writing out.
2175 b4792bf8 Ermal
	$htmltext = str_replace("#PORTAL_ZONE#", htmlspecialchars($cpzone), $htmltext);
2176 ac631bba lgcosta
	$htmltext = str_replace("#PORTAL_REDIRURL#", htmlspecialchars($redirurl), $htmltext);
2177
	$htmltext = str_replace("#PORTAL_MESSAGE#", htmlspecialchars($message), $htmltext);
2178
	$htmltext = str_replace("#CLIENT_MAC#", htmlspecialchars($clientmac), $htmltext);
2179
	$htmltext = str_replace("#CLIENT_IP#", htmlspecialchars($clientip), $htmltext);
2180
	$htmltext = str_replace("#USERNAME#", htmlspecialchars($username), $htmltext);
2181
	$htmltext = str_replace("#PASSWORD#", htmlspecialchars($password), $htmltext);
2182
2183 87e7fdea bcyrill
	echo $htmltext;
2184 ac631bba lgcosta
}
2185
2186 aec0f2fd Ermal
function captiveportal_reapply_attributes($cpentry, $attributes) {
2187 2ec063f9 Warren Baker
	global $config, $cpzone, $g;
2188 87e7fdea bcyrill
2189 ce90c89a Ermal LUÇI
	if (isset($config['captiveportal'][$cpzone]['peruserbw'])) {
2190 384deecb Ermal LUÇI
		$dwfaultbw_up = !empty($config['captiveportal'][$cpzone]['bwdefaultup']) ? $config['captiveportal'][$cpzone]['bwdefaultup'] : 0;
2191
		$dwfaultbw_down = !empty($config['captiveportal'][$cpzone]['bwdefaultdn']) ? $config['captiveportal'][$cpzone]['bwdefaultdn'] : 0;
2192 1e0b1727 Phil Davis
	} else {
2193 ce90c89a Ermal LUÇI
		$dwfaultbw_up = $dwfaultbw_down = 0;
2194 1e0b1727 Phil Davis
	}
2195 7c4e07c6 jim-p
	/* pipe throughputs must always be an integer, enforce that restriction again here. */
2196 f87ddb3b plumbeo
	if (isset($config['captiveportal'][$cpzone]['radiusperuserbw'])) {
2197
		$bw_up = round(!empty($attributes['bw_up']) ? intval($attributes['bw_up'])/1000 : $dwfaultbw_up, 0);
2198
		$bw_down = round(!empty($attributes['bw_down']) ? intval($attributes['bw_down'])/1000 : $dwfaultbw_down, 0);
2199
	} else {
2200
		$bw_up = round($dwfaultbw_up,0);
2201
		$bw_down = round($dwfaultbw_down,0);
2202
	}
2203
2204 5705c60a Renato Botelho
	$bw_up_pipeno = $cpentry[1];
2205
	$bw_down_pipeno = $cpentry[1]+1;
2206 aec0f2fd Ermal
2207 517b893e Renato Botelho
	$_gb = @pfSense_ipfw_pipe("pipe {$bw_up_pipeno} config bw {$bw_up}Kbit/s queue 100 buckets 16");
2208
	$_gb = @pfSense_ipfw_pipe("pipe {$bw_down_pipeno} config bw {$bw_down}Kbit/s queue 100 buckets 16");
2209 10b9dfcf Ermal
	//captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "RADIUS_BANDWIDTH_REAPPLY", "{$bw_up}/{$bw_down}");
2210 aec0f2fd Ermal
2211 87e7fdea bcyrill
	unset($bw_up_pipeno, $bw_down_pipeno, $bw_up, $bw_down);
2212 aec0f2fd Ermal
}
2213
2214 eb43c5b1 Augustin FL
function portal_allow($clientip, $clientmac, $username, $password = null, $attributes = null, $pipeno = null, $authmethod = null, $context = 'first') {
2215 20588aac Augustin-FL
	global $redirurl, $g, $config, $type, $_POST, $cpzone, $cpzoneid;
2216 ac631bba lgcosta
2217
	// Ensure we create an array if we are missing attributes
2218 1e0b1727 Phil Davis
	if (!is_array($attributes)) {
2219 ac631bba lgcosta
		$attributes = array();
2220 1e0b1727 Phil Davis
	}
2221 ac631bba lgcosta
2222 26ee5aaf Ermal
	unset($sessionid);
2223 ac631bba lgcosta
2224 006802ab Ermal
	/* Do not allow concurrent login execution. */
2225 b4792bf8 Ermal
	$cpdblck = lock("captiveportaldb{$cpzone}", LOCK_EX);
2226 006802ab Ermal
2227 1e0b1727 Phil Davis
	if ($attributes['voucher']) {
2228 ac631bba lgcosta
		$remaining_time = $attributes['session_timeout'];
2229 eb43c5b1 Augustin FL
		$authmethod = "voucher"; // Set RADIUS-Attribute to Voucher to prevent ReAuth-Reqeuest for Vouchers Bug: #2155
2230
		$context = "voucher";
2231 1e0b1727 Phil Davis
	}
2232 ac631bba lgcosta
2233
	$writecfg = false;
2234 a8cf8a30 Augustin-FL
	/* If both "Add MAC addresses of connected users as pass-through MAC" and "Disable concurrent logins" are checked, 
2235
	then we need to check if the user was already authenticated using another MAC Address, and if so remove the previous Pass-Through MAC. */	
2236 20588aac Augustin-FL
	if ((isset($config['captiveportal'][$cpzone]['noconcurrentlogins'])) && ($username != 'unauthenticated') && isset($config['captiveportal'][$cpzone]['passthrumacadd'])) {
2237 517b893e Renato Botelho
		$mac = captiveportal_passthrumac_findbyname($username);
2238 a8cf8a30 Augustin-FL
		if (!empty($mac)) {
2239 517b893e Renato Botelho
			foreach ($config['captiveportal'][$cpzone]['passthrumac'] as $idx => $macent) {
2240
				if ($macent['mac'] != $mac['mac']) {
2241
					continue;
2242 ac631bba lgcosta
				}
2243 517b893e Renato Botelho
2244
				$pipeno = captiveportal_get_dn_passthru_ruleno($mac['mac']);
2245
				if ($pipeno) {
2246
					captiveportal_free_dn_ruleno($pipeno);
2247 a8cf8a30 Augustin-FL
					@pfSense_ipfw_table("{$cpzone}_pipe_mac", IP_FW_TABLE_XDEL, "any,{$mac['mac']}");
2248
					@pfSense_ipfw_table("{$cpzone}_pipe_mac", IP_FW_TABLE_XDEL, "{$mac['mac']},any");
2249 bd79529b Renato Botelho
					@pfSense_ipfw_pipe("pipe delete " . $pipeno+1);
2250
					@pfSense_ipfw_pipe("pipe delete " . $pipeno);
2251 517b893e Renato Botelho
				}
2252
				unset($config['captiveportal'][$cpzone]['passthrumac'][$idx]);
2253 ac631bba lgcosta
			}
2254
		}
2255
	}
2256
2257 26ee5aaf Ermal
	/* read in client database */
2258
	$query = "WHERE ip = '{$clientip}'";
2259 0a02fc5e Phil Davis
	$tmpusername = SQLite3::escapeString(strtolower($username));
2260 1e0b1727 Phil Davis
	if (isset($config['captiveportal'][$cpzone]['noconcurrentlogins'])) {
2261 26ee5aaf Ermal
		$query .= " OR (username != 'unauthenticated' AND lower(username) = '{$tmpusername}')";
2262 1e0b1727 Phil Davis
	}
2263 26ee5aaf Ermal
	$cpdb = captiveportal_read_db($query);
2264
2265 ebc0e4b6 Ermal
	/* Snapshot the timestamp */
2266 b09c2d86 Ermal
	$allow_time = time();
2267 26ee5aaf Ermal
	$unsetindexes = array();
2268
2269
	foreach ($cpdb as $cpentry) {
2270 ac631bba lgcosta
		/* on the same ip */
2271 5705c60a Renato Botelho
		if ($cpentry[2] == $clientip) {
2272 1e0b1727 Phil Davis
			if (isset($config['captiveportal'][$cpzone]['nomacfilter']) || $cpentry[3] == $clientmac) {
2273 086cf944 Phil Davis
				captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CONCURRENT LOGIN - REUSING OLD SESSION");
2274 1e0b1727 Phil Davis
			} else {
2275 086cf944 Phil Davis
				captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CONCURRENT LOGIN - REUSING IP {$cpentry[2]} WITH DIFFERENT MAC ADDRESS {$cpentry[3]}");
2276 1e0b1727 Phil Davis
			}
2277 5705c60a Renato Botelho
			$sessionid = $cpentry[5];
2278 ac631bba lgcosta
			break;
2279 1e0b1727 Phil Davis
		} elseif (($attributes['voucher']) && ($username != 'unauthenticated') && ($cpentry[4] == $username)) {
2280
			// user logged in with an active voucher. Check for how long and calculate
2281 ac631bba lgcosta
			// how much time we can give him (voucher credit - used time)
2282 5705c60a Renato Botelho
			$remaining_time = $cpentry[0] + $cpentry[7] - $allow_time;
2283 1e0b1727 Phil Davis
			if ($remaining_time < 0) { // just in case.
2284 ac631bba lgcosta
				$remaining_time = 0;
2285 1e0b1727 Phil Davis
			}
2286 ac631bba lgcosta
2287
			/* This user was already logged in so we disconnect the old one */
2288 eb43c5b1 Augustin FL
			captiveportal_disconnect($cpentry, 13);
2289 086cf944 Phil Davis
			captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CONCURRENT LOGIN - TERMINATING OLD SESSION");
2290 5705c60a Renato Botelho
			$unsetindexes[] = $cpentry[5];
2291 ac631bba lgcosta
			break;
2292 1e0b1727 Phil Davis
		} elseif ((isset($config['captiveportal'][$cpzone]['noconcurrentlogins'])) && ($username != 'unauthenticated')) {
2293 ac631bba lgcosta
			/* on the same username */
2294 5705c60a Renato Botelho
			if (strcasecmp($cpentry[4], $username) == 0) {
2295 ac631bba lgcosta
				/* This user was already logged in so we disconnect the old one */
2296 eb43c5b1 Augustin FL
				captiveportal_disconnect($cpentry, 13);
2297 086cf944 Phil Davis
				captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "CONCURRENT LOGIN - TERMINATING OLD SESSION");
2298 5705c60a Renato Botelho
				$unsetindexes[] = $cpentry[5];
2299 ac631bba lgcosta
				break;
2300
			}
2301
		}
2302
	}
2303 f32eae2d Ermal
	unset($cpdb);
2304 ac631bba lgcosta
2305 1e0b1727 Phil Davis
	if (!empty($unsetindexes)) {
2306 26ee5aaf Ermal
		captiveportal_remove_entries($unsetindexes);
2307 1e0b1727 Phil Davis
	}
2308 26ee5aaf Ermal
2309 1e0b1727 Phil Davis
	if ($attributes['voucher'] && $remaining_time <= 0) {
2310 ac631bba lgcosta
		return 0;       // voucher already used and no time left
2311 1e0b1727 Phil Davis
	}
2312 ac631bba lgcosta
2313
	if (!isset($sessionid)) {
2314
		/* generate unique session ID */
2315
		$tod = gettimeofday();
2316
		$sessionid = substr(md5(mt_rand() . $tod['sec'] . $tod['usec'] . $clientip . $clientmac), 0, 16);
2317
2318 aa1c6774 Jonatan Ramos
		if (isset($config['captiveportal'][$cpzone]['peruserbw'])) {
2319
			$dwfaultbw_up = !empty($config['captiveportal'][$cpzone]['bwdefaultup']) ? $config['captiveportal'][$cpzone]['bwdefaultup'] : 0;
2320
			$dwfaultbw_down = !empty($config['captiveportal'][$cpzone]['bwdefaultdn']) ? $config['captiveportal'][$cpzone]['bwdefaultdn'] : 0;
2321
		} else {
2322
			$dwfaultbw_up = $dwfaultbw_down = 0;
2323
		}
2324 7c4e07c6 jim-p
		/* pipe throughputs must always be an integer, enforce that restriction again here. */
2325 f87ddb3b plumbeo
		if (isset($config['captiveportal'][$cpzone]['radiusperuserbw'])) {
2326
			$bw_up = round(!empty($attributes['bw_up']) ? intval($attributes['bw_up'])/1000 : $dwfaultbw_up, 0);
2327
			$bw_down = round(!empty($attributes['bw_down']) ? intval($attributes['bw_down'])/1000 : $dwfaultbw_down, 0);
2328
		} else {
2329
			$bw_up = round($dwfaultbw_up,0);
2330
			$bw_down = round($dwfaultbw_down,0);
2331
		}
2332 aa1c6774 Jonatan Ramos
2333 20588aac Augustin-FL
		if (isset($config['captiveportal'][$cpzone]['passthrumacadd'])) {
2334 a464eaf7 Stephen Jones
2335 ac631bba lgcosta
			$mac = array();
2336 6ffb064f Renato Botelho
			$mac['action'] = 'pass';
2337 ac631bba lgcosta
			$mac['mac'] = $clientmac;
2338 522f1cc7 Ermal
			$mac['ip'] = $clientip; /* Used only for logging */
2339 20588aac Augustin-FL
			$mac['username'] = $username;
2340
			if ($attributes['voucher']) {
2341
				$mac['logintype'] = "voucher";
2342 522f1cc7 Ermal
			}
2343 cc229ee9 Phil Davis
			if ($username == "unauthenticated") {
2344 6c07db48 Phil Davis
				$mac['descr'] = "Auto-added";
2345 20588aac Augustin-FL
			} else if ($authmethod == "voucher") {
2346
				$mac['descr'] = "Auto-added for voucher {$username}";
2347 1e0b1727 Phil Davis
			} else {
2348 6c07db48 Phil Davis
				$mac['descr'] = "Auto-added for user {$username}";
2349 1e0b1727 Phil Davis
			}
2350
			if (!empty($bw_up)) {
2351 ac631bba lgcosta
				$mac['bw_up'] = $bw_up;
2352 1e0b1727 Phil Davis
			}
2353
			if (!empty($bw_down)) {
2354 ac631bba lgcosta
				$mac['bw_down'] = $bw_down;
2355 1e0b1727 Phil Davis
			}
2356
			if (!is_array($config['captiveportal'][$cpzone]['passthrumac'])) {
2357 b4792bf8 Ermal
				$config['captiveportal'][$cpzone]['passthrumac'] = array();
2358 1e0b1727 Phil Davis
			}
2359 a464eaf7 Stephen Jones
			//check for mac duplicates before adding it to config.
2360
			$mac_duplicate = false;
2361
			foreach($config['captiveportal'][$cpzone]['passthrumac'] as $mac_check){
2362
				if($mac_check['mac'] == $mac['mac']){
2363
					$mac_duplicate = true;
2364
				}
2365
			}
2366
			if(!$mac_duplicate){
2367
				$config['captiveportal'][$cpzone]['passthrumac'][] = $mac;
2368
			}
2369 006802ab Ermal
			unlock($cpdblck);
2370 ac631bba lgcosta
			$macrules = captiveportal_passthrumac_configure_entry($mac);
2371 b4792bf8 Ermal
			file_put_contents("{$g['tmp_path']}/macentry_{$cpzone}.rules.tmp", $macrules);
2372 517b893e Renato Botelho
			mwexec("/sbin/ipfw -q {$g['tmp_path']}/macentry_{$cpzone}.rules.tmp");
2373 ac631bba lgcosta
			$writecfg = true;
2374
		} else {
2375 aea56408 Ermal
			/* See if a pipeno is passed, if not start sessions because this means there isn't one atm */
2376 1e0b1727 Phil Davis
			if (is_null($pipeno)) {
2377 aea56408 Ermal
				$pipeno = captiveportal_get_next_dn_ruleno();
2378 1e0b1727 Phil Davis
			}
2379 aea56408 Ermal
2380
			/* if the pool is empty, return appropriate message and exit */
2381
			if (is_null($pipeno)) {
2382
				portal_reply_page($redirurl, "error", "System reached maximum login capacity");
2383 12feed15 Ermal
				log_error("Zone: {$cpzone} - WARNING!  Captive portal has reached maximum login capacity");
2384 aea56408 Ermal
				unlock($cpdblck);
2385
				return;
2386
			}
2387
2388
			$bw_up_pipeno = $pipeno;
2389
			$bw_down_pipeno = $pipeno + 1;
2390 10b9dfcf Ermal
			//$bw_up /= 1000; // Scale to Kbit/s
2391 517b893e Renato Botelho
			$_gb = @pfSense_ipfw_pipe("pipe {$bw_up_pipeno} config bw {$bw_up}Kbit/s queue 100 buckets 16");
2392
			$_gb = @pfSense_ipfw_pipe("pipe {$bw_down_pipeno} config bw {$bw_down}Kbit/s queue 100 buckets 16");
2393 ac631bba lgcosta
2394 75395abf Renato Botelho
			$rule_entry = "{$clientip}/" . (is_ipaddrv6($clientip) ? "128" : "32");
2395 3c4fcd5b Renato Botelho
			if (!isset($config['captiveportal'][$cpzone]['nomacfilter'])) {
2396
				$rule_entry .= ",{$clientmac}";
2397
			}
2398
			$_gb = @pfSense_ipfw_table("{$cpzone}_auth_up", IP_FW_TABLE_XADD, "{$rule_entry}", $bw_up_pipeno);
2399 bd068df3 Luiz Souza
			$_gb = @pfSense_ipfw_table("{$cpzone}_auth_down", IP_FW_TABLE_XADD, "{$rule_entry}", $bw_down_pipeno);
2400 ac631bba lgcosta
2401 1e0b1727 Phil Davis
			if ($attributes['voucher']) {
2402 ac631bba lgcosta
				$attributes['session_timeout'] = $remaining_time;
2403 1e0b1727 Phil Davis
			}
2404
2405 1974c2d6 bcyrill
			/* handle empty attributes */
2406
			$session_timeout = (!empty($attributes['session_timeout'])) ? $attributes['session_timeout'] : 'NULL';
2407
			$idle_timeout = (!empty($attributes['idle_timeout'])) ? $attributes['idle_timeout'] : 'NULL';
2408
			$session_terminate_time = (!empty($attributes['session_terminate_time'])) ? $attributes['session_terminate_time'] : 'NULL';
2409 338c0941 Ermal
			$interim_interval = (!empty($attributes['interim_interval'])) ? $attributes['interim_interval'] : 'NULL';
2410 f3e403d5 plumbeo
			$traffic_quota = (!empty($attributes['maxbytes'])) ? $attributes['maxbytes'] : 'NULL';
2411 1974c2d6 bcyrill
2412
			/* escape username */
2413 5cf91315 Renato Botelho
			$safe_username = SQLite3::escapeString($username);
2414 ac631bba lgcosta
2415
			/* encode password in Base64 just in case it contains commas */
2416 d4e42c54 jim-p
			$bpassword = (isset($config['captiveportal'][$cpzone]['reauthenticate'])) ? base64_encode($password) : '';
2417 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) ";
2418 1974c2d6 bcyrill
			$insertquery .= "VALUES ({$allow_time}, {$pipeno}, '{$clientip}', '{$clientmac}', '{$safe_username}', '{$sessionid}', '{$bpassword}', ";
2419 eb43c5b1 Augustin FL
			$insertquery .= "{$session_timeout}, {$idle_timeout}, {$session_terminate_time}, {$interim_interval}, {$traffic_quota}, '{$authmethod}', '{$context}')";
2420 ac631bba lgcosta
2421 26ee5aaf Ermal
			/* store information to database */
2422
			captiveportal_write_db($insertquery);
2423 006802ab Ermal
			unlock($cpdblck);
2424 1f965b69 Ermal
			unset($insertquery, $bpassword);
2425 006802ab Ermal
2426 e42ea151 Augustin FL
			$radacct = isset($config['captiveportal'][$cpzone]['radacct_enable']) ? true : false;
2427
			if ($authmethod === 'radius' && $radacct) {
2428
				captiveportal_send_server_accounting('start',
2429
					$pipeno, // ruleno
2430
					$username, // username
2431
					$clientip, // clientip
2432
					$clientmac, // clientmac
2433
					$sessionid, // sessionid
2434
					time());  // start time
2435 ac631bba lgcosta
			}
2436
		}
2437 a7ee038b Ermal
	} else {
2438 eb43c5b1 Augustin FL
		/* NOTE: #3062-11 If the pipeno has been allocated free it to not DoS the CP */
2439 1e0b1727 Phil Davis
		if (!is_null($pipeno)) {
2440 a7ee038b Ermal
			captiveportal_free_dn_ruleno($pipeno);
2441 1e0b1727 Phil Davis
		}
2442 a7ee038b Ermal
2443 006802ab Ermal
		unlock($cpdblck);
2444 a7ee038b Ermal
	}
2445 ac631bba lgcosta
2446 1e0b1727 Phil Davis
	if ($writecfg == true) {
2447 b05c860c doktornotor
		write_config(gettext("Captive Portal allowed users configuration changed"));
2448 1e0b1727 Phil Davis
	}
2449 ac631bba lgcosta
2450
	/* redirect user to desired destination */
2451 1e0b1727 Phil Davis
	if (!empty($attributes['url_redirection'])) {
2452 ac631bba lgcosta
		$my_redirurl = $attributes['url_redirection'];
2453 1e0b1727 Phil Davis
	} else if (!empty($redirurl)) {
2454 ac420abd Ermal
		$my_redirurl = $redirurl;
2455 1e0b1727 Phil Davis
	} else if (!empty($config['captiveportal'][$cpzone]['redirurl'])) {
2456 b4792bf8 Ermal
		$my_redirurl = $config['captiveportal'][$cpzone]['redirurl'];
2457 1e0b1727 Phil Davis
	}
2458 ac631bba lgcosta
2459 20588aac Augustin-FL
	if (isset($config['captiveportal'][$cpzone]['logoutwin_enable']) && !isset($config['captiveportal'][$cpzone]['passthrumacadd'])) {
2460 de132ae3 bcyrill
		$ourhostname = portal_hostname_from_client_ip($clientip);
2461
		$protocol = (isset($config['captiveportal'][$cpzone]['httpslogin'])) ? 'https://' : 'http://';
2462
		$logouturl = "{$protocol}{$ourhostname}/";
2463 ac631bba lgcosta
2464 1e0b1727 Phil Davis
		if (isset($attributes['reply_message'])) {
2465 ac631bba lgcosta
			$message = $attributes['reply_message'];
2466 1e0b1727 Phil Davis
		} else {
2467 ac631bba lgcosta
			$message = 0;
2468 1e0b1727 Phil Davis
		}
2469 ac631bba lgcosta
2470 86573bb9 Phil Davis
		include_once("{$g['varetc_path']}/captiveportal-{$cpzone}-logout.html");
2471 ac631bba lgcosta
2472
	} else {
2473 fb0c2bd6 Ermal
		portal_reply_page($my_redirurl, "redir", "Just redirect the user.");
2474 ac631bba lgcosta
	}
2475
2476
	return $sessionid;
2477
}
2478
2479
2480
/*
2481
 * Used for when pass-through credits are enabled.
2482
 * Returns true when there was at least one free login to deduct for the MAC.
2483
 * Expired entries are removed as they are seen.
2484
 * Active entries are updated according to the configuration.
2485
 */
2486
function portal_consume_passthrough_credit($clientmac) {
2487 b4792bf8 Ermal
	global $config, $cpzone;
2488 ac631bba lgcosta
2489 1e0b1727 Phil Davis
	if (!empty($config['captiveportal'][$cpzone]['freelogins_count']) && is_numeric($config['captiveportal'][$cpzone]['freelogins_count'])) {
2490 b4792bf8 Ermal
		$freeloginscount = $config['captiveportal'][$cpzone]['freelogins_count'];
2491 1e0b1727 Phil Davis
	} else {
2492 ac631bba lgcosta
		return false;
2493 1e0b1727 Phil Davis
	}
2494 ac631bba lgcosta
2495 1e0b1727 Phil Davis
	if (!empty($config['captiveportal'][$cpzone]['freelogins_resettimeout']) && is_numeric($config['captiveportal'][$cpzone]['freelogins_resettimeout'])) {
2496 b4792bf8 Ermal
		$resettimeout = $config['captiveportal'][$cpzone]['freelogins_resettimeout'];
2497 1e0b1727 Phil Davis
	} else {
2498 ac631bba lgcosta
		return false;
2499 1e0b1727 Phil Davis
	}
2500 ac631bba lgcosta
2501 1e0b1727 Phil Davis
	if ($freeloginscount < 1 || $resettimeout <= 0 || !$clientmac) {
2502 ac631bba lgcosta
		return false;
2503 1e0b1727 Phil Davis
	}
2504 ac631bba lgcosta
2505 b4792bf8 Ermal
	$updatetimeouts = isset($config['captiveportal'][$cpzone]['freelogins_updatetimeouts']);
2506 ac631bba lgcosta
2507
	/*
2508
	 * Read database of used MACs.  Lines are a comma-separated list
2509
	 * of the time, MAC, then the count of pass-through credits remaining.
2510
	 */
2511
	$usedmacs = captiveportal_read_usedmacs_db();
2512
2513
	$currenttime = time();
2514
	$found = false;
2515
	foreach ($usedmacs as $key => $usedmac) {
2516
		$usedmac = explode(",", $usedmac);
2517
2518
		if ($usedmac[1] == $clientmac) {
2519
			if ($usedmac[0] + ($resettimeout * 3600) > $currenttime) {
2520
				if ($usedmac[2] < 1) {
2521
					if ($updatetimeouts) {
2522
						$usedmac[0] = $currenttime;
2523
						unset($usedmacs[$key]);
2524
						$usedmacs[] = implode(",", $usedmac);
2525
						captiveportal_write_usedmacs_db($usedmacs);
2526
					}
2527
2528
					return false;
2529
				} else {
2530
					$usedmac[2] -= 1;
2531
					$usedmacs[$key] = implode(",", $usedmac);
2532
				}
2533
2534
				$found = true;
2535 1e0b1727 Phil Davis
			} else {
2536 ac631bba lgcosta
				unset($usedmacs[$key]);
2537 1e0b1727 Phil Davis
			}
2538 ac631bba lgcosta
2539
			break;
2540 1e0b1727 Phil Davis
		} else if ($usedmac[0] + ($resettimeout * 3600) <= $currenttime) {
2541
			unset($usedmacs[$key]);
2542
		}
2543 ac631bba lgcosta
	}
2544
2545
	if (!$found) {
2546
		$usedmac = array($currenttime, $clientmac, $freeloginscount - 1);
2547
		$usedmacs[] = implode(",", $usedmac);
2548
	}
2549
2550
	captiveportal_write_usedmacs_db($usedmacs);
2551
	return true;
2552
}
2553
2554
function captiveportal_read_usedmacs_db() {
2555 b4792bf8 Ermal
	global $g, $cpzone;
2556 ac631bba lgcosta
2557 b4792bf8 Ermal
	$cpumaclck = lock("captiveusedmacs{$cpzone}");
2558
	if (file_exists("{$g['vardb_path']}/captiveportal_usedmacs_{$cpzone}.db")) {
2559
		$usedmacs = file("{$g['vardb_path']}/captiveportal_usedmacs_{$cpzone}.db", FILE_IGNORE_NEW_LINES | FILE_SKIP_EMPTY_LINES);
2560 1e0b1727 Phil Davis
		if (!$usedmacs) {
2561 ac631bba lgcosta
			$usedmacs = array();
2562 1e0b1727 Phil Davis
		}
2563
	} else {
2564 ac631bba lgcosta
		$usedmacs = array();
2565 1e0b1727 Phil Davis
	}
2566 ac631bba lgcosta
2567
	unlock($cpumaclck);
2568
	return $usedmacs;
2569
}
2570
2571
function captiveportal_write_usedmacs_db($usedmacs) {
2572 b4792bf8 Ermal
	global $g, $cpzone;
2573 ac631bba lgcosta
2574 b4792bf8 Ermal
	$cpumaclck = lock("captiveusedmacs{$cpzone}", LOCK_EX);
2575
	@file_put_contents("{$g['vardb_path']}/captiveportal_usedmacs_{$cpzone}.db", implode("\n", $usedmacs));
2576 ac631bba lgcosta
	unlock($cpumaclck);
2577
}
2578
2579 81ce28d8 Renato Botelho
function captiveportal_blocked_mac($mac) {
2580
	global $config, $g, $cpzone;
2581 0d33f1fc Renato Botelho
2582 1e0b1727 Phil Davis
	if (empty($mac) || !is_macaddr($mac)) {
2583 81ce28d8 Renato Botelho
		return false;
2584 1e0b1727 Phil Davis
	}
2585 0d33f1fc Renato Botelho
2586 1e0b1727 Phil Davis
	if (!is_array($config['captiveportal'][$cpzone]['passthrumac'])) {
2587 81ce28d8 Renato Botelho
		return false;
2588 1e0b1727 Phil Davis
	}
2589 0d33f1fc Renato Botelho
2590 1e0b1727 Phil Davis
	foreach ($config['captiveportal'][$cpzone]['passthrumac'] as $passthrumac) {
2591 81ce28d8 Renato Botelho
		if (($passthrumac['action'] == 'block') &&
2592 ae52d165 Renato Botelho
		    ($passthrumac['mac'] == strtolower($mac))) {
2593 81ce28d8 Renato Botelho
			return true;
2594 1e0b1727 Phil Davis
		}
2595
	}
2596 0d33f1fc Renato Botelho
2597 81ce28d8 Renato Botelho
	return false;
2598 3b2769be Renato Botelho
2599
}
2600
2601 e42ea151 Augustin FL
/* Captiveportal Radius Accounting */
2602
2603
function gigawords($bytes) {
2604
2605
	/*
2606
	 * RFC2866 Specifies a 32bit unsigned integer, which is a max of 4294967295
2607
	 * Currently there is a fault in the PECL radius_put_int function which can handle only 32bit signed integer.
2608
	 */
2609
2610
	// We use BCMath functions since normal integers don't work with so large numbers
2611
	$gigawords = bcdiv( bcsub( $bytes, remainder($bytes) ) , GIGAWORDS_RIGHT_OPERAND) ;
2612
2613
	// We need to manually set this to a zero instead of NULL for put_int() safety
2614
	if (is_null($gigawords)) {
2615
		$gigawords = 0;
2616
	}
2617
2618
	return $gigawords;
2619
}
2620
2621
function remainder($bytes) {
2622
	// Calculate the bytes we are going to send to the radius
2623
	$bytes = bcmod($bytes, GIGAWORDS_RIGHT_OPERAND);
2624 748372bc Stephen Jones
2625 e42ea151 Augustin FL
	if (is_null($bytes)) {
2626
		$bytes = 0;
2627
	}
2628
2629
    return $bytes;
2630
}
2631
2632 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) {
2633 62f20eab Michael Newton
	global $cpzone, $config;
2634
2635 5f1aaed4 Augustin FL
	$cpcfg = $config['captiveportal'][$cpzone];
2636
	$acctcfg = auth_get_authserver($cpcfg['radacct_server']);
2637 748372bc Stephen Jones
2638 5f1aaed4 Augustin FL
	if (!isset($cpcfg['radacct_enable']) || empty($acctcfg)) {
2639
		return null;
2640 62f20eab Michael Newton
	}
2641 748372bc Stephen Jones
2642 5f1aaed4 Augustin FL
	if ($type === 'on') {
2643
		$racct = new Auth_RADIUS_Acct_On;
2644
	} elseif ($type === 'off') {
2645 62f20eab Michael Newton
		$racct = new Auth_RADIUS_Acct_Off;
2646 5f1aaed4 Augustin FL
	} elseif ($type === 'start') {
2647
		$racct = new Auth_RADIUS_Acct_Start;
2648
		if (!is_int($start_time)) {
2649
			$start_time = time();
2650
		}
2651
	} elseif ($type === 'stop') {
2652
		$racct = new Auth_RADIUS_Acct_Stop;
2653
		if (!is_int($stop_time)) {
2654
			$stop_time = time();
2655
		}
2656
	} elseif ($type === 'update') {
2657
        $racct = new Auth_RADIUS_Acct_Update;
2658
		if (!is_int($stop_time)) {
2659
			$stop_time = time(); // "top time" here will be used only for calcuating session time.
2660
		}
2661 62f20eab Michael Newton
	} else {
2662 5f1aaed4 Augustin FL
		return null;
2663 62f20eab Michael Newton
	}
2664 5f1aaed4 Augustin FL
2665
	$racct->addServer($acctcfg['host'], $acctcfg['radius_acct_port'],
2666
		$acctcfg['radius_secret'], $acctcfg['radius_timeout']);
2667 748372bc Stephen Jones
2668 5f1aaed4 Augustin FL
	$racct->authentic = RADIUS_AUTH_RADIUS;
2669
	if (!empty($username)) {
2670
		$racct->username = $username;
2671 62f20eab Michael Newton
	}
2672
2673 5f1aaed4 Augustin FL
	if (PEAR::isError($racct->start())) {
2674
		captiveportal_syslog('RADIUS ACCOUNTING FAILED : '.$racct->getError());
2675 62f20eab Michael Newton
		$racct->close();
2676 5f1aaed4 Augustin FL
		return null;
2677
	}
2678 748372bc Stephen Jones
2679 5f1aaed4 Augustin FL
	$nasip = $acctcfg['radius_nasip_attribute'];
2680
	if (!is_ipaddr($nasip)) {
2681 748372bc Stephen Jones
		$nasip = get_interface_ip($nasip);
2682 5f1aaed4 Augustin FL
		if (!is_ipaddr($nasip)) {
2683
			$nasip = get_interface_ip();//We use WAN interface IP as fallback for NAS-IP-Address
2684
		}
2685
	}
2686
	$nasmac = get_interface_mac(find_ip_interface($nasip));
2687
	$racct->putAttribute(RADIUS_NAS_IP_ADDRESS, $nasip, "addr");
2688 b1862963 jim-p
2689
	$racct->putAttribute(RADIUS_NAS_IDENTIFIER, empty($cpcfg["radiusnasid"]) ? "CaptivePortal-{$cpzone}" : $cpcfg["radiusnasid"] );
2690 748372bc Stephen Jones
2691 5f1aaed4 Augustin FL
	if (is_int($ruleno)) {
2692
		$racct->putAttribute(RADIUS_NAS_PORT_TYPE, RADIUS_ETHERNET);
2693
		$racct->putAttribute(RADIUS_NAS_PORT, intval($ruleno), 'integer');
2694
	}
2695 748372bc Stephen Jones
2696 5f1aaed4 Augustin FL
	if (!empty($sessionid)) {
2697
		$racct->putAttribute(RADIUS_ACCT_SESSION_ID, $sessionid);
2698
	}
2699
2700
	if (!empty($clientip) && is_ipaddr($clientip)) {
2701
		$racct->putAttribute(RADIUS_FRAMED_IP_ADDRESS, $clientip, "addr");
2702
	}
2703
	if (!empty($clientmac)) {
2704
		$racct->putAttribute(RADIUS_CALLING_STATION_ID, mac_format($clientmac));
2705
	}
2706
	if (!empty($nasmac)) {
2707
		$racct->putAttribute(RADIUS_CALLED_STATION_ID, mac_format($nasmac).':'.gethostname());
2708
	}
2709 748372bc Stephen Jones
2710 5f1aaed4 Augustin FL
	// Accounting request Stop and Update : send the current data volume
2711
	if (($type === 'stop' || $type === 'update') && is_int($start_time)) {
2712
		$volume = getVolume($clientip);
2713
		$session_time = $stop_time - $start_time;
2714
		$volume['input_bytes_radius'] = remainder($volume['input_bytes']);
2715
		$volume['input_gigawords'] = gigawords($volume['input_bytes']);
2716
		$volume['output_bytes_radius'] = remainder($volume['output_bytes']);
2717
		$volume['output_gigawords'] = gigawords($volume['output_bytes']);
2718
2719
		// Volume stuff: Ingress
2720
		$racct->putAttribute(RADIUS_ACCT_INPUT_PACKETS, intval($volume['input_pkts']), "integer");
2721
		$racct->putAttribute(RADIUS_ACCT_INPUT_OCTETS, intval($volume['input_bytes_radius']), "integer");
2722
		// Volume stuff: Outgress
2723
		$racct->putAttribute(RADIUS_ACCT_OUTPUT_PACKETS, intval($volume['output_pkts']), "integer");
2724
		$racct->putAttribute(RADIUS_ACCT_OUTPUT_OCTETS, intval($volume['output_bytes_radius']), "integer");
2725
		$racct->putAttribute(RADIUS_ACCT_SESSION_TIME, intval($session_time), "integer");
2726 748372bc Stephen Jones
2727 5f1aaed4 Augustin FL
		$racct->putAttribute(CUSTOM_RADIUS_ACCT_OUTPUT_GIGAWORDS, intval($volume['output_gigawords']), "integer");
2728
		$racct->putAttribute(CUSTOM_RADIUS_ACCT_INPUT_GIGAWORDS, intval($volume['input_gigawords']), "integer");
2729
		// Set session_time
2730
		$racct->session_time = $session_time;
2731
	}
2732 748372bc Stephen Jones
2733 5f1aaed4 Augustin FL
	if ($type === 'stop') {
2734
		if (empty($term_cause)) {
2735
			$term_cause = 1;
2736
		}
2737
		$racct->putAttribute(RADIUS_ACCT_TERMINATE_CAUSE, $term_cause);
2738
	}
2739 748372bc Stephen Jones
2740 62f20eab Michael Newton
	// Send request
2741
	$result = $racct->send();
2742 5f1aaed4 Augustin FL
2743 62f20eab Michael Newton
	if (PEAR::isError($result)) {
2744 5f1aaed4 Augustin FL
		 captiveportal_syslog('RADIUS ACCOUNTING FAILED : '.$racct->getError());
2745
		 $result = null;
2746
	} elseif ($result !== true) {
2747
		$result = false;
2748 62f20eab Michael Newton
	}
2749
2750
	$racct->close();
2751 5f1aaed4 Augustin FL
	return $result;
2752 62f20eab Michael Newton
}
2753 d2ecbddc jim-p
2754
function captiveportal_isip_logged($clientip) {
2755
	global $g, $cpzone;
2756
2757
	/* read in client database */
2758
	$query = "WHERE ip = '{$clientip}'";
2759
	$cpdb = captiveportal_read_db($query);
2760
	foreach ($cpdb as $cpentry) {
2761
		return $cpentry;
2762
	}
2763
}
2764 64c0462b Ermal
?>