From bd0551b22076b82a6d49e9f7a2b2e0c90a1b2326 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak <alec@alec.pl> Date: Fri, 05 Feb 2016 07:25:27 -0500 Subject: [PATCH] Secure also downloads of addressbook exports, managesieve script exports and Enigma keys exports --- plugins/password/password.php | 348 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 files changed, 326 insertions(+), 22 deletions(-) diff --git a/plugins/password/password.php b/plugins/password/password.php index 9239cd0..f84dc8f 100644 --- a/plugins/password/password.php +++ b/plugins/password/password.php @@ -6,7 +6,7 @@ * @version @package_version@ * @author Aleksander Machniak <alec@alec.pl> * - * Copyright (C) 2005-2013, The Roundcube Dev Team + * Copyright (C) 2005-2015, The Roundcube Dev Team * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -63,10 +63,6 @@ $this->register_action('plugin.password', array($this, 'password_init')); $this->register_action('plugin.password-save', array($this, 'password_save')); - - if (strpos($rcmail->action, 'plugin.password') === 0) { - $this->include_script('password.js'); - } } else if ($rcmail->config->get('password_force_new_user')) { $this->add_hook('user_create', array($this, 'user_create')); @@ -109,7 +105,8 @@ $rcmail = rcmail::get_instance(); $rcmail->output->set_pagetitle($this->gettext('changepasswd')); - $confirm = $rcmail->config->get('password_confirm_current'); + $form_disabled = $rcmail->config->get('password_disabled'); + $confirm = $rcmail->config->get('password_confirm_current'); $required_length = intval($rcmail->config->get('password_minimum_length')); $check_strength = $rcmail->config->get('password_require_nonalpha'); @@ -196,15 +193,22 @@ 'password.passwordinconsistency' ); + $form_disabled = $rcmail->config->get('password_disabled'); + $rcmail->output->set_env('product_name', $rcmail->config->get('product_name')); + $rcmail->output->set_env('password_disabled', !empty($form_disabled)); $table = new html_table(array('cols' => 2)); if ($rcmail->config->get('password_confirm_current')) { // show current password selection $field_id = 'curpasswd'; - $input_curpasswd = new html_passwordfield(array('name' => '_curpasswd', 'id' => $field_id, - 'size' => 20, 'autocomplete' => 'off')); + $input_curpasswd = new html_passwordfield(array( + 'name' => '_curpasswd', + 'id' => $field_id, + 'size' => 20, + 'autocomplete' => 'off', + )); $table->add('title', html::label($field_id, rcube::Q($this->gettext('curpasswd')))); $table->add(null, $input_curpasswd->show()); @@ -212,16 +216,24 @@ // show new password selection $field_id = 'newpasswd'; - $input_newpasswd = new html_passwordfield(array('name' => '_newpasswd', 'id' => $field_id, - 'size' => 20, 'autocomplete' => 'off')); + $input_newpasswd = new html_passwordfield(array( + 'name' => '_newpasswd', + 'id' => $field_id, + 'size' => 20, + 'autocomplete' => 'off', + )); $table->add('title', html::label($field_id, rcube::Q($this->gettext('newpasswd')))); $table->add(null, $input_newpasswd->show()); // show confirm password selection $field_id = 'confpasswd'; - $input_confpasswd = new html_passwordfield(array('name' => '_confpasswd', 'id' => $field_id, - 'size' => 20, 'autocomplete' => 'off')); + $input_confpasswd = new html_passwordfield(array( + 'name' => '_confpasswd', + 'id' => $field_id, + 'size' => 20, + 'autocomplete' => 'off', + )); $table->add('title', html::label($field_id, rcube::Q($this->gettext('confpasswd')))); $table->add(null, $input_confpasswd->show()); @@ -244,19 +256,27 @@ $rules = html::tag('ul', array('id' => 'ruleslist'), $rules); } + $disabled_msg = ''; + if ($form_disabled) { + $disabled_msg = is_string($form_disabled) ? $form_disabled : $this->gettext('disablednotice'); + $disabled_msg = html::div(array('class' => 'boxwarning', 'id' => 'password-notice'), $disabled_msg); + } + + $submit_button = $rcmail->output->button(array( + 'command' => 'plugin.password-save', + 'type' => 'input', + 'class' => 'button mainaction', + 'label' => 'save', + )); + $out = html::div(array('class' => 'box'), - html::div(array('id' => 'prefs-title', 'class' => 'boxtitle'), $this->gettext('changepasswd')) . - html::div(array('class' => 'boxcontent'), $table->show() . - $rules . - html::p(null, - $rcmail->output->button(array( - 'command' => 'plugin.password-save', - 'type' => 'input', - 'class' => 'button mainaction', - 'label' => 'save' - ))))); + html::div(array('id' => 'prefs-title', 'class' => 'boxtitle'), $this->gettext('changepasswd')) + . html::div(array('class' => 'boxcontent'), + $disabled_msg . $table->show() . $rules . html::p(null, $submit_button))); $rcmail->output->add_gui_object('passform', 'password-form'); + + $this->include_script('password.js'); return $rcmail->output->form_tag(array( 'id' => 'password-form', @@ -297,6 +317,7 @@ $object = new $class; $result = $object->save($curpass, $passwd); + $message = ''; if (is_array($result)) { $message = $result['message']; @@ -367,4 +388,287 @@ return true; } + + /** + * Hashes a password and returns the hash based on the specified method + * + * Parts of the code originally from the phpLDAPadmin development team + * http://phpldapadmin.sourceforge.net/ + * + * @param string Clear password + * @param string Hashing method + * @param bool|string Prefix string or TRUE to add a default prefix + * + * @return string Hashed password + */ + static function hash_password($password, $method = '', $prefixed = true) + { + $method = strtolower($method); + $rcmail = rcmail::get_instance(); + $prefix = ''; + $crypted = ''; + $default = false; + + if (empty($method) || $method == 'default') { + $method = $rcmail->config->get('password_algorithm'); + $prefixed = $rcmail->config->get('password_algorithm_prefix'); + $default = true; + } + else if ($method == 'crypt') { // deprecated + if (!($method = $rcmail->config->get('password_crypt_hash'))) { + $method = 'md5'; + } + + if (!strpos($method, '-crypt')) { + $method .= '-crypt'; + } + } + + switch ($method) { + case 'des': + case 'des-crypt': + $crypted = crypt($password, self::random_salt(2)); + $prefix = '{CRYPT}'; + break; + + case 'ext_des': // for BC + case 'ext-des-crypt': + $crypted = crypt($password, '_' . self::random_salt(8)); + $prefix = '{CRYPT}'; + break; + + case 'md5crypt': // for BC + case 'md5-crypt': + $crypted = crypt($password, '$1$' . self::random_salt(9)); + $prefix = '{CRYPT}'; + break; + + case 'sha256-crypt': + $rounds = (int) $rcmail->config->get('password_crypt_rounds'); + $prefix = '$5$'; + + if ($rounds > 1000) { + $prefix .= 'rounds=' . $rounds . '$'; + } + + $crypted = crypt($password, $prefix . self::random_salt(16)); + $prefix = '{CRYPT}'; + break; + + case 'sha512-crypt': + $rounds = (int) $rcmail->config->get('password_crypt_rounds'); + $prefix = '$6$'; + + if ($rounds > 1000) { + $prefix .= 'rounds=' . $rounds . '$'; + } + + $crypted = crypt($password, $prefix . self::random_salt(16)); + $prefix = '{CRYPT}'; + break; + + case 'blowfish': // for BC + case 'blowfish-crypt': + $cost = (int) $rcmail->config->get('password_blowfish_cost'); + $cost = $cost < 4 || $cost > 31 ? 12 : $cost; + $prefix = sprintf('$2a$%02d$', $cost); + + $crypted = crypt($password, $prefix . self::random_salt(22)); + $prefix = '{CRYPT}'; + break; + + case 'md5': + $crypted = base64_encode(pack('H*', md5($password))); + $prefix = '{MD5}'; + break; + + case 'sha': + if (function_exists('sha1')) { + $crypted = pack('H*', sha1($password)); + } + else if (function_exists('hash')) { + $crypted = hash('sha1', $password, true); + } + else if (function_exists('mhash')) { + $crypted = mhash(MHASH_SHA1, $password); + } + else { + rcube::raise_error(array( + 'code' => 600, 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Password plugin: Your PHP install does not have the mhash()/hash() nor sha1() function" + ), true, true); + } + + $crypted = base64_encode($crypted); + $prefix = '{SHA}'; + break; + + case 'ssha': + $salt = substr(pack('h*', md5(mt_rand())), 0, 8); + + if (function_exists('mhash') && function_exists('mhash_keygen_s2k')) { + $salt = mhash_keygen_s2k(MHASH_SHA1, $password, $salt, 4); + $crypted = mhash(MHASH_SHA1, $password . $salt); + } + else if (function_exists('sha1')) { + $salt = substr(pack("H*", sha1($salt . $password)), 0, 4); + $crypted = sha1($password . $salt, true); + } + else if (function_exists('hash')) { + $salt = substr(pack("H*", hash('sha1', $salt . $password)), 0, 4); + $crypted = hash('sha1', $password . $salt, true); + } + else { + rcube::raise_error(array( + 'code' => 600, 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Password plugin: Your PHP install does not have the mhash()/hash() nor sha1() function" + ), true, true); + } + + $crypted = base64_encode($crypted . $salt); + $prefix = '{SSHA}'; + break; + + case 'smd5': + $salt = substr(pack('h*', md5(mt_rand())), 0, 8); + + if (function_exists('mhash') && function_exists('mhash_keygen_s2k')) { + $salt = mhash_keygen_s2k(MHASH_MD5, $password, $salt, 4); + $crypted = mhash(MHASH_MD5, $password . $salt); + } + else if (function_exists('hash')) { + $salt = substr(pack("H*", hash('md5', $salt . $password)), 0, 4); + $crypted = hash('md5', $password . $salt, true); + } + else { + $salt = substr(pack("H*", md5($salt . $password)), 0, 4); + $crypted = md5($password . $salt, true); + } + + $crypted = base64_encode($crypted . $salt); + $prefix = '{SMD5}'; + break; + + case 'samba': + if (function_exists('hash')) { + $crypted = hash('md4', rcube_charset::convert($password, RCUBE_CHARSET, 'UTF-16LE')); + $crypted = strtoupper($crypted); + } + else { + rcube::raise_error(array( + 'code' => 600, 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Password plugin: Your PHP install does not have hash() function" + ), true, true); + } + break; + + case 'ad': + $crypted = rcube_charset::convert('"' . $password . '"', RCUBE_CHARSET, 'UTF-16LE'); + break; + + case 'cram-md5': // deprecated + require_once __DIR__ . '/../helpers/dovecot_hmacmd5.php'; + $crypted = dovecot_hmacmd5($password); + $prefix = '{CRAM-MD5}'; + break; + + case 'dovecot': + if (!($dovecotpw = $rcmail->config->get('password_dovecotpw'))) { + $dovecotpw = 'dovecotpw'; + } + if (!($method = $rcmail->config->get('password_dovecotpw_method'))) { + $method = 'CRAM-MD5'; + } + + // use common temp dir + $tmp_dir = $rcmail->config->get('temp_dir'); + $tmpfile = tempnam($tmp_dir, 'roundcube-'); + + $pipe = popen("$dovecotpw -s '$method' > '$tmpfile'", "w"); + if (!$pipe) { + unlink($tmpfile); + return false; + } + else { + fwrite($pipe, $password . "\n", 1+strlen($password)); usleep(1000); + fwrite($pipe, $password . "\n", 1+strlen($password)); + pclose($pipe); + + $crypted = trim(file_get_contents($tmpfile), "\n"); + unlink($tmpfile); + + if (!preg_match('/^\{' . $method . '\}/', $crypted)) { + return false; + } + + if (!$default) { + $prefixed = (bool) $rcmail->config->get('password_dovecotpw_with_method'); + } + + if (!$prefixed) { + $crypted = trim(str_replace('{' . $method . '}', '', $crypted)); + } + + $prefixed = false; + } + + break; + + case 'hash': // deprecated + if (!extension_loaded('hash')) { + rcube::raise_error(array( + 'code' => 600, 'file' => __FILE__, 'line' => __LINE__, + 'message' => "Password plugin: 'hash' extension not loaded!" + ), true, true); + } + + if (!($hash_algo = strtolower($rcmail->config->get('password_hash_algorithm')))) { + $hash_algo = 'sha1'; + } + + $crypted = hash($hash_algo, $password); + + if ($rcmail->config->get('password_hash_base64')) { + $crypted = base64_encode(pack('H*', $crypted)); + } + + break; + + case 'clear': + $crypted = $password; + } + + if ($crypted === null || $crypted === false) { + return false; + } + + if ($prefixed && $prefixed !== true) { + $prefix = $prefixed; + $prefixed = true; + } + + if ($prefixed === true && $prefix) { + $crypted = $prefix . $crypted; + } + + return $crypted; + } + + /** + * Used to generate a random salt for crypt-style passwords + * + * Code originaly from the phpLDAPadmin development team + * http://phpldapadmin.sourceforge.net/ + */ + static function random_salt($length) + { + $possible = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ./'; + $str = ''; + + while (strlen($str) < $length) { + $str .= substr($possible, (rand() % strlen($possible)), 1); + } + + return $str; + } } -- Gitblit v1.9.1