Project

General

Profile

Download (14.5 KB) Statistics
| Branch: | Tag: | Revision:
1
<?php
2
/*
3
    Copyright (C) 2007 Marcel Wiget <mwiget@mac.com>.
4
    All rights reserved.
5
    
6
    Redistribution and use in source and binary forms, with or without
7
    modification, are permitted provided that the following conditions are met:
8
    
9
    1. Redistributions of source code must retain the above copyright notice,
10
       this list of conditions and the following disclaimer.
11
    
12
    2. Redistributions in binary form must reproduce the above copyright
13
       notice, this list of conditions and the following disclaimer in the
14
       documentation and/or other materials provided with the distribution.
15
    
16
    THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
17
    INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
18
    AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
19
    AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
20
    OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
21
    SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
22
    INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
23
    CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
24
    ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25
    POSSIBILITY OF SUCH DAMAGE.
26

    
27
*/
28
    
29
/* include all configuration functions */
30
require_once("config.inc");
31

    
32
/* 
33
 *Authenticate a voucher and return the remaining time credit in minutes
34
 * if $test is set, don't mark the voucher as used nor add it to the list
35
 * of active vouchers
36
 */
37
function voucher_auth($voucher_received, $test = 0) {
38

    
39
    global $g, $config;
40

    
41
    // if $test is set, simply test the voucher. Don't change anything
42
    // but return a more verbose error and result message back
43

    
44
    if (! $test)
45
        $voucherlck = lock('voucher');
46

    
47
    // read rolls into assoc array with rollid as key and minutes as value
48
    $a_roll = &$config['voucher']['roll'];
49
    foreach ($a_roll as $rollent) {
50
        $tickets_per_roll[$rollent['number']] = $rollent['count'];
51
        $minutes_per_roll[$rollent['number']] = $rollent['minutes'];
52
    }
53

    
54
    // split into an array. Useful for multiple vouchers given
55
    $a_vouchers_received = split("[\t\n\r ]+",$voucher_received); 
56
    $error = 0;
57
    $test_result = array();     // used to display for voucher test option in GUI
58
    $total_minutes = 0;
59
    $first_voucher = "";
60
    $first_voucher_roll = 0;
61

    
62
    // go through all received vouchers, check their valid and extract
63
    // Roll# and Ticket# using the external readvoucher binary
64

    
65
    foreach ($a_vouchers_received as $voucher) {
66

    
67
        $v = escapeshellarg($voucher);
68
        if (strlen($voucher) < 3)
69
            continue;   // seems too short to be a voucher!
70

    
71
        $result = exec("/usr/local/bin/voucher -c {$g['varetc_path']}/voucher.cfg -k {$g['varetc_path']}/voucher.public -- $v");
72
        list($status, $roll, $nr) = explode(" ", $result);
73
        if ($status == "OK") {
74
            if (!$first_voucher) 
75
            {
76
                $first_voucher = $voucher;  // store first voucher. Thats the one we give the timecredit
77
                $first_voucher_roll = $roll;
78
            }
79
            // check if we have this ticket on a registered roll for this ticket 
80
            if ($tickets_per_roll[$roll] && ($nr <= $tickets_per_roll[$roll])) {
81
                // voucher is from a registered roll. 
82
                if (!isset($active_vouchers[$roll]))
83
                    $active_vouchers[$roll] = voucher_read_active_db($roll);
84
                // valid voucher. Store roll# and ticket#
85
                if ($line = $active_vouchers[$roll][$voucher]) {
86
                    list($timestamp,$minutes) = explode(",", $line);
87
                    // we have an already active voucher here.
88
                    $remaining = intval((($timestamp + 60*$minutes) - time())/60);
89
                    $test_result[] = "$voucher ($roll/$nr) active and good for $remaining Minutes";
90
                    $total_minutes += $remaining;
91
                } else {
92
                    // voucher not used. Check if ticket Id is on the roll (not too high)
93
                    // and if the ticket is marked used.
94
                    // check if voucher already marked as used
95
                    if (!isset($bitstring[$roll]))
96
                        $bitstring[$roll] = voucher_read_used_db($roll);
97
                    $pos = $nr >> 3; // divide by 8 -> octet
98
                    $mask = 1 << ($nr % 8);
99
                    if (ord($bitstring[$roll][$pos]) & $mask) {
100
                        $test_result[] = "$voucher ($roll/$nr) already used and expired";
101
                        $total_minutes = -1;    // voucher expired
102
                        $error++;
103
                    } else {
104
                        // mark bit for this voucher as used
105
                        $bitstring[$roll][$pos] = chr(ord($bitstring[$roll][$pos]) | $mask);
106
                        $test_result[] = "$voucher ($roll/$nr) good for {$minutes_per_roll[$roll]} Minutes";
107
                        $total_minutes += $minutes_per_roll[$roll];
108
                    }
109
                }
110
            } else {
111
                $test_result[] = "$voucher ($roll/$nr): not found on any registererd Roll";
112
            }
113
        } else {
114
            // hmm, thats weired ... not what I expected
115
            $test_result[] = "$voucher invalid: $result !!";
116
            $error++;
117
        }
118
    }
119

    
120
    // if this was a test call, we're done. Return the result.
121
    if ($test) {
122
        if ($error) {
123
            $test_result[] = "Access denied!";
124
        } else {
125
            $test_result[] = "Access granted for $total_minutes Minutes in total.";
126
        }
127
	unlock($voucherlck);
128
        return $test_result;
129
    }
130

    
131
    // if we had an error (one of the vouchers is invalid), return 0.
132
    // Discussion: we could return the time remaining for good vouchers, but then
133
    // the user wouldn't know that he used at least one invalid voucher.
134

    
135
    if ($error) {
136
	unlock($voucherlck);
137
        if ($total_minutes > 0)     // probably not needed, but want to make sure
138
            $total_minutes = 0;     // we only report -1 (expired) or 0 (no access)
139
        return $total_minutes;       // well, at least one voucher had errors. Say NO ACCESS
140
    }
141

    
142
    // All given vouchers were valid and this isn't simply a test.
143
    // Write back the used DB's
144

    
145
    if (is_array($bitstring))
146
        foreach ($bitstring as $roll => $used)
147
            voucher_write_used_db($roll, base64_encode($used));
148

    
149
    // Active DB: we only add the first voucher if multiple given
150
    // and give that one all the time credit. This allows the user to logout and
151
    // log in later using just the first voucher. It also keeps username limited
152
    // to one voucher and that voucher shows the correct time credit in 'active vouchers'
153

    
154
    if ($line = $active_vouchers[$first_voucher_roll][$first_voucher]) {
155
        list($timestamp, $minutes) = explode(",", $line);
156
    } else {
157
        $timestamp = time();    // new voucher
158
        $minutes = $total_minutes;
159
    }
160

    
161
    $active_vouchers[$first_voucher_roll][$first_voucher] = "$timestamp,$minutes";
162
    voucher_write_active_db($roll, $active_vouchers[$first_voucher_roll]);
163

    
164
    // mark the DB's as dirty.
165
    mark_subsystem_dirty('voucher');
166

    
167
    unlock($voucherlck);
168

    
169
    return $total_minutes;
170
}
171

    
172
function voucher_configure() {
173
    global $config, $g;
174
    
175
    /* kill any running minicron */
176
    killbypid("{$g['varrun_path']}/vouchercron.pid");
177

    
178
    if (isset($config['voucher']['enable'])) {
179

    
180
        if ($g['booting']) {
181
            echo "Enabling voucher support... ";
182
        }
183

    
184
        // start cron if we're asked to save runtime DB periodically
185
        // to XML config if it changed
186
        $croninterval = $config['voucher']['saveinterval'] * 60; // need seconds. Config has minutes
187
        if ($croninterval) {
188
            /* start pruning process (interval defaults to 60 seconds) */
189
            mwexec("/usr/local/bin/minicron $croninterval {$g['varrun_path']}/vouchercron.pid " .
190
                    "/etc/rc.savevoucher");
191
        }
192

    
193
	$voucherlck = lock('voucher');
194
        /* write public key used to verify vouchers */
195
        $pubkey = base64_decode($config['voucher']['publickey']);
196
        $fd = fopen("{$g['varetc_path']}/voucher.public", "w");
197
        if (!$fd) {
198
            printf("Error: cannot write voucher.public\n");
199
	    unlock($voucherlck);
200
            return 1;
201
        }
202
        chmod("{$g['varetc_path']}/voucher.public", 0600);
203
        fwrite($fd, $pubkey);
204
        fclose($fd);
205

    
206
        /* write config file used by voucher binary to decode vouchers */
207
        $fd = fopen("{$g['varetc_path']}/voucher.cfg", "w");
208
        if (!$fd) {
209
            printf("Error: cannot write voucher.cfg\n");
210
	    unlock($voucherlck);
211
            return 1;
212
        }
213
        chmod("{$g['varetc_path']}/voucher.cfg", 0600);
214
        fwrite($fd, "{$config['voucher']['rollbits']},{$config['voucher']['ticketbits']},{$config['voucher']['checksumbits']},{$config['voucher']['magic']},{$config['voucher']['charset']}\n");
215
        fclose($fd);
216
	unlock($voucherlck);
217

    
218
        if ($g['booting']) {
219

    
220
            // create active and used DB per roll on ramdisk from config
221
            $a_roll = &$config['voucher']['roll'];
222
	    $voucherlck = lock('voucher');
223

    
224
            foreach ($a_roll as $rollent) {
225

    
226
                $roll = $rollent['number'];
227
                voucher_write_used_db($roll, $rollent['used']);
228
                $minutes = $rollent['minutes'];
229
                $active_vouchers = array();
230
                $a_active = &$rollent['active'];
231
                if (is_array($a_active)) {
232
                    foreach ($a_active as $activent) {
233
                        $voucher = $activent['voucher'];
234
                        $timestamp = $activent['timestamp'];
235
                        $minutes = $activent['minutes'];
236
                        // its tempting to check for expired timestamps, but during
237
                        // bootup, we most likely don't have the correct time time.
238
                        $active_vouchers[$voucher] = "$timestamp,$minutes";
239
                    }
240
                }
241
                voucher_write_active_db($roll, $active_vouchers);
242
            }
243
		
244
	    unlock($voucherlck);
245
            echo "done\n";
246
        }
247
    }
248
    return 0;
249
}
250

    
251
/* write bitstring of used vouchers to ramdisk. 
252
 * Bitstring must already be base64_encoded!
253
 */
