6 * Driver for passwords stored in LDAP
7 * This driver use the PEAR Net_LDAP2 class (http://pear.php.net/package/Net_LDAP2).
9 * @version 1.1 (2010-04-07)
10 * @author Edouard MOREAU <edouard.moreau@ensma.fr>
12 * function hashPassword based on code from the phpLDAPadmin development team (http://phpldapadmin.sourceforge.net/).
13 * function randomSalt based on code from the phpLDAPadmin development team (http://phpldapadmin.sourceforge.net/).
17 function password_save($curpass, $passwd)
19 $rcmail = rcmail::get_instance();
20 require_once ('Net/LDAP2.php');
23 if ($userDN = $rcmail->config->get('password_ldap_userDN_mask')) {
24 $userDN = substitute_vars($userDN);
26 $userDN = search_userdn($rcmail);
30 return PASSWORD_CONNECT_ERROR;
34 switch($rcmail->config->get('password_ldap_method')) {
36 $binddn = $rcmail->config->get('password_ldap_adminDN');
37 $bindpw = $rcmail->config->get('password_ldap_adminPW');
46 // Configuration array
50 'basedn' => $rcmail->config->get('password_ldap_basedn'),
51 'host' => $rcmail->config->get('password_ldap_host'),
52 'port' => $rcmail->config->get('password_ldap_port'),
53 'starttls' => $rcmail->config->get('password_ldap_starttls'),
54 'version' => $rcmail->config->get('password_ldap_version'),
57 // Connecting using the configuration array
58 $ldap = Net_LDAP2::connect($ldapConfig);
60 // Checking for connection error
61 if (PEAR::isError($ldap)) {
62 return PASSWORD_CONNECT_ERROR;
65 $crypted_pass = hashPassword($passwd, $rcmail->config->get('password_ldap_encodage'));
66 $force = $rcmail->config->get('password_ldap_force_replace');
67 $pwattr = $rcmail->config->get('password_ldap_pwattr');
68 $lchattr = $rcmail->config->get('password_ldap_lchattr');
69 $smbpwattr = $rcmail->config->get('password_ldap_samba_pwattr');
70 $smblchattr = $rcmail->config->get('password_ldap_samba_lchattr');
71 $samba = $rcmail->config->get('password_ldap_samba');
73 // Support password_ldap_samba option for backward compat.
74 if ($samba && !$smbpwattr) {
75 $smbpwattr = 'sambaNTPassword';
76 $smblchattr = 'sambaPwdLastSet';
81 return PASSWORD_CRYPT_ERROR;
84 // Crypt new samba password
85 if ($smbpwattr && !($samba_pass = hashPassword($passwd, 'samba'))) {
86 return PASSWORD_CRYPT_ERROR;
89 // Writing new crypted password to LDAP
90 $userEntry = $ldap->getEntry($userDN);
91 if (Net_LDAP2::isError($userEntry)) {
92 return PASSWORD_CONNECT_ERROR;
95 if (!$userEntry->replace(array($pwattr => $crypted_pass), $force)) {
96 return PASSWORD_CONNECT_ERROR;
99 // Updating PasswordLastChange Attribute if desired
101 $current_day = (int)(time() / 86400);
102 if (!$userEntry->replace(array($lchattr => $current_day), $force)) {
103 return PASSWORD_CONNECT_ERROR;
107 // Update Samba password and last change fields
109 $userEntry->replace(array($smbpwattr => $samba_pass), $force);
111 // Update Samba password last change field
113 $userEntry->replace(array($smblchattr => time()), $force);
116 if (Net_LDAP2::isError($userEntry->update())) {
117 return PASSWORD_CONNECT_ERROR;
120 // All done, no error
121 return PASSWORD_SUCCESS;
125 * Bind with searchDN and searchPW and search for the user's DN.
126 * Use search_base and search_filter defined in config file.
127 * Return the found DN.
129 function search_userdn($rcmail)
131 $ldapConfig = array (
132 'binddn' => $rcmail->config->get('password_ldap_searchDN'),
133 'bindpw' => $rcmail->config->get('password_ldap_searchPW'),
134 'basedn' => $rcmail->config->get('password_ldap_basedn'),
135 'host' => $rcmail->config->get('password_ldap_host'),
136 'port' => $rcmail->config->get('password_ldap_port'),
137 'starttls' => $rcmail->config->get('password_ldap_starttls'),
138 'version' => $rcmail->config->get('password_ldap_version'),
141 $ldap = Net_LDAP2::connect($ldapConfig);
143 if (PEAR::isError($ldap)) {
147 $base = $rcmail->config->get('password_ldap_search_base');
148 $filter = substitute_vars($rcmail->config->get('password_ldap_search_filter'));
151 'attributes' => array(),
154 $result = $ldap->search($base, $filter, $options);
156 if (PEAR::isError($result) || ($result->count() != 1)) {
160 return $result->current()->dn();
164 * Substitute %login, %name, %domain, %dc in $str.
165 * See plugin config for details.
167 function substitute_vars($str)
169 $rcmail = rcmail::get_instance();
170 $domain = $rcmail->user->get_username('domain');
171 $dc = 'dc='.strtr($domain, array('.' => ',dc=')); // hierarchal domain string
173 $str = str_replace(array(
179 $_SESSION['username'],
180 $rcmail->user->get_username('local'),
191 * Code originaly from the phpLDAPadmin development team
192 * http://phpldapadmin.sourceforge.net/
194 * Hashes a password and returns the hash based on the specified enc_type.
196 * @param string $passwordClear The password to hash in clear text.
197 * @param string $encodageType Standard LDAP encryption type which must be one of
198 * crypt, ext_des, md5crypt, blowfish, md5, sha, smd5, ssha, or clear.
199 * @return string The hashed password.
203 function hashPassword( $passwordClear, $encodageType )
205 $encodageType = strtolower( $encodageType );
206 switch( $encodageType ) {
208 $cryptedPassword = '{CRYPT}' . crypt($passwordClear,randomSalt(2));
212 // extended des crypt. see OpenBSD crypt man page.
213 if ( ! defined( 'CRYPT_EXT_DES' ) || CRYPT_EXT_DES == 0 ) {
214 // Your system crypt library does not support extended DES encryption.
217 $cryptedPassword = '{CRYPT}' . crypt( $passwordClear, '_' . randomSalt(8) );
221 if( ! defined( 'CRYPT_MD5' ) || CRYPT_MD5 == 0 ) {
222 // Your system crypt library does not support md5crypt encryption.
225 $cryptedPassword = '{CRYPT}' . crypt( $passwordClear , '$1$' . randomSalt(9) );
229 if( ! defined( 'CRYPT_BLOWFISH' ) || CRYPT_BLOWFISH == 0 ) {
230 // Your system crypt library does not support blowfish encryption.
233 // hardcoded to second blowfish version and set number of rounds
234 $cryptedPassword = '{CRYPT}' . crypt( $passwordClear , '$2a$12$' . randomSalt(13) );
238 $cryptedPassword = '{MD5}' . base64_encode( pack( 'H*' , md5( $passwordClear) ) );
242 if( function_exists('sha1') ) {
243 // use php 4.3.0+ sha1 function, if it is available.
244 $cryptedPassword = '{SHA}' . base64_encode( pack( 'H*' , sha1( $passwordClear) ) );
245 } elseif( function_exists( 'mhash' ) ) {
246 $cryptedPassword = '{SHA}' . base64_encode( mhash( MHASH_SHA1, $passwordClear) );
248 return FALSE; //Your PHP install does not have the mhash() function. Cannot do SHA hashes.
253 if( function_exists( 'mhash' ) && function_exists( 'mhash_keygen_s2k' ) ) {
254 mt_srand( (double) microtime() * 1000000 );
255 $salt = mhash_keygen_s2k( MHASH_SHA1, $passwordClear, substr( pack( 'h*', md5( mt_rand() ) ), 0, 8 ), 4 );
256 $cryptedPassword = '{SSHA}'.base64_encode( mhash( MHASH_SHA1, $passwordClear.$salt ).$salt );
258 return FALSE; //Your PHP install does not have the mhash() function. Cannot do SHA hashes.
263 if( function_exists( 'mhash' ) && function_exists( 'mhash_keygen_s2k' ) ) {
264 mt_srand( (double) microtime() * 1000000 );
265 $salt = mhash_keygen_s2k( MHASH_MD5, $passwordClear, substr( pack( 'h*', md5( mt_rand() ) ), 0, 8 ), 4 );
266 $cryptedPassword = '{SMD5}'.base64_encode( mhash( MHASH_MD5, $passwordClear.$salt ).$salt );
268 return FALSE; //Your PHP install does not have the mhash() function. Cannot do SHA hashes.
273 if (function_exists('hash')) {
274 $cryptedPassword = hash('md4', rcube_charset_convert($passwordClear, RCMAIL_CHARSET, 'UTF-16LE'));
276 /* Your PHP install does not have the hash() function */
283 $cryptedPassword = $passwordClear;
286 return $cryptedPassword;
290 * Code originaly from the phpLDAPadmin development team
291 * http://phpldapadmin.sourceforge.net/
293 * Used to generate a random salt for crypt-style passwords. Salt strings are used
294 * to make pre-built hash cracking dictionaries difficult to use as the hash algorithm uses
295 * not only the user's password but also a randomly generated string. The string is
296 * stored as the first N characters of the hash for reference of hashing algorithms later.
298 * --- added 20021125 by bayu irawan <bayuir@divnet.telkom.co.id> ---
299 * --- ammended 20030625 by S C Rigler <srigler@houston.rr.com> ---
301 * @param int $length The length of the salt string to generate.
302 * @return string The generated salt string.
304 function randomSalt( $length )
306 $possible = '0123456789'.
307 'abcdefghijklmnopqrstuvwxyz'.
308 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
311 // mt_srand((double)microtime() * 1000000);
313 while (strlen($str) < $length)
314 $str .= substr($possible, (rand() % strlen($possible)), 1);