Project

General

Profile

Download (27 KB) Statistics
| Branch: | Tag: | Revision:
1 336e3c1c Charlie
<?php
2
/*
3 ce77a9c4 Phil Davis
	voucher.inc
4 09221bc3 Renato Botelho
5
	part of pfSense (https://www.pfsense.org)
6 79262830 Phil Davis
	Copyright (C) 2007 Marcel Wiget <mwiget@mac.com>
7 09221bc3 Renato Botelho
	Copyright (c) 2007-2016 Electric Sheep Fencing, LLC.
8
	All rights reserved.
9
10
	originally part of m0n0wall (http://m0n0.ch/wall)
11
	Copyright (C) 2003-2004 Manuel Kasper <mk@neon1.net>.
12 79262830 Phil Davis
	All rights reserved.
13
14
	Redistribution and use in source and binary forms, with or without
15
	modification, are permitted provided that the following conditions are met:
16
17
	1. Redistributions of source code must retain the above copyright notice,
18
	   this list of conditions and the following disclaimer.
19
20
	2. Redistributions in binary form must reproduce the above copyright
21 09221bc3 Renato Botelho
	   notice, this list of conditions and the following disclaimer in
22
	   the documentation and/or other materials provided with the
23
	   distribution.
24
25
	3. All advertising materials mentioning features or use of this software
26
	   must display the following acknowledgment:
27
	   "This product includes software developed by the pfSense Project
28
	   for use in the pfSense® software distribution. (http://www.pfsense.org/).
29
30
	4. The names "pfSense" and "pfSense Project" must not be used to
31
	   endorse or promote products derived from this software without
32
	   prior written permission. For written permission, please contact
33
	   coreteam@pfsense.org.
34
35
	5. Products derived from this software may not be called "pfSense"
36
	   nor may "pfSense" appear in their names without prior written
37
	   permission of the Electric Sheep Fencing, LLC.
38
39
	6. Redistributions of any form whatsoever must retain the following
40
	   acknowledgment:
41
42
	"This product includes software developed by the pfSense Project
43
	for use in the pfSense software distribution (http://www.pfsense.org/).
44 336e3c1c Charlie
45 09221bc3 Renato Botelho
	THIS SOFTWARE IS PROVIDED BY THE pfSense PROJECT ``AS IS'' AND ANY
46
	EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
47
	IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
48
	PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE pfSense PROJECT OR
49
	ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
50
	SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
51
	NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
52
	LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
53
	HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
54
	STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
55
	ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
56
	OF THE POSSIBILITY OF SUCH DAMAGE.
57 336e3c1c Charlie
*/
58 523855b0 Scott Ullrich
59 336e3c1c Charlie
/* include all configuration functions */
60 79262830 Phil Davis
if (!function_exists('captiveportal_syslog')) {
61 4e8d55dd Scott Ullrich
	require_once("captiveportal.inc");
62 79262830 Phil Davis
}
63 336e3c1c Charlie
64 05771a24 Ermal
function xmlrpc_sync_voucher_expire($vouchers, $syncip, $port, $password, $username) {
65 b4792bf8 Ermal
	global $g, $config, $cpzone;
66 05771a24 Ermal
	require_once("xmlrpc.inc");
67 f9d480ff Ermal
68
	$protocol = "http";
69
	if (is_array($config['system']) && is_array($config['system']['webgui']) && !empty($config['system']['webgui']['protocol']) &&
70 79262830 Phil Davis
	    $config['system']['webgui']['protocol'] == "https") {
71 f9d480ff Ermal
		$protocol = "https";
72 79262830 Phil Davis
	}
73
	if ($protocol == "https" || $port == "443") {
74 05771a24 Ermal
		$url = "https://{$syncip}";
75 79262830 Phil Davis
	} else {
76 d6e569ae Ermal
		$url = "http://{$syncip}";
77 79262830 Phil Davis
	}
78 05771a24 Ermal
79
	/* Construct code that is run on remote machine */
80
	$method = 'pfsense.exec_php';
81 6c07db48 Phil Davis
	$execcmd = <<<EOF
82 fac36dfd Ermal
	global \$cpzone;
83 05771a24 Ermal
	require_once('/etc/inc/captiveportal.inc');
84
	require_once('/etc/inc/voucher.inc');
85 a18eeb51 bcyrill
	\$cpzone = "$cpzone";
86
	voucher_expire("$vouchers");
87 05771a24 Ermal
88
EOF;
89
90
	/* assemble xmlrpc payload */
91
	$params = array(
92
		XML_RPC_encode($password),
93
		XML_RPC_encode($execcmd)
94
	);
95
96
	log_error("Captive Portal Voucher XMLRPC sync data {$url}:{$port}.");
97
	$msg = new XML_RPC_Message($method, $params);
98
	$cli = new XML_RPC_Client('/xmlrpc.php', $url, $port);
99
	$cli->setCredentials($username, $password);
100
	$resp = $cli->send($msg, "250");
101 79262830 Phil Davis
	if (!is_object($resp)) {
102 05771a24 Ermal
		$error = "A communications error occurred while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} (pfsense.exec_php).";
103
		log_error($error);
104
		file_notice("CaptivePortalVoucherSync", $error, "Communications error occurred", "");
105
		return false;
106 79262830 Phil Davis
	} elseif ($resp->faultCode()) {
107 05771a24 Ermal
		$error = "An error code was received while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString();
108
		log_error($error);
109
		file_notice("CaptivePortalVoucherSync", $error, "Error code received", "");
110
		return false;
111
	} else {
112
		log_error("CaptivePortalVoucherSync XMLRPC reload data success with {$url}:{$port} (pfsense.exec_php).");
113
	}
114
115 6c07db48 Phil Davis
	$toreturn = XML_RPC_Decode($resp->value());
116 05771a24 Ermal
117
	return $toreturn;
118
}
119
120 a18eeb51 bcyrill
function xmlrpc_sync_voucher_disconnect($dbent, $syncip, $port, $password, $username, $term_cause = 1, $stop_time = null) {
121 b4792bf8 Ermal
	global $g, $config, $cpzone;
122 d322e3b3 Scott Ullrich
	require_once("xmlrpc.inc");
123 f9d480ff Ermal
124
	$protocol = "http";
125
	if (is_array($config['system']) && is_array($config['system']['webgui']) && !empty($config['system']['webgui']['protocol']) &&
126 79262830 Phil Davis
	    $config['system']['webgui']['protocol'] == "https") {
127 f9d480ff Ermal
		$protocol = "https";
128 79262830 Phil Davis
	}
129
	if ($protocol == "https" || $port == "443") {
130 509e1202 Ermal
		$url = "https://{$syncip}";
131 79262830 Phil Davis
	} else {
132 d6e569ae Ermal
		$url = "http://{$syncip}";
133 79262830 Phil Davis
	}
134 d322e3b3 Scott Ullrich
135
	/* Construct code that is run on remote machine */
136 c2a6cda0 bcyrill
	$dbent_str = serialize($dbent);
137 bbe29bb1 bcyrill
	$tmp_stop_time = (isset($stop_time)) ? $stop_time : "null";
138 d322e3b3 Scott Ullrich
	$method = 'pfsense.exec_php';
139 6c07db48 Phil Davis
	$execcmd = <<<EOF
140 fac36dfd Ermal
	global \$cpzone;
141 d322e3b3 Scott Ullrich
	require_once('/etc/inc/captiveportal.inc');
142
	require_once('/etc/inc/voucher.inc');
143 a18eeb51 bcyrill
	\$cpzone = "$cpzone";
144 d322e3b3 Scott Ullrich
	\$radiusservers = captiveportal_get_radius_servers();
145 a18eeb51 bcyrill
	\$dbent = unserialize("$dbent_str");
146 bbe29bb1 bcyrill
	captiveportal_disconnect(\$dbent, \$radiusservers, $term_cause, $tmp_stop_time);
147 d322e3b3 Scott Ullrich
148
EOF;
149
150
	/* assemble xmlrpc payload */
151
	$params = array(
152
		XML_RPC_encode($password),
153
		XML_RPC_encode($execcmd)
154
	);
155
156
	log_error("Captive Portal Voucher XMLRPC sync data {$url}:{$port}.");
157
	$msg = new XML_RPC_Message($method, $params);
158
	$cli = new XML_RPC_Client('/xmlrpc.php', $url, $port);
159
	$cli->setCredentials($username, $password);
160
	$resp = $cli->send($msg, "250");
161 79262830 Phil Davis
	if (!is_object($resp)) {
162 d322e3b3 Scott Ullrich
		$error = "A communications error occurred while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} (pfsense.exec_php).";
163
		log_error($error);
164
		file_notice("CaptivePortalVoucherSync", $error, "Communications error occurred", "");
165
		return false;
166 79262830 Phil Davis
	} elseif ($resp->faultCode()) {
167 d322e3b3 Scott Ullrich
		$error = "An error code was received while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString();
168
		log_error($error);
169
		file_notice("CaptivePortalVoucherSync", $error, "Error code received", "");
170
		return false;
171
	} else {
172
		log_error("CaptivePortalVoucherSync XMLRPC reload data success with {$url}:{$port} (pfsense.exec_php).");
173
	}
174
175 6c07db48 Phil Davis
	$toreturn = XML_RPC_Decode($resp->value());
176 d322e3b3 Scott Ullrich
177
	return $toreturn;
178
}
179
180 6c9ca678 Scott Ullrich
function xmlrpc_sync_used_voucher($voucher_received, $syncip, $port, $password, $username) {
181 b4792bf8 Ermal
	global $g, $config, $cpzone;
182 6c9ca678 Scott Ullrich
	require_once("xmlrpc.inc");
183 f9d480ff Ermal
184
	$protocol = "http";
185
	if (is_array($config['system']) && is_array($config['system']['webgui']) && !empty($config['system']['webgui']['protocol']) &&
186 79262830 Phil Davis
	    $config['system']['webgui']['protocol'] == "https") {
187 f9d480ff Ermal
		$protocol = "https";
188 79262830 Phil Davis
	}
189
	if ($protocol == "https" || $port == "443") {
190 2fe347eb Ermal
		$url = "https://{$syncip}";
191 79262830 Phil Davis
	} else {
192 d6e569ae Ermal
		$url = "http://{$syncip}";
193 79262830 Phil Davis
	}
194 6c9ca678 Scott Ullrich
195
	/* Construct code that is run on remote machine */
196
	$method = 'pfsense.exec_php';
197 6c07db48 Phil Davis
	$execcmd = <<<EOF
198 fac36dfd Ermal
	global \$cpzone;
199 6c9ca678 Scott Ullrich
	require_once('/etc/inc/voucher.inc');
200 a18eeb51 bcyrill
	\$cpzone = "$cpzone";
201
	\$timeleft = voucher_auth("$voucher_received");
202 6c9ca678 Scott Ullrich
	\$toreturn = array();
203
	\$toreturn['timeleft'] = \$timeleft;
204 90df43c6 Ermal
	\$toreturn['voucher'] = array();
205 bbe29bb1 bcyrill
	\$toreturn['voucher']['roll'] = \$config['voucher'][\$cpzone]['roll'];
206 6c9ca678 Scott Ullrich
207 fa3ab36a Scott Ullrich
EOF;
208 6c9ca678 Scott Ullrich
209
	/* assemble xmlrpc payload */
210
	$params = array(
211
		XML_RPC_encode($password),
212
		XML_RPC_encode($execcmd)
213
	);
214
215
	log_error("Captive Portal Voucher XMLRPC sync data {$url}:{$port}.");
216
	$msg = new XML_RPC_Message($method, $params);
217
	$cli = new XML_RPC_Client('/xmlrpc.php', $url, $port);
218 2f384448 Scott Ullrich
	$cli->setCredentials($username, $password);
219 6c9ca678 Scott Ullrich
	$resp = $cli->send($msg, "250");
220 79262830 Phil Davis
	if (!is_object($resp)) {
221 6c9ca678 Scott Ullrich
		$error = "A communications error occurred while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} (pfsense.exec_php).";
222
		log_error($error);
223
		file_notice("CaptivePortalVoucherSync", $error, "Communications error occurred", "");
224 62f96568 Ermal
		return null; // $timeleft
225 79262830 Phil Davis
	} elseif ($resp->faultCode()) {
226 6c9ca678 Scott Ullrich
		$error = "An error code was received while attempting CaptivePortalVoucherSync XMLRPC sync with {$url}:{$port} - Code " . $resp->faultCode() . ": " . $resp->faultString();
227
		log_error($error);
228
		file_notice("CaptivePortalVoucherSync", $error, "Error code received", "");
229 62f96568 Ermal
		return null; // $timeleft
230 6c9ca678 Scott Ullrich
	} else {
231
		log_error("CaptivePortalVoucherSync XMLRPC reload data success with {$url}:{$port} (pfsense.exec_php).");
232
	}
233 6c07db48 Phil Davis
	$toreturn = XML_RPC_Decode($resp->value());
234 79262830 Phil Davis
	if (!is_array($config['voucher'])) {
235 90df43c6 Ermal
		$config['voucher'] = array();
236 79262830 Phil Davis
	}
237 62f96568 Ermal
238 fac36dfd Ermal
	if (is_array($toreturn['voucher']) && is_array($toreturn['voucher']['roll'])) {
239 b4792bf8 Ermal
		$config['voucher'][$cpzone]['roll'] = $toreturn['voucher']['roll'];
240 6c9ca678 Scott Ullrich
		write_config("Captive Portal Voucher database synchronized with {$url}");
241 b4792bf8 Ermal
		voucher_configure_zone(true);
242 ef60dfa5 Ermal
		unset($toreturn['voucher']);
243 79262830 Phil Davis
	} else if (!isset($toreturn['timeleft'])) {
244 62f96568 Ermal
		return null;
245 79262830 Phil Davis
	}
246 f5c05fcc Ermal
247
	return $toreturn['timeleft'];
248 fa3ab36a Scott Ullrich
}
249
250 05771a24 Ermal
function voucher_expire($voucher_received) {
251 abaa7feb Ermal LUÇI
	global $g, $config, $cpzone, $cpzoneid;
252 05771a24 Ermal
253
	// XMLRPC Call over to the master Voucher node
254 79262830 Phil Davis
	if (!empty($config['voucher'][$cpzone]['vouchersyncdbip'])) {
255 b4792bf8 Ermal
		$syncip   = $config['voucher'][$cpzone]['vouchersyncdbip'];
256
		$syncport = $config['voucher'][$cpzone]['vouchersyncport'];
257
		$syncpass = $config['voucher'][$cpzone]['vouchersyncpass'];
258
		$vouchersyncusername = $config['voucher'][$cpzone]['vouchersyncusername'];
259 05771a24 Ermal
		xmlrpc_sync_voucher_expire($voucher_received, $syncip, $syncport, $syncpass, $vouchersyncusername);
260
	}
261
262 90df43c6 Ermal
	$voucherlck = lock("voucher{$cpzone}", LOCK_EX);
263
264 05771a24 Ermal
	// read rolls into assoc array with rollid as key and minutes as value
265
	$tickets_per_roll = array();
266
	$minutes_per_roll = array();
267 b4792bf8 Ermal
	if (is_array($config['voucher'][$cpzone]['roll'])) {
268
		foreach ($config['voucher'][$cpzone]['roll'] as $rollent) {
269 05771a24 Ermal
			$tickets_per_roll[$rollent['number']] = $rollent['count'];
270
			$minutes_per_roll[$rollent['number']] = $rollent['minutes'];
271
		}
272
	}
273
274
	// split into an array. Useful for multiple vouchers given
275 79262830 Phil Davis
	$a_vouchers_received = preg_split("/[\t\n\r ]+/s", $voucher_received);
276 05771a24 Ermal
	$active_dirty = false;
277 5a6359ae bcyrill
	$unsetindexes = array();
278 2c85b316 Ermal
279 05771a24 Ermal
	// go through all received vouchers, check their valid and extract
280
	// Roll# and Ticket# using the external readvoucher binary
281
	foreach ($a_vouchers_received as $voucher) {
282
		$v = escapeshellarg($voucher);
283 9fcbc9ff Luiz Otavio O Souza
		if (strlen($voucher) < 5) {
284
			captiveportal_syslog("${voucher} invalid: Too short!");
285 05771a24 Ermal
			continue;   // seems too short to be a voucher!
286 79262830 Phil Davis
		}
287 05771a24 Ermal
288 15bec718 Ermal
		unset($output);
289
		$_gb = exec("/usr/local/bin/voucher -c {$g['varetc_path']}/voucher_{$cpzone}.cfg -k {$g['varetc_path']}/voucher_{$cpzone}.public -- $v", $output);
290
		list($status, $roll, $nr) = explode(" ", $output[0]);
291 05771a24 Ermal
		if ($status == "OK") {
292 79262830 Phil Davis
			// check if we have this ticket on a registered roll for this ticket
293 05771a24 Ermal
			if ($tickets_per_roll[$roll] && ($nr <= $tickets_per_roll[$roll])) {
294 79262830 Phil Davis
				// voucher is from a registered roll.
295
				if (!isset($active_vouchers[$roll])) {
296 05771a24 Ermal
					$active_vouchers[$roll] = voucher_read_active_db($roll);
297 79262830 Phil Davis
				}
298 05771a24 Ermal
				// valid voucher. Store roll# and ticket#
299
				if (!empty($active_vouchers[$roll][$voucher])) {
300
					$active_dirty = true;
301
					unset($active_vouchers[$roll][$voucher]);
302
				}
303
				// check if voucher already marked as used
304 79262830 Phil Davis
				if (!isset($bitstring[$roll])) {
305 05771a24 Ermal
					$bitstring[$roll] = voucher_read_used_db($roll);
306 79262830 Phil Davis
				}
307 05771a24 Ermal
				$pos = $nr >> 3; // divide by 8 -> octet
308
				$mask = 1 << ($nr % 8);
309
				// mark bit for this voucher as used
310 79262830 Phil Davis
				if (!(ord($bitstring[$roll][$pos]) & $mask)) {
311 05771a24 Ermal
					$bitstring[$roll][$pos] = chr(ord($bitstring[$roll][$pos]) | $mask);
312 79262830 Phil Davis
				}
313 05771a24 Ermal
				captiveportal_syslog("{$voucher} ({$roll}/{$nr}) forced to expire");
314 2c85b316 Ermal
315
				/* Check if this voucher has any active sessions */
316 26ee5aaf Ermal
				$cpentry = captiveportal_read_db("WHERE username = '{$voucher}'");
317 ea6cbc39 Gertjan
				if (!empty($cpentry) && !empty($cpentry[0])) {
318 086cf944 Phil Davis
					if (empty($cpzoneid) && !empty($config['captiveportal'][$cpzone])) {
319 abaa7feb Ermal LUÇI
						$cpzoneid = $config['captiveportal'][$cpzone]['zoneid'];
320 086cf944 Phil Davis
					}
321 ea6cbc39 Gertjan
					$cpentry = $cpentry[0];
322 086cf944 Phil Davis
					captiveportal_disconnect($cpentry, null, 13);
323
					captiveportal_logportalauth($cpentry[4], $cpentry[3], $cpentry[2], "FORCLY TERMINATING VOUCHER {$voucher} SESSION");
324 5705c60a Renato Botelho
					$unsetindexes[] = $cpentry[5];
325 2c85b316 Ermal
				}
326 79262830 Phil Davis
			} else {
327 f416763b Phil Davis
				captiveportal_syslog("$voucher ($roll/$nr): not found on any registered Roll");
328 79262830 Phil Davis
			}
329
		} else {
330 05771a24 Ermal
			// hmm, thats weird ... not what I expected
331 15bec718 Ermal
			captiveportal_syslog("$voucher invalid: {$output[0]}!!");
332 79262830 Phil Davis
		}
333 05771a24 Ermal
	}
334
335
	// Refresh active DBs
336
	if ($active_dirty == true) {
337 79262830 Phil Davis
		foreach ($active_vouchers as $roll => $active) {
338 05771a24 Ermal
			voucher_write_active_db($roll, $active);
339 79262830 Phil Davis
		}
340 15bec718 Ermal
		unset($active_vouchers);
341 5ebe85e9 Ermal
342 f416763b Phil Davis
		/* Trigger a sync of the vouchers on config */
343 5ebe85e9 Ermal
		send_event("service sync vouchers");
344 05771a24 Ermal
	}
345
346
	// Write back the used DB's
347
	if (is_array($bitstring)) {
348
		foreach ($bitstring as $roll => $used) {
349 79262830 Phil Davis
			if (is_array($used)) {
350
				foreach ($used as $u) {
351 05771a24 Ermal
					voucher_write_used_db($roll, base64_encode($u));
352 79262830 Phil Davis
				}
353 05771a24 Ermal
			} else {
354
				voucher_write_used_db($roll, base64_encode($used));
355
			}
356
		}
357 15bec718 Ermal
		unset($bitstring);
358 05771a24 Ermal
	}
359
360
	unlock($voucherlck);
361
362 2c85b316 Ermal
	/* Write database */
363 79262830 Phil Davis
	if (!empty($unsetindexes)) {
364 26ee5aaf Ermal
		captiveportal_remove_entries($unsetindexes);
365 79262830 Phil Davis
	}
366 2c85b316 Ermal
367 05771a24 Ermal
	return true;
368
}
369
370 79262830 Phil Davis
/*
371 666bc4d1 Ermal
 * Authenticate a voucher and return the remaining time credit in minutes
372 336e3c1c Charlie
 * if $test is set, don't mark the voucher as used nor add it to the list
373
 * of active vouchers
374 666bc4d1 Ermal
 * If $test is set, simply test the voucher. Don't change anything
375
 * but return a more verbose error and result message back
376 336e3c1c Charlie
 */