254
function voucher_write_used_db($roll, $vdb) {
255

    
256
    global $g;
257

    
258
    $fd = fopen("{$g['vardb_path']}/voucher_used_$roll.db", "w");
259
    if ($fd) {
260
        fwrite($fd, $vdb . "\n");
261
        fclose($fd);
262
    } else {
263
        voucher_log(LOG_ERR, "cant write {$g['vardb_path']}/voucher_used_$roll.db");
264
    }
265
}
266

    
267
/* return assoc array of active vouchers with activation timestamp
268
 * voucher is index. 
269
 */
270
function voucher_read_active_db($roll) {
271

    
272
    global $g;
273

    
274
    $active = array();
275
    $dirty = 0;
276
    $file = "{$g['vardb_path']}/voucher_active_$roll.db";
277
    if (file_exists($file)) {
278
        $fd = fopen($file, "r");
279
        if ($fd) {
280
            while (!feof($fd)) {
281
                $line = trim(fgets($fd));
282
                if ($line) {
283
                    list($voucher,$timestamp,$minutes) = explode(",", $line); // voucher,timestamp
284
                    if ((($timestamp + 60*$minutes) - time()) > 0) {
285
                        $active[$voucher] = "$timestamp,$minutes";
286
                    } else {
287
                        $dirty=1;
288
                    }
289
                }
290
            }
291
            fclose($fd);
292
            if ($dirty) // if we found expired entries, lets save our snapshot
293
                voucher_write_active_db($roll, $active);
294
        }
295
    }
296
    return $active;
297
}
298

    
299
/* store array of active vouchers back to DB */
300
function voucher_write_active_db($roll, $active) {
301

    
302
    global $g;
303

    
304
    $fd = fopen("{$g['vardb_path']}/voucher_active_$roll.db", "w");
305
    if ($fd) {
306
        foreach($active as $voucher => $value)
307
            fwrite($fd, "$voucher,$value\n");
308
        fclose($fd);
309
    }
310
}
311

    
312
/* return how many vouchers are marked used on a roll */
313
function voucher_used_count($roll) {
314

    
315
    global $g;
316

    
317
    $bitstring = voucher_read_used_db($roll);
318
    $max = strlen($bitstring) * 8;
319
    $used = 0;
320
    for ($i = 1; $i <= $max; $i++) {
321
        // check if ticket already used or not. 
322
        $pos = $i >> 3;            // divide by 8 -> octet
323
        $mask = 1 << (($i % 8)-1);  // mask to test bit in octet
324
        if (ord($bitstring[$pos]) & $mask)
325
            $used++;
326
    }   
327
    return $used;
328
}
329

    
330
function voucher_read_used_db($roll) {
331

    
332
    global $g;
333

    
334
    $vdb = "";
335
    $file = "{$g['vardb_path']}/voucher_used_$roll.db";
336
    if (file_exists($file)) {
337
        $fd = fopen($file, "r");
338
        if ($fd) {
339
            $vdb = trim(fgets($fd));
340
            fclose($fd);
341
        } else {
342
            voucher_log(LOG_ERR, "cant read {$g['vardb_path']}/voucher_used_$roll.db");
343
        }
344
    }
345
    return base64_decode($vdb);
346
}
347

    
348
function voucher_unlink_db($roll) {
349

    
350
    global $g;
351
    unlink("{$g['vardb_path']}/voucher_used_$roll.db");
352
    unlink("{$g['vardb_path']}/voucher_active_$roll.db");
353
}
354

    
355
/* we share the log with captiveportal for now */
356
function voucher_log($priority, $message) {
357

    
358
    define_syslog_variables();
359
    $message = trim($message);
360
    openlog("logportalauth", LOG_PID, LOG_LOCAL4);
361
    syslog($priority, "Voucher: " . $message);
362
    closelog();
363
}
364

    
365
/* Save active and used voucher DB into XML config and write it to flash
366
 * Called during reboot -> system_reboot_cleanup() and minicron
367
 */
