PageRenderTime 53ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/php-pear-File-Passwd-1.1.7/File_Passwd-1.1.7/Passwd/Unix.php

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