/** * Establish a connection to the LDAP server */ private function _connect() { $rcube = rcube::get_instance(); if ($this->ready) { return true; } if (!is_array($this->prop['hosts'])) { $this->prop['hosts'] = array($this->prop['hosts']); } // try to connect + bind for every host configured // with OpenLDAP 2.x ldap_connect() always succeeds but ldap_bind will fail if host isn't reachable // see http://www.php.net/manual/en/function.ldap-connect.php foreach ($this->prop['hosts'] as $host) { // skip host if connection failed if (!$this->ldap->connect($host)) { continue; } // See if the directory is writeable. if ($this->prop['writable']) { $this->readonly = false; } $bind_pass = $this->prop['bind_pass']; $bind_user = $this->prop['bind_user']; $bind_dn = $this->prop['bind_dn']; $this->base_dn = $this->prop['base_dn']; $this->groups_base_dn = $this->prop['groups']['base_dn'] ? $this->prop['groups']['base_dn'] : $this->base_dn; // User specific access, generate the proper values to use. if ($this->prop['user_specific']) { // No password set, use the session password if (empty($bind_pass)) { $bind_pass = $rcube->get_user_password(); } // Get the pieces needed for variable replacement. if ($fu = $rcube->get_user_email()) { list($u, $d) = explode('@', $fu); } else { $d = $this->mail_domain; } $dc = 'dc=' . strtr($d, array('.' => ',dc=')); // hierarchal domain string $replaces = array('%dn' => '', '%dc' => $dc, '%d' => $d, '%fu' => $fu, '%u' => $u); // Search for the dn to use to authenticate if ($this->prop['search_base_dn'] && $this->prop['search_filter'] && (strstr($bind_dn, '%dn') || strstr($this->base_dn, '%dn') || strstr($this->groups_base_dn, '%dn'))) { $search_attribs = array('uid'); if ($search_bind_attrib = (array) $this->prop['search_bind_attrib']) { foreach ($search_bind_attrib as $r => $attr) { $search_attribs[] = $attr; $replaces[$r] = ''; } } $search_bind_dn = strtr($this->prop['search_bind_dn'], $replaces); $search_base_dn = strtr($this->prop['search_base_dn'], $replaces); $search_filter = strtr($this->prop['search_filter'], $replaces); $cache_key = 'DN.' . md5("{$host}:{$search_bind_dn}:{$search_base_dn}:{$search_filter}:" . $this->prop['search_bind_pw']); if ($this->cache && ($dn = $this->cache->get($cache_key))) { $replaces['%dn'] = $dn; } else { $ldap = $this->ldap; if (!empty($search_bind_dn) && !empty($this->prop['search_bind_pw'])) { // To protect from "Critical extension is unavailable" error // we need to use a separate LDAP connection if (!empty($this->prop['vlv'])) { $ldap = new rcube_ldap_generic($this->prop); $ldap->config_set(array('cache' => $this->cache, 'debug' => $this->debug)); if (!$ldap->connect($host)) { continue; } } if (!$ldap->bind($search_bind_dn, $this->prop['search_bind_pw'])) { continue; // bind failed, try next host } } $res = $ldap->search($search_base_dn, $search_filter, 'sub', $search_attribs); if ($res) { $res->rewind(); $replaces['%dn'] = key($res->entries(TRUE)); // add more replacements from 'search_bind_attrib' config if ($search_bind_attrib) { $res = $res->current(); foreach ($search_bind_attrib as $r => $attr) { $replaces[$r] = $res[$attr][0]; } } } if ($ldap != $this->ldap) { $ldap->close(); } } // DN not found if (empty($replaces['%dn'])) { if (!empty($this->prop['search_dn_default'])) { $replaces['%dn'] = $this->prop['search_dn_default']; } else { rcube::raise_error(array('code' => 100, 'type' => 'ldap', 'file' => __FILE__, 'line' => __LINE__, 'message' => "DN not found using LDAP search."), true); continue; } } if ($this->cache && !empty($replaces['%dn'])) { $this->cache->set($cache_key, $replaces['%dn']); } } // Replace the bind_dn and base_dn variables. $bind_dn = strtr($bind_dn, $replaces); $this->base_dn = strtr($this->base_dn, $replaces); $this->groups_base_dn = strtr($this->groups_base_dn, $replaces); // replace placeholders in filter settings if (!empty($this->prop['filter'])) { $this->prop['filter'] = strtr($this->prop['filter'], $replaces); } foreach (array('base_dn', 'filter', 'member_filter') as $k) { if (!empty($this->prop['groups'][$k])) { $this->prop['groups'][$k] = strtr($this->prop['groups'][$k], $replaces); } } if (is_array($this->prop['group_filters'])) { foreach ($this->prop['group_filters'] as $i => $gf) { if (!empty($gf['base_dn'])) { $this->prop['group_filters'][$i]['base_dn'] = strtr($gf['base_dn'], $replaces); } if (!empty($gf['filter'])) { $this->prop['group_filters'][$i]['filter'] = strtr($gf['filter'], $replaces); } } } if (empty($bind_user)) { $bind_user = $u; } } if (empty($bind_pass)) { $this->ready = true; } else { if (!empty($bind_dn)) { $this->ready = $this->ldap->bind($bind_dn, $bind_pass); } else { if (!empty($this->prop['auth_cid'])) { $this->ready = $this->ldap->sasl_bind($this->prop['auth_cid'], $bind_pass, $bind_user); } else { $this->ready = $this->ldap->sasl_bind($bind_user, $bind_pass); } } } // connection established, we're done here if ($this->ready) { break; } } // end foreach hosts if (!is_resource($this->ldap->conn)) { rcube::raise_error(array('code' => 100, 'type' => 'ldap', 'file' => __FILE__, 'line' => __LINE__, 'message' => "Could not connect to any LDAP server, last tried {$host}"), true); return false; } return $this->ready; }