368
function voucher_save_db_to_config() {
369

    
370
    global $config, $g;
371
    
372
    if (!isset($config['voucher']['enable']) || $config['voucher']['saveinterval'] == 0) 
373
        return;   // no vouchers or don't want to save DB's
374

    
375
    if (!is_subsystem_dirty('voucher'))
376
        return;     // nothing changed.
377

    
378
    $voucherlck = lock('voucher');
379

    
380
    // walk all active rolls and save runtime DB's to flash
381
    $a_roll = &$config['voucher']['roll'];
382
//    foreach ($a_roll as $rollent) {
383
    while (list($key, $value) = each($a_roll)) {
384
        $rollent = &$a_roll[$key];
385
        $roll = $rollent['number'];
386
        $bitmask = voucher_read_used_db($roll);
387
        $rollent['used'] = base64_encode($bitmask);
388
        $active_vouchers = voucher_read_active_db($roll);
389
        $db = array();
390
        foreach($active_vouchers as $voucher => $line) {
391
            list($timestamp,$minutes) = explode(",", $line);
392
            $activent['voucher'] = $voucher;
393
            $activent['timestamp'] = $timestamp;
394
            $activent['minutes'] = $minutes;
395
            $db[] = $activent;
396
        }
397
        $rollent['active'] = $db;
398
    }
399
    clear_subsystem_dirty('voucher');
400
    unlock($voucherlck);
401
    write_config();
402
    return;
403
}
404

    
405
?>
(37-37/43)