Aleksander Machniak
2016-05-20 ab6fdfa8bc3a441590778b026b52225473424eec
commit | author | age
48e9c1 1 <?php
T 2
07c6c6 3 /**
TB 4  * Password Plugin for Roundcube
5  *
6  * @version @package_version@
7  * @author Aleksander Machniak <alec@alec.pl>
8  *
252cc4 9  * Copyright (C) 2005-2015, The Roundcube Dev Team
07c6c6 10  *
TB 11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program. If not, see http://www.gnu.org/licenses/.
23  */
48e9c1 24
T 25 define('PASSWORD_CRYPT_ERROR', 1);
26 define('PASSWORD_ERROR', 2);
27 define('PASSWORD_CONNECT_ERROR', 3);
28 define('PASSWORD_SUCCESS', 0);
29
30 /**
31  * Change password plugin
32  *
33  * Plugin that adds functionality to change a users password.
34  * It provides common functionality and user interface and supports
35  * several backends to finally update the password.
36  *
37  * For installation and configuration instructions please read the README file.
38  *
39  * @author Aleksander Machniak
40  */
41 class password extends rcube_plugin
42 {
e7ee70 43     public $task    = 'settings|login';
48e9c1 44     public $noframe = true;
T 45     public $noajax  = true;
4520fa 46
e7ee70 47     private $newuser = false;
48e9c1 48
T 49     function init()
50     {
51         $rcmail = rcmail::get_instance();
52
53         $this->load_config();
98128f 54
4520fa 55         if ($rcmail->task == 'settings') {
AM 56             if (!$this->check_host_login_exceptions()) {
57                 return;
58             }
59
90ab9f 60             $this->add_texts('localization/');
AM 61
4520fa 62             $this->add_hook('settings_actions', array($this, 'settings_actions'));
AM 63
64             $this->register_action('plugin.password', array($this, 'password_init'));
65             $this->register_action('plugin.password-save', array($this, 'password_save'));
7978f8 66         }
4520fa 67         else if ($rcmail->config->get('password_force_new_user')) {
e7ee70 68             $this->add_hook('user_create', array($this, 'user_create'));
S 69             $this->add_hook('login_after', array($this, 'login_after'));
9556f3 70         }
48e9c1 71     }
T 72
cf46ae 73     function settings_actions($args)
TB 74     {
75         // register as settings action
7e309b 76         $args['actions'][] = array(
AM 77             'action' => 'plugin.password',
78             'class'  => 'password',
79             'label'  => 'password',
80             'title'  => 'changepasswd',
81             'domain' => 'password',
82         );
83
cf46ae 84         return $args;
TB 85     }
86
48e9c1 87     function password_init()
T 88     {
89         $this->register_handler('plugin.body', array($this, 'password_form'));
90
91         $rcmail = rcmail::get_instance();
92         $rcmail->output->set_pagetitle($this->gettext('changepasswd'));
4520fa 93
AM 94         if (rcube_utils::get_input_value('_first', rcube_utils::INPUT_GET)) {
9e9c03 95             $rcmail->output->command('display_message', $this->gettext('firstloginchange'), 'notice');
S 96         }
4520fa 97
48e9c1 98         $rcmail->output->send('plugin');
T 99     }
100
101     function password_save()
102     {
103         $this->register_handler('plugin.body', array($this, 'password_form'));
4520fa 104
AM 105         $rcmail = rcmail::get_instance();
48e9c1 106         $rcmail->output->set_pagetitle($this->gettext('changepasswd'));
T 107
252cc4 108         $form_disabled   = $rcmail->config->get('password_disabled');
AM 109         $confirm         = $rcmail->config->get('password_confirm_current');
48e9c1 110         $required_length = intval($rcmail->config->get('password_minimum_length'));
4520fa 111         $check_strength  = $rcmail->config->get('password_require_nonalpha');
48e9c1 112
T 113         if (($confirm && !isset($_POST['_curpasswd'])) || !isset($_POST['_newpasswd'])) {
114             $rcmail->output->command('display_message', $this->gettext('nopassword'), 'error');
115         }
116         else {
117             $charset    = strtoupper($rcmail->config->get('password_charset', 'ISO-8859-1'));
118             $rc_charset = strtoupper($rcmail->output->get_charset());
119
120             $sespwd = $rcmail->decrypt($_SESSION['password']);
61be82 121             $curpwd = $confirm ? rcube_utils::get_input_value('_curpasswd', rcube_utils::INPUT_POST, true, $charset) : $sespwd;
AM 122             $newpwd = rcube_utils::get_input_value('_newpasswd', rcube_utils::INPUT_POST, true);
123             $conpwd = rcube_utils::get_input_value('_confpasswd', rcube_utils::INPUT_POST, true);
48e9c1 124
T 125             // check allowed characters according to the configured 'password_charset' option
126             // by converting the password entered by the user to this charset and back to UTF-8
127             $orig_pwd = $newpwd;
61be82 128             $chk_pwd = rcube_charset::convert($orig_pwd, $rc_charset, $charset);
AM 129             $chk_pwd = rcube_charset::convert($chk_pwd, $charset, $rc_charset);
48e9c1 130
T 131             // WARNING: Default password_charset is ISO-8859-1, so conversion will
132             // change national characters. This may disable possibility of using
133             // the same password in other MUA's.
134             // We're doing this for consistence with Roundcube core
61be82 135             $newpwd = rcube_charset::convert($newpwd, $rc_charset, $charset);
AM 136             $conpwd = rcube_charset::convert($conpwd, $rc_charset, $charset);
48e9c1 137
T 138             if ($chk_pwd != $orig_pwd) {
139                 $rcmail->output->command('display_message', $this->gettext('passwordforbidden'), 'error');
140             }
141             // other passwords validity checks
142             else if ($conpwd != $newpwd) {
143                 $rcmail->output->command('display_message', $this->gettext('passwordinconsistency'), 'error');
144             }
145             else if ($confirm && $sespwd != $curpwd) {
146                 $rcmail->output->command('display_message', $this->gettext('passwordincorrect'), 'error');
147             }
148             else if ($required_length && strlen($newpwd) < $required_length) {
149                 $rcmail->output->command('display_message', $this->gettext(
61be82 150                     array('name' => 'passwordshort', 'vars' => array('length' => $required_length))), 'error');
48e9c1 151             }
T 152             else if ($check_strength && (!preg_match("/[0-9]/", $newpwd) || !preg_match("/[^A-Za-z0-9]/", $newpwd))) {
153                 $rcmail->output->command('display_message', $this->gettext('passwordweak'), 'error');
154             }
859a7a 155             // password is the same as the old one, do nothing, return success
b343ad 156             else if ($sespwd == $newpwd && !$rcmail->config->get('password_force_save')) {
48e9c1 157                 $rcmail->output->command('display_message', $this->gettext('successfullysaved'), 'confirmation');
859a7a 158             }
S 159             // try to save the password
160             else if (!($res = $this->_save($curpwd, $newpwd))) {
161                 $rcmail->output->command('display_message', $this->gettext('successfullysaved'), 'confirmation');
48e9c1 162
T 163                 // allow additional actions after password change (e.g. reset some backends)
164                 $plugin = $rcmail->plugins->exec_hook('password_change', array(
165                     'old_pass' => $curpwd, 'new_pass' => $newpwd));
166
167                 // Reset session password
168                 $_SESSION['password'] = $rcmail->encrypt($plugin['new_pass']);
169
170                 // Log password change
171                 if ($rcmail->config->get('password_log')) {
61be82 172                     rcube::write_log('password', sprintf('Password changed for user %s (ID: %d) from %s',
AM 173                         $rcmail->get_user_name(), $rcmail->user->ID, rcube_utils::remote_ip()));
48e9c1 174                 }
T 175             }
176             else {
177                 $rcmail->output->command('display_message', $res, 'error');
178             }
179         }
180
61be82 181         $rcmail->overwrite_action('plugin.password');
48e9c1 182         $rcmail->output->send('plugin');
T 183     }
184
185     function password_form()
186     {
187         $rcmail = rcmail::get_instance();
188
189         // add some labels to client
190         $rcmail->output->add_label(
191             'password.nopassword',
192             'password.nocurpassword',
193             'password.passwordinconsistency'
194         );
195
252cc4 196         $form_disabled = $rcmail->config->get('password_disabled');
AM 197
48e9c1 198         $rcmail->output->set_env('product_name', $rcmail->config->get('product_name'));
252cc4 199         $rcmail->output->set_env('password_disabled', !empty($form_disabled));
48e9c1 200
T 201         $table = new html_table(array('cols' => 2));
202
203         if ($rcmail->config->get('password_confirm_current')) {
204             // show current password selection
205             $field_id = 'curpasswd';
252cc4 206             $input_curpasswd = new html_passwordfield(array(
AM 207                     'name'         => '_curpasswd',
208                     'id'           => $field_id,
209                     'size'         => 20,
210                     'autocomplete' => 'off',
211             ));
48e9c1 212
61be82 213             $table->add('title', html::label($field_id, rcube::Q($this->gettext('curpasswd'))));
48e9c1 214             $table->add(null, $input_curpasswd->show());
T 215         }
216
217         // show new password selection
218         $field_id = 'newpasswd';
252cc4 219         $input_newpasswd = new html_passwordfield(array(
AM 220                 'name'         => '_newpasswd',
221                 'id'           => $field_id,
222                 'size'         => 20,
223                 'autocomplete' => 'off',
224         ));
48e9c1 225
61be82 226         $table->add('title', html::label($field_id, rcube::Q($this->gettext('newpasswd'))));
48e9c1 227         $table->add(null, $input_newpasswd->show());
T 228
229         // show confirm password selection
230         $field_id = 'confpasswd';
252cc4 231         $input_confpasswd = new html_passwordfield(array(
AM 232                 'name'         => '_confpasswd',
233                 'id'           => $field_id,
234                 'size'         => 20,
235                 'autocomplete' => 'off',
236         ));
48e9c1 237
61be82 238         $table->add('title', html::label($field_id, rcube::Q($this->gettext('confpasswd'))));
48e9c1 239         $table->add(null, $input_confpasswd->show());
T 240
1c5fdd 241         $rules = '';
1a3132 242
1c5fdd 243         $required_length = intval($rcmail->config->get('password_minimum_length'));
1a3132 244         if ($required_length > 0) {
1c5fdd 245             $rules .= html::tag('li', array('id' => 'required-length'), $this->gettext(array(
1a3132 246                 'name' => 'passwordshort',
1c5fdd 247                 'vars' => array('length' => $required_length)
1a3132 248             )));
1c5fdd 249         }
1a3132 250
AM 251         if ($rcmail->config->get('password_require_nonalpha')) {
1c5fdd 252             $rules .= html::tag('li', array('id' => 'require-nonalpha'), $this->gettext('passwordweak'));
S 253         }
1a3132 254
AM 255         if (!empty($rules)) {
1c5fdd 256             $rules = html::tag('ul', array('id' => 'ruleslist'), $rules);
S 257         }
258
ed4f65 259         $disabled_msg = '';
252cc4 260         if ($form_disabled) {
AM 261             $disabled_msg = is_string($form_disabled) ? $form_disabled : $this->gettext('disablednotice');
262             $disabled_msg = html::div(array('class' => 'boxwarning', 'id' => 'password-notice'), $disabled_msg);
263         }
264
265         $submit_button = $rcmail->output->button(array(
266                 'command' => 'plugin.password-save',
267                 'type'    => 'input',
268                 'class'   => 'button mainaction',
269                 'label'   => 'save',
270         ));
271
48e9c1 272         $out = html::div(array('class' => 'box'),
252cc4 273             html::div(array('id' => 'prefs-title', 'class' => 'boxtitle'), $this->gettext('changepasswd'))
AM 274             . html::div(array('class' => 'boxcontent'),
275                 $disabled_msg . $table->show() . $rules . html::p(null, $submit_button)));
48e9c1 276
T 277         $rcmail->output->add_gui_object('passform', 'password-form');
278
252cc4 279         $this->include_script('password.js');
AM 280
48e9c1 281         return $rcmail->output->form_tag(array(
1a3132 282             'id'     => 'password-form',
AM 283             'name'   => 'password-form',
48e9c1 284             'method' => 'post',
T 285             'action' => './?_task=settings&_action=plugin.password-save',
286         ), $out);
287     }
288
289     private function _save($curpass, $passwd)
290     {
291         $config = rcmail::get_instance()->config;
292         $driver = $config->get('password_driver', 'sql');
293         $class  = "rcube_{$driver}_password";
294         $file   = $this->home . "/drivers/$driver.php";
295
296         if (!file_exists($file)) {
61be82 297             rcube::raise_error(array(
48e9c1 298                 'code' => 600,
T 299                 'type' => 'php',
300                 'file' => __FILE__, 'line' => __LINE__,
301                 'message' => "Password plugin: Unable to open driver file ($file)"
302             ), true, false);
303             return $this->gettext('internalerror');
304         }
305
306         include_once $file;
307
308         if (!class_exists($class, false) || !method_exists($class, 'save')) {
61be82 309             rcube::raise_error(array(
48e9c1 310                 'code' => 600,
T 311                 'type' => 'php',
312                 'file' => __FILE__, 'line' => __LINE__,
313                 'message' => "Password plugin: Broken driver $driver"
314             ), true, false);
315             return $this->gettext('internalerror');
316         }
317
318         $object = new $class;
319         $result = $object->save($curpass, $passwd);
ed4f65 320         $message = '';
48e9c1 321
T 322         if (is_array($result)) {
323             $message = $result['message'];
324             $result  = $result['code'];
325         }
326
327         switch ($result) {
328             case PASSWORD_SUCCESS:
329                 return;
54462b 330             case PASSWORD_CRYPT_ERROR:
48e9c1 331                 $reason = $this->gettext('crypterror');
306745 332                 break;
54462b 333             case PASSWORD_CONNECT_ERROR:
48e9c1 334                 $reason = $this->gettext('connecterror');
306745 335                 break;
48e9c1 336             case PASSWORD_ERROR:
T 337             default:
338                 $reason = $this->gettext('internalerror');
339         }
340
341         if ($message) {
342             $reason .= ' ' . $message;
343         }
344
345         return $reason;
346     }
4520fa 347
e7ee70 348     function user_create($args)
S 349     {
350         $this->newuser = true;
351         return $args;
352     }
4520fa 353
e7ee70 354     function login_after($args)
S 355     {
4520fa 356         if ($this->newuser && $this->check_host_login_exceptions()) {
AM 357             $args['_task']   = 'settings';
9e9c03 358             $args['_action'] = 'plugin.password';
4520fa 359             $args['_first']  = 'true';
e7ee70 360         }
4520fa 361
e7ee70 362         return $args;
S 363     }
4520fa 364
125142 365     // Check if host and login is allowed to change the password, false = not allowed, true = not allowed
S 366     private function check_host_login_exceptions()
367     {
368         $rcmail = rcmail::get_instance();
4520fa 369
125142 370         // Host exceptions
S 371         $hosts = $rcmail->config->get('password_hosts');
ab6fdf 372         if (!empty($hosts) && !in_array($_SESSION['storage_host'], (array) $hosts)) {
125142 373             return false;
S 374         }
375
376         // Login exceptions
377         if ($exceptions = $rcmail->config->get('password_login_exceptions')) {
378             $exceptions = array_map('trim', (array) $exceptions);
379             $exceptions = array_filter($exceptions);
380             $username   = $_SESSION['username'];
381
382             foreach ($exceptions as $ec) {
383                 if ($username === $ec) {
384                     return false;
385                 }
386             }
387         }
4520fa 388
125142 389         return true;
S 390     }
3cc6ec 391
AM 392     /**
393      * Hashes a password and returns the hash based on the specified method
394      *
395      * Parts of the code originally from the phpLDAPadmin development team
396      * http://phpldapadmin.sourceforge.net/
397      *
398      * @param string      Clear password
399      * @param string      Hashing method
400      * @param bool|string Prefix string or TRUE to add a default prefix
401      *
402      * @return string Hashed password
403      */
404     static function hash_password($password, $method = '', $prefixed = true)
405     {
406         $method = strtolower($method);
407         $rcmail = rcmail::get_instance();
ed4f65 408         $prefix = '';
H 409         $crypted = '';
410         $default = false;
3cc6ec 411
AM 412         if (empty($method) || $method == 'default') {
413             $method   = $rcmail->config->get('password_algorithm');
414             $prefixed = $rcmail->config->get('password_algorithm_prefix');
415             $default  = true;
416         }
417         else if ($method == 'crypt') { // deprecated
418             if (!($method = $rcmail->config->get('password_crypt_hash'))) {
419                 $method = 'md5';
420             }
421
422             if (!strpos($method, '-crypt')) {
423                 $method .= '-crypt';
424             }
425         }
426
427         switch ($method) {
428         case 'des':
429         case 'des-crypt':
430             $crypted = crypt($password, self::random_salt(2));
431             $prefix  = '{CRYPT}';
432             break;
433
434         case 'ext_des': // for BC
435         case 'ext-des-crypt':
436             $crypted = crypt($password, '_' . self::random_salt(8));
437             $prefix  = '{CRYPT}';
438             break;
439
440         case 'md5crypt': // for BC
441         case 'md5-crypt':
442             $crypted = crypt($password, '$1$' . self::random_salt(9));
443             $prefix  = '{CRYPT}';
444             break;
445
446         case 'sha256-crypt':
c10f97 447             $rounds = (int) $rcmail->config->get('password_crypt_rounds');
b92299 448             $prefix = '$5$';
AM 449
450             if ($rounds > 1000) {
451                 $prefix .= 'rounds=' . $rounds . '$';
452             }
453
c10f97 454             $crypted = crypt($password, $prefix . self::random_salt(16));
3cc6ec 455             $prefix  = '{CRYPT}';
AM 456             break;
457
458         case 'sha512-crypt':
c10f97 459             $rounds = (int) $rcmail->config->get('password_crypt_rounds');
b92299 460             $prefix = '$6$';
AM 461
462             if ($rounds > 1000) {
463                 $prefix .= 'rounds=' . $rounds . '$';
464             }
465
c10f97 466             $crypted = crypt($password, $prefix . self::random_salt(16));
3cc6ec 467             $prefix  = '{CRYPT}';
AM 468             break;
469
470         case 'blowfish': // for BC
471         case 'blowfish-crypt':
472             $cost   = (int) $rcmail->config->get('password_blowfish_cost');
473             $cost   = $cost < 4 || $cost > 31 ? 12 : $cost;
474             $prefix = sprintf('$2a$%02d$', $cost);
475
476             $crypted = crypt($password, $prefix . self::random_salt(22));
477             $prefix  = '{CRYPT}';
478             break;
479
480         case 'md5':
481             $crypted = base64_encode(pack('H*', md5($password)));
482             $prefix  = '{MD5}';
483             break;
484
485         case 'sha':
486             if (function_exists('sha1')) {
487                 $crypted = pack('H*', sha1($password));
488             }
489             else if (function_exists('hash')) {
490                 $crypted = hash('sha1', $password, true);
491             }
492             else if (function_exists('mhash')) {
493                 $crypted = mhash(MHASH_SHA1, $password);
494             }
495             else {
496                 rcube::raise_error(array(
497                     'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
498                     'message' => "Password plugin: Your PHP install does not have the mhash()/hash() nor sha1() function"
499                 ), true, true);
500             }
501
502             $crypted = base64_encode($crypted);
503             $prefix = '{SHA}';
504             break;
505
506         case 'ssha':
507             $salt = substr(pack('h*', md5(mt_rand())), 0, 8);
508
509             if (function_exists('mhash') && function_exists('mhash_keygen_s2k')) {
510                 $salt    = mhash_keygen_s2k(MHASH_SHA1, $password, $salt, 4);
511                 $crypted = mhash(MHASH_SHA1, $password . $salt);
512             }
513             else if (function_exists('sha1')) {
514                 $salt    = substr(pack("H*", sha1($salt . $password)), 0, 4);
515                 $crypted = sha1($password . $salt, true);
516             }
517             else if (function_exists('hash')) {
518                 $salt    = substr(pack("H*", hash('sha1', $salt . $password)), 0, 4);
519                 $crypted = hash('sha1', $password . $salt, true);
520             }
521             else {
522                 rcube::raise_error(array(
523                     'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
524                     'message' => "Password plugin: Your PHP install does not have the mhash()/hash() nor sha1() function"
525                 ), true, true);
526             }
527
528             $crypted = base64_encode($crypted . $salt);
529             $prefix  = '{SSHA}';
530             break;
531
532         case 'smd5':
533             $salt = substr(pack('h*', md5(mt_rand())), 0, 8);
534
535             if (function_exists('mhash') && function_exists('mhash_keygen_s2k')) {
536                 $salt    = mhash_keygen_s2k(MHASH_MD5, $password, $salt, 4);
537                 $crypted = mhash(MHASH_MD5, $password . $salt);
538             }
539             else if (function_exists('hash')) {
540                 $salt    = substr(pack("H*", hash('md5', $salt . $password)), 0, 4);
541                 $crypted = hash('md5', $password . $salt, true);
542             }
543             else {
544                 $salt    = substr(pack("H*", md5($salt . $password)), 0, 4);
545                 $crypted = md5($password . $salt, true);
546             }
547
548             $crypted = base64_encode($crypted . $salt);
549             $prefix  = '{SMD5}';
550             break;
551
552         case 'samba':
553             if (function_exists('hash')) {
554                 $crypted = hash('md4', rcube_charset::convert($password, RCUBE_CHARSET, 'UTF-16LE'));
ed4f65 555                 $crypted = strtoupper($crypted);
3cc6ec 556             }
AM 557             else {
558                 rcube::raise_error(array(
559                     'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
560                     'message' => "Password plugin: Your PHP install does not have hash() function"
561                 ), true, true);
562             }
563             break;
564
565         case 'ad':
566             $crypted = rcube_charset::convert('"' . $password . '"', RCUBE_CHARSET, 'UTF-16LE');
567             break;
568
569         case 'cram-md5': // deprecated
570             require_once __DIR__ . '/../helpers/dovecot_hmacmd5.php';
571             $crypted = dovecot_hmacmd5($password);
572             $prefix  = '{CRAM-MD5}';
573             break;
574
575         case 'dovecot':
576             if (!($dovecotpw = $rcmail->config->get('password_dovecotpw'))) {
577                 $dovecotpw = 'dovecotpw';
578             }
579             if (!($method = $rcmail->config->get('password_dovecotpw_method'))) {
580                 $method = 'CRAM-MD5';
581             }
582
583             // use common temp dir
584             $tmp_dir = $rcmail->config->get('temp_dir');
585             $tmpfile = tempnam($tmp_dir, 'roundcube-');
586
587             $pipe = popen("$dovecotpw -s '$method' > '$tmpfile'", "w");
588             if (!$pipe) {
589                 unlink($tmpfile);
590                 return false;
591             }
592             else {
ed4f65 593                 fwrite($pipe, $password . "\n", 1+strlen($password)); usleep(1000);
H 594                 fwrite($pipe, $password . "\n", 1+strlen($password));
3cc6ec 595                 pclose($pipe);
AM 596
597                 $crypted = trim(file_get_contents($tmpfile), "\n");
598                 unlink($tmpfile);
599
ed4f65 600                 if (!preg_match('/^\{' . $method . '\}/', $crypted)) {
3cc6ec 601                     return false;
AM 602                 }
603
604                 if (!$default) {
605                     $prefixed = (bool) $rcmail->config->get('password_dovecotpw_with_method');
606                 }
607
608                 if (!$prefixed) {
609                     $crypted = trim(str_replace('{' . $method . '}', '', $crypted));
610                 }
611
612                 $prefixed = false;
613             }
614
615             break;
616
617         case 'hash': // deprecated
618             if (!extension_loaded('hash')) {
619                 rcube::raise_error(array(
620                     'code' => 600, 'file' => __FILE__, 'line' => __LINE__,
621                     'message' => "Password plugin: 'hash' extension not loaded!"
622                 ), true, true);
623             }
624
625             if (!($hash_algo = strtolower($rcmail->config->get('password_hash_algorithm')))) {
626                 $hash_algo = 'sha1';
627             }
628
629             $crypted = hash($hash_algo, $password);
630
631             if ($rcmail->config->get('password_hash_base64')) {
632                 $crypted = base64_encode(pack('H*', $crypted));
633             }
634
635             break;
636
637         case 'clear':
638             $crypted = $password;
639         }
640
641         if ($crypted === null || $crypted === false) {
642             return false;
643         }
644
645         if ($prefixed && $prefixed !== true) {
646             $prefix   = $prefixed;
647             $prefixed = true;
648         }
649
650         if ($prefixed === true && $prefix) {
651             $crypted = $prefix . $crypted;
652         }
653
654         return $crypted;
655     }
656
657     /**
658      * Used to generate a random salt for crypt-style passwords
659      *
660      * Code originaly from the phpLDAPadmin development team
661      * http://phpldapadmin.sourceforge.net/
662      */
663     static function random_salt($length)
664     {
665         $possible = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./';
666         $str      = '';
667
668         while (strlen($str) < $length) {
669             $str .= substr($possible, (rand() % strlen($possible)), 1);
670         }
671
672         return $str;
673     }
48e9c1 674 }