From 3cc6ec573d93ed05adf98d69a6fab6e38c776731 Mon Sep 17 00:00:00 2001 From: Aleksander Machniak <alec@alec.pl> Date: Wed, 27 May 2015 07:37:10 -0400 Subject: [PATCH] Make password encryption algorithms available for all drivers (#1490134) --- plugins/password/password.php | 266 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 266 insertions(+), 0 deletions(-) diff --git a/plugins/password/password.php b/plugins/password/password.php index 9239cd0..476e9ea 100644 --- a/plugins/password/password.php +++ b/plugins/password/password.php @@ -367,4 +367,270 @@ 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(); + + 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': + $crypted = crypt($password, '$5$' . self::random_salt(16)); + $prefix = '{CRYPT}'; + break; + + case 'sha512-crypt': + $crypted = crypt($password, '$6$' . 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_password); + } + 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, $passwd . "\n", 1+strlen($passwd)); usleep(1000); + fwrite($pipe, $passwd . "\n", 1+strlen($passwd)); + pclose($pipe); + + $crypted = trim(file_get_contents($tmpfile), "\n"); + unlink($tmpfile); + + if (!preg_match('/^\{' . $method . '\}/', $newpass)) { + 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