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 softtabstop=4: */
3
 
4
/**
5
 * File::Passwd::Unix
6
 *
7
 * PHP versions 4 and 5
8
 *
9
 * LICENSE: This source file is subject to version 3.0 of the PHP license
10
 * that is available through the world-wide-web at the following URI:
11
 * http://www.php.net/license/3_0.txt.  If you did not receive a copy of
12
 * the PHP License and are unable to obtain it through the web, please
13
 * send a note to license@php.net so we can mail you a copy immediately.
14
 *
15
 * @category   FileFormats
16
 * @package    File_Passwd
17
 * @author     Michael Wallner <mike@php.net>
18
 * @copyright  2003-2005 Michael Wallner
19
 * @license    http://www.php.net/license/3_0.txt  PHP License 3.0
20
 * @version    CVS: $Id: Unix.php,v 1.17 2005/03/30 18:33:33 mike Exp $
21
 * @link       http://pear.php.net/package/File_Passwd
22
 */
23
 
24
/**
25
* Requires File::Passwd::Common
26
*/
27
require_once 'File/Passwd/Common.php';
28
 
29
/**
30
* Manipulate standard Unix passwd files.
31
*
32
* <kbd><u>Usage Example:</u></kbd>
33
* <code>
34
*   $passwd = &File_Passwd::factory('Unix');
35
*   $passwd->setFile('/my/passwd/file');
36
*   $passwd->load();
37
*   $passwd->addUser('mike', 'secret');
38
*   $passwd->save();
39
* </code>
40
*
41
*
42
* <kbd><u>Output of listUser()</u></kbd>
43
* # using the 'name map':
44
* <pre>
45
*      array
46
*       + user  => array
47
*                   + pass  => crypted_passwd or 'x' if shadowed
48
*                   + uid   => user id
49
*                   + gid   => group id
50
*                   + gecos => comments
51
*                   + home  => home directory
52
*                   + shell => standard shell
53
* </pre>
54
* # without 'name map':
55
* <pre>
56
*      array
57
*       + user  => array
58
*                   + 0  => crypted_passwd
59
*                   + 1  => ...
60
*                   + 2  => ...
61
* </pre>
62
*
63
* @author   Michael Wallner <mike@php.net>
64
* @package  File_Passwd
65
* @version  $Revision: 1.17 $
66
* @access   public
67
*/
68
class File_Passwd_Unix extends File_Passwd_Common
69
{
70
    /**
71
    * A 'name map' wich refer to the extra properties
72
    *
73
    * @var array
74
    * @access private
75
    */
76
    var $_map = array('uid', 'gid', 'gecos', 'home', 'shell');
77
 
78
    /**
79
    * Whether to use the 'name map' or not
80
    *
81
    * @var boolean
82
    * @access private
83
    */
84
    var $_usemap = true;
85
 
86
    /**
87
    * Whether the passwords of this passwd file are shadowed in another file
88
    *
89
    * @var boolean
90
    * @access private
91
    */
92
    var $_shadowed = false;
93
 
94
    /**
95
    * Encryption mode, either md5 or des
96
    *
97
    * @var string
98
    * @access private
99
    */
100
    var $_mode = 'des';
101
 
102
    /**
103
    * Supported encryption modes
104
    *
105
    * @var array
106
    * @access private
107
    */
108
    var $_modes = array('md5' => 'md5', 'des' => 'des');
109
 
110
    /**
111
    * Constructor
112
    *
113
    * @access public
114
    * @param  string    $file   path to passwd file
115
    */
116
    function File_Passwd_Unix($file = 'passwd')
117
    {
118
        parent::__construct($file);
119
    }
120
 
121
    /**
122
    * Fast authentication of a certain user
123
    *
124
    * Returns a PEAR_Error if:
125
    *   o file doesn't exist
126
    *   o file couldn't be opened in read mode
127
    *   o file couldn't be locked exclusively
128
    *   o file couldn't be unlocked (only if auth fails)
129
    *   o file couldn't be closed (only if auth fails)
130
    *   o invalid encryption mode <var>$mode</var> was provided
131
    *
132
    * @static   call this method statically for a reasonable fast authentication
133
    * @access   public
134
    * @return   mixed   true if authenticated, false if not or PEAR_Error
135
    * @param    string  $file   path to passwd file
136
    * @param    string  $user   user to authenticate
137
    * @param    string  $pass   plaintext password
138
    * @param    string  $mode   encryption mode to use (des or md5)
139
    */
140
    function staticAuth($file, $user, $pass, $mode)
141
    {
142
        $line = File_Passwd_Common::_auth($file, $user);
143
        if (!$line || PEAR::isError($line)) {
144
            return $line;
145
        }
146
        list(,$real)= explode(':', $line);
147
        $crypted    = File_Passwd_Unix::_genPass($pass, $real, $mode);
148
        if (PEAR::isError($crypted)) {
149
            return $crypted;
150
        }
151
        return ($crypted === $real);
152
    }
153
 
154
    /**
155
    * Apply changes an rewrite passwd file
156
    *
157
    * Returns a PEAR_Error if:
158
    *   o directory in which the file should reside couldn't be created
159
    *   o file couldn't be opened in write mode
160
    *   o file couldn't be locked exclusively
161
    *   o file couldn't be unlocked
162
    *   o file couldn't be closed
163
    *
164
    * @throws PEAR_Error
165
    * @access public
166
    * @return mixed true on success or PEAR_Error
167
    */
168
    function save()
169
    {
170
        $content = '';
171
        foreach ($this->_users as $user => $array){
172
            $pass   = array_shift($array);
173
            $extra  = implode(':', $array);
174
            $content .= $user . ':' . $pass;
175
            if (!empty($extra)) {
176
                $content .= ':' . $extra;
177
            }
178
            $content .= "\n";
179
        }
180
        return $this->_save($content);
181
    }
182
 
183
    /**
184
    * Parse the Unix password file
185
    *
186
    * Returns a PEAR_Error if passwd file has invalid format.
187
    *
188
    * @throws PEAR_Error
189
    * @access public
190
    * @return mixed true on success or PEAR_Error
191
    */
192
    function parse()
193
    {
194
        $this->_users = array();
195
        foreach ($this->_contents as $line){
196
            $parts = explode(':', $line);
197
            if (count($parts) < 2) {
198
                return PEAR::raiseError(
199
                    FILE_PASSWD_E_INVALID_FORMAT_STR,
200
                    FILE_PASSWD_E_INVALID_FORMAT
201
                );
202
            }
203
            $user = array_shift($parts);
204
            $pass = array_shift($parts);
205
            if ($pass == 'x') {
206
                $this->_shadowed = true;
207
            }
208
            $values = array();
209
            if ($this->_usemap) {
210
                $values['pass'] = $pass;
211
                foreach ($parts as $i => $value){
212
                    if (isset($this->_map[$i])) {
213
                        $values[$this->_map[$i]] = $value;
214
                    } else {
215
                        $values[$i+1] = $value;
216
                    }
217
                }
218
            } else {
219
                $values = array_merge(array($pass), $parts);
220
            }
221
            $this->_users[$user] = $values;
222
 
223
        }
224
        $this->_contents = array();
225
        return true;
226
    }
227
 
228
    /**
229
    * Set the encryption mode
230
    *
231
    * Supported encryption modes are des and md5.
232
    *
233
    * Returns a PEAR_Error if supplied encryption mode is not supported.
234
    *
235
    * @see      setMode()
236
    * @see      listModes()
237
    *
238
    * @throws   PEAR_Error
239
    * @access   public
240
    * @return   mixed   true on succes or PEAR_Error
241
    * @param    string  $mode   encryption mode to use; either md5 or des
242
    */
243
    function setMode($mode)
244
    {
245
        $mode = strToLower($mode);
246
        if (!isset($this->_modes[$mode])) {
247
            return PEAR::raiseError(
248
                sprintf(FILE_PASSWD_E_INVALID_ENC_MODE_STR, $mode),
249
                FILE_PASSWD_E_INVALID_ENC_MODE
250
            );
251
        }
252
        $this->_mode = $mode;
253
        return true;
254
    }
255
 
256
    /**
257
    * Get supported encryption modes
258
    *
259
    * <pre>
260
    *   array
261
    *    + md5
262
    *    + des
263
    * </pre>
264
    *
265
    * @see      setMode()
266
    * @see      getMode()
267
    *
268
    * @access   public
269
    * @return   array
270
    */
271
    function listModes()
272
    {
273
        return $this->_modes;
274
    }
275
 
276
    /**
277
    * Get actual encryption mode
278
    *
279
    * @see      listModes()
280
    * @see      setMode()
281
    *
282
    * @access   public
283
    * @return   string
284
    */
285
    function getMode()
286
    {
287
        return $this->_mode;
288
    }
289
 
290
    /**
291
    * Whether to use the 'name map' of the extra properties or not
292
    *
293
    * Default Unix passwd files look like:
294
    * <pre>
295
    * user:password:user_id:group_id:gecos:home_dir:shell
296
    * </pre>
297
    *
298
    * The default 'name map' for properties except user and password looks like:
299
    *   o uid
300
    *   o gid
301
    *   o gecos
302
    *   o home
303
    *   o shell
304
    *
305
    * If you want to change the naming of the standard map use
306
    * File_Passwd_Unix::setMap(array()).
307
    *
308
    * @see      setMap()
309
    * @see      getMap()
310
    *
311
    * @access   public
312
    * @return   boolean always true if you set a value (true/false) OR
313
    *                   the actual value if called without param
314
    *
315
    * @param    boolean $bool   whether to use the 'name map' or not
316
    */
317
    function useMap($bool = null)
318
    {
319
        if (is_null($bool)) {
320
            return $this->_usemap;
321
        }
322
        $this->_usemap = (bool) $bool;
323
        return true;
324
    }
325
 
326
    /**
327
    * Set the 'name map' to use with the extra properties of the user
328
    *
329
    * This map is used for naming the associative array of the extra properties.
330
    *
331
    * Returns a PEAR_Error if <var>$map</var> was not of type array.
332
    *
333
    * @see      getMap()
334
    * @see      useMap()
335
    *
336
    * @throws   PEAR_Error
337
    * @access   public
338
    * @return   mixed       true on success or PEAR_Error
339
    */
340
    function setMap($map = array())
341
    {
342
        if (!is_array($map)) {
343
            return PEAR::raiseError(
344
                sprintf(FILE_PASSWD_E_PARAM_MUST_BE_ARRAY_STR, '$map'),
345
                FILE_PASSWD_E_PARAM_MUST_BE_ARRAY
346
            );
347
        }
348
        $this->_map = $map;
349
        return true;
350
    }
351
 
352
    /**
353
    * Get the 'name map' which is used for the extra properties of the user
354
    *
355
    * @see      setMap()
356
    * @see      useMap()
357
    *
358
    * @access public
359
    * @return array
360
    */
361
    function getMap()
362
    {
363
        return $this->_map;
364
    }
365
 
366
    /**
367
    * If the passwords of this passwd file are shadowed in another file.
368
    *
369
    * @access public
370
    * @return boolean
371
    */
372
    function isShadowed()
373
    {
374
        return $this->_shadowed;
375
    }
376
 
377
    /**
378
    * Add an user
379
    *
380
    * The username must start with an alphabetical character and must NOT
381
    * contain any other characters than alphanumerics, the underline and dash.
382
    *
383
    * If you use the 'name map' you should also use these naming in
384
    * the supplied extra array, because your values would get mixed up
385
    * if they are in the wrong order, which is always true if you
386
    * DON'T use the 'name map'!
387
    *
388
    * So be warned and USE the 'name map'!
389
    *
390
    * If the passwd file is shadowed, the user will be added though, but
391
    * with an 'x' as password, and a PEAR_Error will be returned, too.
392
    *
393
    * Returns a PEAR_Error if:
394
    *   o user already exists
395
    *   o user contains illegal characters
396
    *   o encryption mode is not supported
397
    *   o passwords are shadowed in another file
398
    *   o any element of the <var>$extra</var> array contains a colon (':')
399
    *
400
    * @throws PEAR_Error
401
    * @access public
402
    * @return mixed true on success or PEAR_Error
403
    * @param  string    $user   the name of the user to add
404
    * @param  string    $pass   the password of the user to add
405
    * @param  array     $extra  extra properties of user to add
406
    */
407
    function addUser($user, $pass, $extra = array())
408
    {
409
        if ($this->userExists($user)) {
410
            return PEAR::raiseError(
411
                sprintf(FILE_PASSWD_E_EXISTS_ALREADY_STR, 'User ', $user),
412
                FILE_PASSWD_E_EXISTS_ALREADY
413
            );
414
        }
415
        if (!preg_match($this->_pcre, $user)) {
416
            return PEAR::raiseError(
417
                sprintf(FILE_PASSWD_E_INVALID_CHARS_STR, 'User ', $user),
418
                FILE_PASSWD_E_INVALID_CHARS
419
            );
420
        }
421
        if (!is_array($extra)) {
422
            setType($extra, 'array');
423
        }
424
        foreach ($extra as $e){
425
            if (strstr($e, ':')) {
426
            return PEAR::raiseError(
427
                sprintf(FILE_PASSWD_E_INVALID_CHARS_STR, 'Property ', $e),
428
                FILE_PASSWD_E_INVALID_CHARS
429
            );
430
            }
431
        }
432
 
433
        /**
434
        * If passwords of the passwd file are shadowed,
435
        * the password of the user will be set to 'x'.
436
        */
437
        if ($this->_shadowed) {
438
            $pass = 'x';
439
        } else {
440
            $pass = $this->_genPass($pass);
441
            if (PEAR::isError($pass)) {
442
                return $pass;
443
            }
444
        }
445
 
446
        /**
447
        * If you don't use the 'name map' the user array will be numeric.
448
        */
449
        if (!$this->_usemap) {
450
            array_unshift($extra, $pass);
451
            $this->_users[$user] = $extra;
452
        } else {
453
            $map = $this->_map;
454
            array_unshift($map, 'pass');
455
            $extra['pass'] = $pass;
456
            foreach ($map as $key){
457
                $this->_users[$user][$key] = @$extra[$key];
458
            }
459
        }
460
 
461
        /**
462
        * Raise a PEAR_Error if passwords are shadowed.
463
        */
464
        if ($this->_shadowed) {
465
            return PEAR::raiseError(
466
                'Password has been set to \'x\' because they are '.
467
                'shadowed in another file.', 0
468
            );
469
        }
470
        return true;
471
    }
472
 
473
    /**
474
    * Modify properties of a certain user
475
    *
476
    * # DON'T MODIFY THE PASSWORD WITH THIS METHOD!
477
    *
478
    * You should use this method only if the 'name map' is used, too.
479
    *
480
    * Returns a PEAR_Error if:
481
    *   o user doesn't exist
482
    *   o any property contains a colon (':')
483
    *
484
    * @see      changePasswd()
485
    *
486
    * @throws   PEAR_Error
487
    * @access   public
488
    * @return   mixed       true on success or PEAR_Error
489
    * @param    string      $user           the user to modify
490
    * @param    array       $properties     an associative array of
491
    *                                       properties to modify
492
    */
493
    function modUser($user, $properties = array())
494
    {
495
        if (!$this->userExists($user)) {
496
            return PEAR::raiseError(
497
                sprintf(FILE_PASSWD_E_EXISTS_NOT_STR, 'User ', $user),
498
                FILE_PASSWD_E_EXISTS_NOT
499
            );
500
        }
501
 
502
        if (!is_array($properties)) {
503
            setType($properties, 'array');
504
        }
505
 
506
        foreach ($properties as $key => $value){
507
            if (strstr($value, ':')) {
508
                return PEAR::raiseError(
509
                    sprintf(FILE_PASSWD_E_INVALID_CHARS_STR, 'User ', $user),
510
                    FILE_PASSWD_E_INVALID_CHARS
511
                );
512
            }
513
            $this->_users[$user][$key] = $value;
514
        }
515
 
516
        return true;
517
    }
518
 
519
    /**
520
    * Change the password of a certain user
521
    *
522
    * Returns a PEAR_Error if:
523
    *   o user doesn't exists
524
    *   o passwords are shadowed in another file
525
    *   o encryption mode is not supported
526
    *
527
    * @throws PEAR_Error
528
    * @access public
529
    * @return mixed true on success or PEAR_Error
530
    * @param string $user   the user whose password should be changed
531
    * @param string $pass   the new plaintext password
532
    */
533
    function changePasswd($user, $pass)
534
    {
535
        if ($this->_shadowed) {
536
            return PEAR::raiseError(
537
                'Passwords of this passwd file are shadowed.',
538
 
539
            );
540
        }
541
 
542
        if (!$this->userExists($user)) {
543
            return PEAR::raiseError(
544
                sprintf(FILE_PASSWD_E_EXISTS_NOT_STR, 'User ', $user),
545
                FILE_PASSWD_E_EXISTS_NOT
546
            );
547
        }
548
 
549
        $pass = $this->_genPass($pass);
550
        if (PEAR::isError($pass)) {
551
            return $pass;
552
        }
553
 
554
        if ($this->_usemap) {
555
            $this->_users[$user]['pass'] = $pass;
556
        } else {
557
            $this->_users[$user][0] = $pass;
558
        }
559
 
560
        return true;
561
    }
562
 
563
    /**
564
    * Verify the password of a certain user
565
    *
566
    * Returns a PEAR_Error if:
567
    *   o user doesn't exist
568
    *   o encryption mode is not supported
569
    *
570
    * @throws PEAR_Error
571
    * @access public
572
    * @return mixed true if passwors equal, false if they don't or PEAR_Error
573
    * @param  string    $user   the user whose password should be verified
574
    * @param  string    $pass   the password to verify
575
    */
576
    function verifyPasswd($user, $pass)
577
    {
578
        if (!$this->userExists($user)) {
579
            return PEAR::raiseError(
580
                sprintf(FILE_PASSWD_E_EXISTS_NOT_STR, 'User ', $user),
581
                FILE_PASSWD_E_EXISTS_NOT
582
            );
583
        }
584
        $real =
585
            $this->_usemap ?
586
            $this->_users[$user]['pass'] :
587
            $this->_users[$user][0]
588
        ;
589
        return ($real === $this->_genPass($pass, $real));
590
    }
591
 
592
    /**
593
    * Generate crypted password from the plaintext password
594
    *
595
    * Returns a PEAR_Error if actual encryption mode is not supported.
596
    *
597
    * @throws PEAR_Error
598
    * @access private
599
    * @return mixed     the crypted password or PEAR_Error
600
    * @param  string    $pass   the plaintext password
601
    * @param  string    $salt   the crypted password from which to gain the salt
602
    * @param  string    $mode   the encryption mode to use; don't set, because
603
    *                           it's usually taken from File_Passwd_Unix::_mode
604
    */
605
    function _genPass($pass, $salt = null, $mode = null)
606
    {
607
        static $crypters;
608
        if (!isset($crypters)) {
609
            $crypters = get_class_methods('File_Passwd');
610
        }
611
 
612
        $mode = !isset($mode) ? strToLower($this->_mode) : strToLower($mode);
613
        $func = 'crypt_' . $mode;
614
 
615
        if (!in_array($func, $crypters)) {
616
            return PEAR::raiseError(
617
                sprintf(FILE_PASSWD_E_INVALID_ENC_MODE_STR, $mode),
618
                FILE_PASSWD_E_INVALID_ENC_MODE
619
            );
620
        }
621
 
622
        return call_user_func(array('File_Passwd', $func), $pass, $salt);
623
    }
624
 
625
    /**
626
    * Generate Password
627
    *
628
    * Returns PEAR_Error FILE_PASSD_E_INVALID_ENC_MODE if the supplied
629
    * encryption mode is not supported.
630
    *
631
    * @see File_Passwd
632
    * @static
633
    * @access   public
634
    * @return   mixed   The crypted password on success or PEAR_Error on failure.
635
    * @param    string  $pass The plaintext password.
636
    * @param    string  $mode The encryption mode to use.
637
    * @param    string  $salt The salt to use.
638
    */
639
    function generatePasswd($pass, $mode = FILE_PASSWD_MD5, $salt = null)
640
    {
641
        if (!isset($mode)) {
642
            return PEAR::raiseError(
643
                sprintf(FILE_PASSWD_E_INVALID_ENC_MODE_STR, '<NULL>'),
644
                FILE_PASSWD_E_INVALID_ENC_MODE
645
            );
646
        }
647
        return File_Passwd_Unix::_genPass($pass, $salt, $mode);
648
    }
649
 
650
    /**
651
     * @ignore
652
     * @deprecated
653
     */
654
    function generatePassword($pass, $mode = FILE_PASSWD_MD5, $salt = null)
655
    {
656
        return File_Passwd_Unix::generatePasswd($pass, $mode, $salt);
657
    }
658
 
659
}
660
?>