Project

General

Profile

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