Commit 4931ffab authored by Thomas Löffler's avatar Thomas Löffler

Merge branch 'task/Remove-deprecated-password-hashing-methods' into 'develop'

[TASK] Provide password hashing method CRYPT SHA512

See merge request !168
parents b5c0e9f4 e0dbc43d
Pipeline #7388 passed with stages
in 3 minutes and 58 seconds
......@@ -159,34 +159,39 @@ class Ldap implements \Psr\Log\LoggerAwareInterface
* to syslog.
*
* @param string $username Username for bind
* @param array $values The password array
* @param array $values The password hashes as array
* @return bool
*/
public function setLdapPasswords($username, $values)
public function setLdapPasswords(string $username, array $values): bool
{
$ret = false;
// Create LDAP connection
if ($this->createLdapConnection() === true) {
// Try to bind as admin
if ($this->ldapBind($this->ldapConnection, $this->ldapBindDn, $this->ldapBindPassword) === true) {
$dn = $this->getDnForUserName($username);
// TODO Check if user exists and create if not exists?
// Finally try to update passwords
$result = $this->updateLdapAttribute($dn, 'userPassword', $values, true);
if ($result === false) {
$this->logger->error(ldap_error($this->ldapConnection));
try {
if ($this->createLdapConnection() === true) {
// Try to bind as admin
if ($this->ldapBind($this->ldapConnection, $this->ldapBindDn, $this->ldapBindPassword) === true) {
$dn = $this->getDnForUserName($username);
// Unset userPassword to remove deprecated password hashes
$unsetPasswordResult = ldap_mod_del($this->ldapConnection, trim($dn), ['userPassword' => []]);
if ($unsetPasswordResult === false) {
$this->logger->error(ldap_error($this->ldapConnection));
} else {
$updatePasswordResult = $this->updateLdapAttribute($dn, 'userPassword', $values, true);
if ($updatePasswordResult === false) {
$this->logger->error(ldap_error($this->ldapConnection));
} else {
return true;
}
}
} else {
$this->logger->error('Unable to bind to LDAP using: ' . ldap_error($this->ldapConnection));
}
} else {
$this->logger->error('Unable to bind to LDAP using: ' . ldap_error($this->ldapConnection));
$this->logger->error('No active LDAP connection available');
}
} else {
$this->logger->error('No active LDAP connection available');
} catch (\Exception $e) {
$this->logger->error('Could not create LDAP connection');
}
return $ret;
return false;
}
/**
......@@ -256,7 +261,7 @@ class Ldap implements \Psr\Log\LoggerAwareInterface
private function createLdapConnection()
{
$ret = false;
$port = intval($this->ldapServerPort);
$port = (int)$this->ldapServerPort;
try {
if (function_exists('ldap_connect')) {
$this->ldapConnection = @ldap_connect($this->ldapServer, ($port > 0 ? $port : null));
......@@ -274,12 +279,12 @@ class Ldap implements \Psr\Log\LoggerAwareInterface
}
}
} else {
throw new \RuntimeException(
throw new \Exception(
'Could not create LDAP connection: ' . ldap_error($this->ldapConnection),
1453993539
);
}
} catch (\RuntimeException $e) {
} catch (\Exception $e) {
$this->logger->error($e->getMessage());
}
......@@ -507,19 +512,16 @@ class Ldap implements \Psr\Log\LoggerAwareInterface
/** @var \T3o\T3oLdap\Utility\PasswordHashing $passwordHashing */
$passwordHashing = GeneralUtility::makeInstance(\T3o\T3oLdap\Utility\PasswordHashing::class);
$ldapUserArray['userPassword'][] = $passwordHashing->getPasswordHash($user->getPassword(), 'sha1');
$ldapUserArray['userPassword'][] = $passwordHashing->getPasswordHash($user->getPassword(), 'crypt');
$ldapUserArray['userPassword'][] = $passwordHashing->getPasswordHash($user->getPassword(), 'md5');
$ldapUserArray['userPassword'][] = $passwordHashing->getPasswordHash($user->getPassword(), 'crypt_sha512');
}
// if hash fields are filled, store them into ldap user and remove them afterwards
if ($this->isSaltedPassword($user->getPassword()) && ($myProfileUser->getHashMd5() || $myProfileUser->getHashSha1() || $myProfileUser->getHashCrypt())) {
if ($this->isSaltedPassword($user->getPassword()) && ($myProfileUser->getHashSha1() || $myProfileUser->getHashCryptSha512())) {
$ldapUserArray['userPassword'][] = $myProfileUser->getHashSha1();
$ldapUserArray['userPassword'][] = $myProfileUser->getHashCrypt();
$ldapUserArray['userPassword'][] = $myProfileUser->getHashMd5();
$ldapUserArray['userPassword'][] = $myProfileUser->getHashCryptSha512();
$myProfileUser->setHashCrypt('');
$myProfileUser->setHashMd5('');
$myProfileUser->setHashSha1('');
$myProfileUser->setHashCryptSha512('');
$myProfileRepository->update($myProfileUser);
GeneralUtility::makeInstance(\TYPO3\CMS\Extbase\Persistence\Generic\PersistenceManager::class)->persistAll();
}
......
......@@ -25,10 +25,10 @@ class PasswordHashing
*
* @param string $clearText Cleartext representation of the password
* @param string $algorithm The hashing mechanism
* @param string $salt Optional salt
* @param int $rounds The number of rounds for Crypt Salt
* @return bool|string False on failure or the hashed password as string
*/
public function getPasswordHash($clearText, $algorithm = 'crypt', $salt = 'xy')
public function getPasswordHash($clearText, $algorithm = 'crypt_sha512', $rounds = 5000)
{
$ret = false;
if (trim($clearText) !== '') {
......@@ -37,12 +37,19 @@ class PasswordHashing
$passwordHash = sha1($clearText, true);
$ret = '{SHA}' . base64_encode($passwordHash);
break;
case 'md5':
$passwordHash = md5($clearText, true);
$ret = '{MD5}' . base64_encode($passwordHash);
break;
case 'crypt':
$passwordHash = crypt($clearText, $salt);
case 'crypt_sha512':
$characters = array_merge(
range('0', '9'),
range('a', 'z'),
range('A', 'Z'),
['.', '/']
);
$salt = '';
$length = count($characters) - 1;
for ($i = 0; $i < 16; $i++) {
$salt .= $characters[rand(0, $length)];
}
$passwordHash = crypt($clearText, '$6$rounds=' . (int)$rounds . '$' . $salt . '$');
$ret = '{CRYPT}' . $passwordHash;
// no break
default:
......
......@@ -12,6 +12,8 @@ namespace T3o\T3oLdap\Utility;
* LICENSE.txt file that was distributed with this source code.
*/
use T3o\T3oLdap\Connectors\Ldap;
use TYPO3\CMS\Core\Log\LogManager;
use TYPO3\CMS\Core\Utility\GeneralUtility;
/**
......@@ -21,16 +23,18 @@ class PasswordUpdate implements \Psr\Log\LoggerAwareInterface
{
use \Psr\Log\LoggerAwareTrait;
const PASSWORD_METHODS = ['md5', 'sha1', 'crypt'];
const PASSWORD_METHODS = ['crypt_sha512'];
/**
* Update a password in various places (LDAP, TYPO3)
*
* @param string $username The username to update the password for
* @param string $clearTextPassword Cleartext password to hash and update
* @return bool
*/
public function updatePassword(string $username, string $clearTextPassword)
public function updatePassword(string $username, string $clearTextPassword): bool
{
$ret = false;
if (version_compare(TYPO3_version, '9.0', '<')) {
$extensionConfiguration = unserialize($GLOBALS['TYPO3_CONF_VARS']['EXT']['extConf']['t3o_ldap'] ?? '') ?? [];
} else {
......@@ -39,15 +43,24 @@ class PasswordUpdate implements \Psr\Log\LoggerAwareInterface
// Check if LDAP updates are enabled in extension configuration
if ((int)$extensionConfiguration['enableLdapPasswordUpdates'] === 1) {
/** @var \T3o\T3oLdap\Connectors\Ldap $ldap */
$ldap = GeneralUtility::makeInstance(\T3o\T3oLdap\Connectors\Ldap::class);
if ($ldap->setLdapPasswords($username, $this->getHashedPasswords($clearTextPassword))) {
$this->logger = GeneralUtility::makeInstance(\TYPO3\CMS\Core\Log\LogManager::class)->getLogger(__CLASS__);
/** @var Ldap $ldap */
$ldap = GeneralUtility::makeInstance(Ldap::class);
$passwordUpdateResult = $ldap->setLdapPasswords($username, $this->getHashedPasswords($clearTextPassword));
$this->logger = GeneralUtility::makeInstance(LogManager::class)->getLogger(__CLASS__);
if ($passwordUpdateResult === true) {
$this->logger->info('Password successfully updated (Mechanisms: ' . strtoupper(implode(', ', self::PASSWORD_METHODS)) . ')');
$ret = true;
} else {
$this->logger->info('Password has not been updated (Mechanisms: ' . strtoupper(implode(', ', self::PASSWORD_METHODS)) . ')');
}
}
return $ret;
}
/**
* @param string $clearTextPassword
* @return array
*/
public function getHashedPasswords(string $clearTextPassword): array
{
$passwords = [];
......
......@@ -13,6 +13,7 @@ namespace T3o\T3oLdap\Utility;
* LICENSE.txt file that was distributed with this source code.
*/
use T3o\T3oLdap\Connectors\Ldap;
use TYPO3\CMS\Core\Messaging\FlashMessage;
use TYPO3\CMS\Core\Messaging\FlashMessageQueue;
use TYPO3\CMS\Core\Utility\GeneralUtility;
......@@ -38,9 +39,9 @@ class UserCreateUpdateDelete
{
$ret = false;
/** @var \T3o\T3oLdap\Connectors\Ldap $ldap */
/** @var Ldap $ldap */
try {
$ldap = new \T3o\T3oLdap\Connectors\Ldap();
$ldap = new Ldap();
} catch (\Exception $e) {
throw $e;
}
......
......@@ -28,11 +28,6 @@ class MyProfile extends \In2code\Femanager\Domain\Model\User
*/
protected $termsVersion = '';
/**
* @var string
*/
protected $hashMd5 = '';
/**
* @var string
*/
......@@ -41,7 +36,7 @@ class MyProfile extends \In2code\Femanager\Domain\Model\User
/**
* @var string
*/
protected $hashCrypt = '';
protected $hashCryptSha512 = '';
/**
* @return string
......@@ -107,16 +102,6 @@ class MyProfile extends \In2code\Femanager\Domain\Model\User
$this->termsVersion = $termsVersion;
}
public function getHashMd5(): string
{
return $this->hashMd5;
}
public function setHashMd5(string $hashMd5)
{
$this->hashMd5 = $hashMd5;
}
public function getHashSha1(): string
{
return $this->hashSha1;
......@@ -127,13 +112,13 @@ class MyProfile extends \In2code\Femanager\Domain\Model\User
$this->hashSha1 = $hashSha1;
}
public function getHashCrypt(): string
public function getHashCryptSha512(): string
{
return $this->hashCrypt;
return $this->hashCryptSha512;
}
public function setHashCrypt(string $hashCrypt)
public function setHashCryptSha512(string $hashCryptSha512): void
{
$this->hashCrypt = $hashCrypt;
$this->hashCryptSha512 = $hashCryptSha512;
}
}
......@@ -7,9 +7,8 @@ CREATE TABLE fe_users (
facebook VARCHAR (255),
terms_version VARCHAR (255),
hash_md5 VARCHAR(255) DEFAULT '' NOT NULL,
hash_sha1 VARCHAR(255) DEFAULT '' NOT NULL,
hash_crypt VARCHAR(255) DEFAULT '' NOT NULL
hash_crypt_sha512 VARCHAR(255) DEFAULT '' NOT NULL
);
CREATE TABLE old_users (
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment