Project

General

Profile

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