187 lines
5.8 KiB
PHP
187 lines
5.8 KiB
PHP
<?php
|
|
/**
|
|
* Copyright 2000-2017 Horde LLC (http://www.horde.org/)
|
|
*
|
|
* See the enclosed file COPYING for license information (GPL). If you
|
|
* did not receive this file, see http://www.horde.org/licenses/gpl.
|
|
*
|
|
* @category Horde
|
|
* @copyright 2000-2017 Horde LLC
|
|
* @license http://www.horde.org/licenses/gpl GPL
|
|
* @package Passwd
|
|
*/
|
|
|
|
/**
|
|
* The LDAP class attempts to change a user's password stored in an LDAP
|
|
* directory service.
|
|
*
|
|
* @author Mike Cochrane <mike@graftonhall.co.nz>
|
|
* @author Mattias Webjörn Eriksson <mattias@webjorn.org>
|
|
* @author Ralf Lang <lang@b1-systems.de>
|
|
* @author Eric Jon Rostetter <eric.rostetter@physics.utexas.edu>
|
|
* @author Jan Schneider <jan@horde.org>
|
|
* @author Tjeerd van der Zee <admin@xar.nl>
|
|
* @category Horde
|
|
* @copyright 2000-2017 Horde LLC
|
|
* @license http://www.horde.org/licenses/gpl GPL
|
|
* @package Passwd
|
|
*/
|
|
class Passwd_Driver_Ldap extends Passwd_Driver
|
|
{
|
|
/**
|
|
* LDAP object.
|
|
*
|
|
* @var resource
|
|
*/
|
|
protected $_ldap = false;
|
|
|
|
/**
|
|
* The user's DN.
|
|
*
|
|
* @var string
|
|
*/
|
|
protected $_userdn;
|
|
|
|
/**
|
|
*/
|
|
public function __construct(array $params = array())
|
|
{
|
|
foreach (array('basedn', 'ldap', 'uid') as $val) {
|
|
if (!isset($params[$val])) {
|
|
throw new InvalidArgumentException(__CLASS__ . ': Missing ' . $val . ' parameter.');
|
|
}
|
|
}
|
|
|
|
$this->_ldap = $params['ldap'];
|
|
unset($params['ldap']);
|
|
|
|
parent::__construct(array_merge(array(
|
|
'host' => 'localhost',
|
|
'port' => 389,
|
|
'encryption' => 'crypt',
|
|
'show_encryption' => 'true',
|
|
'uid' => 'uid',
|
|
'basedn' => '',
|
|
'admindn' => '',
|
|
'adminpw' => '',
|
|
'realm' => '',
|
|
'filter' => null,
|
|
'tls' => false,
|
|
'attribute' => 'userPassword',
|
|
'shadowlastchange' => '',
|
|
'shadowmin' => ''
|
|
), $params));
|
|
|
|
if (!empty($this->_params['tls']) &&
|
|
empty($this->_params['sslhost'])) {
|
|
$this->_params['sslhost'] = $this->_params['host'];
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Compares a plaintext password with an encrypted password.
|
|
*
|
|
* @param string $encrypted An encrypted password.
|
|
* @param string $plaintext An unencrypted password.
|
|
*
|
|
* @throws Passwd_Exception if passwords don't match.
|
|
*/
|
|
protected function _comparePasswords($encrypted, $plaintext)
|
|
{
|
|
$encrypted = preg_replace('/^{MD5}(.*)/i', '{MD5-BASE64}$1', $encrypted);
|
|
return parent::_comparePasswords($encrypted, $plaintext);
|
|
}
|
|
|
|
/**
|
|
*/
|
|
protected function _changePassword($user, $oldpass, $newpass)
|
|
{
|
|
global $injector;
|
|
|
|
// Append realm as username@realm if 'realm' parameter is set.
|
|
if (!empty($this->_params['realm'])) {
|
|
$user .= '@' . $this->_params['realm'];
|
|
}
|
|
|
|
// Try to get the user's dn from config.
|
|
if (isset($this->_params['userdn'])) {
|
|
$this->_userdn = str_replace('%u', $user, $this->_params['userdn']);
|
|
} else {
|
|
try {
|
|
$this->_userdn = $injector->getInstance('Horde_Core_Hooks')->callHook(
|
|
'userdn',
|
|
'passwd',
|
|
array($user)
|
|
);
|
|
} catch (Horde_Exception_HookNotSet $e) {
|
|
// @todo Fix finding the user DN.
|
|
// $this->_userdn = $this->_ldap->findUserDN($user);
|
|
$this->_userdn = $this->_params['uid'] . '=' . $user . ',' . $this->_params['basedn'];
|
|
}
|
|
}
|
|
|
|
try {
|
|
// Check the old password by binding as the userdn.
|
|
$this->_ldap->bind($this->_userdn, $oldpass);
|
|
} catch (Horde_Ldap_Exception $e) {
|
|
throw new Passwd_Exception($e);
|
|
}
|
|
|
|
// Rebind with admin credentials.
|
|
if (!empty($this->_params['admindn'])) {
|
|
try {
|
|
$this->_ldap->bind();
|
|
} catch (Horde_Ldap_Exception $e) {
|
|
throw new Passwd_Exception($e);
|
|
}
|
|
}
|
|
|
|
// Get existing user information.
|
|
try {
|
|
if (!($entry = $this->_ldap->getEntry($this->_userdn))) {
|
|
throw new Passwd_Exception(_("User not found."));
|
|
}
|
|
} catch (Horde_Ldap_Exception $e) {
|
|
throw new Passwd_Exception($e);
|
|
}
|
|
|
|
// Init the shadow policy array.
|
|
$lookupshadow = array(
|
|
'shadowlastchange' => false,
|
|
'shadowmin' => false
|
|
);
|
|
foreach (array_keys($lookupshadow) as $val) {
|
|
if (!empty($this->_params[$val]) &&
|
|
$entry->exists($this->_params[$val])) {
|
|
$lookupshadow[$val] = $entry->getValue($this->_params[$val]);
|
|
}
|
|
}
|
|
|
|
// Check if we may change the password.
|
|
if ($lookupshadow['shadowlastchange'] &&
|
|
$lookupshadow['shadowmin'] &&
|
|
(($lookupshadow['shadowlastchange'] + $lookupshadow['shadowmin']) > (time() / 86400))) {
|
|
throw new Passwd_Exception(_("Minimum password age has not yet expired"));
|
|
}
|
|
|
|
// Change the user's password and update lastchange.
|
|
try {
|
|
$entry->replace(array(
|
|
$this->_params['attribute'] => $this->_encryptPassword($newpass)
|
|
), true);
|
|
|
|
if (!empty($this->_params['shadowlastchange']) &&
|
|
$lookupshadow['shadowlastchange']) {
|
|
$entry->replace(array(
|
|
$this->_params['shadowlastchange'] => floor(time() / 86400)
|
|
));
|
|
}
|
|
|
|
$entry->update();
|
|
} catch (Horde_Ldap_Exception $e) {
|
|
throw new Passwd_Exception($e);
|
|
}
|
|
}
|
|
|
|
}
|