1
0
Fork 0
mirror of https://github.com/dducret/kopano-webapp-passwd synced 2026-08-06 15:33:14 +02:00

Samba AD update

This commit is contained in:
Matthias Fulz 2021-06-10 15:20:01 +02:00
commit b1322106ee
2 changed files with 366 additions and 303 deletions

View file

@ -23,4 +23,10 @@ define('PLUGIN_PASSWD_LDAP_BIND_PW', "");
/** Set to true if you login with username@tenantname **/ /** Set to true if you login with username@tenantname **/
define('PLUGIN_PASSWD_LOGIN_WITH_TENANT', false); define('PLUGIN_PASSWD_LOGIN_WITH_TENANT', false);
/** Set to user login attribute **/
define('PLUGIN_PASSWD_LDAP_USER_LOGIN_ATTR', 'sAMAccountName');
/** Ldap filter used to search valid users **/
define('PLUGIN_PASSWD_LDAP_FILTER', "(objectClass=person)");
?> ?>

View file

@ -3,309 +3,366 @@
* Passwd module. * Passwd module.
* Module that will be used to change passwords of the user * Module that will be used to change passwords of the user
*/ */
class PasswdModule extends Module class PasswdModule extends Module
{ {
/** /**
* Process the incoming events that were fire by the client. * Process the incoming events that were fire by the client.
*/ */
public function execute() public function execute()
{ {
foreach($this->data as $actionType => $actionData) foreach($this->data as $actionType => $actionData)
{ {
if(isset($actionType)) { if(isset($actionType)) {
try { try {
switch($actionType) switch($actionType)
{ {
case 'save': case 'save':
$this->save($actionData); $this->save($actionData);
break; break;
default: default:
$this->handleUnknownActionType($actionType); $this->handleUnknownActionType($actionType);
} }
} catch (MAPIException $e) { } catch (MAPIException $e) {
$this->sendFeedback(false, $this->errorDetailsFromException($e)); $this->sendFeedback(false, $this->errorDetailsFromException($e));
} }
} }
} }
} }
/** /**
* Change the password of user. Do some calidation and call proper methods based on * Change the password of user. Do some calidation and call proper methods based on
* zarafa setup. * zarafa setup.
* @param {Array} $data data sent by client. * @param {Array} $data data sent by client.
*/ */
public function save($data) public function save($data)
{ {
$errorMessage = ''; $errorMessage = '';
// some sanity checks // some sanity checks
if(empty($data)) { if(empty($data)) {
$errorMessage = dgettext("plugin_passwd", 'No data received.'); $errorMessage = dgettext("plugin_passwd", 'No data received.');
} }
if(empty($data['username'])) { if(empty($data['username'])) {
$errorMessage = dgettext("plugin_passwd", 'User name is empty.'); $errorMessage = dgettext("plugin_passwd", 'User name is empty.');
} }
if(empty($data['current_password'])) { if(empty($data['current_password'])) {
$errorMessage = dgettext("plugin_passwd", 'Current password is empty.'); $errorMessage = dgettext("plugin_passwd", 'Current password is empty.');
} }
if(empty($data['new_password']) || empty($data['new_password_repeat'])) { if(empty($data['new_password']) || empty($data['new_password_repeat'])) {
$errorMessage = dgettext("plugin_passwd", 'New password is empty.'); $errorMessage = dgettext("plugin_passwd", 'New password is empty.');
} }
if($data['new_password'] !== $data['new_password_repeat']) { if($data['new_password'] !== $data['new_password_repeat']) {
$errorMessage = dgettext("plugin_passwd", 'New passwords do not match.'); $errorMessage = dgettext("plugin_passwd", 'New passwords do not match.');
} }
if(empty($errorMessage)) { if(empty($errorMessage)) {
if(PLUGIN_PASSWD_LDAP) { if(PLUGIN_PASSWD_LDAP) {
$this->saveInLDAP($data); $this->saveInLDAP($data);
} else { } else {
$this->saveInDB($data); $this->saveInDB($data);
} }
} else { } else {
$this->sendFeedback(false, array( $this->sendFeedback(false, array(
'type' => ERROR_ZARAFA, 'type' => ERROR_ZARAFA,
'info' => array( 'info' => array(
'display_message' => $errorMessage 'display_message' => $errorMessage
) )
)); ));
} }
} }
/** /**
* Function will connect to LDAP and will try to modify user's password. * Function will connect to LDAP and will try to modify user's password.
* @param {Array} $data data sent by client. * @param {Array} $data data sent by client.
*/ */
public function saveInLDAP($data) public function saveInLDAP($data)
{ {
$errorMessage = ''; $errorMessage = '';
// connect to LDAP server // connect to LDAP server
$ldapconn = ldap_connect(PLUGIN_PASSWD_LDAP_URI); $ldapconn = ldap_connect(PLUGIN_PASSWD_LDAP_URI);
// check connection is successfull // check connection is successfull
if(ldap_errno($ldapconn) === 0) { if(ldap_errno($ldapconn) === 0) {
// get the users uid, if we have a multi tenant installation then remove company name from user name // get the users uid, if we have a multi tenant installation then remove company name from user name
if (PLUGIN_PASSWD_LOGIN_WITH_TENANT){ if (PLUGIN_PASSWD_LOGIN_WITH_TENANT){
$parts = explode('@', $data['username']); $parts = explode('@', $data['username']);
$uid = $parts[0]; $uid = $parts[0];
} else { } else {
$uid = $data['username']; $uid = $data['username'];
} }
// check if we should use tls! // check if we should use tls!
if(strrpos(PLUGIN_PASSWD_LDAP_URI, "ldaps://", -strlen(PLUGIN_PASSWD_LDAP_URI)) === FALSE && PLUGIN_PASSWD_LDAP_USE_TLS === true) { if(strrpos(PLUGIN_PASSWD_LDAP_URI, "ldaps://", -strlen(PLUGIN_PASSWD_LDAP_URI)) === FALSE && PLUGIN_PASSWD_LDAP_USE_TLS === true) {
ldap_start_tls($ldapconn); ldap_start_tls($ldapconn);
} }
// set connection parametes // set connection parametes
ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3); ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3);
ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0); ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0);
// now bind to the ldap server to search the user dn // now bind to the ldap server to search the user dn
ldap_bind($ldapconn, PLUGIN_PASSWD_LDAP_BIND_DN, PLUGIN_PASSWD_LDAP_BIND_PW); ldap_bind($ldapconn, PLUGIN_PASSWD_LDAP_BIND_DN, PLUGIN_PASSWD_LDAP_BIND_PW);
// search for the user dn that will be used to do login into LDAP // set ldapfilter
$userdn = ldap_search ( $ldap_filter = "(&(" . PLUGIN_PASSWD_LDAP_USER_LOGIN_ATTR . "=" . $uid . ")" . PLUGIN_PASSWD_LDAP_FILTER . ")";
$ldapconn, // connection-identify
PLUGIN_PASSWD_LDAP_BASEDN, // basedn
'uid=' . $uid, // search filter
array('dn', 'objectClass') // needed attributes. we need dn and objectclass
);
if ($userdn) { // search for the user dn that will be used to do login into LDAP
$entries = ldap_get_entries($ldapconn, $userdn); $userdn = ldap_search (
$userdn = $entries[0]['dn']; $ldapconn, // connection-identify
PLUGIN_PASSWD_LDAP_BASEDN, // basedn
$ldap_filter
);
// bind to ldap directory if ($userdn) {
// login with current password if that fails then current password is wrong $entries = ldap_get_entries($ldapconn, $userdn);
ldap_bind($ldapconn, $userdn, $data['current_password']); $userdn = $entries[0]['dn'];
if(ldap_errno($ldapconn) === 0) { // bind to ldap directory
// login with current password if that fails then current password is wrong
ldap_bind($ldapconn, $userdn, $data['current_password']);
$passwd = $data['new_password']; if(ldap_errno($ldapconn) === 0) {
if ($this->checkPasswordStrenth($passwd)) { $passwd = $data['new_password'];
$password_hash = $this->sshaEncode($passwd); $oldpass = $data['current_password'];
$entry = array('userPassword' => $password_hash);
if (in_array('sambaSamAccount', $entries[0]['objectclass'])) {
$nthash = strtoupper(bin2hex(mhash(MHASH_MD4, iconv("UTF-8","UTF-16LE", $passwd))));
$entry['sambaNTPassword'] = $nthash;
$entry['sambaPwdLastSet'] = strval(time());
}
ldap_modify($ldapconn, $userdn, $entry);
if (ldap_errno($ldapconn) === 0) {
// password changed successfully
// write new password to session because we don't want user to re-authenticate if ($this->checkPasswordStrenth($passwd)) {
session_start(); $msg = $this->change_password($ldapconn, $userdn, $passwd, $oldpass);
// if user has openssl module installed
if(function_exists("openssl_encrypt")) {
// In PHP 5.3.3 the iv parameter was added
if(version_compare(phpversion(), "5.3.3", "<")) {
$_SESSION['password'] = openssl_encrypt($passwd,"des-ede3-cbc",PASSWORD_KEY,0);
} else {
$_SESSION['password'] = openssl_encrypt($passwd,"des-ede3-cbc",PASSWORD_KEY,0,PASSWORD_IV);
}
}
else {
$_SESSION['password'] = $passwd;
}
session_write_close();
// send feedback to client if ($msg === "passwordchanged") {
$this->sendFeedback(true, array( // password changed successfully
'info' => array(
'display_message' => dgettext("plugin_passwd", 'Password is changed successfully.')
)
));
} else {
$errorMessage = dgettext("plugin_passwd", 'Password is not changed.');
}
} else {
$errorMessage = dgettext("plugin_passwd", 'Password is weak. Password should contain capital, non-capital letters and numbers. Password should have 8 to 20 characters.');
}
} else {
$errorMessage = dgettext("plugin_passwd", 'Current password does not match.');
}
// release ldap-bind // write new password to session because we don't want user to re-authenticate
ldap_unbind($ldapconn); session_start();
} // if user has openssl module installed
} if(function_exists("openssl_encrypt")) {
// In PHP 5.3.3 the iv parameter was added
if(version_compare(phpversion(), "5.3.3", "<")) {
$_SESSION['password'] = openssl_encrypt($passwd,"des-ede3-cbc",PASSWORD_KEY,0);
} else {
$_SESSION['password'] = openssl_encrypt($passwd,"des-ede3-cbc",PASSWORD_KEY,0,PASSWORD_IV);
}
}
else {
$_SESSION['password'] = $passwd;
}
session_write_close();
if(!empty($errorMessage)) { // send feedback to client
$this->sendFeedback(false, array( $this->sendFeedback(true, array(
'type' => ERROR_ZARAFA, 'info' => array(
'info' => array( 'display_message' => dgettext("plugin_passwd", 'Password is changed successfully.')
'ldap_error' => ldap_errno($ldapconn), )
'ldap_error_name' => ldap_error($ldapconn), ));
'display_message' => $errorMessage } else {
) $errorMessage = dgettext("plugin_passwd", 'Password is not changed. Error: ' . $msg);
)); }
} } else {
} $errorMessage = dgettext("plugin_passwd", 'Password is weak. Password should contain capital, non-capital letters and numbers. Password should have 8 to 20 characters.');
}
} else {
$errorMessage = dgettext("plugin_passwd", 'Current password does not match.');
}
// release ldap-bind
ldap_unbind($ldapconn);
}
}
/** if(!empty($errorMessage)) {
* Function will try to change user's password via MAPI in SOAP connection. $this->sendFeedback(false, array(
* @param {Array} $data data sent by client. 'type' => ERROR_ZARAFA,
*/ 'info' => array(
public function saveInDB($data) 'ldap_error' => ldap_errno($ldapconn),
{ 'ldap_error_name' => ldap_error($ldapconn),
$errorMessage = ''; 'display_message' => $errorMessage
$passwd = $data['new_password']; )
));
}
}
/* /**
// get current session password * Function will try to change user's password via MAPI in SOAP connection.
$sessionPass = $_SESSION['password']; * @param {Array} $data data sent by client.
// if user has openssl module installed */
if (function_exists("openssl_decrypt")) { public function saveInDB($data)
if (version_compare(phpversion(), "5.3.3", "<")) { {
$sessionPass = openssl_decrypt($sessionPass, "des-ede3-cbc", PASSWORD_KEY, 0); $errorMessage = '';
} else { $passwd = $data['new_password'];
$sessionPass = openssl_decrypt($sessionPass, "des-ede3-cbc", PASSWORD_KEY, 0, PASSWORD_IV);
}
if (!$sessionPass) { /*
$sessionPass = $_SESSION['password']; // get current session password
} $sessionPass = $_SESSION['password'];
} // if user has openssl module installed
*/ if (function_exists("openssl_decrypt")) {
// Get current user password if (version_compare(phpversion(), "5.3.3", "<")) {
$encryptionStore = EncryptionStore::getInstance(); $sessionPass = openssl_decrypt($sessionPass, "des-ede3-cbc", PASSWORD_KEY, 0);
$sessionPass = $encryptionStore->get('password'); } else {
$sessionPass = openssl_decrypt($sessionPass, "des-ede3-cbc", PASSWORD_KEY, 0, PASSWORD_IV);
}
if($data['current_password'] === $sessionPass) { if (!$sessionPass) {
if ($this->checkPasswordStrenth($passwd)) { $sessionPass = $_SESSION['password'];
// all information correct, change password }
$store = $GLOBALS['mapisession']->getDefaultMessageStore(); }
$userinfo = mapi_zarafa_getuser_by_name($store, $data['username']); */
// Get current user password
$encryptionStore = EncryptionStore::getInstance();
$sessionPass = $encryptionStore->get('password');
if (mapi_zarafa_setuser($store, $userinfo['userid'], $data['username'], $userinfo['fullname'], $userinfo['emailaddress'], $passwd, 0, $userinfo['admin'])) { if($data['current_password'] === $sessionPass) {
// password changed successfully if ($this->checkPasswordStrenth($passwd)) {
// all information correct, change password
$store = $GLOBALS['mapisession']->getDefaultMessageStore();
$userinfo = mapi_zarafa_getuser_by_name($store, $data['username']);
/* Not able to find a way, session is discarded and user should log in again if (mapi_zarafa_setuser($store, $userinfo['userid'], $data['username'], $userinfo['fullname'], $userinfo['emailaddress'], $passwd, 0, $userinfo['admin'])) {
// write new password to session because we don't want user to re-authenticate // password changed successfully
session_start();
// if user has openssl module installed
if (function_exists("openssl_encrypt")) {
// In PHP 5.3.3 the iv parameter was added
if (version_compare(phpversion(), "5.3.3", "<")) {
$_SESSION['password'] = openssl_encrypt($passwd, "des-ede3-cbc", PASSWORD_KEY, 0);
} else {
$_SESSION['password'] = openssl_encrypt($passwd, "des-ede3-cbc", PASSWORD_KEY, 0, PASSWORD_IV);
}
} else {
$_SESSION['password'] = $passwd;
}
session_write_close();
// send feedback to client /* Not able to find a way, session is discarded and user should log in again
$this->sendFeedback(true, array( // write new password to session because we don't want user to re-authenticate
'info' => array( session_start();
'display_message' => dgettext("plugin_passwd", 'Password is changed successfully.') // if user has openssl module installed
) if (function_exists("openssl_encrypt")) {
)); // In PHP 5.3.3 the iv parameter was added
*/ if (version_compare(phpversion(), "5.3.3", "<")) {
// Give the session a new id $_SESSION['password'] = openssl_encrypt($passwd, "des-ede3-cbc", PASSWORD_KEY, 0);
session_regenerate_id(); } else {
} else { $_SESSION['password'] = openssl_encrypt($passwd, "des-ede3-cbc", PASSWORD_KEY, 0, PASSWORD_IV);
$errorMessage = dgettext("plugin_passwd", 'Password is not changed.'); }
} } else {
} else { $_SESSION['password'] = $passwd;
$errorMessage = dgettext("plugin_passwd", 'Password is weak. Password should contain capital, non-capital letters and numbers. Password should have 8 to 20 characters.'); }
} session_write_close();
} else {
$errorMessage = dgettext("plugin_passwd", 'Current password does not match.');
}
if(!empty($errorMessage)) { // send feedback to client
$this->sendFeedback(false, array( $this->sendFeedback(true, array(
'type' => ERROR_ZARAFA, 'info' => array(
'info' => array( 'display_message' => dgettext("plugin_passwd", 'Password is changed successfully.')
'display_message' => $errorMessage )
) ));
)); */
} // Give the session a new id
} session_regenerate_id();
} else {
$errorMessage = dgettext("plugin_passwd", 'Password is not changed.');
}
} else {
$errorMessage = dgettext("plugin_passwd", 'Password is weak. Password should contain capital, non-capital letters and numbers. Password should have 8 to 20 characters.');
}
} else {
$errorMessage = dgettext("plugin_passwd", 'Current password does not match.');
}
/** if(!empty($errorMessage)) {
* Function will check strength of the password and if it does not meet minimum requirements then $this->sendFeedback(false, array(
* will return false. 'type' => ERROR_ZARAFA,
* Password should meet the following criteria: 'info' => array(
* - min. 8 chars, max. 20 'display_message' => $errorMessage
* - contain caps and noncaps characters )
* - contain numbers ));
* @param {String} $password password which should be checked. }
* @return {Boolean} true if password passes the minimum requirement else false. }
*/
public function checkPasswordStrenth($password)
{
if (preg_match("#.*^(?=.{8,20})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).*$#", $password)) {
return true;
} else {
return false;
}
}
/** /**
* Function will generate SSHA hash to use to store user's password in LDAP. * Function will check strength of the password and if it does not meet minimum requirements then
* @param {String} $text text based on which hash will be generated. * will return false.
*/ * Password should meet the following criteria:
function sshaEncode($text) * - min. 8 chars, max. 20
{ * - contain caps and noncaps characters
$salt = ''; * - contain numbers
for ($i=1; $i<=10; $i++) { * @param {String} $password password which should be checked.
$salt .= substr('0123456789abcdef', rand(0, 15), 1); * @return {Boolean} true if password passes the minimum requirement else false.
} */
public function checkPasswordStrenth($password)
{
if (preg_match("#.*^(?=.{8,20})(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9]).*$#", $password)) {
return true;
} else {
return false;
}
}
$hash = '{SSHA}' . base64_encode(pack('H*',sha1($text . $salt)) . $salt); /**
* Function will generate SSHA hash to use to store user's password in LDAP.
* @param {String} $text text based on which hash will be generated.
*/
function sshaEncode($text)
{
$salt = '';
for ($i=1; $i<=10; $i++) {
$salt .= substr('0123456789abcdef', rand(0, 15), 1);
}
return $hash; $hash = '{SSHA}' . base64_encode(pack('H*',sha1($text . $salt)) . $salt);
}
return $hash;
}
function make_ad_password($password) {
$password = "\"" . $password . "\"";
$adpassword = mb_convert_encoding($password, "UTF-16LE", "UTF-8");
return $adpassword;
}
function change_password( $ldap, $dn, $password, $oldpassword ) {
$result = "";
$error_code = "";
$error_msg = "";
$ppolicy_error_code = "";
$time = time();
# Transform password value
$password = $this->make_ad_password($password);
# Set password value
$userdata["unicodePwd"] = $password;
# Commit modification on directory
# The AD password change procedure is modifying the attribute unicodePwd by
# first deleting unicodePwd with the old password and them adding it with the
# the new password
$oldpassword = $this->make_ad_password($oldpassword);
$modifications = array(
array(
"attrib" => "unicodePwd",
"modtype" => LDAP_MODIFY_BATCH_REMOVE,
"values" => array($oldpassword),
),
array(
"attrib" => "unicodePwd",
"modtype" => LDAP_MODIFY_BATCH_ADD,
"values" => array($password),
),
);
$bmod = ldap_modify_batch($ldap, $dn, $modifications);
$error_code = ldap_errno($ldap);
$error_msg = ldap_error($ldap);
if ( !isset($error_code) ) {
$result = "ldaperror";
} elseif ( $error_code > 0 ) {
$result = "passworderror";
error_log("LDAP - Modify password error $error_code ($error_msg)");
if ( $ppolicy_error_code === 5 ) { $result = "badquality"; }
if ( $ppolicy_error_code === 6 ) { $result = "tooshort"; }
if ( $ppolicy_error_code === 7 ) { $result = "tooyoung"; }
if ( $ppolicy_error_code === 8 ) { $result = "inhistory"; }
} else {
$result = "passwordchanged";
}
return $result;
}
} }
?>