An error occurred while loading the file. Please try again.
-
Côme Chilliet authored
issue #6072
Unverifiedfff28020
<?php
/*
This code is part of FusionDirectory (http://www.fusiondirectory.org/)
Copyright (C) 2003-2010 Cajus Pollmeier
Copyright (C) 2011-2019 FusionDirectory
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
*/
/*
* \file class_passwordMethod.inc
* Source code for class passwordMethod
*/
/*!
* \brief This class contains all the basic function for password methods
*/
abstract class passwordMethod
{
var $display = FALSE;
var $hash = '';
protected $lockable = TRUE;
/*!
* \brief Password method contructor
*
* \param string $dn The DN
* \param object $userTab The user main tab object
*/
function __construct ($dn = '', $userTab = NULL)
{
}
/*!
* \brief Get the Hash name
*/
abstract static function get_hash_name ();
/*!
* \brief Generate template hash
*
* \param string $pwd Password
* \param bool $locked Should the password be locked
*
* \return string the password hash
*/
abstract public function generate_hash (string $pwd, bool $locked = FALSE): string;
/*!
* \brief Is available
*
* \return TRUE
*/
public function is_available (): bool
{
return TRUE;
7172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140
}
/*!
* \brief If we need password
*
* \return boolean TRUE
*/
public function need_password (): bool
{
return TRUE;
}
/*!
* \brief If we can lock the password
*
* \return boolean
*/
public function is_lockable (): bool
{
return $this->lockable;
}
/*!
* \brief Is locked
*
* \param string $dn The DN
*/
function is_locked ($dn = '', $pwd = ''): bool
{
global $config;
if (!$this->lockable) {
return FALSE;
}
/* Get current password hash */
if (!empty($dn)) {
$ldap = $config->get_ldap_link();
$ldap->cd($config->current['BASE']);
$ldap->cat($dn, ['userPassword']);
$attrs = $ldap->fetch();
if (isset($attrs['userPassword'][0])) {
$pwd = $attrs['userPassword'][0];
}
}
return preg_match("/^[^\}]*+\}!/", $pwd);
}
/*! \brief Locks an account by adding a '!' as prefix to the password hashes.
* This makes login impossible, due to the fact that the hash becomes invalid.
* userPassword: {SHA}!q02NKl9IChNwZEAJxzRdmB6E
* sambaLMPassword: !EBD223B61F8C259AD3B435B51404EE
* sambaNTPassword: !98BB35737013AAF181D0FE9FDA09E
*
* \param string $dn
*/
function lock_account ($dn = '')
{
return $this->generic_modify_account($dn, 'LOCK');
}
/*!
* \brief Unlocks an account which was locked by 'lock_account()'.
* For details about the locking mechanism see 'lock_account()'.
*/
function unlock_account ($dn = '')
{
return $this->generic_modify_account($dn, 'UNLOCK');
}
/*!
141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210
* \brief Unlocks an account which was locked by 'lock_account()'.
* For details about the locking mechanism see 'lock_account()'.
*/
private function generic_modify_account ($dn, string $mode)
{
global $config;
if (!$this->lockable) {
return FALSE;
}
if ($mode != 'LOCK' && $mode != 'UNLOCK') {
throw new FusionDirectoryException('Invalid mode "'.$mode.'"');
}
/* Open the user */
$userObject = objects::open($dn, 'user');
$userMainTab = $userObject->getBaseObject();
/* Check if this entry is already (un)locked. */
if ($userMainTab->attributesAccess['userPassword']->isLocked()) {
if ($mode == 'LOCK') {
return TRUE;
}
} elseif ($mode == 'UNLOCK') {
return TRUE;
}
/* Fill modification array */
$modify = [];
foreach ($userObject->by_object as $tab) {
if ($tab instanceof UserTabLockingAction) {
$tab->fillLockingLDAPAttrs($mode, $modify);
}
}
// Call pre hooks
$errors = $userMainTab->callHook('PRE'.$mode, [], $ret);
if (!empty($errors)) {
msg_dialog::displayChecks($errors);
return FALSE;
}
/* Get current password hash */
$pwd = $userMainTab->attributesAccess['userPassword']->computeLdapValue();
// (Un)lock the account by modifying the password hash.
if ($mode == 'LOCK') {
/* Lock entry */
if (empty($pwd)) {
$pwd = passwordMethodEmpty::LOCKVALUE;
} else {
$pwd = preg_replace("/(^[^\}]+\})(.*$)/", "\\1!\\2", $pwd);
}
} else {
/* Unlock entry */
if ($pwd == passwordMethodEmpty::LOCKVALUE) {
$pwd = '';
} else {
$pwd = preg_replace("/(^[^\}]+\})!(.*$)/", "\\1\\2", $pwd);
}
}
$modify['userPassword'] = $pwd;
$ldap = $config->get_ldap_link();
$ldap->cd($dn);
$ldap->modify($modify);
// Call the password post-lock hook, if defined.
if ($ldap->success()) {
$userClass = new user($dn);
$errors = $userClass->callHook('POST'.$mode, [], $ret);
211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
if (!empty($errors)) {
msg_dialog::displayChecks($errors);
}
} else {
$error = new FusionDirectoryLdapError($dn, LDAP_MOD, $ldap->get_error(), $ldap->get_errno());
$error->display();
}
return $ldap->success();
}
/*!
* \brief This function returns all loaded classes for password encryption
*/
static function get_available_methods (): array
{
global $class_mapping;
$ret = FALSE;
$i = 0;
if (!session::is_set('passwordMethod::get_available_methods')) {
foreach (array_keys($class_mapping) as $class) {
if (preg_match('/^passwordMethod.+/i', $class)) {
$test = new $class('');
if ($test->is_available()) {
$plugs = $test->get_hash_name();
if (!is_array($plugs)) {
$plugs = [$plugs];
}
$cfg = $test->is_configurable();
foreach ($plugs as $plugname) {
$ret['name'][$i] = $plugname;
$ret['class'][$i] = $class;
$ret['is_configurable'][$i] = $cfg;
$ret['object'][$i] = $test;
$ret[$i]['name'] = $plugname;
$ret[$i]['class'] = $class;
$ret[$i]['object'] = $test;
$ret[$i]['is_configurable'] = $cfg;
$ret[$plugname] = $class;
$i++;
}
}
}
}
session::set('passwordMethod::get_available_methods', $ret);
}
return session::get('passwordMethod::get_available_methods');
}
/*!
* \brief Method to check if a password matches a hash
*/
function checkPassword ($pwd, $hash): bool
{
return ($hash == $this->generate_hash($pwd));
}
/*!
* \brief Return true if this password method provides a configuration dialog
*/
function is_configurable (): bool
{
return FALSE;
}
281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350
/*!
* \brief Provide a subdialog to configure a password method
*/
function configure (): string
{
return '';
}
/*!
* \brief Save information to LDAP
*
* \param string $dn The DN
*/
function save ($dn)
{
}
/*!
* \brief Try to find out if it's our hash...
*
* \param string $password_hash
*
* \param string $dn The DN
*/
static function get_method ($password_hash, $dn = ''): passwordMethod
{
$methods = passwordMethod::get_available_methods();
if (isset($methods['class']['passwordMethodEmpty']) && (passwordMethodEmpty::_extract_method($password_hash) != '')) {
/* Test empty method first as it gets priority */
$method = new passwordMethodEmpty();
return $method;
}
foreach ($methods['class'] as $class) {
$method = $class::_extract_method($password_hash);
if ($method != '') {
$test = new $class($dn);
$test->set_hash($method);
return $test;
}
}
$method = new passwordMethodClear();
return $method;
}
/*!
* \brief Extract a method
*
* \param string $classname The password method class name
*
* \param string $password_hash
*/
static function _extract_method ($password_hash): string
{
$hash = static::get_hash_name();
if (preg_match("/^\{$hash\}/i", $password_hash)) {
return $hash;
}
return '';
}
/*!
* \brief Make a hash
*
351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400
* \param string $password The password
*
* \param string $hash
*/
static function make_hash ($password, $hash): string
{
$methods = passwordMethod::get_available_methods();
$tmp = new $methods[$hash]();
$tmp->set_hash($hash);
return $tmp->generate_hash($password);
}
/*!
* \brief Set a hash
*
* \param string $hash
*/
function set_hash ($hash)
{
$this->hash = $hash;
}
/*!
* \brief Get a hash
*/
function get_hash ()
{
return $this->hash;
}
/*!
* \brief Test for problematic unicode caracters in password
* This can be activated with the keyword strictPasswordRules in the
* fusiondirectory.conf
*
* \param string $password The password
*/
static function is_harmless ($password): bool
{
global $config;
if ($config->get_cfg_value('strictPasswordRules') == 'TRUE') {
// Do we have UTF8 characters in the password?
return ($password == utf8_decode($password));
}
return TRUE;
}
}