commit | author | age
|
48e9c1
|
1 |
<?php |
T |
2 |
|
|
3 |
/** |
|
4 |
* Simple LDAP Password Driver |
|
5 |
* |
|
6 |
* Driver for passwords stored in LDAP |
|
7 |
* This driver is based on Edouard's LDAP Password Driver, but does not |
|
8 |
* require PEAR's Net_LDAP2 to be installed |
|
9 |
* |
|
10 |
* @version 2.0 |
|
11 |
* @author Wout Decre <wout@canodus.be> |
|
12 |
*/ |
|
13 |
|
|
14 |
class rcube_ldap_simple_password |
|
15 |
{ |
|
16 |
function save($curpass, $passwd) |
|
17 |
{ |
d6938b
|
18 |
$rcmail = rcmail::get_instance(); |
48e9c1
|
19 |
|
d6938b
|
20 |
// Connect |
AM |
21 |
if (!$ds = ldap_connect($rcmail->config->get('password_ldap_host'), $rcmail->config->get('password_ldap_port'))) { |
|
22 |
ldap_unbind($ds); |
|
23 |
return PASSWORD_CONNECT_ERROR; |
|
24 |
} |
48e9c1
|
25 |
|
d6938b
|
26 |
// Set protocol version |
AM |
27 |
if (!ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, $rcmail->config->get('password_ldap_version'))) { |
|
28 |
ldap_unbind($ds); |
|
29 |
return PASSWORD_CONNECT_ERROR; |
|
30 |
} |
48e9c1
|
31 |
|
d6938b
|
32 |
// Start TLS |
AM |
33 |
if ($rcmail->config->get('password_ldap_starttls')) { |
|
34 |
if (!ldap_start_tls($ds)) { |
|
35 |
ldap_unbind($ds); |
|
36 |
return PASSWORD_CONNECT_ERROR; |
|
37 |
} |
|
38 |
} |
48e9c1
|
39 |
|
d6938b
|
40 |
// Build user DN |
AM |
41 |
if ($user_dn = $rcmail->config->get('password_ldap_userDN_mask')) { |
|
42 |
$user_dn = $this->substitute_vars($user_dn); |
|
43 |
} |
|
44 |
else { |
|
45 |
$user_dn = $this->search_userdn($rcmail, $ds); |
|
46 |
} |
48e9c1
|
47 |
|
d6938b
|
48 |
if (empty($user_dn)) { |
AM |
49 |
ldap_unbind($ds); |
|
50 |
return PASSWORD_CONNECT_ERROR; |
|
51 |
} |
48e9c1
|
52 |
|
d6938b
|
53 |
// Connection method |
AM |
54 |
switch ($rcmail->config->get('password_ldap_method')) { |
|
55 |
case 'admin': |
|
56 |
$binddn = $rcmail->config->get('password_ldap_adminDN'); |
|
57 |
$bindpw = $rcmail->config->get('password_ldap_adminPW'); |
|
58 |
break; |
|
59 |
case 'user': |
|
60 |
default: |
|
61 |
$binddn = $user_dn; |
|
62 |
$bindpw = $curpass; |
|
63 |
break; |
|
64 |
} |
48e9c1
|
65 |
|
d6938b
|
66 |
$crypted_pass = $this->hash_password($passwd, $rcmail->config->get('password_ldap_encodage')); |
AM |
67 |
$lchattr = $rcmail->config->get('password_ldap_lchattr'); |
|
68 |
$pwattr = $rcmail->config->get('password_ldap_pwattr'); |
48e9c1
|
69 |
$smbpwattr = $rcmail->config->get('password_ldap_samba_pwattr'); |
T |
70 |
$smblchattr = $rcmail->config->get('password_ldap_samba_lchattr'); |
|
71 |
$samba = $rcmail->config->get('password_ldap_samba'); |
|
72 |
|
|
73 |
// Support password_ldap_samba option for backward compat. |
|
74 |
if ($samba && !$smbpwattr) { |
|
75 |
$smbpwattr = 'sambaNTPassword'; |
|
76 |
$smblchattr = 'sambaPwdLastSet'; |
|
77 |
} |
|
78 |
|
d6938b
|
79 |
// Crypt new password |
AM |
80 |
if (!$crypted_pass) { |
|
81 |
return PASSWORD_CRYPT_ERROR; |
|
82 |
} |
48e9c1
|
83 |
|
T |
84 |
// Crypt new Samba password |
|
85 |
if ($smbpwattr && !($samba_pass = $this->hash_password($passwd, 'samba'))) { |
d6938b
|
86 |
return PASSWORD_CRYPT_ERROR; |
48e9c1
|
87 |
} |
T |
88 |
|
d6938b
|
89 |
// Bind |
AM |
90 |
if (!ldap_bind($ds, $binddn, $bindpw)) { |
|
91 |
ldap_unbind($ds); |
|
92 |
return PASSWORD_CONNECT_ERROR; |
|
93 |
} |
48e9c1
|
94 |
|
d6938b
|
95 |
$entree[$pwattr] = $crypted_pass; |
48e9c1
|
96 |
|
d6938b
|
97 |
// Update PasswordLastChange Attribute if desired |
AM |
98 |
if ($lchattr) { |
|
99 |
$entree[$lchattr] = (int)(time() / 86400); |
|
100 |
} |
48e9c1
|
101 |
|
T |
102 |
// Update Samba password |
|
103 |
if ($smbpwattr) { |
|
104 |
$entree[$smbpwattr] = $samba_pass; |
|
105 |
} |
|
106 |
|
|
107 |
// Update Samba password last change |
|
108 |
if ($smblchattr) { |
|
109 |
$entree[$smblchattr] = time(); |
|
110 |
} |
|
111 |
|
d6938b
|
112 |
if (!ldap_modify($ds, $user_dn, $entree)) { |
AM |
113 |
ldap_unbind($ds); |
|
114 |
return PASSWORD_CONNECT_ERROR; |
|
115 |
} |
48e9c1
|
116 |
|
d6938b
|
117 |
// All done, no error |
AM |
118 |
ldap_unbind($ds); |
|
119 |
return PASSWORD_SUCCESS; |
48e9c1
|
120 |
} |
T |
121 |
|
|
122 |
/** |
|
123 |
* Bind with searchDN and searchPW and search for the user's DN |
|
124 |
* Use search_base and search_filter defined in config file |
|
125 |
* Return the found DN |
|
126 |
*/ |
|
127 |
function search_userdn($rcmail, $ds) |
|
128 |
{ |
d6938b
|
129 |
/* Bind */ |
AM |
130 |
if (!ldap_bind($ds, $rcmail->config->get('password_ldap_searchDN'), $rcmail->config->get('password_ldap_searchPW'))) { |
|
131 |
return false; |
|
132 |
} |
48e9c1
|
133 |
|
d6938b
|
134 |
/* Search for the DN */ |
AM |
135 |
if (!$sr = ldap_search($ds, $rcmail->config->get('password_ldap_search_base'), $this->substitute_vars($rcmail->config->get('password_ldap_search_filter')))) { |
|
136 |
return false; |
|
137 |
} |
48e9c1
|
138 |
|
d6938b
|
139 |
/* If no or more entries were found, return false */ |
AM |
140 |
if (ldap_count_entries($ds, $sr) != 1) { |
|
141 |
return false; |
|
142 |
} |
48e9c1
|
143 |
|
d6938b
|
144 |
return ldap_get_dn($ds, ldap_first_entry($ds, $sr)); |
48e9c1
|
145 |
} |
T |
146 |
|
|
147 |
/** |
|
148 |
* Substitute %login, %name, %domain, %dc in $str |
|
149 |
* See plugin config for details |
|
150 |
*/ |
|
151 |
function substitute_vars($str) |
|
152 |
{ |
d6938b
|
153 |
$str = str_replace('%login', $_SESSION['username'], $str); |
AM |
154 |
$str = str_replace('%l', $_SESSION['username'], $str); |
48e9c1
|
155 |
|
d6938b
|
156 |
$parts = explode('@', $_SESSION['username']); |
48e9c1
|
157 |
|
d6938b
|
158 |
if (count($parts) == 2) { |
48e9c1
|
159 |
$dc = 'dc='.strtr($parts[1], array('.' => ',dc=')); // hierarchal domain string |
T |
160 |
|
d6938b
|
161 |
$str = str_replace('%name', $parts[0], $str); |
48e9c1
|
162 |
$str = str_replace('%n', $parts[0], $str); |
T |
163 |
$str = str_replace('%dc', $dc, $str); |
d6938b
|
164 |
$str = str_replace('%domain', $parts[1], $str); |
AM |
165 |
$str = str_replace('%d', $parts[1], $str); |
|
166 |
} |
48e9c1
|
167 |
|
d6938b
|
168 |
return $str; |
48e9c1
|
169 |
} |
T |
170 |
|
|
171 |
/** |
|
172 |
* Code originaly from the phpLDAPadmin development team |
|
173 |
* http://phpldapadmin.sourceforge.net/ |
|
174 |
* |
|
175 |
* Hashes a password and returns the hash based on the specified enc_type |
|
176 |
*/ |
|
177 |
function hash_password($password_clear, $encodage_type) |
|
178 |
{ |
d6938b
|
179 |
$encodage_type = strtolower($encodage_type); |
AM |
180 |
switch ($encodage_type) { |
|
181 |
case 'crypt': |
|
182 |
$crypted_password = '{CRYPT}' . crypt($password_clear, $this->random_salt(2)); |
|
183 |
break; |
|
184 |
case 'ext_des': |
|
185 |
/* Extended DES crypt. see OpenBSD crypt man page */ |
|
186 |
if (!defined('CRYPT_EXT_DES') || CRYPT_EXT_DES == 0) { |
|
187 |
/* Your system crypt library does not support extended DES encryption */ |
|
188 |
return false; |
|
189 |
} |
|
190 |
$crypted_password = '{CRYPT}' . crypt($password_clear, '_' . $this->random_salt(8)); |
|
191 |
break; |
|
192 |
case 'md5crypt': |
|
193 |
if (!defined('CRYPT_MD5') || CRYPT_MD5 == 0) { |
|
194 |
/* Your system crypt library does not support md5crypt encryption */ |
|
195 |
return false; |
|
196 |
} |
|
197 |
$crypted_password = '{CRYPT}' . crypt($password_clear, '$1$' . $this->random_salt(9)); |
|
198 |
break; |
|
199 |
case 'blowfish': |
|
200 |
if (!defined('CRYPT_BLOWFISH') || CRYPT_BLOWFISH == 0) { |
|
201 |
/* Your system crypt library does not support blowfish encryption */ |
|
202 |
return false; |
|
203 |
} |
|
204 |
/* Hardcoded to second blowfish version and set number of rounds */ |
|
205 |
$crypted_password = '{CRYPT}' . crypt($password_clear, '$2a$12$' . $this->random_salt(13)); |
|
206 |
break; |
|
207 |
case 'md5': |
|
208 |
$crypted_password = '{MD5}' . base64_encode(pack('H*', md5($password_clear))); |
|
209 |
break; |
|
210 |
case 'sha': |
|
211 |
if (function_exists('sha1')) { |
|
212 |
/* Use PHP 4.3.0+ sha1 function, if it is available */ |
|
213 |
$crypted_password = '{SHA}' . base64_encode(pack('H*', sha1($password_clear))); |
|
214 |
} else if (function_exists('mhash')) { |
|
215 |
$crypted_password = '{SHA}' . base64_encode(mhash(MHASH_SHA1, $password_clear)); |
|
216 |
} else { |
|
217 |
/* Your PHP install does not have the mhash() function */ |
|
218 |
return false; |
|
219 |
} |
|
220 |
break; |
|
221 |
case 'ssha': |
|
222 |
if (function_exists('mhash') && function_exists('mhash_keygen_s2k')) { |
|
223 |
mt_srand((double) microtime() * 1000000 ); |
|
224 |
$salt = mhash_keygen_s2k(MHASH_SHA1, $password_clear, substr(pack('h*', md5(mt_rand())), 0, 8), 4); |
|
225 |
$crypted_password = '{SSHA}' . base64_encode(mhash(MHASH_SHA1, $password_clear . $salt) . $salt); |
|
226 |
} else { |
|
227 |
/* Your PHP install does not have the mhash() function */ |
|
228 |
return false; |
|
229 |
} |
|
230 |
break; |
|
231 |
case 'smd5': |
|
232 |
if (function_exists('mhash') && function_exists('mhash_keygen_s2k')) { |
|
233 |
mt_srand((double) microtime() * 1000000 ); |
|
234 |
$salt = mhash_keygen_s2k(MHASH_MD5, $password_clear, substr(pack('h*', md5(mt_rand())), 0, 8), 4); |
|
235 |
$crypted_password = '{SMD5}' . base64_encode(mhash(MHASH_MD5, $password_clear . $salt) . $salt); |
|
236 |
} else { |
|
237 |
/* Your PHP install does not have the mhash() function */ |
|
238 |
return false; |
|
239 |
} |
|
240 |
break; |
48e9c1
|
241 |
case 'samba': |
T |
242 |
if (function_exists('hash')) { |
61be82
|
243 |
$crypted_password = hash('md4', rcube_charset::convert($password_clear, RCUBE_CHARSET, 'UTF-16LE')); |
48e9c1
|
244 |
$crypted_password = strtoupper($crypted_password); |
T |
245 |
} else { |
d6938b
|
246 |
/* Your PHP install does not have the hash() function */ |
AM |
247 |
return false; |
48e9c1
|
248 |
} |
T |
249 |
break; |
d6938b
|
250 |
case 'clear': |
AM |
251 |
default: |
|
252 |
$crypted_password = $password_clear; |
|
253 |
} |
48e9c1
|
254 |
|
d6938b
|
255 |
return $crypted_password; |
48e9c1
|
256 |
} |
T |
257 |
|
|
258 |
/** |
|
259 |
* Code originaly from the phpLDAPadmin development team |
|
260 |
* http://phpldapadmin.sourceforge.net/ |
|
261 |
* |
|
262 |
* Used to generate a random salt for crypt-style passwords |
|
263 |
*/ |
|
264 |
function random_salt($length) |
|
265 |
{ |
d6938b
|
266 |
$possible = '0123456789' . 'abcdefghijklmnopqrstuvwxyz' . 'ABCDEFGHIJKLMNOPQRSTUVWXYZ' . './'; |
AM |
267 |
$str = ''; |
|
268 |
// mt_srand((double)microtime() * 1000000); |
48e9c1
|
269 |
|
d6938b
|
270 |
while (strlen($str) < $length) { |
AM |
271 |
$str .= substr($possible, (rand() % strlen($possible)), 1); |
|
272 |
} |
48e9c1
|
273 |
|
d6938b
|
274 |
return $str; |
48e9c1
|
275 |
} |
T |
276 |
} |