Subversion-Projekte lars-tiefland.php_share

Revision

Details | Letzte Änderung | Log anzeigen | RSS feed

Revision Autor Zeilennr. Zeile
1 lars 1
<?php
2
/* vim: set expandtab tabstop=4 shiftwidth=4: */
3
/*
4
Copyright (c) 2003, Michael Bretterklieber <michael@bretterklieber.com>
5
All rights reserved.
6
 
7
Redistribution and use in source and binary forms, with or without
8
modification, are permitted provided that the following conditions
9
are met:
10
 
11
1. Redistributions of source code must retain the above copyright
12
   notice, this list of conditions and the following disclaimer.
13
2. Redistributions in binary form must reproduce the above copyright
14
   notice, this list of conditions and the following disclaimer in the
15
   documentation and/or other materials provided with the distribution.
16
3. The names of the authors may not be used to endorse or promote products
17
   derived from this software without specific prior written permission.
18
 
19
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22
IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23
INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24
BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
26
OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
27
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
28
EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
29
 
30
This code cannot simply be copied and put under the GNU Public License or
31
any other GPL-like (LGPL, GPL2) License.
32
 
33
    $Id: SMBPasswd.php,v 1.4 2005/05/08 09:10:32 mbretter Exp $
34
*/
35
 
36
require_once 'PEAR.php';
37
require_once 'Crypt/CHAP.php';
38
 
39
/**
40
 * Class to manage SAMBA smbpasswd-style files
41
 *
42
 * Example 1 (modifying existing file):
43
 *
44
 * $f = new File_SMBPasswd('./smbpasswd');
45
 * $f->load();
46
 * $f->addAccount('sepp3', 12, 'MyPw');
47
 * $f->modAccount('sepp', '', 'MyPw');
48
 * $f->delAccount('karli');
49
 * $f->printAccounts();
50
 * $f->save();
51
 *
52
 * Example 2 (creating a new file):
53
 *
54
 * $f = new File_SMBPasswd('./smbpasswdnew');
55
 * $f->addAccount('sepp1', 12, 'MyPw');
56
 * $f->addAccount('sepp3', 1000, 'MyPw');
57
 * $f->save();
58
 *
59
 * Example 3 (authentication):
60
 *
61
 * $f = new File_SMBPasswd('./smbpasswdnew');
62
 * $f->load();
63
 * if ($f->verifyAccount('sepp', 'MyPw')) {
64
 *     echo "Account valid";
65
 * } else {
66
 *     echo "Account invalid or disabled";
67
 * }
68
 *
69
 * @author Michael Bretterklieber <mbretter@jawa.at>
70
 * @access  public
71
 * @version 0.9.0
72
 * @package File_SMBPasswd
73
 * @category File
74
 */
