Passed
Push — master ( 0f9140...c4489d )
by Alxarafe
22:27
created

Helpers/AlSecurity2.php (1 issue)

Labels
Severity
1
<?php
2
/* Copyright (C) 2008-2011 Laurent Destailleur  <[email protected]>
3
 * Copyright (C) 2008-2017 Regis Houssin        <[email protected]>
4
 *
5
 * This program is free software; you can redistribute it and/or modify
6
 * it under the terms of the GNU General Public License as published by
7
 * the Free Software Foundation; either version 3 of the License, or
8
 * (at your option) any later version.
9
 *
10
 * This program is distributed in the hope that it will be useful,
11
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13
 * GNU General Public License for more details.
14
 *
15
 * You should have received a copy of the GNU General Public License
16
 * along with this program. If not, see <http://www.gnu.org/licenses/>.
17
 * or see http://www.gnu.org/
18
 */
19
namespace Alixar\Helpers;
20
21
use Alixar\Views\LoginView;
22
23
/**
24
 *  \file		htdocs/core/lib/security2.lib.php
25
 *  \ingroup    core
26
 *  \brief		Set of function used for dolibarr security (not common functions).
27
 *  			Warning, this file must not depends on other library files, except function.lib.php
28
 *  			because it is used at low code level.
29
 */
