Page Menu
Home
Phorge
Search
Configure Global Search
Log In
Files
F2530265
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Flag For Later
Award Token
Size
81 KB
Referenced Files
None
Subscribers
None
View Options
diff --git a/plugins/password/composer.json b/plugins/password/composer.json
index 3aba2a2ba..743d15e10 100644
--- a/plugins/password/composer.json
+++ b/plugins/password/composer.json
@@ -1,24 +1,24 @@
{
"name": "roundcube/password",
"type": "roundcube-plugin",
"description": "Password Change for Roundcube. Plugin adds a possibility to change user password using many methods (drivers) via Settings/Password tab.",
"license": "GPLv3+",
- "version": "3.5",
+ "version": "4.0",
"authors": [
{
"name": "Aleksander Machniak",
"email": "alec@alec.pl",
"role": "Lead"
}
],
"repositories": [
{
"type": "composer",
"url": "http://plugins.roundcube.net"
}
],
"require": {
"php": ">=5.3.0",
"roundcube/plugin-installer": ">=0.1.3"
}
}
diff --git a/plugins/password/config.inc.php.dist b/plugins/password/config.inc.php.dist
index af87a8361..0b004b876 100644
--- a/plugins/password/config.inc.php.dist
+++ b/plugins/password/config.inc.php.dist
@@ -1,406 +1,420 @@
<?php
// Password Plugin options
// -----------------------
// A driver to use for password change. Default: "sql".
// See README file for list of supported driver names.
$config['password_driver'] = 'sql';
// Determine whether current password is required to change password.
// Default: false.
$config['password_confirm_current'] = true;
// Require the new password to be a certain length.
// set to blank to allow passwords of any length
$config['password_minimum_length'] = 0;
// Require the new password to contain a letter and punctuation character
// Change to false to remove this check.
$config['password_require_nonalpha'] = false;
// Enables logging of password changes into logs/password
$config['password_log'] = false;
// Comma-separated list of login exceptions for which password change
// will be not available (no Password tab in Settings)
$config['password_login_exceptions'] = null;
// Array of hosts that support password changing. Default is NULL.
// Listed hosts will feature a Password option in Settings; others will not.
-// Example:
-//$config['password_hosts'] = array('mail.example.com', 'mail2.example.org');
+// Example: array('mail.example.com', 'mail2.example.org');
$config['password_hosts'] = null;
// Enables saving the new password even if it matches the old password. Useful
// for upgrading the stored passwords after the encryption scheme has changed.
$config['password_force_save'] = false;
// Enables forcing new users to change their password at their first login.
$config['password_force_new_user'] = false;
+// Default password hashing/crypting algorithm.
+// Possible options: des-crypt, ext-des-crypt, md5-crypt, blowfish-crypt,
+// sha256-crypt, sha512-crypt, md5, sha, smd5, ssha, samba, ad, dovecot, clear.
+// For details see password::hash_password() method.
+$config['password_algorithm'] = 'clear';
+
+// Password prefix (e.g. {CRYPT}, {SHA}) for passwords generated
+// using password_algorithm above. Default: empty.
+$config['password_algorithm_prefix'] = '';
+
+// Path for dovecotpw/doveadm-pw (if not in $PATH)
+// Used for password_algorithm = 'dovecot'.
+// $config['password_dovecotpw'] = '/usr/local/sbin/dovecotpw';
+
+// Dovecot method (dovecotpw -s 'method')
+// Used for password_algorithm = 'dovecot'.
+$config['password_dovecotpw_method'] = 'CRAM-MD5';
+
+// Iteration count parameter for Blowfish-based hashing algo.
+// It must be between 4 and 31. Default: 12.
+// Be aware, the higher the value, the longer it takes to generate the password hashes.
+$config['password_blowfish_cost'] = 12;
+
+
// SQL Driver options
// ------------------
// PEAR database DSN for performing the query. By default
// Roundcube DB settings are used.
$config['password_db_dsn'] = '';
// The SQL query used to change the password.
// The query can contain the following macros that will be expanded as follows:
// %p is replaced with the plaintext new password
-// %c is replaced with the crypt version of the new password, MD5 if available
-// otherwise DES. More hash function can be enabled using the password_crypt_hash
-// configuration parameter.
-// %D is replaced with the dovecotpw-crypted version of the new password
-// %o is replaced with the password before the change
-// %n is replaced with the hashed version of the new password
-// %q is replaced with the hashed password before the change
+// %P is replaced with the crypted/hashed new password
+// according to configured password_method
+// %o is replaced with the old (current) password
+// %O is replaced with the crypted/hashed old (current) password
+// according to configured password_method
// %h is replaced with the imap host (from the session info)
// %u is replaced with the username (from the session info)
// %l is replaced with the local part of the username
// (in case the username is an email address)
// %d is replaced with the domain part of the username
// (in case the username is an email address)
+// Deprecated macros:
+// %c is replaced with the crypt version of the new password, MD5 if available
+// otherwise DES. More hash function can be enabled using the password_crypt_hash
+// configuration parameter.
+// %D is replaced with the dovecotpw-crypted version of the new password
+// %n is replaced with the hashed version of the new password
+// %q is replaced with the hashed password before the change
// Escaping of macros is handled by this module.
// Default: "SELECT update_passwd(%c, %u)"
$config['password_query'] = 'SELECT update_passwd(%c, %u)';
-// By default the crypt() function which is used to create the '%c'
-// parameter uses the md5 algorithm. To use different algorithms
-// you can choose between: des, md5, blowfish, sha256, sha512.
-// Before using other hash functions than des or md5 please make sure
-// your operating system supports the other hash functions.
+// By default the crypt() function which is used to create the %c
+// parameter uses the md5 algorithm (deprecated, use %P).
+// You can choose between: des, md5, blowfish, sha256, sha512.
$config['password_crypt_hash'] = 'md5';
// By default domains in variables are using unicode.
// Enable this option to use punycoded names
$config['password_idn_ascii'] = false;
-// Path for dovecotpw (if not in $PATH)
-// $config['password_dovecotpw'] = '/usr/local/sbin/dovecotpw';
-
-// Dovecot method (dovecotpw -s 'method')
-$config['password_dovecotpw_method'] = 'CRAM-MD5';
-
// Enables use of password with crypt method prefix in %D, e.g. {MD5}$1$LUiMYWqx$fEkg/ggr/L6Mb2X7be4i1/
+// when using the %D macro (deprecated, use %P)
$config['password_dovecotpw_with_method'] = false;
-// Using a password hash for %n and %q variables.
+// Using a password hash for %n and %q variables (deprecated, use %P).
// Determine which hashing algorithm should be used to generate
// the hashed new and current password for using them within the
// SQL query. Requires PHP's 'hash' extension.
$config['password_hash_algorithm'] = 'sha1';
// You can also decide whether the hash should be provided
// as hex string or in base64 encoded format.
$config['password_hash_base64'] = false;
-// Iteration count parameter for Blowfish-based hashing algo.
-// It must be between 4 and 31. Default: 12.
-// Be aware, the higher the value, the longer it takes to generate the password hashes.
-$config['password_blowfish_cost'] = 12;
-
// Poppassd Driver options
// -----------------------
// The host which changes the password
$config['password_pop_host'] = 'localhost';
// TCP port used for poppassd connections
$config['password_pop_port'] = 106;
// SASL Driver options
// -------------------
// Additional arguments for the saslpasswd2 call
$config['password_saslpasswd_args'] = '';
// LDAP and LDAP_SIMPLE Driver options
// -----------------------------------
// LDAP server name to connect to.
// You can provide one or several hosts in an array in which case the hosts are tried from left to right.
// Exemple: array('ldap1.exemple.com', 'ldap2.exemple.com');
// Default: 'localhost'
$config['password_ldap_host'] = 'localhost';
// LDAP server port to connect to
// Default: '389'
$config['password_ldap_port'] = '389';
// TLS is started after connecting
// Using TLS for password modification is recommanded.
// Default: false
$config['password_ldap_starttls'] = false;
// LDAP version
// Default: '3'
$config['password_ldap_version'] = '3';
// LDAP base name (root directory)
// Exemple: 'dc=exemple,dc=com'
$config['password_ldap_basedn'] = 'dc=exemple,dc=com';
// LDAP connection method
// There is two connection method for changing a user's LDAP password.
// 'user': use user credential (recommanded, require password_confirm_current=true)
// 'admin': use admin credential (this mode require password_ldap_adminDN and password_ldap_adminPW)
// Default: 'user'
$config['password_ldap_method'] = 'user';
// LDAP Admin DN
// Used only in admin connection mode
// Default: null
$config['password_ldap_adminDN'] = null;
// LDAP Admin Password
// Used only in admin connection mode
// Default: null
$config['password_ldap_adminPW'] = null;
// LDAP user DN mask
// The user's DN is mandatory and as we only have his login,
// we need to re-create his DN using a mask
// '%login' will be replaced by the current roundcube user's login
// '%name' will be replaced by the current roundcube user's name part
// '%domain' will be replaced by the current roundcube user's domain part
// '%dc' will be replaced by domain name hierarchal string e.g. "dc=test,dc=domain,dc=com"
// Exemple: 'uid=%login,ou=people,dc=exemple,dc=com'
$config['password_ldap_userDN_mask'] = 'uid=%login,ou=people,dc=exemple,dc=com';
// LDAP search DN
// The DN roundcube should bind with to find out user's DN
// based on his login. Note that you should comment out the default
// password_ldap_userDN_mask setting for this to take effect.
// Use this if you cannot specify a general template for user DN with
// password_ldap_userDN_mask. You need to perform a search based on
// users login to find his DN instead. A common reason might be that
// your users are placed under different ou's like engineering or
// sales which cannot be derived from their login only.
$config['password_ldap_searchDN'] = 'cn=roundcube,ou=services,dc=example,dc=com';
// LDAP search password
// If password_ldap_searchDN is set, the password to use for
// binding to search for user's DN. Note that you should comment out the default
// password_ldap_userDN_mask setting for this to take effect.
// Warning: Be sure to set approperiate permissions on this file so this password
// is only accesible to roundcube and don't forget to restrict roundcube's access to
// your directory as much as possible using ACLs. Should this password be compromised
// you want to minimize the damage.
$config['password_ldap_searchPW'] = 'secret';
// LDAP search base
// If password_ldap_searchDN is set, the base to search in using the filter below.
// Note that you should comment out the default password_ldap_userDN_mask setting
// for this to take effect.
$config['password_ldap_search_base'] = 'ou=people,dc=example,dc=com';
// LDAP search filter
// If password_ldap_searchDN is set, the filter to use when
// searching for user's DN. Note that you should comment out the default
// password_ldap_userDN_mask setting for this to take effect.
// '%login' will be replaced by the current roundcube user's login
// '%name' will be replaced by the current roundcube user's name part
// '%domain' will be replaced by the current roundcube user's domain part
// '%dc' will be replaced by domain name hierarchal string e.g. "dc=test,dc=domain,dc=com"
// Example: '(uid=%login)'
// Example: '(&(objectClass=posixAccount)(uid=%login))'
$config['password_ldap_search_filter'] = '(uid=%login)';
// LDAP password hash type
// Standard LDAP encryption type which must be one of: crypt,
// ext_des, md5crypt, blowfish, md5, sha, smd5, ssha, ad, cram-md5 (dovecot style) or clear.
-// Please note that most encodage types require external libraries
-// to be included in your PHP installation, see function hashPassword in drivers/ldap.php for more info.
+// Set to 'default' if you want to use method specified in password_algorithm option above.
// Multiple password Values can be generated by concatenating encodings with a +. E.g. 'cram-md5+crypt'
// Default: 'crypt'.
$config['password_ldap_encodage'] = 'crypt';
// LDAP password attribute
// Name of the ldap's attribute used for storing user password
// Default: 'userPassword'
$config['password_ldap_pwattr'] = 'userPassword';
// LDAP password force replace
// Force LDAP replace in cases where ACL allows only replace not read
// See http://pear.php.net/package/Net_LDAP2/docs/latest/Net_LDAP2/Net_LDAP2_Entry.html#methodreplace
// Default: true
$config['password_ldap_force_replace'] = true;
// LDAP Password Last Change Date
// Some places use an attribute to store the date of the last password change
// The date is meassured in "days since epoch" (an integer value)
// Whenever the password is changed, the attribute will be updated if set (e.g. shadowLastChange)
$config['password_ldap_lchattr'] = '';
// LDAP Samba password attribute, e.g. sambaNTPassword
// Name of the LDAP's Samba attribute used for storing user password
$config['password_ldap_samba_pwattr'] = '';
// LDAP Samba Password Last Change Date attribute, e.g. sambaPwdLastSet
// Some places use an attribute to store the date of the last password change
// The date is meassured in "seconds since epoch" (an integer value)
// Whenever the password is changed, the attribute will be updated if set
$config['password_ldap_samba_lchattr'] = '';
// DirectAdmin Driver options
// --------------------------
// The host which changes the password
// Use 'ssl://host' instead of 'tcp://host' when running DirectAdmin over SSL.
// The host can contain the following macros that will be expanded as follows:
// %h is replaced with the imap host (from the session info)
// %d is replaced with the domain part of the username (if the username is an email)
$config['password_directadmin_host'] = 'tcp://localhost';
// TCP port used for DirectAdmin connections
$config['password_directadmin_port'] = 2222;
// vpopmaild Driver options
// -----------------------
// The host which changes the password
$config['password_vpopmaild_host'] = 'localhost';
// TCP port used for vpopmaild connections
$config['password_vpopmaild_port'] = 89;
// Timout used for the connection to vpopmaild (in seconds)
$config['password_vpopmaild_timeout'] = 10;
// cPanel Driver options
// --------------------------
// The cPanel Host name
$config['password_cpanel_host'] = 'host.domain.com';
// The cPanel admin username
$config['password_cpanel_username'] = 'username';
// The cPanel admin password
$config['password_cpanel_password'] = 'password';
// The cPanel port to use
$config['password_cpanel_port'] = 2087;
// XIMSS (Communigate server) Driver options
// -----------------------------------------
// Host name of the Communigate server
$config['password_ximss_host'] = 'mail.example.com';
// XIMSS port on Communigate server
$config['password_ximss_port'] = 11024;
// chpasswd Driver options
// ---------------------
// Command to use (see "Sudo setup" in README)
$config['password_chpasswd_cmd'] = 'sudo /usr/sbin/chpasswd 2> /dev/null';
// XMail Driver options
// ---------------------
$config['xmail_host'] = 'localhost';
$config['xmail_user'] = 'YourXmailControlUser';
$config['xmail_pass'] = 'YourXmailControlPass';
$config['xmail_port'] = 6017;
// hMail Driver options
// -----------------------
// Remote hMailServer configuration
// true: HMailserver is on a remote box (php.ini: com.allow_dcom = true)
// false: Hmailserver is on same box as PHP
$config['hmailserver_remote_dcom'] = false;
// Windows credentials
$config['hmailserver_server'] = array(
- 'Server' => 'localhost', // hostname or ip address
- 'Username' => 'administrator', // windows username
- 'Password' => 'password' // windows user password
+ 'Server' => 'localhost', // hostname or ip address
+ 'Username' => 'administrator', // windows username
+ 'Password' => 'password' // windows user password
);
// Virtualmin Driver options
// -------------------------
// Username format:
// 0: username@domain
// 1: username%domain
// 2: username.domain
// 3: domain.username
// 4: username-domain
// 5: domain-username
// 6: username_domain
// 7: domain_username
$config['password_virtualmin_format'] = 0;
// pw_usermod Driver options
// --------------------------
// Use comma delimited exlist to disable password change for users.
// See "Sudo setup" in README file.
$config['password_pw_usermod_cmd'] = 'sudo /usr/sbin/pw usermod -h 0 -n';
// DBMail Driver options
// -------------------
// Additional arguments for the dbmail-users call
$config['password_dbmail_args'] = '-p sha512';
// Expect Driver options
// ---------------------
// Location of expect binary
$config['password_expect_bin'] = '/usr/bin/expect';
// Location of expect script (see helpers/passwd-expect)
$config['password_expect_script'] = '';
// Arguments for the expect script. See the helpers/passwd-expect file for details.
// This is probably a good starting default:
// -telent -host localhost -output /tmp/passwd.log -log /tmp/passwd.log
$config['password_expect_params'] = '';
// smb Driver options
// ---------------------
// Samba host (default: localhost)
// Supported replacement variables:
// %n - hostname ($_SERVER['SERVER_NAME'])
// %t - hostname without the first part
// %d - domain (http hostname $_SERVER['HTTP_HOST'] without the first part)
$config['password_smb_host'] = 'localhost';
// Location of smbpasswd binary
$config['password_smb_cmd'] = '/usr/bin/smbpasswd';
// gearman driver options
// ---------------------
// Gearman host (default: localhost)
$config['password_gearman_host'] = 'localhost';
-
// Plesk/PPA Driver options
// --------------------
// You need to allow RCP for IP of roundcube-server in Plesk/PPA Panel
// Plesk RCP Host
$config['password_plesk_host'] = '10.0.0.5';
// Plesk RPC Username
$config['password_plesk_user'] = 'admin';
// Plesk RPC Password
$config['password_plesk_pass'] = 'password';
// Plesk RPC Port
$config['password_plesk_rpc_port'] = '8443';
// Plesk RPC Path
$config['password_plesk_rpc_path'] = 'enterprise/control/agent.php';
// kasswd Driver options
// ---------------------
// Command to use
$config['password_kpasswd_cmd'] = '/usr/bin/kpasswd';
diff --git a/plugins/password/drivers/ldap.php b/plugins/password/drivers/ldap.php
index 6ed5ada04..46c783587 100644
--- a/plugins/password/drivers/ldap.php
+++ b/plugins/password/drivers/ldap.php
@@ -1,381 +1,221 @@
<?php
/**
* LDAP Password Driver
*
* Driver for passwords stored in LDAP
* This driver use the PEAR Net_LDAP2 class (http://pear.php.net/package/Net_LDAP2).
*
* @version 2.0
* @author Edouard MOREAU <edouard.moreau@ensma.fr>
*
* method hashPassword based on code from the phpLDAPadmin development team (http://phpldapadmin.sourceforge.net/).
* method randomSalt based on code from the phpLDAPadmin development team (http://phpldapadmin.sourceforge.net/).
*
* Copyright (C) 2005-2014, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
class rcube_ldap_password
{
public function save($curpass, $passwd)
{
$rcmail = rcmail::get_instance();
require_once 'Net/LDAP2.php';
// Building user DN
if ($userDN = $rcmail->config->get('password_ldap_userDN_mask')) {
$userDN = self::substitute_vars($userDN);
}
else {
$userDN = $this->search_userdn($rcmail);
}
if (empty($userDN)) {
return PASSWORD_CONNECT_ERROR;
}
// Connection Method
switch($rcmail->config->get('password_ldap_method')) {
case 'admin':
$binddn = $rcmail->config->get('password_ldap_adminDN');
$bindpw = $rcmail->config->get('password_ldap_adminPW');
break;
case 'user':
default:
$binddn = $userDN;
$bindpw = $curpass;
break;
}
// Configuration array
$ldapConfig = array (
'binddn' => $binddn,
'bindpw' => $bindpw,
'basedn' => $rcmail->config->get('password_ldap_basedn'),
'host' => $rcmail->config->get('password_ldap_host'),
'port' => $rcmail->config->get('password_ldap_port'),
'starttls' => $rcmail->config->get('password_ldap_starttls'),
'version' => $rcmail->config->get('password_ldap_version'),
);
// Connecting using the configuration array
$ldap = Net_LDAP2::connect($ldapConfig);
// Checking for connection error
if (is_a($ldap, 'PEAR_Error')) {
return PASSWORD_CONNECT_ERROR;
}
$force = $rcmail->config->get('password_ldap_force_replace');
$pwattr = $rcmail->config->get('password_ldap_pwattr');
$lchattr = $rcmail->config->get('password_ldap_lchattr');
$smbpwattr = $rcmail->config->get('password_ldap_samba_pwattr');
$smblchattr = $rcmail->config->get('password_ldap_samba_lchattr');
$samba = $rcmail->config->get('password_ldap_samba');
$encodage = $rcmail->config->get('password_ldap_encodage');
// Support multiple userPassword values where desired.
// multiple encodings can be specified separated by '+' (e.g. "cram-md5+ssha")
$encodages = explode('+', $encodage);
$crypted_pass = array();
foreach ($encodages as $enc) {
- $cpw = self::hash_password($passwd, $enc);
- if (!empty($cpw)) {
+ if ($cpw = password::hash_password($passwd, $enc)) {
$crypted_pass[] = $cpw;
}
}
// Support password_ldap_samba option for backward compat.
if ($samba && !$smbpwattr) {
$smbpwattr = 'sambaNTPassword';
$smblchattr = 'sambaPwdLastSet';
}
// Crypt new password
if (empty($crypted_pass)) {
return PASSWORD_CRYPT_ERROR;
}
// Crypt new samba password
- if ($smbpwattr && !($samba_pass = self::hash_password($passwd, 'samba'))) {
+ if ($smbpwattr && !($samba_pass = password::hash_password($passwd, 'samba'))) {
return PASSWORD_CRYPT_ERROR;
}
// Writing new crypted password to LDAP
$userEntry = $ldap->getEntry($userDN);
if (Net_LDAP2::isError($userEntry)) {
return PASSWORD_CONNECT_ERROR;
}
if (!$userEntry->replace(array($pwattr => $crypted_pass), $force)) {
return PASSWORD_CONNECT_ERROR;
}
// Updating PasswordLastChange Attribute if desired
if ($lchattr) {
$current_day = (int)(time() / 86400);
if (!$userEntry->replace(array($lchattr => $current_day), $force)) {
return PASSWORD_CONNECT_ERROR;
}
}
// Update Samba password and last change fields
if ($smbpwattr) {
$userEntry->replace(array($smbpwattr => $samba_pass), $force);
}
// Update Samba password last change field
if ($smblchattr) {
$userEntry->replace(array($smblchattr => time()), $force);
}
if (Net_LDAP2::isError($userEntry->update())) {
return PASSWORD_CONNECT_ERROR;
}
// All done, no error
return PASSWORD_SUCCESS;
}
/**
* Bind with searchDN and searchPW and search for the user's DN.
* Use search_base and search_filter defined in config file.
* Return the found DN.
*/
function search_userdn($rcmail)
{
$binddn = $rcmail->config->get('password_ldap_searchDN');
$bindpw = $rcmail->config->get('password_ldap_searchPW');
$ldapConfig = array (
'basedn' => $rcmail->config->get('password_ldap_basedn'),
'host' => $rcmail->config->get('password_ldap_host'),
'port' => $rcmail->config->get('password_ldap_port'),
'starttls' => $rcmail->config->get('password_ldap_starttls'),
'version' => $rcmail->config->get('password_ldap_version'),
);
// allow anonymous searches
if (!empty($binddn)) {
$ldapConfig['binddn'] = $binddn;
$ldapConfig['bindpw'] = $bindpw;
}
$ldap = Net_LDAP2::connect($ldapConfig);
if (is_a($ldap, 'PEAR_Error')) {
return '';
}
$base = self::substitute_vars($rcmail->config->get('password_ldap_search_base'));
$filter = self::substitute_vars($rcmail->config->get('password_ldap_search_filter'));
$options = array (
'scope' => 'sub',
'attributes' => array(),
);
$result = $ldap->search($base, $filter, $options);
$ldap->done();
if (is_a($result, 'PEAR_Error') || ($result->count() != 1)) {
return '';
}
return $result->current()->dn();
}
/**
* Substitute %login, %name, %domain, %dc in $str
* See plugin config for details
*/
static function substitute_vars($str)
{
$str = str_replace('%login', $_SESSION['username'], $str);
$str = str_replace('%l', $_SESSION['username'], $str);
$parts = explode('@', $_SESSION['username']);
if (count($parts) == 2) {
$dc = 'dc='.strtr($parts[1], array('.' => ',dc=')); // hierarchal domain string
$str = str_replace('%name', $parts[0], $str);
$str = str_replace('%n', $parts[0], $str);
$str = str_replace('%dc', $dc, $str);
$str = str_replace('%domain', $parts[1], $str);
$str = str_replace('%d', $parts[1], $str);
}
return $str;
}
-
- /**
- * Code originaly from the phpLDAPadmin development team
- * http://phpldapadmin.sourceforge.net/
- *
- * Hashes a password and returns the hash based on the specified enc_type
- */
- static function hash_password($password_clear, $encodage_type)
- {
- $encodage_type = strtolower($encodage_type);
- switch ($encodage_type) {
- case 'crypt':
- $crypted_password = '{CRYPT}' . crypt($password_clear, self::random_salt(2));
- break;
-
- case 'ext_des':
- /* Extended DES crypt. see OpenBSD crypt man page */
- if (!defined('CRYPT_EXT_DES') || CRYPT_EXT_DES == 0) {
- /* Your system crypt library does not support extended DES encryption */
- return false;
- }
-
- $crypted_password = '{CRYPT}' . crypt($password_clear, '_' . self::random_salt(8));
- break;
-
- case 'md5crypt':
- if (!defined('CRYPT_MD5') || CRYPT_MD5 == 0) {
- /* Your system crypt library does not support md5crypt encryption */
- return false;
- }
-
- $crypted_password = '{CRYPT}' . crypt($password_clear, '$1$' . self::random_salt(9));
- break;
-
- case 'blowfish':
- if (!defined('CRYPT_BLOWFISH') || CRYPT_BLOWFISH == 0) {
- /* Your system crypt library does not support blowfish encryption */
- return false;
- }
-
- $rcmail = rcmail::get_instance();
- $cost = (int) $rcmail->config->get('password_blowfish_cost');
- $cost = $cost < 4 || $cost > 31 ? 12 : $cost;
- $prefix = sprintf('$2a$%02d$', $cost);
-
- $crypted_password = '{CRYPT}' . crypt($password_clear, $prefix . self::random_salt(22));
- break;
-
- case 'md5':
- $crypted_password = '{MD5}' . base64_encode(pack('H*', md5($password_clear)));
- break;
-
- case 'sha':
- if (function_exists('sha1')) {
- /* Use PHP 4.3.0+ sha1 function, if it is available */
- $crypted_password = '{SHA}' . base64_encode(pack('H*', sha1($password_clear)));
- }
- else if (function_exists('hash')) {
- $crypted_password = '{SHA}' . base64_encode(hash('sha1', $password_clear, true));
- }
- else if (function_exists('mhash')) {
- $crypted_password = '{SHA}' . base64_encode(mhash(MHASH_SHA1, $password_clear));
- }
- else {
- /* Your PHP install does not have the mhash()/hash() nor sha1() function */
- return false;
- }
- 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_clear, $salt, 4);
- $password = mhash(MHASH_SHA1, $password_clear . $salt);
- }
- else if (function_exists('sha1')) {
- $salt = substr(pack("H*", sha1($salt . $password_clear)), 0, 4);
- $password = sha1($password_clear . $salt, true);
- }
- else if (function_exists('hash')) {
- $salt = substr(pack("H*", hash('sha1', $salt . $password_clear)), 0, 4);
- $password = hash('sha1', $password_clear . $salt, true);
- }
-
- if ($password) {
- $crypted_password = '{SSHA}' . base64_encode($password . $salt);
- }
- else {
- /* Your PHP install does not have the mhash()/hash() nor sha1() function */
- return false;
- }
- 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_clear, $salt, 4);
- $password = mhash(MHASH_MD5, $password_clear . $salt);
- }
- else if (function_exists('hash')) {
- $salt = substr(pack("H*", hash('md5', $salt . $password_clear)), 0, 4);
- $password = hash('md5', $password_clear . $salt, true);
- }
- else {
- $salt = substr(pack("H*", md5($salt . $password_clear)), 0, 4);
- $password = md5($password_clear . $salt, true);
- }
-
- $crypted_password = '{SMD5}' . base64_encode($password . $salt);
- break;
-
- case 'samba':
- if (function_exists('hash')) {
- $crypted_password = hash('md4', rcube_charset::convert($password_clear, RCUBE_CHARSET, 'UTF-16LE'));
- $crypted_password = strtoupper($crypted_password);
- }
- else {
- /* Your PHP install does not have the hash() function */
- return false;
- }
- break;
-
- case 'ad':
- $crypted_password = rcube_charset::convert('"' . $password_clear . '"', RCUBE_CHARSET, 'UTF-16LE');
- break;
-
- case 'cram-md5':
- require_once __DIR__ . '/../helpers/dovecot_hmacmd5.php';
- return dovecot_hmacmd5($password_clear);
- break;
-
- case 'clear':
- default:
- $crypted_password = $password_clear;
- }
-
- return $crypted_password;
- }
-
- /**
- * Code originaly from the phpLDAPadmin development team
- * http://phpldapadmin.sourceforge.net/
- *
- * Used to generate a random salt for crypt-style passwords
- */
- static function random_salt($length)
- {
- $possible = '0123456789' . 'abcdefghijklmnopqrstuvwxyz' . 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' . './';
- $str = '';
-
- while (strlen($str) < $length) {
- $str .= substr($possible, (rand() % strlen($possible)), 1);
- }
-
- return $str;
- }
}
diff --git a/plugins/password/drivers/ldap_simple.php b/plugins/password/drivers/ldap_simple.php
index 9123ea81f..5b8220577 100644
--- a/plugins/password/drivers/ldap_simple.php
+++ b/plugins/password/drivers/ldap_simple.php
@@ -1,238 +1,238 @@
<?php
/**
* Simple LDAP Password Driver
*
* Driver for passwords stored in LDAP
* This driver is based on Edouard's LDAP Password Driver, but does not
* require PEAR's Net_LDAP2 to be installed
*
* @version 2.0
* @author Wout Decre <wout@canodus.be>
* @author Aleksander Machniak <machniak@kolabsys.com>
*
* Copyright (C) 2005-2014, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
class rcube_ldap_simple_password
{
private $debug = false;
function save($curpass, $passwd)
{
$rcmail = rcmail::get_instance();
$this->debug = $rcmail->config->get('ldap_debug');
$ldap_host = $rcmail->config->get('password_ldap_host');
$ldap_port = $rcmail->config->get('password_ldap_port');
$this->_debug("C: Connect to $ldap_host:$ldap_port");
// Connect
if (!$ds = ldap_connect($ldap_host, $ldap_port)) {
$this->_debug("S: NOT OK");
rcube::raise_error(array(
'code' => 100, 'type' => 'ldap',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Could not connect to LDAP server"
),
true);
return PASSWORD_CONNECT_ERROR;
}
$this->_debug("S: OK");
// Set protocol version
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, $rcmail->config->get('password_ldap_version'));
// Start TLS
if ($rcmail->config->get('password_ldap_starttls')) {
if (!ldap_start_tls($ds)) {
ldap_unbind($ds);
return PASSWORD_CONNECT_ERROR;
}
}
// include 'ldap' driver, we share some static methods with it
require_once INSTALL_PATH . 'plugins/password/drivers/ldap.php';
// other plugins might want to modify user DN
$plugin = $rcmail->plugins->exec_hook('password_ldap_bind', array(
'user_dn' => '', 'conn' => $ds));
// Build user DN
if (!empty($plugin['user_dn'])) {
$user_dn = $plugin['user_dn'];
}
else if ($user_dn = $rcmail->config->get('password_ldap_userDN_mask')) {
$user_dn = rcube_ldap_password::substitute_vars($user_dn);
}
else {
$user_dn = $this->search_userdn($rcmail, $ds);
}
if (empty($user_dn)) {
ldap_unbind($ds);
return PASSWORD_CONNECT_ERROR;
}
// Connection method
switch ($rcmail->config->get('password_ldap_method')) {
case 'admin':
$binddn = $rcmail->config->get('password_ldap_adminDN');
$bindpw = $rcmail->config->get('password_ldap_adminPW');
break;
case 'user':
default:
$binddn = $user_dn;
$bindpw = $curpass;
break;
}
$lchattr = $rcmail->config->get('password_ldap_lchattr');
$pwattr = $rcmail->config->get('password_ldap_pwattr');
$smbpwattr = $rcmail->config->get('password_ldap_samba_pwattr');
$smblchattr = $rcmail->config->get('password_ldap_samba_lchattr');
$samba = $rcmail->config->get('password_ldap_samba');
$pass_mode = $rcmail->config->get('password_ldap_encodage');
- $crypted_pass = rcube_ldap_password::hash_password($passwd, $pass_mode);
+ $crypted_pass = password::hash_password($passwd, $pass_mode);
// Support password_ldap_samba option for backward compat.
if ($samba && !$smbpwattr) {
$smbpwattr = 'sambaNTPassword';
$smblchattr = 'sambaPwdLastSet';
}
// Crypt new password
if (!$crypted_pass) {
return PASSWORD_CRYPT_ERROR;
}
// Crypt new Samba password
- if ($smbpwattr && !($samba_pass = rcube_ldap_password::hash_password($passwd, 'samba'))) {
+ if ($smbpwattr && !($samba_pass = password::hash_password($passwd, 'samba'))) {
return PASSWORD_CRYPT_ERROR;
}
$this->_debug("C: Bind $binddn, pass: **** [" . strlen($bindpw) . "]");
// Bind
if (!ldap_bind($ds, $binddn, $bindpw)) {
$this->_debug("S: ".ldap_error($ds));
ldap_unbind($ds);
return PASSWORD_CONNECT_ERROR;
}
$this->_debug("S: OK");
$entry[$pwattr] = $crypted_pass;
// Update PasswordLastChange Attribute if desired
if ($lchattr) {
$entry[$lchattr] = (int)(time() / 86400);
}
// Update Samba password
if ($smbpwattr) {
$entry[$smbpwattr] = $samba_pass;
}
// Update Samba password last change
if ($smblchattr) {
$entry[$smblchattr] = time();
}
$this->_debug("C: Modify $user_dn: " . print_r($entry, true));
if (!ldap_modify($ds, $user_dn, $entry)) {
$this->_debug("S: ".ldap_error($ds));
ldap_unbind($ds);
return PASSWORD_CONNECT_ERROR;
}
$this->_debug("S: OK");
// All done, no error
ldap_unbind($ds);
return PASSWORD_SUCCESS;
}
/**
* Bind with searchDN and searchPW and search for the user's DN
* Use search_base and search_filter defined in config file
* Return the found DN
*/
function search_userdn($rcmail, $ds)
{
$search_user = $rcmail->config->get('password_ldap_searchDN');
$search_pass = $rcmail->config->get('password_ldap_searchPW');
$search_base = $rcmail->config->get('password_ldap_search_base');
$search_filter = $rcmail->config->get('password_ldap_search_filter');
if (empty($search_filter)) {
return false;
}
$this->_debug("C: Bind " . ($search_user ? $search_user : '[anonymous]'));
// Bind
if (!ldap_bind($ds, $search_user, $search_pass)) {
$this->_debug("S: ".ldap_error($ds));
return false;
}
$this->_debug("S: OK");
$search_base = rcube_ldap_password::substitute_vars($search_base);
$search_filter = rcube_ldap_password::substitute_vars($search_filter);
$this->_debug("C: Search $search_base for $search_filter");
// Search for the DN
if (!$sr = ldap_search($ds, $search_base, $search_filter)) {
$this->_debug("S: ".ldap_error($ds));
return false;
}
$found = ldap_count_entries($ds, $sr);
$this->_debug("S: OK [found $found records]");
// If no or more entries were found, return false
if ($found != 1) {
return false;
}
return ldap_get_dn($ds, ldap_first_entry($ds, $sr));
}
/**
* Prints debug info to the log
*/
private function _debug($str)
{
if ($this->debug) {
rcube::write_log('ldap', $str);
}
}
}
diff --git a/plugins/password/drivers/sql.php b/plugins/password/drivers/sql.php
index 37e162e22..9659fa0e0 100644
--- a/plugins/password/drivers/sql.php
+++ b/plugins/password/drivers/sql.php
@@ -1,212 +1,172 @@
<?php
/**
* SQL Password Driver
*
* Driver for passwords stored in SQL database
*
* @version 2.0
* @author Aleksander 'A.L.E.C' Machniak <alec@alec.pl>
*
* Copyright (C) 2005-2013, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
class rcube_sql_password
{
function save($curpass, $passwd)
{
$rcmail = rcmail::get_instance();
if (!($sql = $rcmail->config->get('password_query'))) {
$sql = 'SELECT update_passwd(%c, %u)';
}
if ($dsn = $rcmail->config->get('password_db_dsn')) {
$db = rcube_db::factory($dsn, '', false);
$db->set_debug((bool)$rcmail->config->get('sql_debug'));
}
else {
$db = $rcmail->get_dbh();
}
if ($db->is_error()) {
return PASSWORD_ERROR;
}
- // crypted password
- if (strpos($sql, '%c') !== FALSE) {
- $salt = '';
-
- if (!($crypt_hash = $rcmail->config->get('password_crypt_hash'))) {
- if (CRYPT_MD5)
- $crypt_hash = 'md5';
- else if (CRYPT_STD_DES)
- $crypt_hash = 'des';
- }
+ // new password - default hash method
+ if (strpos($sql, '%P') !== false) {
+ $password = password::hash_password($passwd);
- switch ($crypt_hash) {
- case 'md5':
- $len = 8;
- $salt_hashindicator = '$1$';
- break;
- case 'des':
- $len = 2;
- break;
- case 'blowfish':
- $cost = (int) $rcmail->config->get('password_blowfish_cost');
- $cost = $cost < 4 || $cost > 31 ? 12 : $cost;
- $len = 22;
- $salt_hashindicator = sprintf('$2a$%02d$', $cost);
- break;
- case 'sha256':
- $len = 16;
- $salt_hashindicator = '$5$';
- break;
- case 'sha512':
- $len = 16;
- $salt_hashindicator = '$6$';
- break;
- default:
+ if ($password === false) {
return PASSWORD_CRYPT_ERROR;
}
- //Restrict the character set used as salt (#1488136)
- $seedchars = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz';
- for ($i = 0; $i < $len ; $i++) {
- $salt .= $seedchars[rand(0, 63)];
+ $sql = str_replace('%P', $db->quote($password), $sql);
+ }
+
+ // old password - default hash method
+ if (strpos($sql, '%O') !== false) {
+ $password = password::hash_password($curpass);
+
+ if ($password === false) {
+ return PASSWORD_CRYPT_ERROR;
}
- $sql = str_replace('%c', $db->quote(crypt($passwd, $salt_hashindicator ? $salt_hashindicator .$salt.'$' : $salt)), $sql);
+ $sql = str_replace('%O', $db->quote($password), $sql);
}
- // dovecotpw
- if (strpos($sql, '%D') !== FALSE) {
- if (!($dovecotpw = $rcmail->config->get('password_dovecotpw')))
- $dovecotpw = 'dovecotpw';
- if (!($method = $rcmail->config->get('password_dovecotpw_method')))
- $method = 'CRAM-MD5';
+ // crypted password (deprecated, use %P)
+ if (strpos($sql, '%c') !== false) {
+ $password = password::hash_password($passwd, 'crypt', false);
- // 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);
+ if ($password === false) {
return PASSWORD_CRYPT_ERROR;
}
- else {
- fwrite($pipe, $passwd . "\n", 1+strlen($passwd)); usleep(1000);
- fwrite($pipe, $passwd . "\n", 1+strlen($passwd));
- pclose($pipe);
- $newpass = trim(file_get_contents($tmpfile), "\n");
- if (!preg_match('/^\{' . $method . '\}/', $newpass)) {
- return PASSWORD_CRYPT_ERROR;
- }
- if (!$rcmail->config->get('password_dovecotpw_with_method'))
- $newpass = trim(str_replace('{' . $method . '}', '', $newpass));
- unlink($tmpfile);
- }
- $sql = str_replace('%D', $db->quote($newpass), $sql);
+
+ $sql = str_replace('%c', $db->quote($password), $sql);
}
- // hashed passwords
- if (preg_match('/%[n|q]/', $sql)) {
- if (!extension_loaded('hash')) {
- rcube::raise_error(array(
- 'code' => 600,
- 'type' => 'php',
- 'file' => __FILE__, 'line' => __LINE__,
- 'message' => "Password plugin: 'hash' extension not loaded!"
- ), true, false);
-
- return PASSWORD_ERROR;
+ // dovecotpw (deprecated, use %P)
+ if (strpos($sql, '%D') !== false) {
+ $password = password::hash_password($passwd, 'dovecot', false);
+
+ if ($password === false) {
+ return PASSWORD_CRYPT_ERROR;
}
- if (!($hash_algo = strtolower($rcmail->config->get('password_hash_algorithm')))) {
- $hash_algo = 'sha1';
+ $sql = str_replace('%D', $db->quote($password), $sql);
+ }
+
+ // hashed passwords (deprecated, use %P)
+ if (strpos($sql, '%n') !== false) {
+ $password = password::hash_password($passwd, 'hash', false);
+
+ if ($password === false) {
+ return PASSWORD_CRYPT_ERROR;
}
- $hash_passwd = hash($hash_algo, $passwd);
- $hash_curpass = hash($hash_algo, $curpass);
+ $sql = str_replace('%n', $db->quote($password, 'text'), $sql);
+ }
+
+ // hashed passwords (deprecated, use %P)
+ if (strpos($sql, '%q') !== false) {
+ $password = password::hash_password($curpass, 'hash', false);
- if ($rcmail->config->get('password_hash_base64')) {
- $hash_passwd = base64_encode(pack('H*', $hash_passwd));
- $hash_curpass = base64_encode(pack('H*', $hash_curpass));
+ if ($password === false) {
+ return PASSWORD_CRYPT_ERROR;
}
- $sql = str_replace('%n', $db->quote($hash_passwd, 'text'), $sql);
- $sql = str_replace('%q', $db->quote($hash_curpass, 'text'), $sql);
+ $sql = str_replace('%q', $db->quote($password, 'text'), $sql);
}
// Handle clear text passwords securely (#1487034)
$sql_vars = array();
if (preg_match_all('/%[p|o]/', $sql, $m)) {
foreach ($m[0] as $var) {
if ($var == '%p') {
$sql = preg_replace('/%p/', '?', $sql, 1);
$sql_vars[] = (string) $passwd;
}
else { // %o
$sql = preg_replace('/%o/', '?', $sql, 1);
$sql_vars[] = (string) $curpass;
}
}
}
$local_part = $rcmail->user->get_username('local');
$domain_part = $rcmail->user->get_username('domain');
$username = $_SESSION['username'];
$host = $_SESSION['imap_host'];
// convert domains to/from punnycode
if ($rcmail->config->get('password_idn_ascii')) {
$domain_part = rcube_utils::idn_to_ascii($domain_part);
$username = rcube_utils::idn_to_ascii($username);
$host = rcube_utils::idn_to_ascii($host);
}
else {
$domain_part = rcube_utils::idn_to_utf8($domain_part);
$username = rcube_utils::idn_to_utf8($username);
$host = rcube_utils::idn_to_utf8($host);
}
// at least we should always have the local part
$sql = str_replace('%l', $db->quote($local_part, 'text'), $sql);
$sql = str_replace('%d', $db->quote($domain_part, 'text'), $sql);
$sql = str_replace('%u', $db->quote($username, 'text'), $sql);
$sql = str_replace('%h', $db->quote($host, 'text'), $sql);
$res = $db->query($sql, $sql_vars);
if (!$db->is_error()) {
if (strtolower(substr(trim($sql),0,6)) == 'select') {
if ($db->fetch_array($res)) {
return PASSWORD_SUCCESS;
}
}
else {
// This is the good case: 1 row updated
if ($db->affected_rows($res) == 1)
return PASSWORD_SUCCESS;
// @TODO: Some queries don't affect any rows
// Should we assume a success if there was no error?
}
}
return PASSWORD_ERROR;
}
}
diff --git a/plugins/password/helpers/dovecot_hmacmd5.php b/plugins/password/helpers/dovecot_hmacmd5.php
index 644b5377e..da4d005f7 100644
--- a/plugins/password/helpers/dovecot_hmacmd5.php
+++ b/plugins/password/helpers/dovecot_hmacmd5.php
@@ -1,191 +1,191 @@
<?php
/**
*
* dovecot_hmacmd5.php V1.01
*
* Generates HMAC-MD5 'contexts' for Dovecot's password files.
*
* (C) 2008 Hajo Noerenberg
*
* http://www.noerenberg.de/hajo/pub/dovecot_hmacmd5.php.txt
*
* Most of the code has been shamelessly stolen from various sources:
*
* (C) Paul Johnston 1999 - 2000 / http://pajhome.org.uk/crypt/md5/
* (C) William K. Cole 2008 / http://www.scconsult.com/bill/crampass.pl
* (C) Borfast 2002 / http://www.zend.com/code/codex.php?ozid=962&single=1
* (C) Thomas Weber / http://pajhome.org.uk/crypt/md5/contrib/md5.java.txt
*
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 3.0 as
* published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see <http://www.gnu.org/licenses/gpl-3.0.txt>.
*
*/
/* Convert a 32-bit number to a hex string with ls-byte first
*/
function rhex($n) {
$hex_chr = "0123456789abcdef"; $r = '';
for($j = 0; $j <= 3; $j++)
$r .= $hex_chr[($n >> ($j * 8 + 4)) & 0x0F] . $hex_chr[($n >> ($j * 8)) & 0x0F];
return $r;
}
/* zeroFill() is needed because PHP doesn't have a zero-fill
* right shift operator like JavaScript's >>>
*/
function zeroFill($a, $b) {
$z = hexdec(80000000);
if ($z & $a) {
$a >>= 1;
$a &= (~$z);
$a |= 0x40000000;
$a >>= ($b-1);
} else {
$a >>= $b;
}
return $a;
}
/* Bitwise rotate a 32-bit number to the left
*/
function bit_rol($num, $cnt) {
return ($num << $cnt) | (zeroFill($num, (32 - $cnt)));
}
/* Add integers, wrapping at 2^32
*/
function safe_add($x, $y) {
return (($x&0x7FFFFFFF) + ($y&0x7FFFFFFF)) ^ ($x&0x80000000) ^ ($y&0x80000000);
}
/* These functions implement the four basic operations the algorithm uses.
*/
function md5_cmn($q, $a, $b, $x, $s, $t) {
return safe_add(bit_rol(safe_add(safe_add($a, $q), safe_add($x, $t)), $s), $b);
}
function md5_ff($a, $b, $c, $d, $x, $s, $t) {
return md5_cmn(($b & $c) | ((~$b) & $d), $a, $b, $x, $s, $t);
}
function md5_gg($a, $b, $c, $d, $x, $s, $t) {
return md5_cmn(($b & $d) | ($c & (~$d)), $a, $b, $x, $s, $t);
}
function md5_hh($a, $b, $c, $d, $x, $s, $t) {
return md5_cmn($b ^ $c ^ $d, $a, $b, $x, $s, $t);
}
function md5_ii($a, $b, $c, $d, $x, $s, $t) {
return md5_cmn($c ^ ($b | (~$d)), $a, $b, $x, $s, $t);
}
/* Calculate the first round of the MD5 algorithm
*/
function md5_oneround($s, $io) {
$s = str_pad($s, 64, chr(0x00));
$x = array_fill(0, 16, 0);
for($i = 0; $i < 64; $i++)
$x[$i >> 2] |= (($io ? 0x36 : 0x5c) ^ ord($s[$i])) << (($i % 4) * 8);
$a = $olda = 1732584193;
$b = $oldb = -271733879;
$c = $oldc = -1732584194;
$d = $oldd = 271733878;
$a = md5_ff($a, $b, $c, $d, $x[ 0], 7 , -680876936);
$d = md5_ff($d, $a, $b, $c, $x[ 1], 12, -389564586);
$c = md5_ff($c, $d, $a, $b, $x[ 2], 17, 606105819);
$b = md5_ff($b, $c, $d, $a, $x[ 3], 22, -1044525330);
$a = md5_ff($a, $b, $c, $d, $x[ 4], 7 , -176418897);
$d = md5_ff($d, $a, $b, $c, $x[ 5], 12, 1200080426);
$c = md5_ff($c, $d, $a, $b, $x[ 6], 17, -1473231341);
$b = md5_ff($b, $c, $d, $a, $x[ 7], 22, -45705983);
$a = md5_ff($a, $b, $c, $d, $x[ 8], 7 , 1770035416);
$d = md5_ff($d, $a, $b, $c, $x[ 9], 12, -1958414417);
$c = md5_ff($c, $d, $a, $b, $x[10], 17, -42063);
$b = md5_ff($b, $c, $d, $a, $x[11], 22, -1990404162);
$a = md5_ff($a, $b, $c, $d, $x[12], 7 , 1804603682);
$d = md5_ff($d, $a, $b, $c, $x[13], 12, -40341101);
$c = md5_ff($c, $d, $a, $b, $x[14], 17, -1502002290);
$b = md5_ff($b, $c, $d, $a, $x[15], 22, 1236535329);
$a = md5_gg($a, $b, $c, $d, $x[ 1], 5 , -165796510);
$d = md5_gg($d, $a, $b, $c, $x[ 6], 9 , -1069501632);
$c = md5_gg($c, $d, $a, $b, $x[11], 14, 643717713);
$b = md5_gg($b, $c, $d, $a, $x[ 0], 20, -373897302);
$a = md5_gg($a, $b, $c, $d, $x[ 5], 5 , -701558691);
$d = md5_gg($d, $a, $b, $c, $x[10], 9 , 38016083);
$c = md5_gg($c, $d, $a, $b, $x[15], 14, -660478335);
$b = md5_gg($b, $c, $d, $a, $x[ 4], 20, -405537848);
$a = md5_gg($a, $b, $c, $d, $x[ 9], 5 , 568446438);
$d = md5_gg($d, $a, $b, $c, $x[14], 9 , -1019803690);
$c = md5_gg($c, $d, $a, $b, $x[ 3], 14, -187363961);
$b = md5_gg($b, $c, $d, $a, $x[ 8], 20, 1163531501);
$a = md5_gg($a, $b, $c, $d, $x[13], 5 , -1444681467);
$d = md5_gg($d, $a, $b, $c, $x[ 2], 9 , -51403784);
$c = md5_gg($c, $d, $a, $b, $x[ 7], 14, 1735328473);
$b = md5_gg($b, $c, $d, $a, $x[12], 20, -1926607734);
$a = md5_hh($a, $b, $c, $d, $x[ 5], 4 , -378558);
$d = md5_hh($d, $a, $b, $c, $x[ 8], 11, -2022574463);
$c = md5_hh($c, $d, $a, $b, $x[11], 16, 1839030562);
$b = md5_hh($b, $c, $d, $a, $x[14], 23, -35309556);
$a = md5_hh($a, $b, $c, $d, $x[ 1], 4 , -1530992060);
$d = md5_hh($d, $a, $b, $c, $x[ 4], 11, 1272893353);
$c = md5_hh($c, $d, $a, $b, $x[ 7], 16, -155497632);
$b = md5_hh($b, $c, $d, $a, $x[10], 23, -1094730640);
$a = md5_hh($a, $b, $c, $d, $x[13], 4 , 681279174);
$d = md5_hh($d, $a, $b, $c, $x[ 0], 11, -358537222);
$c = md5_hh($c, $d, $a, $b, $x[ 3], 16, -722521979);
$b = md5_hh($b, $c, $d, $a, $x[ 6], 23, 76029189);
$a = md5_hh($a, $b, $c, $d, $x[ 9], 4 , -640364487);
$d = md5_hh($d, $a, $b, $c, $x[12], 11, -421815835);
$c = md5_hh($c, $d, $a, $b, $x[15], 16, 530742520);
$b = md5_hh($b, $c, $d, $a, $x[ 2], 23, -995338651);
$a = md5_ii($a, $b, $c, $d, $x[ 0], 6 , -198630844);
$d = md5_ii($d, $a, $b, $c, $x[ 7], 10, 1126891415);
$c = md5_ii($c, $d, $a, $b, $x[14], 15, -1416354905);
$b = md5_ii($b, $c, $d, $a, $x[ 5], 21, -57434055);
$a = md5_ii($a, $b, $c, $d, $x[12], 6 , 1700485571);
$d = md5_ii($d, $a, $b, $c, $x[ 3], 10, -1894986606);
$c = md5_ii($c, $d, $a, $b, $x[10], 15, -1051523);
$b = md5_ii($b, $c, $d, $a, $x[ 1], 21, -2054922799);
$a = md5_ii($a, $b, $c, $d, $x[ 8], 6 , 1873313359);
$d = md5_ii($d, $a, $b, $c, $x[15], 10, -30611744);
$c = md5_ii($c, $d, $a, $b, $x[ 6], 15, -1560198380);
$b = md5_ii($b, $c, $d, $a, $x[13], 21, 1309151649);
$a = md5_ii($a, $b, $c, $d, $x[ 4], 6 , -145523070);
$d = md5_ii($d, $a, $b, $c, $x[11], 10, -1120210379);
$c = md5_ii($c, $d, $a, $b, $x[ 2], 15, 718787259);
$b = md5_ii($b, $c, $d, $a, $x[ 9], 21, -343485551);
$a = safe_add($a, $olda);
$b = safe_add($b, $oldb);
$c = safe_add($c, $oldc);
$d = safe_add($d, $oldd);
return rhex($a) . rhex($b) . rhex($c) . rhex($d);
}
function dovecot_hmacmd5 ($s) {
if (strlen($s) > 64) $s=pack("H*", md5($s));
- return "{CRAM-MD5}" . md5_oneround($s, 0) . md5_oneround($s, 1);
+ return md5_oneround($s, 0) . md5_oneround($s, 1);
}
diff --git a/plugins/password/password.php b/plugins/password/password.php
index 9239cd0b0..476e9eac8 100644
--- a/plugins/password/password.php
+++ b/plugins/password/password.php
@@ -1,370 +1,636 @@
<?php
/**
* Password Plugin for Roundcube
*
* @version @package_version@
* @author Aleksander Machniak <alec@alec.pl>
*
* Copyright (C) 2005-2013, 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
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see http://www.gnu.org/licenses/.
*/
define('PASSWORD_CRYPT_ERROR', 1);
define('PASSWORD_ERROR', 2);
define('PASSWORD_CONNECT_ERROR', 3);
define('PASSWORD_SUCCESS', 0);
/**
* Change password plugin
*
* Plugin that adds functionality to change a users password.
* It provides common functionality and user interface and supports
* several backends to finally update the password.
*
* For installation and configuration instructions please read the README file.
*
* @author Aleksander Machniak
*/
class password extends rcube_plugin
{
public $task = 'settings|login';
public $noframe = true;
public $noajax = true;
private $newuser = false;
function init()
{
$rcmail = rcmail::get_instance();
$this->load_config();
if ($rcmail->task == 'settings') {
if (!$this->check_host_login_exceptions()) {
return;
}
$this->add_texts('localization/');
$this->add_hook('settings_actions', array($this, 'settings_actions'));
$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'));
$this->add_hook('login_after', array($this, 'login_after'));
}
}
function settings_actions($args)
{
// register as settings action
$args['actions'][] = array(
'action' => 'plugin.password',
'class' => 'password',
'label' => 'password',
'title' => 'changepasswd',
'domain' => 'password',
);
return $args;
}
function password_init()
{
$this->register_handler('plugin.body', array($this, 'password_form'));
$rcmail = rcmail::get_instance();
$rcmail->output->set_pagetitle($this->gettext('changepasswd'));
if (rcube_utils::get_input_value('_first', rcube_utils::INPUT_GET)) {
$rcmail->output->command('display_message', $this->gettext('firstloginchange'), 'notice');
}
$rcmail->output->send('plugin');
}
function password_save()
{
$this->register_handler('plugin.body', array($this, 'password_form'));
$rcmail = rcmail::get_instance();
$rcmail->output->set_pagetitle($this->gettext('changepasswd'));
$confirm = $rcmail->config->get('password_confirm_current');
$required_length = intval($rcmail->config->get('password_minimum_length'));
$check_strength = $rcmail->config->get('password_require_nonalpha');
if (($confirm && !isset($_POST['_curpasswd'])) || !isset($_POST['_newpasswd'])) {
$rcmail->output->command('display_message', $this->gettext('nopassword'), 'error');
}
else {
$charset = strtoupper($rcmail->config->get('password_charset', 'ISO-8859-1'));
$rc_charset = strtoupper($rcmail->output->get_charset());
$sespwd = $rcmail->decrypt($_SESSION['password']);
$curpwd = $confirm ? rcube_utils::get_input_value('_curpasswd', rcube_utils::INPUT_POST, true, $charset) : $sespwd;
$newpwd = rcube_utils::get_input_value('_newpasswd', rcube_utils::INPUT_POST, true);
$conpwd = rcube_utils::get_input_value('_confpasswd', rcube_utils::INPUT_POST, true);
// check allowed characters according to the configured 'password_charset' option
// by converting the password entered by the user to this charset and back to UTF-8
$orig_pwd = $newpwd;
$chk_pwd = rcube_charset::convert($orig_pwd, $rc_charset, $charset);
$chk_pwd = rcube_charset::convert($chk_pwd, $charset, $rc_charset);
// WARNING: Default password_charset is ISO-8859-1, so conversion will
// change national characters. This may disable possibility of using
// the same password in other MUA's.
// We're doing this for consistence with Roundcube core
$newpwd = rcube_charset::convert($newpwd, $rc_charset, $charset);
$conpwd = rcube_charset::convert($conpwd, $rc_charset, $charset);
if ($chk_pwd != $orig_pwd) {
$rcmail->output->command('display_message', $this->gettext('passwordforbidden'), 'error');
}
// other passwords validity checks
else if ($conpwd != $newpwd) {
$rcmail->output->command('display_message', $this->gettext('passwordinconsistency'), 'error');
}
else if ($confirm && $sespwd != $curpwd) {
$rcmail->output->command('display_message', $this->gettext('passwordincorrect'), 'error');
}
else if ($required_length && strlen($newpwd) < $required_length) {
$rcmail->output->command('display_message', $this->gettext(
array('name' => 'passwordshort', 'vars' => array('length' => $required_length))), 'error');
}
else if ($check_strength && (!preg_match("/[0-9]/", $newpwd) || !preg_match("/[^A-Za-z0-9]/", $newpwd))) {
$rcmail->output->command('display_message', $this->gettext('passwordweak'), 'error');
}
// password is the same as the old one, do nothing, return success
else if ($sespwd == $newpwd && !$rcmail->config->get('password_force_save')) {
$rcmail->output->command('display_message', $this->gettext('successfullysaved'), 'confirmation');
}
// try to save the password
else if (!($res = $this->_save($curpwd, $newpwd))) {
$rcmail->output->command('display_message', $this->gettext('successfullysaved'), 'confirmation');
// allow additional actions after password change (e.g. reset some backends)
$plugin = $rcmail->plugins->exec_hook('password_change', array(
'old_pass' => $curpwd, 'new_pass' => $newpwd));
// Reset session password
$_SESSION['password'] = $rcmail->encrypt($plugin['new_pass']);
// Log password change
if ($rcmail->config->get('password_log')) {
rcube::write_log('password', sprintf('Password changed for user %s (ID: %d) from %s',
$rcmail->get_user_name(), $rcmail->user->ID, rcube_utils::remote_ip()));
}
}
else {
$rcmail->output->command('display_message', $res, 'error');
}
}
$rcmail->overwrite_action('plugin.password');
$rcmail->output->send('plugin');
}
function password_form()
{
$rcmail = rcmail::get_instance();
// add some labels to client
$rcmail->output->add_label(
'password.nopassword',
'password.nocurpassword',
'password.passwordinconsistency'
);
$rcmail->output->set_env('product_name', $rcmail->config->get('product_name'));
$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'));
$table->add('title', html::label($field_id, rcube::Q($this->gettext('curpasswd'))));
$table->add(null, $input_curpasswd->show());
}
// show new password selection
$field_id = 'newpasswd';
$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'));
$table->add('title', html::label($field_id, rcube::Q($this->gettext('confpasswd'))));
$table->add(null, $input_confpasswd->show());
$rules = '';
$required_length = intval($rcmail->config->get('password_minimum_length'));
if ($required_length > 0) {
$rules .= html::tag('li', array('id' => 'required-length'), $this->gettext(array(
'name' => 'passwordshort',
'vars' => array('length' => $required_length)
)));
}
if ($rcmail->config->get('password_require_nonalpha')) {
$rules .= html::tag('li', array('id' => 'require-nonalpha'), $this->gettext('passwordweak'));
}
if (!empty($rules)) {
$rules = html::tag('ul', array('id' => 'ruleslist'), $rules);
}
$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'
)))));
$rcmail->output->add_gui_object('passform', 'password-form');
return $rcmail->output->form_tag(array(
'id' => 'password-form',
'name' => 'password-form',
'method' => 'post',
'action' => './?_task=settings&_action=plugin.password-save',
), $out);
}
private function _save($curpass, $passwd)
{
$config = rcmail::get_instance()->config;
$driver = $config->get('password_driver', 'sql');
$class = "rcube_{$driver}_password";
$file = $this->home . "/drivers/$driver.php";
if (!file_exists($file)) {
rcube::raise_error(array(
'code' => 600,
'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Password plugin: Unable to open driver file ($file)"
), true, false);
return $this->gettext('internalerror');
}
include_once $file;
if (!class_exists($class, false) || !method_exists($class, 'save')) {
rcube::raise_error(array(
'code' => 600,
'type' => 'php',
'file' => __FILE__, 'line' => __LINE__,
'message' => "Password plugin: Broken driver $driver"
), true, false);
return $this->gettext('internalerror');
}
$object = new $class;
$result = $object->save($curpass, $passwd);
if (is_array($result)) {
$message = $result['message'];
$result = $result['code'];
}
switch ($result) {
case PASSWORD_SUCCESS:
return;
case PASSWORD_CRYPT_ERROR:
$reason = $this->gettext('crypterror');
break;
case PASSWORD_CONNECT_ERROR:
$reason = $this->gettext('connecterror');
break;
case PASSWORD_ERROR:
default:
$reason = $this->gettext('internalerror');
}
if ($message) {
$reason .= ' ' . $message;
}
return $reason;
}
function user_create($args)
{
$this->newuser = true;
return $args;
}
function login_after($args)
{
if ($this->newuser && $this->check_host_login_exceptions()) {
$args['_task'] = 'settings';
$args['_action'] = 'plugin.password';
$args['_first'] = 'true';
}
return $args;
}
// Check if host and login is allowed to change the password, false = not allowed, true = not allowed
private function check_host_login_exceptions()
{
$rcmail = rcmail::get_instance();
// Host exceptions
$hosts = $rcmail->config->get('password_hosts');
if (!empty($hosts) && !in_array($_SESSION['storage_host'], $hosts)) {
return false;
}
// Login exceptions
if ($exceptions = $rcmail->config->get('password_login_exceptions')) {
$exceptions = array_map('trim', (array) $exceptions);
$exceptions = array_filter($exceptions);
$username = $_SESSION['username'];
foreach ($exceptions as $ec) {
if ($username === $ec) {
return false;
}
}
}
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;
+ }
}
File Metadata
Details
Attached
Mime Type
text/x-diff
Expires
Tue, Feb 3, 8:39 AM (1 d, 6 h)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
427157
Default Alt Text
(81 KB)
Attached To
Mode
R3 roundcubemail
Attached
Detach File
Event Timeline
Log In to Comment