Project

General

Profile

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