377
function voucher_auth($voucher_received, $test = 0) {
378 b4792bf8 Ermal
	global $g, $config, $cpzone, $dbc;
379 336e3c1c Charlie
380 79262830 Phil Davis
	if (!isset($config['voucher'][$cpzone]['enable'])) {
381 1a863be2 Ermal
		return 0;
382 79262830 Phil Davis
	}
383 1a863be2 Ermal
384 6c9ca678 Scott Ullrich
	// XMLRPC Call over to the master Voucher node
385 79262830 Phil Davis
	if (!empty($config['voucher'][$cpzone]['vouchersyncdbip'])) {
386 b4792bf8 Ermal
		$syncip   = $config['voucher'][$cpzone]['vouchersyncdbip'];
387
		$syncport = $config['voucher'][$cpzone]['vouchersyncport'];
388
		$syncpass = $config['voucher'][$cpzone]['vouchersyncpass'];
389
		$vouchersyncusername = $config['voucher'][$cpzone]['vouchersyncusername'];
390 6c9ca678 Scott Ullrich
		$remote_time_used = xmlrpc_sync_used_voucher($voucher_received, $syncip, $syncport, $syncpass, $vouchersyncusername);
391
	}
392 fa3ab36a Scott Ullrich
393 90df43c6 Ermal
	$voucherlck = lock("voucher{$cpzone}", LOCK_EX);
394
395 7afb7ea9 Ermal
	// read rolls into assoc array with rollid as key and minutes as value
396
	$tickets_per_roll = array();
397
	$minutes_per_roll = array();
398 b4792bf8 Ermal
	if (is_array($config['voucher'][$cpzone]['roll'])) {
399
		foreach ($config['voucher'][$cpzone]['roll'] as $rollent) {
400 7afb7ea9 Ermal
			$tickets_per_roll[$rollent['number']] = $rollent['count'];
401
			$minutes_per_roll[$rollent['number']] = $rollent['minutes'];
402
		}
403
	}
404 336e3c1c Charlie
405 05771a24 Ermal
	// split into an array. Useful for multiple vouchers given
406 79262830 Phil Davis
	$a_vouchers_received = preg_split("/[\t\n\r ]+/s", $voucher_received);
407 05771a24 Ermal
	$error = 0;
408
	$test_result = array();     // used to display for voucher test option in GUI
409
	$total_minutes = 0;
410
	$first_voucher = "";
411
	$first_voucher_roll = 0;
412
413
	// go through all received vouchers, check their valid and extract
414
	// Roll# and Ticket# using the external readvoucher binary
415
	foreach ($a_vouchers_received as $voucher) {
416
		$v = escapeshellarg($voucher);
417 9fcbc9ff Luiz Otavio O Souza
		if (strlen($voucher) < 5) {
418 b08758c3 jim-p
			$test_result[] = "{$voucher} invalid: Too short!";
419
			captiveportal_syslog("{$voucher} invalid: Too short!");
420
			$error++;
421 05771a24 Ermal
			continue;   // seems too short to be a voucher!
422 79262830 Phil Davis
		}
423 05771a24 Ermal
424 b4792bf8 Ermal
		$result = exec("/usr/local/bin/voucher -c {$g['varetc_path']}/voucher_{$cpzone}.cfg -k {$g['varetc_path']}/voucher_{$cpzone}.public -- $v");
425 05771a24 Ermal
		list($status, $roll, $nr) = explode(" ", $result);
426
		if ($status == "OK") {
427
			if (!$first_voucher) {
428
				// store first voucher. Thats the one we give the timecredit
429
				$first_voucher = $voucher;
430
				$first_voucher_roll = $roll;
431
			}
432 79262830 Phil Davis
			// check if we have this ticket on a registered roll for this ticket
433 05771a24 Ermal
			if ($tickets_per_roll[$roll] && ($nr <= $tickets_per_roll[$roll])) {
434 79262830 Phil Davis
				// voucher is from a registered roll.
435
				if (!isset($active_vouchers[$roll])) {
436 05771a24 Ermal
					$active_vouchers[$roll] = voucher_read_active_db($roll);
437 79262830 Phil Davis
				}
438 05771a24 Ermal
				// valid voucher. Store roll# and ticket#
439
				if (!empty($active_vouchers[$roll][$voucher])) {
440 086cf944 Phil Davis
					list($timestamp, $minutes) = explode(",", $active_vouchers[$roll][$voucher]);
441 05771a24 Ermal
					// we have an already active voucher here.
442
					$remaining = intval((($timestamp + (60*$minutes)) - time())/60);
443 d8012adb Vinicius Coque
					$test_result[] = sprintf(gettext('%1$s (%2$s/%3$s) active and good for %4$d Minutes'), $voucher, $roll, $nr, $remaining);
444 05771a24 Ermal
					$total_minutes += $remaining;
445
				} else {
446
					// voucher not used. Check if ticket Id is on the roll (not too high)
447
					// and if the ticket is marked used.
448
					// check if voucher already marked as used
449 79262830 Phil Davis
					if (!isset($bitstring[$roll])) {
450 05771a24 Ermal
						$bitstring[$roll] = voucher_read_used_db($roll);
451 79262830 Phil Davis
					}
452 05771a24 Ermal
					$pos = $nr >> 3; // divide by 8 -> octet
453
					$mask = 1 << ($nr % 8);
454
					if (ord($bitstring[$roll][$pos]) & $mask) {
455
						$test_result[] = "$voucher ($roll/$nr) already used and expired";
456 34507786 Scott Ullrich
						captiveportal_syslog("$voucher ($roll/$nr) already used and expired");
457 05771a24 Ermal
						$total_minutes = -1;    // voucher expired
458
						$error++;
459
					} else {
460
						// mark bit for this voucher as used
461
						$bitstring[$roll][$pos] = chr(ord($bitstring[$roll][$pos]) | $mask);
462
						$test_result[] = "$voucher ($roll/$nr) good for {$minutes_per_roll[$roll]} Minutes";
463
						$total_minutes += $minutes_per_roll[$roll];
464
					}
465
				}
466
			} else {
467 f416763b Phil Davis
				$test_result[] = "$voucher ($roll/$nr): not found on any registered Roll";
468
				captiveportal_syslog("$voucher ($roll/$nr): not found on any registered Roll");
469 05771a24 Ermal
			}
470
		} else {
471
			// hmm, thats weird ... not what I expected
472
			$test_result[] = "$voucher invalid: $result !!";
473
			captiveportal_syslog("$voucher invalid: $result !!");
474
			$error++;
475
		}
476
	}
477 336e3c1c Charlie
478 05771a24 Ermal
	// if this was a test call, we're done. Return the result.
479
	if ($test) {
480
		if ($error) {
481 d8012adb Vinicius Coque
			$test_result[] = gettext("Access denied!");
482 05771a24 Ermal
		} else {
483 086cf944 Phil Davis
			$test_result[] = sprintf(gettext("Access granted for %d Minutes in total."), $total_minutes);
484 05771a24 Ermal
		}
485
		unlock($voucherlck);
486 336e3c1c Charlie
487 05771a24 Ermal
		return $test_result;
488
	}
489 336e3c1c Charlie
490 05771a24 Ermal
	// if we had an error (one of the vouchers is invalid), return 0.
491
	// Discussion: we could return the time remaining for good vouchers, but then
492
	// the user wouldn't know that he used at least one invalid voucher.
493
	if ($error) {
494 830c33be Scott Ullrich
		unlock($voucherlck);
495 79262830 Phil Davis
		if ($total_minutes > 0) {   // probably not needed, but want to make sure
496 05771a24 Ermal
			$total_minutes = 0;     // we only report -1 (expired) or 0 (no access)
497 79262830 Phil Davis
		}
498 05771a24 Ermal
		return $total_minutes;       // well, at least one voucher had errors. Say NO ACCESS
499
	}
500 336e3c1c Charlie
501 6c9ca678 Scott Ullrich
	// If we did a XMLRPC sync earlier check the timeleft
502 62f96568 Ermal
	if (!empty($config['voucher'][$cpzone]['vouchersyncdbip'])) {
503 79262830 Phil Davis
		if (!is_null($remote_time_used)) {
504 62f96568 Ermal
			$total_minutes = $remote_time_used;
505 79262830 Phil Davis
		} else if ($remote_time_used < $total_minutes) {
506 f3512fca jim-p
			$total_minutes -= $remote_time_used;
507 79262830 Phil Davis
		}
508 62f96568 Ermal
	}
509 830c33be Scott Ullrich
510 05771a24 Ermal
	// All given vouchers were valid and this isn't simply a test.
511
	// Write back the used DB's
512 c14d9967 Scott Ullrich
	if (is_array($bitstring)) {
513
		foreach ($bitstring as $roll => $used) {
514 79262830 Phil Davis
			if (is_array($used)) {
515
				foreach ($used as $u) {
516 c14d9967 Scott Ullrich
					voucher_write_used_db($roll, base64_encode($u));
517 79262830 Phil Davis
				}
518 c14d9967 Scott Ullrich
			} else {
519
				voucher_write_used_db($roll, base64_encode($used));
520
			}
521
		}
522
	}
523 336e3c1c Charlie
524 05771a24 Ermal
	// Active DB: we only add the first voucher if multiple given
525
	// and give that one all the time credit. This allows the user to logout and
526
	// log in later using just the first voucher. It also keeps username limited
527
	// to one voucher and that voucher shows the correct time credit in 'active vouchers'
528
	if (!empty($active_vouchers[$first_voucher_roll][$first_voucher])) {
529
		list($timestamp, $minutes) = explode(",", $active_vouchers[$first_voucher_roll][$first_voucher]);
530
	} else {
531
		$timestamp = time();    // new voucher
532
		$minutes = $total_minutes;
533
	}
534 336e3c1c Charlie
535 05771a24 Ermal
	$active_vouchers[$first_voucher_roll][$first_voucher] = "$timestamp,$minutes";
536 80b86de5 Ermal
	voucher_write_active_db($first_voucher_roll, $active_vouchers[$first_voucher_roll]);
537 336e3c1c Charlie
538 f416763b Phil Davis
	/* Trigger a sync of the vouchers on config */
539 5ebe85e9 Ermal
	send_event("service sync vouchers");
540
541 05771a24 Ermal
	unlock($voucherlck);
542 336e3c1c Charlie
543 05771a24 Ermal
	return $total_minutes;
544 336e3c1c Charlie
}
545
546 351b6990 Ermal
function voucher_configure($sync = false) {
547 b4792bf8 Ermal
	global $config, $g, $cpzone;
548 336e3c1c Charlie
549 b4792bf8 Ermal
	if (is_array($config['voucher'])) {
550
		foreach ($config['voucher'] as $voucherzone => $vcfg) {
551 79262830 Phil Davis
			if (platform_booting()) {
552
				echo gettext("Enabling voucher support... ");
553
			}
554 b4792bf8 Ermal
			$cpzone = $voucherzone;
555 abf421ce Ermal
			$error = voucher_configure_zone($sync);
556 285ef132 Ermal LUÇI
			if (platform_booting()) {
557 79262830 Phil Davis
				if ($error) {
558 abf421ce Ermal
					echo "error\n";
559 79262830 Phil Davis
				} else {
560 abf421ce Ermal
					echo "done\n";
561 79262830 Phil Davis
				}
562 abf421ce Ermal
			}
563 b4792bf8 Ermal
		}
564
	}
565
}
566
567
function voucher_configure_zone($sync = false) {
568
	global $config, $g, $cpzone;
569
570 79262830 Phil Davis
	if (!isset($config['voucher'][$cpzone]['enable'])) {
571 eaca40df Ermal
		return 0;
572 79262830 Phil Davis
	}
573 336e3c1c Charlie
574 79262830 Phil Davis
	if ($sync == true) {
575
		captiveportal_syslog("Writing voucher db from sync data...");
576
	}
577 336e3c1c Charlie
578 b4792bf8 Ermal
	$voucherlck = lock("voucher{$cpzone}", LOCK_EX);
579 eaca40df Ermal
580 79262830 Phil Davis
	/* write public key used to verify vouchers */
581
	$pubkey = base64_decode($config['voucher'][$cpzone]['publickey']);
582
	$fd = fopen("{$g['varetc_path']}/voucher_{$cpzone}.public", "w");
583
	if (!$fd) {
584
		captiveportal_syslog("Voucher error: cannot write voucher.public\n");
585
		unlock($voucherlck);
586
		return 1;
587
	}
588
	fwrite($fd, $pubkey);
589
	fclose($fd);
590
	@chmod("{$g['varetc_path']}/voucher_{$cpzone}.public", 0600);
591
592
	/* write config file used by voucher binary to decode vouchers */
593
	$fd = fopen("{$g['varetc_path']}/voucher_{$cpzone}.cfg", "w");
594
	if (!$fd) {
595
		printf(gettext("Error: cannot write voucher.cfg") . "\n");
596
		unlock($voucherlck);
597
		return 1;
598
	}
599
	fwrite($fd, "{$config['voucher'][$cpzone]['rollbits']},{$config['voucher'][$cpzone]['ticketbits']},{$config['voucher'][$cpzone]['checksumbits']},{$config['voucher'][$cpzone]['magic']},{$config['voucher'][$cpzone]['charset']}\n");
600
	fclose($fd);
601
	@chmod("{$g['varetc_path']}/voucher_{$cpzone}.cfg", 0600);
602 c4ea3691 Ermal
	unlock($voucherlck);
603 336e3c1c Charlie
604 79262830 Phil Davis
	if ((platform_booting() || $sync == true) && is_array($config['voucher'][$cpzone]['roll'])) {
605 336e3c1c Charlie
606 b4792bf8 Ermal
		$voucherlck = lock("voucher{$cpzone}", LOCK_EX);
607 336e3c1c Charlie
608 79262830 Phil Davis
		// create active and used DB per roll on ramdisk from config
609
		foreach ($config['voucher'][$cpzone]['roll'] as $rollent) {
610
611
			$roll = $rollent['number'];
612 351b8519 Luiz Otavio O Souza
			$len = ($rollent['count'] >> 3) + 1;
613 4e322e2c Phil Davis
			if (strlen(base64_decode($rollent['used'])) != $len) {
614 351b8519 Luiz Otavio O Souza
				$rollent['used'] = base64_encode(str_repeat("\000", $len));
615 4e322e2c Phil Davis
			}
616 79262830 Phil Davis
			voucher_write_used_db($roll, $rollent['used']);
617
			$minutes = $rollent['minutes'];
618
			$active_vouchers = array();
619
			$a_active = &$rollent['active'];
620
			if (is_array($a_active)) {
621
				foreach ($a_active as $activent) {
622
					$voucher = $activent['voucher'];
623
					$timestamp = $activent['timestamp'];
624
					$minutes = $activent['minutes'];
625
					// its tempting to check for expired timestamps, but during
626
					// bootup, we most likely don't have the correct time.
627
					$active_vouchers[$voucher] = "$timestamp,$minutes";
628
				}
629
			}
630
			voucher_write_active_db($roll, $active_vouchers);
631
		}
632 c4ea3691 Ermal
633
		unlock($voucherlck);
634 79262830 Phil Davis
	}
635 eaca40df Ermal
636
	return 0;
637 336e3c1c Charlie
}
638
639 79262830 Phil Davis
/* write bitstring of used vouchers to ramdisk.
640 336e3c1c Charlie
 * Bitstring must already be base64_encoded!
641
 */
