FusionDirectory
class_passwordMethod.inc
1 <?php
2 /*
3  This code is part of FusionDirectory (http://www.fusiondirectory.org/)
4 
5  Copyright (C) 2003-2010 Cajus Pollmeier
6  Copyright (C) 2011-2019 FusionDirectory
7 
8  This program is free software; you can redistribute it and/or modify
9  it under the terms of the GNU General Public License as published by
10  the Free Software Foundation; either version 2 of the License, or
11  (at your option) any later version.
12 
13  This program is distributed in the hope that it will be useful,
14  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  GNU General Public License for more details.
17 
18  You should have received a copy of the GNU General Public License
19  along with this program; if not, write to the Free Software
20  Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22 
23 /*
24  * \file class_passwordMethod.inc
25  * Source code for class passwordMethod
26  */
27 
31 abstract class passwordMethod
32 {
33  var $display = FALSE;
34  var $hash = '';
35 
36  protected $lockable = TRUE;
37 
44  function __construct ($dn = '', $userTab = NULL)
45  {
46  }
47 
51  abstract static function get_hash_name ();
52 
61  abstract public function generate_hash (string $pwd, bool $locked = FALSE): string;
62 
68  public function is_available (): bool
69  {
70  return TRUE;
71  }
72 
78  public function need_password (): bool
79  {
80  return TRUE;
81  }
82 
88  public function is_lockable (): bool
89  {
90  return $this->lockable;
91  }
92 
98  function is_locked ($dn = '', $pwd = ''): bool
99  {
100  global $config;
101  if (!$this->lockable) {
102  return FALSE;
103  }
104 
105  /* Get current password hash */
106  if (!empty($dn)) {
107  $ldap = $config->get_ldap_link();
108  $ldap->cd($config->current['BASE']);
109  $ldap->cat($dn, ['userPassword']);
110  $attrs = $ldap->fetch();
111  if (isset($attrs['userPassword'][0])) {
112  $pwd = $attrs['userPassword'][0];
113  }
114  }
115  return preg_match("/^[^\}]*+\}!/", $pwd);
116  }
117 
126  function lock_account ($dn = '', bool $lockEverything = TRUE)
127  {
128  return $this->generic_modify_account($dn, 'LOCK', $lockEverything);
129  }
130 
135  function unlock_account ($dn = '')
136  {
137  return $this->generic_modify_account($dn, 'UNLOCK');
138  }
139 
144  private function generic_modify_account ($dn, string $mode, bool $lockEverything = TRUE)
145  {
146  global $config;
147  if (!$this->lockable) {
148  return FALSE;
149  }
150  if ($mode != 'LOCK' && $mode != 'UNLOCK') {
151  throw new FusionDirectoryException('Invalid mode "'.$mode.'"');
152  }
153 
154  /* Open the user */
155  $userObject = objects::open($dn, 'user');
156  $userMainTab = $userObject->getBaseObject();
157 
158  /* Check if this entry is already (un)locked. */
159  if ($userMainTab->attributesAccess['userPassword']->isLocked()) {
160  if ($mode == 'LOCK') {
161  return TRUE;
162  }
163  } elseif ($mode == 'UNLOCK') {
164  return TRUE;
165  }
166  /* Fill modification array */
167  $modify = [];
168 
169  // Only trigger if general lock is set
170  if ($lockEverything) {
171  foreach ($userObject->by_object as $tab) {
172  if ($tab instanceof UserTabLockingAction) {
173  // Execute below function if available in each plugin tab to lock what is required to be locked. (webservice etc).
174  $tab->fillLockingLDAPAttrs($mode, $modify);
175  }
176  }
177  }
178 
179 
180 
181  // Call pre hooks
182  $errors = $userMainTab->callHook('PRE'.$mode, [], $ret);
183  if (!empty($errors)) {
184  msg_dialog::displayChecks($errors);
185  return FALSE;
186  }
187 
188  /* Get current password hash */
189  $pwd = $userMainTab->attributesAccess['userPassword']->computeLdapValue();
190 
191  // (Un)lock the account by modifying the password hash.
192  if ($mode == 'LOCK') {
193  /* Lock entry */
194  if (empty($pwd)) {
195  $pwd = passwordMethodEmpty::LOCKVALUE;
196  } else {
197  $pwd = preg_replace("/(^[^\}]+\})(.*$)/", "\\1!\\2", $pwd);
198  }
199  } else {
200  /* Unlock entry */
201  if ($pwd == passwordMethodEmpty::LOCKVALUE) {
202  $pwd = '';
203  } else {
204  $pwd = preg_replace("/(^[^\}]+\})!(.*$)/", "\\1\\2", $pwd);
205  }
206  }
207  $modify['userPassword'] = $pwd;
208 
209  $ldap = $config->get_ldap_link();
210  $ldap->cd($dn);
211  $ldap->modify($modify);
212 
213  // Call the password post-lock hook, if defined.
214  if ($ldap->success()) {
215  $userClass = new user($dn);
216  $errors = $userClass->callHook('POST'.$mode, [], $ret);
217  if (!empty($errors)) {
218  msg_dialog::displayChecks($errors);
219  }
220  } else {
221  $error = new FusionDirectoryLdapError($dn, LDAP_MOD, $ldap->get_error(), $ldap->get_errno());
222  $error->display();
223  }
224  return $ldap->success();
225  }
226 
227 
231  static function get_available_methods (): array
232  {
233  global $class_mapping;
234  $ret = FALSE;
235  $i = 0;
236 
237  if (!session::is_set('passwordMethod::get_available_methods')) {
238  foreach (array_keys($class_mapping) as $class) {
239  if (preg_match('/^passwordMethod.+/i', $class)) {
240  $test = new $class('');
241  if ($test->is_available()) {
242  $plugs = $test->get_hash_name();
243  if (!is_array($plugs)) {
244  $plugs = [$plugs];
245  }
246 
247  $cfg = $test->is_configurable();
248 
249  foreach ($plugs as $plugname) {
250  $ret['name'][$i] = $plugname;
251  $ret['class'][$i] = $class;
252  $ret['is_configurable'][$i] = $cfg;
253  $ret['object'][$i] = $test;
254 
255  $ret[$i]['name'] = $plugname;
256  $ret[$i]['class'] = $class;
257  $ret[$i]['object'] = $test;
258  $ret[$i]['is_configurable'] = $cfg;
259 
260  $ret[$plugname] = $class;
261  $i++;
262  }
263  }
264  }
265  }
266  session::set('passwordMethod::get_available_methods', $ret);
267  }
268  return session::get('passwordMethod::get_available_methods');
269  }
270 
274  function checkPassword ($pwd, $hash): bool
275  {
276  return ($hash == $this->generate_hash($pwd));
277  }
278 
279 
283  function is_configurable (): bool
284  {
285  return FALSE;
286  }
287 
291  function configure (): string
292  {
293  return '';
294  }
295 
296 
302  function save ($dn)
303  {
304  }
305 
306 
314  static function get_method ($password_hash, $dn = ''): passwordMethod
315  {
317 
318  if (isset($methods['class']['passwordMethodEmpty']) && (passwordMethodEmpty::_extract_method($password_hash) != '')) {
319  /* Test empty method first as it gets priority */
320  $method = new passwordMethodEmpty();
321  return $method;
322  }
323 
324  foreach ($methods['class'] as $class) {
325  $method = $class::_extract_method($password_hash);
326  if ($method != '') {
327  $test = new $class($dn);
328  $test->set_hash($method);
329  return $test;
330  }
331  }
332 
333  $method = new passwordMethodClear();
334  return $method;
335  }
336 
344  static function _extract_method ($password_hash): string
345  {
346  $hash = static::get_hash_name();
347  if (preg_match("/^\{$hash\}/i", $password_hash)) {
348  return $hash;
349  }
350 
351  return '';
352  }
353 
361  static function make_hash ($password, $hash): string
362  {
364  $tmp = new $methods[$hash]();
365  $tmp->set_hash($hash);
366  return $tmp->generate_hash($password);
367  }
368 
374  function set_hash ($hash)
375  {
376  $this->hash = $hash;
377  }
378 
379 
383  function get_hash ()
384  {
385  return $this->hash;
386  }
387 
395  static function is_harmless ($password): bool
396  {
397  global $config;
398  if ($config->get_cfg_value('strictPasswordRules') == 'TRUE') {
399  // Do we have UTF8 characters in the password?
400  return ($password == utf8_decode($password));
401  }
402 
403  return TRUE;
404  }
405 }
static is_harmless($password)
Test for problematic unicode caracters in password This can be activated with the keyword strictPassw...
This class contains all the functions for clear password method.
static get_hash_name()
Get the Hash name.
static get($name)
Accessor of a session var.
checkPassword($pwd, $hash)
Method to check if a password matches a hash.
static _extract_method($password_hash)
Extract a method.
is_lockable()
If we can lock the password.
static make_hash($password, $hash)
Make a hash.
is_available()
Is available.
__construct($dn='', $userTab=NULL)
Password method contructor.
get_hash()
Get a hash.
generate_hash(string $pwd, bool $locked=FALSE)
Generate template hash.
static set($name, $value)
Set a value in a session.
configure()
Provide a subdialog to configure a password method.
This interface is implemented when a user tabs needs to alter the locking LDAP modification for users...
Error returned by an LDAP operation called from FusionDirectory.
lock_account($dn='', bool $lockEverything=TRUE)
Locks an account by adding a &#39;!&#39; as prefix to the password hashes. This makes login impossible...
is_configurable()
Return true if this password method provides a configuration dialog.
static open(string $dn, string $type)
Create the tab object for the given dn.
Parent class for all exceptions thrown in FusionDirectory.
This class contains all the functions for empty password method.
is_locked($dn='', $pwd='')
Is locked.
static get_available_methods()
This function returns all loaded classes for password encryption.
This class contains all the basic function for password methods.
need_password()
If we need password.
static get_method($password_hash, $dn='')
Try to find out if it&#39;s our hash...
save($dn)
Save information to LDAP.
set_hash($hash)
Set a hash.
static is_set($name)
Check if the name of the session is set.
unlock_account($dn='')
Unlocks an account which was locked by &#39;lock_account()&#39;. For details about the locking mechanism see ...