Mercurial > hg > rc1
comparison plugins/password/drivers/ldap.php @ 0:1e000243b222
vanilla 1.3.3 distro, I hope
| author | Charlie Root |
|---|---|
| date | Thu, 04 Jan 2018 15:50:29 -0500 |
| parents | |
| children |
comparison
equal
deleted
inserted
replaced
| -1:000000000000 | 0:1e000243b222 |
|---|---|
| 1 <?php | |
| 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 2.0 | |
| 10 * @author Edouard MOREAU <edouard.moreau@ensma.fr> | |
| 11 * | |
| 12 * method hashPassword based on code from the phpLDAPadmin development team (http://phpldapadmin.sourceforge.net/). | |
| 13 * method randomSalt based on code from the phpLDAPadmin development team (http://phpldapadmin.sourceforge.net/). | |
| 14 * | |
| 15 * Copyright (C) 2005-2014, The Roundcube Dev Team | |
| 16 * | |
| 17 * This program is free software: you can redistribute it and/or modify | |
| 18 * it under the terms of the GNU General Public License as published by | |
| 19 * the Free Software Foundation, either version 3 of the License, or | |
| 20 * (at your option) any later version. | |
| 21 * | |
| 22 * This program is distributed in the hope that it will be useful, | |
| 23 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| 24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
| 25 * GNU General Public License for more details. | |
| 26 * | |
| 27 * You should have received a copy of the GNU General Public License | |
| 28 * along with this program. If not, see http://www.gnu.org/licenses/. | |
| 29 */ | |
| 30 | |
| 31 class rcube_ldap_password | |
| 32 { | |
| 33 public function save($curpass, $passwd) | |
| 34 { | |
| 35 $rcmail = rcmail::get_instance(); | |
| 36 require_once 'Net/LDAP2.php'; | |
| 37 | |
| 38 // Building user DN | |
| 39 if ($userDN = $rcmail->config->get('password_ldap_userDN_mask')) { | |
| 40 $userDN = self::substitute_vars($userDN); | |
| 41 } | |
| 42 else { | |
| 43 $userDN = $this->search_userdn($rcmail); | |
| 44 } | |
| 45 | |
| 46 if (empty($userDN)) { | |
| 47 return PASSWORD_CONNECT_ERROR; | |
| 48 } | |
| 49 | |
| 50 // Connection Method | |
| 51 switch($rcmail->config->get('password_ldap_method')) { | |
| 52 case 'admin': | |
| 53 $binddn = $rcmail->config->get('password_ldap_adminDN'); | |
| 54 $bindpw = $rcmail->config->get('password_ldap_adminPW'); | |
| 55 break; | |
| 56 case 'user': | |
| 57 default: | |
| 58 $binddn = $userDN; | |
| 59 $bindpw = $curpass; | |
| 60 break; | |
| 61 } | |
| 62 | |
| 63 // Configuration array | |
| 64 $ldapConfig = array ( | |
| 65 'binddn' => $binddn, | |
| 66 'bindpw' => $bindpw, | |
| 67 'basedn' => $rcmail->config->get('password_ldap_basedn'), | |
| 68 'host' => $rcmail->config->get('password_ldap_host', 'localhost'), | |
| 69 'port' => $rcmail->config->get('password_ldap_port', '389'), | |
| 70 'starttls' => $rcmail->config->get('password_ldap_starttls'), | |
| 71 'version' => $rcmail->config->get('password_ldap_version', '3'), | |
| 72 ); | |
| 73 | |
| 74 // Connecting using the configuration array | |
| 75 $ldap = Net_LDAP2::connect($ldapConfig); | |
| 76 | |
| 77 // Checking for connection error | |
| 78 if (is_a($ldap, 'PEAR_Error')) { | |
| 79 return PASSWORD_CONNECT_ERROR; | |
| 80 } | |
| 81 | |
| 82 $force = $rcmail->config->get('password_ldap_force_replace', true); | |
| 83 $pwattr = $rcmail->config->get('password_ldap_pwattr', 'userPassword'); | |
| 84 $lchattr = $rcmail->config->get('password_ldap_lchattr'); | |
| 85 $smbpwattr = $rcmail->config->get('password_ldap_samba_pwattr'); | |
| 86 $smblchattr = $rcmail->config->get('password_ldap_samba_lchattr'); | |
| 87 $samba = $rcmail->config->get('password_ldap_samba'); | |
| 88 $encodage = $rcmail->config->get('password_ldap_encodage', 'crypt'); | |
| 89 | |
| 90 // Support multiple userPassword values where desired. | |
| 91 // multiple encodings can be specified separated by '+' (e.g. "cram-md5+ssha") | |
| 92 $encodages = explode('+', $encodage); | |
| 93 $crypted_pass = array(); | |
| 94 | |
| 95 foreach ($encodages as $enc) { | |
| 96 if ($cpw = password::hash_password($passwd, $enc)) { | |
| 97 $crypted_pass[] = $cpw; | |
| 98 } | |
| 99 } | |
| 100 | |
| 101 // Support password_ldap_samba option for backward compat. | |
| 102 if ($samba && !$smbpwattr) { | |
| 103 $smbpwattr = 'sambaNTPassword'; | |
| 104 $smblchattr = 'sambaPwdLastSet'; | |
| 105 } | |
| 106 | |
| 107 // Crypt new password | |
| 108 if (empty($crypted_pass)) { | |
| 109 return PASSWORD_CRYPT_ERROR; | |
| 110 } | |
| 111 | |
| 112 // Crypt new samba password | |
| 113 if ($smbpwattr && !($samba_pass = password::hash_password($passwd, 'samba'))) { | |
| 114 return PASSWORD_CRYPT_ERROR; | |
| 115 } | |
| 116 | |
| 117 // Writing new crypted password to LDAP | |
| 118 $userEntry = $ldap->getEntry($userDN); | |
| 119 if (Net_LDAP2::isError($userEntry)) { | |
| 120 return PASSWORD_CONNECT_ERROR; | |
| 121 } | |
| 122 | |
| 123 if (!$userEntry->replace(array($pwattr => $crypted_pass), $force)) { | |
| 124 return PASSWORD_CONNECT_ERROR; | |
| 125 } | |
| 126 | |
| 127 // Updating PasswordLastChange Attribute if desired | |
| 128 if ($lchattr) { | |
| 129 $current_day = (int)(time() / 86400); | |
| 130 if (!$userEntry->replace(array($lchattr => $current_day), $force)) { | |
| 131 return PASSWORD_CONNECT_ERROR; | |
| 132 } | |
| 133 } | |
| 134 | |
| 135 // Update Samba password and last change fields | |
| 136 if ($smbpwattr) { | |
| 137 $userEntry->replace(array($smbpwattr => $samba_pass), $force); | |
| 138 } | |
| 139 // Update Samba password last change field | |
| 140 if ($smblchattr) { | |
| 141 $userEntry->replace(array($smblchattr => time()), $force); | |
| 142 } | |
| 143 | |
| 144 if (Net_LDAP2::isError($userEntry->update())) { | |
| 145 return PASSWORD_CONNECT_ERROR; | |
| 146 } | |
| 147 | |
| 148 // All done, no error | |
| 149 return PASSWORD_SUCCESS; | |
| 150 } | |
| 151 | |
| 152 /** | |
| 153 * Bind with searchDN and searchPW and search for the user's DN. | |
| 154 * Use search_base and search_filter defined in config file. | |
| 155 * Return the found DN. | |
| 156 */ | |
| 157 function search_userdn($rcmail) | |
| 158 { | |
| 159 $binddn = $rcmail->config->get('password_ldap_searchDN'); | |
| 160 $bindpw = $rcmail->config->get('password_ldap_searchPW'); | |
| 161 | |
| 162 $ldapConfig = array ( | |
| 163 'basedn' => $rcmail->config->get('password_ldap_basedn'), | |
| 164 'host' => $rcmail->config->get('password_ldap_host', 'localhost'), | |
| 165 'port' => $rcmail->config->get('password_ldap_port', '389'), | |
| 166 'starttls' => $rcmail->config->get('password_ldap_starttls'), | |
| 167 'version' => $rcmail->config->get('password_ldap_version', '3'), | |
| 168 ); | |
| 169 | |
| 170 // allow anonymous searches | |
| 171 if (!empty($binddn)) { | |
| 172 $ldapConfig['binddn'] = $binddn; | |
| 173 $ldapConfig['bindpw'] = $bindpw; | |
| 174 } | |
| 175 | |
| 176 $ldap = Net_LDAP2::connect($ldapConfig); | |
| 177 | |
| 178 if (is_a($ldap, 'PEAR_Error')) { | |
| 179 return ''; | |
| 180 } | |
| 181 | |
| 182 $base = self::substitute_vars($rcmail->config->get('password_ldap_search_base')); | |
| 183 $filter = self::substitute_vars($rcmail->config->get('password_ldap_search_filter')); | |
| 184 $options = array ( | |
| 185 'scope' => 'sub', | |
| 186 'attributes' => array(), | |
| 187 ); | |
| 188 | |
| 189 $result = $ldap->search($base, $filter, $options); | |
| 190 if (is_a($result, 'PEAR_Error') || ($result->count() != 1)) { | |
| 191 $ldap->done(); | |
| 192 return ''; | |
| 193 } | |
| 194 $userDN = $result->current()->dn(); | |
| 195 $ldap->done(); | |
| 196 | |
| 197 return $userDN; | |
| 198 } | |
| 199 | |
| 200 /** | |
| 201 * Substitute %login, %name, %domain, %dc in $str | |
| 202 * See plugin config for details | |
| 203 */ | |
| 204 static function substitute_vars($str) | |
| 205 { | |
| 206 $str = str_replace('%login', $_SESSION['username'], $str); | |
| 207 $str = str_replace('%l', $_SESSION['username'], $str); | |
| 208 | |
| 209 $parts = explode('@', $_SESSION['username']); | |
| 210 | |
| 211 if (count($parts) == 2) { | |
| 212 $dc = 'dc='.strtr($parts[1], array('.' => ',dc=')); // hierarchal domain string | |
| 213 | |
| 214 $str = str_replace('%name', $parts[0], $str); | |
| 215 $str = str_replace('%n', $parts[0], $str); | |
| 216 $str = str_replace('%dc', $dc, $str); | |
| 217 $str = str_replace('%domain', $parts[1], $str); | |
| 218 $str = str_replace('%d', $parts[1], $str); | |
| 219 } | |
| 220 | |
| 221 return $str; | |
| 222 } | |
| 223 } |