642
function voucher_write_used_db($roll, $vdb) {
643 b4792bf8 Ermal
	global $g, $cpzone;
644 666bc4d1 Ermal
645 b4792bf8 Ermal
	$fd = fopen("{$g['vardb_path']}/voucher_{$cpzone}_used_$roll.db", "w");
646 666bc4d1 Ermal
	if ($fd) {
647
		fwrite($fd, $vdb . "\n");
648
		fclose($fd);
649 79262830 Phil Davis
	} else {
650 b4792bf8 Ermal
		voucher_log(LOG_ERR, sprintf(gettext('cant write %1$s/voucher_%s_used_%2$s.db'), $g['vardb_path'], $cpzone, $roll));
651 79262830 Phil Davis
	}
652 336e3c1c Charlie
}
653
654
/* return assoc array of active vouchers with activation timestamp
655 79262830 Phil Davis
 * voucher is index.
656 336e3c1c Charlie
 */
657
function voucher_read_active_db($roll) {
658 b4792bf8 Ermal
	global $g, $cpzone;
659 666bc4d1 Ermal
660
	$active = array();
661
	$dirty = 0;
662 b4792bf8 Ermal
	$file = "{$g['vardb_path']}/voucher_{$cpzone}_active_$roll.db";
663 666bc4d1 Ermal
	if (file_exists($file)) {
664
		$fd = fopen($file, "r");
665
		if ($fd) {
666
			while (!feof($fd)) {
667
				$line = trim(fgets($fd));
668
				if ($line) {
669 086cf944 Phil Davis
					list($voucher, $timestamp, $minutes) = explode(",", $line); // voucher,timestamp
670 79262830 Phil Davis
					if ((($timestamp + (60*$minutes)) - time()) > 0) {
671 666bc4d1 Ermal
						$active[$voucher] = "$timestamp,$minutes";
672 79262830 Phil Davis
					} else {
673 666bc4d1 Ermal
						$dirty=1;
674 79262830 Phil Davis
					}
675 666bc4d1 Ermal
				}
676
			}
677
			fclose($fd);
678 5ebe85e9 Ermal
			if ($dirty) { // if we found expired entries, lets save our snapshot
679 666bc4d1 Ermal
				voucher_write_active_db($roll, $active);
680 5ebe85e9 Ermal
681 f416763b Phil Davis
				/* Trigger a sync of the vouchers on config */
682 5ebe85e9 Ermal
				send_event("service sync vouchers");
683
			}
684 666bc4d1 Ermal
		}
685
	}
686
	return $active;
687 336e3c1c Charlie
}
688
689
/* store array of active vouchers back to DB */
690
function voucher_write_active_db($roll, $active) {
691 79262830 Phil Davis
	global $g, $cpzone;
692 336e3c1c Charlie
693 79262830 Phil Davis
	if (!is_array($active)) {
694 05771a24 Ermal
		return;
695 79262830 Phil Davis
	}
696
	$fd = fopen("{$g['vardb_path']}/voucher_{$cpzone}_active_$roll.db", "w");
697
	if ($fd) {
698
		foreach ($active as $voucher => $value) {
699
			fwrite($fd, "$voucher,$value\n");
700
		}
701
		fclose($fd);
702
	}
703 336e3c1c Charlie
}
704
705
/* return how many vouchers are marked used on a roll */
706
function voucher_used_count($roll) {
707 79262830 Phil Davis
	global $g, $cpzone;
708
709
	$bitstring = voucher_read_used_db($roll);
710
	$max = strlen($bitstring) * 8;
711
	$used = 0;
712
	for ($i = 1; $i <= $max; $i++) {
713
		// check if ticket already used or not.
714
		$pos = $i >> 3;            // divide by 8 -> octet
715
		$mask = 1 << ($i % 8);  // mask to test bit in octet
716
		if (ord($bitstring[$pos]) & $mask) {
717
			$used++;
718
		}
719
	}
720
	unset($bitstring);
721
722
	return $used;
723 336e3c1c Charlie
}
724
725
function voucher_read_used_db($roll) {
726 79262830 Phil Davis
	global $g, $cpzone;
727
728
	$vdb = "";
729
	$file = "{$g['vardb_path']}/voucher_{$cpzone}_used_$roll.db";
730
	if (file_exists($file)) {
731
		$fd = fopen($file, "r");
732
		if ($fd) {
733
			$vdb = trim(fgets($fd));
734
			fclose($fd);
735
		} else {
736
			voucher_log(LOG_ERR, sprintf(gettext('cant read %1$s/voucher_%s_used_%2$s.db'), $g['vardb_path'], $cpzone, $roll));
737
		}
738
	}
739
	return base64_decode($vdb);
740 336e3c1c Charlie
}
741
742
function voucher_unlink_db($roll) {
743 79262830 Phil Davis
	global $g, $cpzone;
744
	@unlink("{$g['vardb_path']}/voucher_{$cpzone}_used_$roll.db");
745
	@unlink("{$g['vardb_path']}/voucher_{$cpzone}_active_$roll.db");
746 336e3c1c Charlie
}
747
748
/* we share the log with captiveportal for now */
749
function voucher_log($priority, $message) {
750
751 79262830 Phil Davis
	$message = trim($message);
752
	openlog("logportalauth", LOG_PID, LOG_LOCAL4);
753 086cf944 Phil Davis
	syslog($priority, sprintf(gettext("Voucher: %s"), $message));
754 79262830 Phil Davis
	closelog();
755 336e3c1c Charlie
}
756
757
/* Save active and used voucher DB into XML config and write it to flash
758 5ebe85e9 Ermal
 * Called during reboot -> system_reboot_cleanup() and every active voucher change
759 336e3c1c Charlie
 */
