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