thomascube
2010-12-17 db1a87cd6c506f2afbd1a37c64cb56ae11120b49
commit | author | age
5fec6d 1 <?php
T 2
3 /**
4  * LDAP Password Driver
5  *
6  * Driver for passwords stored in LDAP
7  * This driver use the PEAR Net_LDAP2 class (http://pear.php.net/package/Net_LDAP2).
8  *
9  * @version 1.1 (2010-04-07)
10  * @author Edouard MOREAU <edouard.moreau@ensma.fr>
11  *
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/).
14  *
15  */
16
17 function password_save($curpass, $passwd)
18 {
19     $rcmail = rcmail::get_instance();
20     require_once ('Net/LDAP2.php');
db1a87 21
5fec6d 22     // Building user DN
T 23     if ($userDN = $rcmail->config->get('password_ldap_userDN_mask')) {
24         $userDN = substitute_vars($userDN);
25     } else {
26         $userDN = search_userdn($rcmail);
27     }
db1a87 28
5fec6d 29     if (empty($userDN)) {
T 30         return PASSWORD_CONNECT_ERROR;
31     }
db1a87 32
5fec6d 33     // Connection Method
T 34     switch($rcmail->config->get('password_ldap_method')) {
35         case 'admin':
36             $binddn = $rcmail->config->get('password_ldap_adminDN');
37             $bindpw = $rcmail->config->get('password_ldap_adminPW');
38             break;
39         case 'user':
40         default:
41             $binddn = $userDN;
42             $bindpw = $curpass;
43             break;
44     }
db1a87 45
5fec6d 46     // Configuration array
T 47     $ldapConfig = array (
48         'binddn'    => $binddn,
49         'bindpw'    => $bindpw,
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'),
55     );
db1a87 56
5fec6d 57     // Connecting using the configuration array
T 58     $ldap = Net_LDAP2::connect($ldapConfig);
db1a87 59
5fec6d 60     // Checking for connection error
T 61     if (PEAR::isError($ldap)) {
62         return PASSWORD_CONNECT_ERROR;
63     }
db1a87 64
5fec6d 65     // Crypting new password
T 66     $newCryptedPassword = hashPassword($passwd, $rcmail->config->get('password_ldap_encodage'));
67     if (!$newCryptedPassword) {
68         return PASSWORD_CRYPT_ERROR;
69     }
db1a87 70
5fec6d 71     // Writing new crypted password to LDAP
T 72     $userEntry = $ldap->getEntry($userDN);
73     if (Net_LDAP2::isError($userEntry)) {
74         return PASSWORD_CONNECT_ERROR;
75     }
db1a87 76
5fec6d 77     $pwattr = $rcmail->config->get('password_ldap_pwattr');
T 78     $force = $rcmail->config->get('password_ldap_force_replace');
79
80     if (!$userEntry->replace(array($pwattr => $newCryptedPassword), $force)) {
81         return PASSWORD_CONNECT_ERROR;
82     }
83
84     // Updating PasswordLastChange Attribute if desired
85     if ($lchattr = $rcmail->config->get('password_ldap_lchattr')) {
86        $current_day = (int)(time() / 86400);
87        if (!$userEntry->replace(array($lchattr => $current_day), $force)) {
88            return PASSWORD_CONNECT_ERROR;
89        }
90     }
91
92     if (Net_LDAP2::isError($userEntry->update())) {
93         return PASSWORD_CONNECT_ERROR;
94     }
95     
96     // All done, no error
97     return PASSWORD_SUCCESS;
98 }
99
100 /**
101  * Bind with searchDN and searchPW and search for the user's DN.
102  * Use search_base and search_filter defined in config file.
103  * Return the found DN.
104  */
105 function search_userdn($rcmail)
106 {
107     $ldapConfig = array (
108         'binddn'    => $rcmail->config->get('password_ldap_searchDN'),
109         'bindpw'    => $rcmail->config->get('password_ldap_searchPW'),
110         'basedn'    => $rcmail->config->get('password_ldap_basedn'),
111         'host'      => $rcmail->config->get('password_ldap_host'),
112         'port'      => $rcmail->config->get('password_ldap_port'),
113         'starttls'  => $rcmail->config->get('password_ldap_starttls'),
114         'version'   => $rcmail->config->get('password_ldap_version'),
115     );
116
117     $ldap = Net_LDAP2::connect($ldapConfig);
118
119     if (PEAR::isError($ldap)) {
120         return '';
121     }
122
123     $base = $rcmail->config->get('password_ldap_search_base');
124     $filter = substitute_vars($rcmail->config->get('password_ldap_search_filter'));
125     $options = array (
126             'scope' => 'sub',
127             'attributes' => array(),
128     );
129
130     $result = $ldap->search($base, $filter, $options);
131     $ldap->done();
132     if (PEAR::isError($result) || ($result->count() != 1)) {
133         return '';
134     }
db1a87 135
5fec6d 136     return $result->current()->dn();
T 137 }
138
139 /**
db1a87 140  * Substitute %login, %name, %domain, %dc in $str.
5fec6d 141  * See plugin config for details.
T 142  */
143 function substitute_vars($str)
144 {
145     $rcmail = rcmail::get_instance();
db1a87 146     $domain = $rcmail->user->get_username('domain');
T 147     $dc     = 'dc='.strtr($domain, array('.' => ',dc=')); // hierarchal domain string
148
5fec6d 149     $str = str_replace(array(
T 150             '%login',
151             '%name',
152             '%domain',
db1a87 153             '%dc',
5fec6d 154         ), array(
T 155             $_SESSION['username'],
156             $rcmail->user->get_username('local'),
db1a87 157             $domain,
T 158             $dc,
5fec6d 159         ), $str
T 160     );
161
162     return $str;
163 }
164
165
166 /**
167  * Code originaly from the phpLDAPadmin development team
168  * http://phpldapadmin.sourceforge.net/
169  *
170  * Hashes a password and returns the hash based on the specified enc_type.
171  *
172  * @param string $passwordClear The password to hash in clear text.
173  * @param string $encodageType Standard LDAP encryption type which must be one of
174  *        crypt, ext_des, md5crypt, blowfish, md5, sha, smd5, ssha, or clear.
175  * @return string The hashed password.
176  *
177  */
178
179 function hashPassword( $passwordClear, $encodageType ) 
180 {
181     $encodageType = strtolower( $encodageType );
182     switch( $encodageType ) {
183         case 'crypt': 
184             $cryptedPassword = '{CRYPT}' . crypt($passwordClear,randomSalt(2)); 
185             break;
db1a87 186
5fec6d 187         case 'ext_des':
T 188             // extended des crypt. see OpenBSD crypt man page.
189             if ( ! defined( 'CRYPT_EXT_DES' ) || CRYPT_EXT_DES == 0 ) {
190                 // Your system crypt library does not support extended DES encryption.
191                 return FALSE;
192             }
193             $cryptedPassword = '{CRYPT}' . crypt( $passwordClear, '_' . randomSalt(8) );
194             break;
195
196         case 'md5crypt':
197             if( ! defined( 'CRYPT_MD5' ) || CRYPT_MD5 == 0 ) {
198                 // Your system crypt library does not support md5crypt encryption.
199                 return FALSE;
200             }
201             $cryptedPassword = '{CRYPT}' . crypt( $passwordClear , '$1$' . randomSalt(9) );
202             break;
203
204         case 'blowfish':
205             if( ! defined( 'CRYPT_BLOWFISH' ) || CRYPT_BLOWFISH == 0 ) {
206                 // Your system crypt library does not support blowfish encryption.
207                 return FALSE;
208             }
209             // hardcoded to second blowfish version and set number of rounds
210             $cryptedPassword = '{CRYPT}' . crypt( $passwordClear , '$2a$12$' . randomSalt(13) );
211             break;
212
213         case 'md5':
214             $cryptedPassword = '{MD5}' . base64_encode( pack( 'H*' , md5( $passwordClear) ) );
215             break;
216
217         case 'sha':
218             if( function_exists('sha1') ) {
219                 // use php 4.3.0+ sha1 function, if it is available.
220                 $cryptedPassword = '{SHA}' . base64_encode( pack( 'H*' , sha1( $passwordClear) ) );
221             } elseif( function_exists( 'mhash' ) ) {
222                 $cryptedPassword = '{SHA}' . base64_encode( mhash( MHASH_SHA1, $passwordClear) );
223             } else {
224                 return FALSE; //Your PHP install does not have the mhash() function. Cannot do SHA hashes.
225             }
226             break;
227
228         case 'ssha':
229             if( function_exists( 'mhash' ) && function_exists( 'mhash_keygen_s2k' ) ) {
230                 mt_srand( (double) microtime() * 1000000 );
231                 $salt = mhash_keygen_s2k( MHASH_SHA1, $passwordClear, substr( pack( 'h*', md5( mt_rand() ) ), 0, 8 ), 4 );
232                 $cryptedPassword = '{SSHA}'.base64_encode( mhash( MHASH_SHA1, $passwordClear.$salt ).$salt );
233             } else {
234                 return FALSE; //Your PHP install does not have the mhash() function. Cannot do SHA hashes.
235             }
236             break;
237
238         case 'smd5':
239             if( function_exists( 'mhash' ) && function_exists( 'mhash_keygen_s2k' ) ) {
240                 mt_srand( (double) microtime() * 1000000 );
241                 $salt = mhash_keygen_s2k( MHASH_MD5, $passwordClear, substr( pack( 'h*', md5( mt_rand() ) ), 0, 8 ), 4 );
242                 $cryptedPassword = '{SMD5}'.base64_encode( mhash( MHASH_MD5, $passwordClear.$salt ).$salt );
243             } else {
244                 return FALSE; //Your PHP install does not have the mhash() function. Cannot do SHA hashes.
245             }
246             break;
247
248         case 'clear':
249         default:
250             $cryptedPassword = $passwordClear;
251     }
252
253     return $cryptedPassword;
254 }
255
256 /**
257  * Code originaly from the phpLDAPadmin development team
258  * http://phpldapadmin.sourceforge.net/
259  *
260  * Used to generate a random salt for crypt-style passwords. Salt strings are used
261  * to make pre-built hash cracking dictionaries difficult to use as the hash algorithm uses
262  * not only the user's password but also a randomly generated string. The string is
263  * stored as the first N characters of the hash for reference of hashing algorithms later.
264  *
265  * --- added 20021125 by bayu irawan <bayuir@divnet.telkom.co.id> ---
266  * --- ammended 20030625 by S C Rigler <srigler@houston.rr.com> ---
267  *
268  * @param int $length The length of the salt string to generate.
269  * @return string The generated salt string.
270  */
db1a87 271 function randomSalt( $length )
5fec6d 272 {
T 273     $possible = '0123456789'.
274         'abcdefghijklmnopqrstuvwxyz'.
275         'ABCDEFGHIJKLMNOPQRSTUVWXYZ'.
276         './';
277     $str = '';
278 //    mt_srand((double)microtime() * 1000000);
279
db1a87 280     while (strlen($str) < $length)
T 281         $str .= substr($possible, (rand() % strlen($possible)), 1);
5fec6d 282
T 283     return $str;
284 }