760
function voucher_save_db_to_config() {
761 79262830 Phil Davis
	global $config, $g, $cpzone;
762 b4792bf8 Ermal
763
	if (is_array($config['voucher'])) {
764 573ae2d1 Chris Buechler
		foreach ($config['voucher'] as $voucherzone => $vcfg) {
765 b4792bf8 Ermal
			$cpzone = $voucherzone;
766
			voucher_save_db_to_config_zone();
767
		}
768
	}
769
}
770
771 52bb3619 Cyrill Bannwart
function voucher_save_db_to_config_zone() {
772 79262830 Phil Davis
	global $config, $g, $cpzone;
773 336e3c1c Charlie
774 79262830 Phil Davis
	if (!isset($config['voucher'][$cpzone]['enable'])) {
775
		return;   // no vouchers or don't want to save DB's
776
	}
777
778
	if (!is_array($config['voucher'][$cpzone]['roll'])) {
779
		return;
780
	}
781
782
	$voucherlck = lock("voucher{$cpzone}", LOCK_EX);
783 c65c3a5d Ermal
784 79262830 Phil Davis
	// walk all active rolls and save runtime DB's to flash
785
	$a_roll = &$config['voucher'][$cpzone]['roll'];
786
	while (list($key, $value) = each($a_roll)) {
787
		$rollent = &$a_roll[$key];
788
		$roll = $rollent['number'];
789
		$bitmask = voucher_read_used_db($roll);
790
		$rollent['used'] = base64_encode($bitmask);
791
		$active_vouchers = voucher_read_active_db($roll);
792
		$db = array();
793 4d5bbdfb Scott Ullrich
		$dbi = 1;
794 79262830 Phil Davis
		foreach ($active_vouchers as $voucher => $line) {
795 086cf944 Phil Davis
			list($timestamp, $minutes) = explode(",", $line);
796 79262830 Phil Davis
			$activent['voucher'] = $voucher;
797
			$activent['timestamp'] = $timestamp;
798
			$activent['minutes'] = $minutes;
799
			$db["v{$dbi}"] = $activent;
800
			$dbi++;
801
		}
802
		$rollent['active'] = $db;
803
		unset($active_vouchers);
804
	}
805
806
	unlock($voucherlck);
807
808
	write_config("Syncing vouchers");
809
	return;
810 336e3c1c Charlie
}
811
812 2ee5fe9b Ermal Lu?i
?>