0
|
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 }
|