109 lines
2.9 KiB
PHP
109 lines
2.9 KiB
PHP
<?php
|
|
/**
|
|
* @package solo
|
|
* @copyright Copyright (c)2014-2022 Nicholas K. Dionysopoulos / Akeeba Ltd
|
|
* @license GNU General Public License version 3, or later
|
|
*/
|
|
|
|
namespace Solo\Application;
|
|
|
|
use Awf\User\Authentication;
|
|
|
|
class UserAuthenticationPassword extends Authentication
|
|
{
|
|
/**
|
|
* Is this user authenticated by this object? The $params array contains at least one key, 'password'.
|
|
*
|
|
* @param array $params The parameters used in the authentication process
|
|
*
|
|
* @return boolean True if the user is authenticated (or this plugin doesn't apply), false otherwise
|
|
*/
|
|
public function onAuthentication($params = array())
|
|
{
|
|
$password = isset($params['password']) ? $params['password'] : '';
|
|
$hashedPassword = $this->user->getPassword();
|
|
|
|
if (substr($hashedPassword, 0, 4) == '$2y$')
|
|
{
|
|
return password_verify($password, $hashedPassword);
|
|
}
|
|
else
|
|
{
|
|
$parts = explode(':', $hashedPassword, 3);
|
|
|
|
switch ($parts[0])
|
|
{
|
|
case 'SHA512':
|
|
return $this->timingSafeEquals($parts[1], hash('sha512', $password . $parts[2], false));
|
|
break;
|
|
|
|
case 'SHA256':
|
|
return $this->timingSafeEquals($parts[1], hash('sha256', $password . $parts[2], false));
|
|
break;
|
|
|
|
case 'SHA1':
|
|
return $this->timingSafeEquals($parts[1], sha1($password . $parts[2]));
|
|
break;
|
|
|
|
case 'MD5':
|
|
return $this->timingSafeEquals($parts[1], md5($password . $parts[2]));
|
|
break;
|
|
}
|
|
}
|
|
|
|
// If all else fails, we assume we can't verify this password
|
|
return false;
|
|
}
|
|
|
|
public function onTfaSave($tfaParams)
|
|
{
|
|
$tfaMethod = isset($tfaParams['method']) ? $tfaParams['method'] : '';
|
|
|
|
if ($tfaMethod == 'none')
|
|
{
|
|
// Reset other TFA options
|
|
$this->user->getParameters()->set('tfa', null);
|
|
// Set the TFA method to "none"
|
|
$this->user->getParameters()->set('tfa.method', 'none');
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* A timing safe equals comparison
|
|
*
|
|
* To prevent leaking length information, it is important
|
|
* that user input is always used as the second parameter.
|
|
*
|
|
* @param string $safe The internal (safe) value to be checked
|
|
* @param string $user The user submitted (unsafe) value
|
|
*
|
|
* @return boolean True if the two strings are identical.
|
|
*/
|
|
protected function timingSafeEquals($safe, $user)
|
|
{
|
|
// Prevent issues if string length is 0
|
|
$safe .= chr(0);
|
|
$user .= chr(0);
|
|
|
|
$safeLen = strlen($safe);
|
|
$userLen = strlen($user);
|
|
|
|
// Set the result to the difference between the lengths
|
|
$result = $safeLen - $userLen;
|
|
|
|
// Note that we ALWAYS iterate over the user-supplied length
|
|
// This is to prevent leaking length information
|
|
for ($i = 0; $i < $userLen; $i++) {
|
|
// Using % here is a trick to prevent notices
|
|
// It's safe, since if the lengths are different
|
|
// $result is already non-0
|
|
$result |= (ord($safe[$i % $safeLen]) ^ ord($user[$i]));
|
|
}
|
|
|
|
// They are only identical strings if $result is exactly 0...
|
|
return $result === 0;
|
|
}
|
|
}
|