From 1f70db76b193633bbcd26d255bbc802faa6b0a3f Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Sun, 29 Mar 2015 15:23:22 +0000 Subject: [PATCH] * ActiveDirectory: real password change (not reset) for PHP 5.4>=5.4.26, 5.5>=5.5.10, 5.6+ (subject to minimum password age policy!) --- phpgwapi/inc/class.accounts_ads.inc.php | 94 +++++++++++++++++++++++++ phpgwapi/inc/class.auth_ads.inc.php | 9 +-- 2 files changed, 99 insertions(+), 4 deletions(-) diff --git a/phpgwapi/inc/class.accounts_ads.inc.php b/phpgwapi/inc/class.accounts_ads.inc.php index f8364d9e78..d789cda319 100644 --- a/phpgwapi/inc/class.accounts_ads.inc.php +++ b/phpgwapi/inc/class.accounts_ads.inc.php @@ -1433,6 +1433,80 @@ class adLDAPUsers_egw extends adLDAPUsers return $result; } + /** + * Check if we can to a real password change, not just a password reset + * + * Requires PHP 5.4 >= 5.4.26, PHP 5.5 >= 5.5.10 or PHP 5.6 >= 5.6.0 + * + * @return boolean + */ + public static function changePasswordSupported() + { + return function_exists('ldap_modify_batch'); + } + + /** + * Set the password of a user - This must be performed over SSL + * + * @param string $username The username to modify + * @param string $password The new password + * @param bool $isGUID Is the username passed a GUID or a samAccountName + * @param string $old_password old password for password change, if supported + * @return bool + */ + public function password($username, $password, $isGUID = false, $old_password=null) + { + if ($username === NULL) { return false; } + if ($password === NULL) { return false; } + if (!$this->adldap->getLdapBind()) { return false; } + if (!$this->adldap->getUseSSL() && !$this->adldap->getUseTLS()) { + throw new adLDAPException('SSL must be configured on your webserver and enabled in the class to set passwords.'); + } + + $userDn = $this->dn($username, $isGUID); + if ($userDn === false) { + return false; + } + + $add=array(); + + if (empty($old_password) || !function_exists('ldap_modify_batch')) { + $add["unicodePwd"][0] = $this->encodePassword($password); + + $result = @ldap_mod_replace($this->adldap->getLdapConnection(), $userDn, $add); + } + else { + $mods = array( + array( + "attrib" => "unicodePwd", + "modtype" => LDAP_MODIFY_BATCH_REMOVE, + "values" => array($this->encodePassword($old_password)), + ), + array( + "attrib" => "unicodePwd", + "modtype" => LDAP_MODIFY_BATCH_ADD, + "values" => array($this->encodePassword($password)), + ), + ); + $result = ldap_modify_batch($this->adldap->getLdapConnection(), $userDn, $mods); + } + if ($result === false){ + $err = ldap_errno($this->adldap->getLdapConnection()); + if ($err) { + $msg = 'Error ' . $err . ': ' . ldap_err2str($err) . '.'; + if($err == 53) { + $msg .= ' Your password might not match the password policy.'; + } + throw new adLDAPException($msg); + } + else { + return false; + } + } + + return true; + } + /** * Modify a user * @@ -1550,4 +1624,24 @@ class adLDAPUtils_egw extends adLDAPUtils { return $this->adldap->encode8bit($item, $key); } + + /** + * Escape strings for the use in LDAP filters + * + * DEVELOPERS SHOULD BE DOING PROPER FILTERING IF THEY'RE ACCEPTING USER INPUT + * Ported from Perl's Net::LDAP::Util escape_filter_value + * + * @param string $str The string the parse + * @author Port by Andreas Gohr + * @return string + */ + public function ldapSlashes($str){ + return preg_replace_callback( + '/([\x00-\x1F\*\(\)\\\\])/', + function ($matches) { + return "\\".join("", unpack("H2", $matches[1])); + }, + $str + ); + } } diff --git a/phpgwapi/inc/class.auth_ads.inc.php b/phpgwapi/inc/class.auth_ads.inc.php index 2e4d265175..34edeeca4e 100644 --- a/phpgwapi/inc/class.auth_ads.inc.php +++ b/phpgwapi/inc/class.auth_ads.inc.php @@ -219,14 +219,15 @@ class auth_ads implements auth_backend $admin = true; $username = $GLOBALS['egw']->accounts->id2name($account_id); } - // Check the old_passwd to make sure this is legal - if(!$admin && !$this->authenticate($username, $old_passwd)) + // Check the old_passwd to make sure this is legal, if we dont support password change + if (!$admin && (!method_exists($adldap->user(), 'passwordChangeSupported') || + !$adldap->user()->passwordChangeSupported()) && !$this->authenticate($username, $old_passwd)) { //error_log(__METHOD__."() old password '$old_passwd' for '$username' is wrong!"); return false; } try { - $ret = $adldap->user()->password($username, $new_passwd); + $ret = $adldap->user()->password($username, $new_passwd, false, $old_passwd); //error_log(__METHOD__."('$old_passwd', '$new_passwd', $account_id) admin=$admin adldap->user()->password('$username', '$new_passwd') returned ".array2string($ret)); return $ret; } @@ -243,7 +244,7 @@ class auth_ads implements auth_backend 'Server is unwilling to perform.' => lang('Server is unwilling to perform.'), 'Your password might not match the password policy.' => lang('Your password might not match the password policy.'), )); - throw new egw_exception('

'.lang('Failed to change password.')."

\n".$msg.($error ? "\n

".$error."

\n" : '')); + throw new egw_exception('

'.lang('Failed to change password.')."

\n".$msg.($error ? "\n

".$error."

\n" : '')); } return false; }