30
class AlSecurity2
31
{
32
33
    /**
34
     *  Return user/group account of web server
35
     *
36
     *  @param	string	$mode       'user' or 'group'
37
     *  @return string				Return user or group of web server
38
     */
39
    function dol_getwebuser($mode)
40
    {
41
        $t = '?';
42
        if ($mode == 'user')
43
            $t = getenv('APACHE_RUN_USER');   // $_ENV['APACHE_RUN_USER'] is empty
44
        if ($mode == 'group')
45
            $t = getenv('APACHE_RUN_GROUP');
46
        return $t;
47
    }
48
49
    /**
50
     *  Return a login if login/pass was successfull
51
     *
52
     * 	@param		string	$usertotest			Login value to test
53
     * 	@param		string	$passwordtotest		Password value to test
54
     * 	@param		string	$entitytotest		Instance of data we must check
55
     * 	@param		array	$authmode			Array list of selected authentication mode array('http', 'dolibarr', 'xxx'...)
56
     *  @return		string						Login or ''
57
     */
58
    static function checkLoginPassEntity($usertotest, $passwordtotest, $entitytotest, $authmode)
59
    {
60
        //global Globals::$conf, Globals::$langs;
61
        //global $dolauthmode;    // To return authentication finally used
62
        // Check parameters
63
        if ($entitytotest == '') {
64
            $entitytotest = 1;
65
        }
66
67
        AlDolUtils::dol_syslog("checkLoginPassEntity usertotest=" . $usertotest . " entitytotest=" . $entitytotest . " authmode=" . join(',', $authmode));
68
        $login = '';
69
70
        // Validation of login/pass/entity with standard modules
71
        if (empty($login)) {
72
            $test = true;
73
            foreach ($authmode as $mode) {
74
                if ($test && $mode && !$login) {
75
                    // Validation of login/pass/entity for mode $mode
76
                    $mode = trim($mode);
77
                    $authfile = 'functions_' . $mode . '.php';
78
                    $fullauthfile = '';
79
80
                    /*
81
                      $dirlogin = array_merge(array("/core/login"), (array) Globals::$conf->modules_parts['login']);
82
                      foreach ($dirlogin as $reldir) {
83
                      $dir = AlDolUtils::dol_buildpath($reldir, 0);
84
                      $newdir = AlDolUtils::dol_osencode($dir);
85
86
                      // Check if file found (do not use dol_is_file to avoid loading files.lib.php)
87
                      $tmpnewauthfile = $newdir . (preg_match('/\/$/', $newdir) ? '' : '/') . $authfile;
88
                      if (is_file($tmpnewauthfile))
89
                      {$fullauthfile = $tmpnewauthfile;}
90
                      }
91
                     */
92
93
                    $dirlogin = array_merge(array("/Helpers/login"), (array) Globals::$conf->modules_parts['login']);
94
                    foreach ($dirlogin as $reldir) {
95
                        // Check if file found (do not use dol_is_file to avoid loading files.lib.php)
96
                        $tmpnewauthfile = BASE_PATH . $reldir . '/' . $authfile;
97
                        if (is_file($tmpnewauthfile)) {
98
                            $fullauthfile = $tmpnewauthfile;
99
                        }
100
                    }
101
                    $result = false;
102
103
                    if ($fullauthfile) {
104
                        $result = include_once $fullauthfile;
105
                    }
106
                    if ($fullauthfile && $result) {
107
                        // Call function to check user/password
108
                        $function = 'check_user_password_' . $mode;
109
                        $login = \call_user_func($function, $usertotest, $passwordtotest, $entitytotest);
110
                        if ($login) { // Login is successfull
111
                            $test = false;            // To stop once at first login success
112
                            Globals::$conf->authmode = $mode; // This properties is defined only when logged to say what mode was successfully used
113
                            $dol_tz = AlDolUtils::GETPOST('tz');
114
                            $dol_dst = AlDolUtils::GETPOST('dst');
115
                            $dol_screenwidth = AlDolUtils::GETPOST('screenwidth');
116
                            $dol_screenheight = AlDolUtils::GETPOST('screenheight');
117
                        }
118
                    } else {
119
                        AlDolUtils::dol_syslog("Authentification ko - failed to load file '" . $authfile . "'", LOG_ERR);
120
                        sleep(1);
121
                        // Load translation files required by the page
122
                        Globals::$langs->loadLangs(array('other', 'main', 'errors'));
123
124
                        $_SESSION["dol_loginmesg"] = Globals::$langs->trans("ErrorFailedToLoadLoginFileForMode", $mode);
125
                    }
126
                }
127
            }
128
        }
129
130
        return $login;
131
    }
132
133
    /**
134
     * Show Dolibarr default login page.
135
     * Part of this code is also duplicated into main.inc.php::top_htmlhead
136
     *
137
     * @param       Translate   Globals::$langs      Lang object (must be initialized by a new).
138
     * @param       Conf        Globals::$conf       Conf object
139
     * @param       Societe     $mysoc      Company object
140
     * @return      void
141
     */
142
    static function dol_loginfunction($ctrl)
143
    {
144
145
        Globals::$langs->loadLangs(array("main", "other", "help", "admin"));
146
147
        // Instantiate hooks of thirdparty module only if not already define
148
        Globals::$hookManager->initHooks(array('mainloginpage'));
149
150
        $main_authentication = Globals::$conf->file->main_authentication;
151
152
        $session_name = session_name(); // Get current session name
153
154
        $dol_url_root = DOL_BASE_URI;
155
156
        // Title
157
        $appli = constant('DOL_APPLICATION_TITLE');
158
        $title = $appli . ' ' . constant('DOL_VERSION');
159
        if (!empty(Globals::$conf->global->MAIN_APPLICATION_TITLE)) {
160
            $title = Globals::$conf->global->MAIN_APPLICATION_TITLE;
161
        }
162
        $titletruedolibarrversion = constant('DOL_VERSION'); // $title used by login template after the @ to inform of true Dolibarr version
163
        // Note: Globals::$conf->css looks like '/theme/eldy/style.css.php'
164
        /*
165
          Globals::$conf->css = "/theme/".(AlDolUtils::GETPOST('theme','alpha')?DolUtils::GETPOST('theme','alpha'):Globals::$conf->theme)."/style.css.php";
166
          $themepath=DolUtils::dol_buildpath(Globals::$conf->css,1);
167
          if (! empty(Globals::$conf->modules_parts['theme']))		// Using this feature slow down application
168
          {
169
          foreach(Globals::$conf->modules_parts['theme'] as $reldir)
170
          {
171
          if (file_exists(AlDolUtils::dol_buildpath($reldir.Globals::$conf->css, 0)))
172
          {
173
          $themepath=DolUtils::dol_buildpath($reldir.Globals::$conf->css, 1);
174
          break;
175
          }
176
          }
177
          }
178
          Globals::$conf_css = $themepath."?lang=".Globals::$langs->defaultlang;
179
         */
180
181
        // Select templates dir
182
        if (!empty(Globals::$conf->modules_parts['tpl'])) { // Using this feature slow down application
183
            $dirtpls = array_merge(Globals::$conf->modules_parts['tpl'], array('/core/tpl/'));
184
            foreach ($dirtpls as $reldir) {
185
                $tmp = AlDolUtils::dol_buildpath($reldir . 'login.tpl.php');
186
                if (file_exists($tmp)) {
187
                    $template_dir = preg_replace('/login\.tpl\.php$/', '', $tmp);
188
                    break;
189
                }
190
            }
191
        } else {
192
            $template_dir = DOL_DOCUMENT_ROOT . "/core/tpl/";
193
        }
194
195
// Set cookie for timeout management
196
        $prefix = AlDolUtils::dol_getprefix('');
197
        $sessiontimeout = 'DOLSESSTIMEOUT_' . $prefix;
198
        if (!empty(Globals::$conf->global->MAIN_SESSION_TIMEOUT)) {
199
            setcookie($sessiontimeout, Globals::$conf->global->MAIN_SESSION_TIMEOUT, 0, "/", null, false, true);
200
        }
201
202
        if (AlDolUtils::GETPOST('urlfrom', 'alpha')) {
203
            $_SESSION["urlfrom"] = AlDolUtils::GETPOST('urlfrom', 'alpha');
204
        } else {
205
            unset($_SESSION["urlfrom"]);
206
        }
207
208
        if (!AlDolUtils::GETPOST("username", 'alpha')) {
209
            $focus_element = 'username';
210
        } else {
211
            $focus_element = 'password';
212
        }
213
214
        $demologin = '';
215
        $demopassword = '';
216
        if (!empty($dolibarr_main_demo)) {
217
            $tab = explode(',', $dolibarr_main_demo);
218
            $demologin = $tab[0];
219
            $demopassword = $tab[1];
220
        }
221
222
// Execute hook getLoginPageOptions (for table)
223
        $parameters = array('entity' => AlDolUtils::GETPOST('entity', 'int'));
224
        $reshook = Globals::$hookManager->executeHooks('getLoginPageOptions', $parameters);    // Note that $action and $object may have been modified by some hooks.
225
        if (is_array(Globals::$hookManager->resArray) && !empty(Globals::$hookManager->resArray)) {
226
            $morelogincontent = Globals::$hookManager->resArray; // (deprecated) For compatibility
227
        } else {
228
            $morelogincontent = Globals::$hookManager->resPrint;
229
        }
230
231
// Execute hook getLoginPageExtraOptions (eg for js)
232
        $parameters = array('entity' => AlDolUtils::GETPOST('entity', 'int'));
233
        $reshook = Globals::$hookManager->executeHooks('getLoginPageExtraOptions', $parameters);    // Note that $action and $object may have been modified by some hooks.
234
        $moreloginextracontent = Globals::$hookManager->resPrint;
235
236
// Login
237
        $login = (!empty(Globals::$hookManager->resArray['username']) ? Globals::$hookManager->resArray['username'] : (AlDolUtils::GETPOST("username", "alpha") ? AlDolUtils::GETPOST("username", "alpha") : $demologin));
238
        $password = $demopassword;
239
240
// Show logo (search in order: small company logo, large company logo, theme logo, common logo)
241
        $width = 0;
242
        $urllogo = DOL_BASE_URI . '/theme/login_logo.png';
243
244
        if (!empty($mysoc->logo_small) && is_readable(Globals::$conf->mycompany->dir_output . '/logos/thumbs/' . $mysoc->logo_small)) {
245
            $urllogo = DOL_BASE_URI . '/viewimage.php?cache=1&amp;modulepart=mycompany&amp;file=' . urlencode('logos/thumbs/' . $mysoc->logo_small);
246
        } elseif (!empty($mysoc->logo) && is_readable(Globals::$conf->mycompany->dir_output . '/logos/' . $mysoc->logo)) {
247
            $urllogo = DOL_BASE_URI . '/viewimage.php?cache=1&amp;modulepart=mycompany&amp;file=' . urlencode('logos/' . $mysoc->logo);
248
            $width = 128;
249
        } elseif (is_readable(DOL_BASE_URI . '/theme/' . Globals::$conf->theme . '/img/dolibarr_logo.png')) {
250
            $urllogo = DOL_BASE_URI . '/theme/' . Globals::$conf->theme . '/img/dolibarr_logo.png';
251
        } elseif (is_readable(DOL_BASE_URI . '/theme/dolibarr_logo.png')) {
252
            $urllogo = DOL_BASE_URI . '/theme/dolibarr_logo.png';
253
        }
254
255
// Security graphical code
256
        $captcha = 0;
257
        $captcha_refresh = '';
258
        if (function_exists("imagecreatefrompng") &&!empty(Globals::$conf->global->MAIN_SECURITY_ENABLECAPTCHA)) {
259
            $captcha = 1;
260
            $captcha_refresh = img_picto(Globals::$langs->trans("Refresh"), 'refresh', 'id="captcha_refresh_img"');
261
        }
262
263
// Extra link
264
        $forgetpasslink = 0;
265
        $helpcenterlink = 0;
266
        if (empty(Globals::$conf->global->MAIN_SECURITY_DISABLEFORGETPASSLINK) || empty(Globals::$conf->global->MAIN_HELPCENTER_DISABLELINK)) {
267
            if (empty(Globals::$conf->global->MAIN_SECURITY_DISABLEFORGETPASSLINK)) {
268
                $forgetpasslink = 1;
269
            }
270
271
            if (empty(Globals::$conf->global->MAIN_HELPCENTER_DISABLELINK)) {
272
                $helpcenterlink = 1;
273
            }
274
        }
275
276
// Home message
277
        $main_home = '';
278
        if (!empty(Globals::$conf->global->MAIN_HOME)) {
279
            $substitutionarray = getCommonSubstitutionArray(Globals::$langs);
280
            complete_substitutions_array($substitutionarray, Globals::$langs);
281
            $texttoshow = make_substitutions(Globals::$conf->global->MAIN_HOME, $substitutionarray, Globals::$langs);
282
283
            $main_home = dol_htmlcleanlastbr($texttoshow);
284
        }
285
286
// Google AD
287
        $main_google_ad_client = ((!empty(Globals::$conf->global->MAIN_GOOGLE_AD_CLIENT) &&!empty(Globals::$conf->global->MAIN_GOOGLE_AD_SLOT)) ? 1 : 0);
288
289
// Set jquery theme
290
        $dol_loginmesg = (!empty($_SESSION["dol_loginmesg"]) ? $_SESSION["dol_loginmesg"] : '');
291
        $favicon = AlDolUtils::dol_buildpath('/theme/' . Globals::$conf->theme . '/img/favicon.ico', 1);
292
        if (!empty(Globals::$conf->global->MAIN_FAVICON_URL)) {
293
            $favicon = Globals::$conf->global->MAIN_FAVICON_URL;
294
        }
295
        $jquerytheme = 'base';
296
        if (!empty(Globals::$conf->global->MAIN_USE_JQUERY_THEME)) {
297
            $jquerytheme = Globals::$conf->global->MAIN_USE_JQUERY_THEME;
298
        }
299
300
// Set dol_hide_topmenu, dol_hide_leftmenu, dol_optimize_smallscreen, dol_no_mouse_hover
301
        $dol_hide_topmenu = AlDolUtils::GETPOST('dol_hide_topmenu', 'int');
302
        $dol_hide_leftmenu = AlDolUtils::GETPOST('dol_hide_leftmenu', 'int');
303
        $dol_optimize_smallscreen = AlDolUtils::GETPOST('dol_optimize_smallscreen', 'int');
304
        $dol_no_mouse_hover = AlDolUtils::GETPOST('dol_no_mouse_hover', 'int');
305
        $dol_use_jmobile = AlDolUtils::GETPOST('dol_use_jmobile', 'int');
306
307
        $_SESSION["dol_loginmesg"] = '';
308
// Include login page template
309
        // include $template_dir . 'login.tpl.php';
310
311
        (new LoginView($ctrl))->render();
0 ignored issues
show
The method render() does not exist on Alixar\Views\LoginView. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

311
        (new LoginView($ctrl))->/** @scrutinizer ignore-call */ render();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
312
    }
313
314
    /**
315
     *  Fonction pour initialiser un salt pour la fonction crypt.
316
     *
317
     *  @param		int		$type		2=>renvoi un salt pour cryptage DES
318
     * 									12=>renvoi un salt pour cryptage MD5
319
     * 									non defini=>renvoi un salt pour cryptage par defaut
320
     * 	@return		string				Salt string
321
     */
322
    function makesalt($type = CRYPT_SALT_LENGTH)
323
    {
324
        AlDolUtils::dol_syslog("makesalt type=" . $type);
325
        switch ($type) {
326
            case 12: // 8 + 4
327
                $saltlen = 8;
328
                $saltprefix = '$1$';
329
                $saltsuffix = '$';
330
                break;
331
            case 8:  // 8 (Pour compatibilite, ne devrait pas etre utilise)
332
                $saltlen = 8;
333
                $saltprefix = '$1$';
334
                $saltsuffix = '$';
335
                break;
336
            case 2:  // 2
337
            default:  // by default, fall back on Standard DES (should work everywhere)
338
                $saltlen = 2;
339
                $saltprefix = '';
340
                $saltsuffix = '';
341
                break;
342
        }
343
        $salt = '';
344
        while (dol_strlen($salt) < $saltlen)
345
            $salt .= chr(mt_rand(64, 126));
346
347
        $result = $saltprefix . $salt . $saltsuffix;
348
        AlDolUtils::dol_syslog("makesalt return=" . $result);
349
        return $result;
350
    }
351
352
    /**
353
     *  Encode or decode database password in config file
354
     *
355
     *  @param   	int		$level   	Encode level: 0 no encoding, 1 encoding
356
     * 	@return		int					<0 if KO, >0 if OK
357
     */
358
    function encodedecode_dbpassconf($level = 0)
359
    {
360
        AlDolUtils::dol_syslog("encodedecode_dbpassconf level=" . $level, LOG_DEBUG);
361
        Globals::$config = '';
362
        $passwd = '';
363
        $passwd_crypted = '';
364
365
        if ($fp = fopen(DOL_DOCUMENT_ROOT . '/conf/conf.php', 'r')) {
366
            while (!feof($fp)) {
367
                $buffer = fgets($fp, 4096);
368
369
                $lineofpass = 0;
370
371
                if (preg_match('/^[^#]*dolibarr_main_db_encrypted_pass[\s]*=[\s]*(.*)/i', $buffer, $reg)) { // Old way to save crypted value
372
                    $val = trim($reg[1]); // This also remove CR/LF
373
                    $val = preg_replace('/^["\']/', '', $val);
374
                    $val = preg_replace('/["\'][\s;]*$/', '', $val);
375
                    if (!empty($val)) {
376
                        $passwd_crypted = $val;
377
                        $val = dol_decode($val);
378
                        $passwd = $val;
379
                        $lineofpass = 1;
380
                    }
381
                } elseif (preg_match('/^[^#]*dolibarr_main_db_pass[\s]*=[\s]*(.*)/i', $buffer, $reg)) {
382
                    $val = trim($reg[1]); // This also remove CR/LF
383
                    $val = preg_replace('/^["\']/', '', $val);
384
                    $val = preg_replace('/["\'][\s;]*$/', '', $val);
385
                    if (preg_match('/crypted:/i', $buffer)) {
386
                        $val = preg_replace('/crypted:/i', '', $val);
387
                        $passwd_crypted = $val;
388
                        $val = dol_decode($val);
389
                        $passwd = $val;
390
                    } else {
391
                        $passwd = $val;
392
                        $val = dol_encode($val);
393
                        $passwd_crypted = $val;
394
                    }
395
                    $lineofpass = 1;
396
                }
397
398
// Output line
399
                if ($lineofpass) {
400
// Add value at end of file
401
                    if ($level == 0) {
402
                        Globals::$config .= '$dolibarr_main_db_pass=\'' . $passwd . '\';' . "\n";
403
                    }
404
                    if ($level == 1) {
405
                        Globals::$config .= '$dolibarr_main_db_pass=\'crypted:' . $passwd_crypted . '\';' . "\n";
406
                    }
407
408
//print 'passwd = '.$passwd.' - passwd_crypted = '.$passwd_crypted;
409
//exit;
410
                } else {
411
                    Globals::$config .= $buffer;
412
                }
413
            }
414
            fclose($fp);
415
416
// Write new conf file
417
            $file = DOL_DOCUMENT_ROOT . '/conf/conf.php';
418
            if ($fp = @fopen($file, 'w')) {
419
                fputs($fp, Globals::$config);
420
                fflush($fp);
421
                fclose($fp);
422
                clearstatcache();
423
424
// It's config file, so we set read permission for creator only.
425
// Should set permission to web user and groups for users used by batch
426
//@chmod($file, octdec('0600'));
427
428
                return 1;
429
            } else {
430
                AlDolUtils::dol_syslog("encodedecode_dbpassconf Failed to open conf.php file for writing", LOG_WARNING);
431
                return -1;
432
            }
433
        } else {
434
            AlDolUtils::dol_syslog("encodedecode_dbpassconf Failed to read conf.php", LOG_ERR);
435
            return -2;
436
        }
437
    }
438
439
    /**
440
     * Return a generated password using default module
441
     *
442
     * @param		boolean		$generic				true=Create generic password (32 chars/numbers), false=Use the configured password generation module
443
     * @param		array		$replaceambiguouschars	Discard ambigous characters. For example array('I').
444
     * @return		string								New value for password
445
     * @see dol_hash
446
     */
447
    function getRandomPassword($generic = false, $replaceambiguouschars = null)
448
    {
449
        //global $db, Globals::$conf, Globals::$langs, $user;
450
451
        $generated_password = '';
452
        if ($generic) {
453
            $length = 32;
454
            $lowercase = "qwertyuiopasdfghjklzxcvbnm";
455
            $uppercase = "ASDFGHJKLZXCVBNMQWERTYUIOP";
456
            $numbers = "1234567890";
457
            $randomCode = "";
458
            $nbofchar = round($length / 3);
459
            $nbofcharlast = ($length - 2 * $nbofchar);
460
//var_dump($nbofchar.'-'.$nbofcharlast);
461
            if (function_exists('random_int')) { // Cryptographic random
462
                $max = strlen($lowercase) - 1;
463
                for ($x = 0; $x < $nbofchar; $x++) {
464
                    $randomCode .= $lowercase{random_int(0, $max)};
465
                }
466
                $max = strlen($uppercase) - 1;
467
                for ($x = 0; $x < $nbofchar; $x++) {
468
                    $randomCode .= $uppercase{random_int(0, $max)};
469
                }
470
                $max = strlen($numbers) - 1;
471
                for ($x = 0; $x < $nbofcharlast; $x++) {
472
                    $randomCode .= $numbers{random_int(0, $max)};
473
                }
474
475
                $generated_password = str_shuffle($randomCode);
476
            } else { // Old platform, non cryptographic random
477
                $max = strlen($lowercase) - 1;
478
                for ($x = 0; $x < $nbofchar; $x++) {
479
                    $randomCode .= $lowercase{mt_rand(0, $max)};
480
                }
481
                $max = strlen($uppercase) - 1;
482
                for ($x = 0; $x < $nbofchar; $x++) {
483
                    $randomCode .= $uppercase{mt_rand(0, $max)};
484
                }
485
                $max = strlen($numbers) - 1;
486
                for ($x = 0; $x < $nbofcharlast; $x++) {
487
                    $randomCode .= $numbers{mt_rand(0, $max)};
488
                }
489
490
                $generated_password = str_shuffle($randomCode);
491
            }
492
        } else
493
            if (!empty (Globals::$conf->global->USER_PASSWORD_GENERATED)) {
494
            $nomclass = "modGeneratePass" . ucfirst(Globals::$conf->global->USER_PASSWORD_GENERATED);
495
            $nomfichier = $nomclass . ".class.php";
496
//print DOL_DOCUMENT_ROOT."/core/modules/security/generate/".$nomclass;
497
            require_once DOL_DOCUMENT_ROOT . "/core/modules/security/generate/" . $nomfichier;
498
            $genhandler = new $nomclass($db, Globals::$conf, Globals::$langs, $user);
499
            $generated_password = $genhandler->getNewGeneratedPassword();
500
            unset($genhandler);
501
        }
502
503
// Do we have to discard some alphabetic characters ?
504
        if (is_array($replaceambiguouschars) && count($replaceambiguouschars) > 0) {
505
            $numbers = "ABCDEF";
506
            $max = strlen($numbers) - 1;
507
            $generated_password = str_replace($replaceambiguouschars, $numbers{random_int(0, $max)}, $generated_password);
508
        }
509
510
        return $generated_password;
511
    }
512
}
513