75
class File_SMBPasswd extends PEAR {
76
 
77
    /**
78
     * Multidimensional array of accounts
79
     * @var array
80
     */
81
    var $accounts = array();
82
 
83
    /**
84
     * Path to the smbpasswd file
85
     * @var string
86
     */
87
    var $file;
88
 
89
    /**
90
     * Class who generates the NT-Hash and LAN-Manager-Hash
91
     * @var object
92
     */
93
    var $cryptEngine;
94
 
95
    /**
96
     * Filehandle, of locked File
97
     * @var resource
98
     */
99
    var $fplock;
100
 
101
    /**
102
     * Constructor
103
     *
104
     * @access public
105
     * @param  string $file
106
     * @return object File_SMBPasswd
107
     */
108
    function File_SMBPasswd($file = 'smbpasswd')
109
    {
110
        $this->file = $file;
111
        $this->cryptEngine = new Crypt_CHAP_MSv1;
112
    }
113
 
114
    /**
115
     * Load the given smbpasswd file
116
     *
117
     * @access public
118
     * @return mixed   true on success, PEAR_Error on failure
119
     */
120
    function load()
121
    {
122
        $fd = fopen($this->getFile(), 'r') ;
123
        if (!$fd) {
124
            return $this->raiseError('Could not open ' . $this->getFile() .
125
                                    ' for reading.');
126
        }
127
 
128
        while (!feof($fd)) {
129
            $line = fgets($fd, 4096);
130
            if (preg_match('/^#|^$/', trim($line))) {
131
                continue;
132
            }
133
            @list($user, $userid, $lmhash, $nthash, $flags, $lct, $comment) = explode(':', $line);
134
            if (strlen($user)) {
135
                $this->accounts[$user] = array(
136
                    'userid'    => trim($userid),
137
                    'lmhash'    => trim($lmhash),
138
                    'nthash'    => trim($nthash),
139
                    'flags'     => trim($flags),
140
                    'lct'       => trim($lct),
141
                    'comment'   => trim($comment)
142
                    );
143
            }
144
        }
145
 
146
        fclose($fd);
147
        return true;
148
    }
149
 
150
    /**
151
     * Get the value of file property
152
     *
153
     * @access public
154
     * @return string
155
     */
156
    function getFile() {
157
        return $this->file;
158
    }
159
 
160
    /**
161
     * Get the value of accounts property
162
     *
163
     * @access public
164
     * @return array
165
     */
166
    function &getAccounts() {
167
        return $this->accounts;
168
    }
169
 
170
    /**
171
     * Adds an account, with pre-encrypted passwords
172
     *
173
     * @param $user new username
174
     * @param $userid new userid
175
     * @param $lmhash LAN-Manager-Hash
176
     * @param $nthash NT-Hash
177
     * @param $comment Comment
178
     * @param $flags Account-flags (see man 5 smbpasswd)
179
     *
180
     * @return mixed returns PEAR_Error, if the user already exists
181
     * @access public
182
     */
183
    function addAccountEncrypted($user, $userid, $lmhash = '', $nthash = '', $comment = '', $flags = '[U          ]')
184
    {
185
        if (empty($lmhash)) $lmhash = str_repeat('X', 32);
186
        if (empty($nthash)) $nthash = str_repeat('X', 32);
187
 
188
        if (!isset($this->accounts[$user])) {
189
            $this->accounts[$user] = array(
190
                'userid'    => trim($userid),
191
                'lmhash'    => trim($lmhash),
192
                'nthash'    => trim($nthash),
193
                'flags'     => trim($flags),
194
                'lct'       => sprintf('LCT-%08s', strtoupper(dechex(time()))),
195
                'comment'   => trim($comment)
196
                );
197
 
198
            return true;
199
        } else {
200
            return $this->raiseError( "Couldn't add user '$user', because the user already exists!");
201
        }
202
    }
203
 
204
    /**
205
     * Adds an account
206
     *
207
     * @param $user new username
208
     * @param $userid new userid
209
     * @param $pass Plaintext password
210
     * @param $comment Comment
211
     * @param $flags Account-flags (see man 5 smbpasswd)
212
     *
213
     * @return mixed returns PEAR_Error, if the user already exists
214
     * @access public
215
     */
216
    function addAccount($user, $userid, $pass, $comment = '', $flags = '[U          ]')
217
    {
218
        if (empty($pass)) {
219
            return $this->addAccountEncrypted($user, $userid, '', '', $comment, $flags) ;
220
        } else {
221
            return $this->addAccountEncrypted(
222
                        $user,
223
                        $userid,
224
                        strtoupper(bin2hex($this->cryptEngine->lmPasswordHash($pass))),
225
                        strtoupper(bin2hex($this->cryptEngine->ntPasswordHash($pass))),
226
                        $comment,
227
                        $flags);
228
        }
229
 
230
    }
231
 
232
    /**
233
     * Adds a user-account
234
     *
235
     * @param $user new username
236
     * @param $userid new userid
237
     * @param $pass Plaintext password
238
     * @param $comment Comment
239
     *
240
     * @return mixed returns PEAR_Error, if the user already exists
241
     * @access public
242
     */
243
    function addUser($user, $userid, $pass, $comment = '')
244
    {
245
        return $this->addAccount($user, $userid, $pass, $comment, '[U          ]');
246
    }
247
 
248
    /**
249
     * Adds a machine-account
250
     *
251
     * @param $machine new username
252
     * @param $userid new userid
253
     * @param $comment Comment
254
     *
255
     * @return mixed returns PEAR_Error, if the user already exists
256
     * @access public
257
     */
258
    function addMachine($machine, $userid, $comment = '')
259
    {
260
        return $this->addAccount($machine . '$', $userid, $machine, $comment, '[W          ]');
261
    }
262
 
263
    /**
264
     * Modifies an account with the pre-encrypted Hashes
265
     *
266
     * @param $user new username
267
     * @param $userid new userid
268
     * @param $lmhash LAN-Manager-Hash
269
     * @param $nthash NT-Hash
270
     * @param $comment Comment
271
     * @param $flags Account-flags (see man 5 smbpasswd)
272
     *
273
     * @return mixed returns PEAR_Error, if the user doesen't exists
274
     * @access public
275
     */
276
    function modAccountEncrypted($user, $userid = '', $lmhash = '', $nthash = '', $comment = '', $flags = '')
277
    {
278
        $account = $this->accounts[$user];
279
        if (is_array($account)) {
280
            if ($userid === '') $userid   = $account['userid'];
281
            if (empty($lmhash)) $lmhash   = $account['lmhash'];
282
            if (empty($nthash)) $nthash   = $account['nthash'];
283
            if (empty($flags))  $flags    = $account['flags'];
284
            if (empty($comment)) $comment = $account['comment'];
285
 
286
            $this->accounts[$user] = array(
287
                'userid'    => trim($userid),
288
                'lmhash'    => trim($lmhash),
289
                'nthash'    => trim($nthash),
290
                'flags'     => trim($flags),
291
                'lct'       => sprintf('LCT-%08s', strtoupper(dechex(time()))),
292
                'comment'   => trim($comment)
293
                );
294
 
295
            return true;
296
        } else {
297
            return $this->raiseError( "Couldn't modify user '$user', because the user doesn't exists!") ;
298
        }
299
    }
300
 
301
    /**
302
     * Modifies an account with given plaintext password
303
     *
304
     * @param $user new username
305
     * @param $userid new userid
306
     * @param $pass Plaintext password
307
     * @param $comment Comment
308
     * @param $flags Account-flags (see man 5 smbpasswd)
309
     *
310
     * @return mixed returns PEAR_Error, if the user doesen't exists
311
     * @access public
312
     */
313
    function modAccount($user, $userid = '', $pass = '', $comment = '', $flags = '')
314
    {
315
        if (empty($pass)) {
316
            return $this->modAccountEncrypted($user, $userid, '', '', $comment, $flags) ;
317
        } else {
318
            return $this->modAccountEncrypted(
319
                        $user,
320
                        $userid,
321
                        strtoupper(bin2hex($this->cryptEngine->lmPasswordHash($pass))),
322
                        strtoupper(bin2hex($this->cryptEngine->ntPasswordHash($pass))),
323
                        $comment,
324
                        $flags);
325
        }
326
    }
327
 
328
    /**
329
     * This is an alias for modAccount
330
     *
331
     * @see File_SMBPasswd::modAccount
332
     */
333
    function modUser($user, $userid = '', $pass = '', $comment = '', $flags = '')
334
    {
335
       return $this->modAccount($user, $userid, $pass, $comment, $flags);
336
    }
337
 
338
    /**
339
     * Deletes an account
340
     *
341
     * @param $user username
342
     *
343
     * @return mixed returns PEAR_Error, if the user doesn't exists
344
     * @access public
345
     */
346
    function delAccount($name)
347
    {
348
        if (isset($this->accounts[$name])) {
349
            unset($this->accounts[$name]);
350
            return true;
351
        } else {
352
            return $this->raiseError( "Couldn't delete account '$name', because the account doesn't exists!") ;
353
        }
354
    }
355
 
356
    /**
357
     * This is an alias for delAccount
358
     *
359
     * @see File_SMBPasswd::delAccount
360
     */
361
    function delUser($user)
362
    {
363
       return $this->delAccount($user);
364
    }
365
 
366
 
367
    /**
368
     * Verifies a user's password
369
     * Prefer NT-Hash instead of weak LAN-Manager-Hash
370
     *
371
     * @param $user username
372
     * @param $nthash NT-Hash in hex
373
     * @param $lmhash LAN-Manager-Hash in hex
374
     *
375
     * @return boolean true if password is ok
376
     * @access public
377
     */
378
    function verifyAccountEncrypted($user, $nthash, $lmhash = '')
379
    {
380
        $account = $this->accounts[$user];
381
        if (is_array($account)) {
382
 
383
            // checking wether account is disabled
384
            if (preg_match('/D/', $account['flags'])) {
385
                return false;
386
            }
387
            if (!empty($lmhash)) {
388
                return $account['lmhash'] == strtoupper($lmhash);
389
            } else {
390
                return $account['nthash'] == strtoupper($nthash);
391
            }
392
        }
393
        return false;
394
    }
395
 
396
    /**
397
     * Verifies an account with the given plaintext password
398
     *
399
     * @param $user username
400
     * @param $pass The plaintext password
401
     *
402
     * @return boolean true if password is ok
403
     * @access public
404
     */
405
    function verifyAccount($user, $pass)
406
    {
407
        return $this->verifyAccountEncrypted(
408
                        $user,
409
                        strtoupper(bin2hex($this->cryptEngine->ntPasswordHash($pass))),
410
                        strtoupper(bin2hex($this->cryptEngine->lmPasswordHash($pass))));
411
    }
412
 
413
    /**
414
     * Locks the given file
415
     *
416
     * @return mixed PEAR_Error, true on succes
417
     * @access public
418
     */
419
    function lock()
420
    {
421
        if (!is_resource($this->fplock)) {
422
            $this->fplock = @fopen($this->getFile(), 'w');
423
        }
424
 
425
        if (!is_resource($this->fplock)) {
426
            return $this->raiseError('Could not open ' . $this->getFile() .
427
                                    ' for writing.');
428
        }
429
 
430
        if (!flock($this->fplock, LOCK_EX)) {
431
            return $this->raiseError('Could not open lock file ' . $this->getFile());
432
        }
433
 
434
        return true;
435
    }
436
 
437
    /**
438
     * Unlocks the given file
439
     *
440
     * @return mixed PEAR_Error, true on succes
441
     * @access public
442
     */
443
    function unlock()
444
    {
445
        if (is_resource($this->fplock)) {
446
            if (!flock($this->fplock, LOCK_UN)) {
447
                return $this->raiseError('Could not open unlock file ' . $this->getFile());
448
            }
449
        }
450
 
451
        return true;
452
    }
453
 
454
    /**
455
     * Writes changes to smbpasswd file and locks, unlocks and closes it
456
     *
457
     * @param $file Filename
458
     *
459
     * @return mixed returns PEAR_Error, if the file is not writeable
460
     * @access public
461
     */
462
    function save($file = '')
463
    {
464
        if (!empty($file)) {
465
            $this->file = $file;
466
        }
467
 
468
        $ret = $this->lock();
469
        if ($ret !== true) {
470
            return $ret;
471
        }
472
 
473
        foreach ($this->accounts as $user => $userdata) {
474
            fputs($this->fplock, sprintf("%s:%s:%s:%s:%s:%s:%s\n",
475
                    $user,
476
                    $userdata['userid'],
477
                    $userdata['lmhash'],
478
                    $userdata['nthash'],
479
                    $userdata['flags'],
480
                    $userdata['lct'],
481
                    $userdata['comment']));
482
        }
483
 
484
        $this->unlock();
485
        fclose($this->fplock);
486
        return true;
487
    }
488
 
489
    /**
490
     * Print all accounts from smbpasswd file
491
     *
492
     * @access public
493
     * @return void
494
     */
495
    function printAccounts() {
496
        foreach ($this->accounts as $user => $userdata) {
497
            printf("%s:%s:%s:%s:%s:%s:%s\n",
498
                $user,
499
                $userdata['userid'],
500
                $userdata['lmhash'],
501
                $userdata['nthash'],
502
                $userdata['flags'],
503
                $userdata['lct'],
504
                $userdata['comment']);
505
        }
506
    }
507
 
508
}
509
?>