settings['ldap_hostname']; $port = $GLOBALS['ldap_config']->settings['ldap_port']; if(!$port) $port = DEFAULT_PORT; $GLOBALS['log']->debug("ldapauth: Connecting to LDAP server: $server"); $ldapconn = ldap_connect($server, $port); $error = ldap_errno($ldapconn); if($this->loginError($error)){ return ''; } @ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3); @ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0); // required for AD $bind_user = $this->ldap_rdn_lookup($name, $password); $GLOBALS['log']->debug("ldapauth.ldap_authenticate_user: ldap_rdn_lookup returned bind_user=" . $bind_user); if (!$bind_user) { $GLOBALS['log']->fatal("SECURITY: ldapauth: failed LDAP bind (login) by " . $name . ", could not construct bind_user"); return ''; } // MRF - Bug #18578 - punctuation was being passed as HTML entities, i.e. & $bind_password = html_entity_decode($password,ENT_QUOTES); $GLOBALS['log']->info("ldapauth: Binding user " . $bind_user); $bind = ldap_bind($ldapconn, $bind_user, $bind_password); $error = ldap_errno($ldapconn); if($this->loginError($error)){ $GLOBALS['log']->fatal('[LDAP] ATTEMPTING BIND USING BASE DN PARAMS'); $bind = ldap_bind($ldapconn, $GLOBALS['ldap_config']->settings['ldap_bind_attr'] . "=" . $bind_user . "," . $GLOBALS['ldap_config']->settings['ldap_base_dn'], $bind_password); $error = ldap_errno($ldapconn); if($this->loginError($error)){ return ''; } } $GLOBALS['log']->info("ldapauth: Bind attempt complete."); if ($bind) { // Authentication succeeded, get info from LDAP directory $attrs = array_keys($GLOBALS['ldapConfig']['users']['fields']); $base_dn = $GLOBALS['ldap_config']->settings['ldap_base_dn']; $name_filter = $this->getUserNameFilter($name); //add the group user attribute that we will compare to the group attribute for membership validation if group membership is turned on if(!empty($GLOBALS['ldap_config']->settings['ldap_group']) && !empty($GLOBALS['ldap_config']->settings['ldap_group_user_attr']) && !empty($GLOBALS['ldap_config']->settings['ldap_group_attr'])){ if(!in_array($attrs, $GLOBALS['ldap_config']->settings['ldap_group_user_attr'])){ $attrs[] = $GLOBALS['ldap_config']->settings['ldap_group_user_attr']; } } $GLOBALS['log']->debug("ldapauth: Fetching user info from Directory."); $result = @ldap_search($ldapconn, $base_dn, $name_filter, $attrs); $error = ldap_errno($ldapconn); if($this->loginError($error)){ return ''; } $GLOBALS['log']->debug("ldapauth: ldap_search complete."); $info = @ldap_get_entries($ldapconn, $result); $error = ldap_errno($ldapconn); if($this->loginError($error)){ return ''; } $GLOBALS['log']->debug("ldapauth: User info from Directory fetched."); // some of these don't seem to work $this->ldapUserInfo = array(); foreach($GLOBALS['ldapConfig']['users']['fields'] as $key=>$value){ //MRF - BUG:19765 $key = strtolower($key); if(isset($info[0]) && isset($info[0][$key]) && isset($info[0][$key][0])){ $this->ldapUserInfo[$value] = $info[0][$key][0]; } } //we should check that a user is a member of a specific group if(!empty($GLOBALS['ldap_config']->settings['ldap_group'])){ $GLOBALS['log']->debug("LDAPAuth: scanning group for user membership"); $group_user_attr = $GLOBALS['ldap_config']->settings['ldap_group_user_attr']; $group_attr = $GLOBALS['ldap_config']->settings['ldap_group_attr']; if(!isset($info[0][$group_user_attr])){ $GLOBALS['log']->fatal("ldapauth: $group_user_attr not found for user $name cannot authenticate against an LDAP group"); ldap_close($ldapconn); return ''; }else{ $user_uid = $info[0][$group_user_attr]; } //user is not a member of the group if the count is zero get the logs and return no id so it fails login if(!isset($user_uid[0]) || ldap_count_entries($ldapconn, ldap_search($ldapconn,$GLOBALS['ldap_config']->settings['ldap_group_name'] . ",". $GLOBALS['ldap_config']->settings['ldap_group_dn'] ,"($group_attr=" . $user_uid[0] . ")")) == 0){ $GLOBALS['log']->fatal("ldapauth: User ($name) is not a member of the LDAP group"); $user_id = var_export($user_uid, true); $GLOBALS['log']->debug("ldapauth: Group DN:{$GLOBALS['ldap_config']->settings['ldap_group_dn']} Group Name: " . $GLOBALS['ldap_config']->settings['ldap_group_name'] . " Group Attribute: $group_attr User Attribute: $group_user_attr :(" . $user_uid[0] . ")"); ldap_close($ldapconn); return ''; } } ldap_close($ldapconn); $dbresult = $GLOBALS['db']->query("SELECT id, status FROM users WHERE user_name='" . $name . "' AND deleted = 0"); //user already exists use this one if($row = $GLOBALS['db']->fetchByAssoc($dbresult)){ if($row['status'] != 'Inactive') return $row['id']; else return ''; } //create a new user and return the user if($GLOBALS['ldap_config']->settings['ldap_auto_create_users']){ return $this->createUser($name); } return ''; } else { $GLOBALS['log']->fatal("SECURITY: failed LDAP bind (login) by $this->user_name using bind_user=$bind_user"); $GLOBALS['log']->fatal("ldapauth: failed LDAP bind (login) by $this->user_name using bind_user=$bind_user"); ldap_close($ldapconn); return ''; } } /** * takes in a name and creates the appropriate search filter for that user name including any additional filters specified in the system settings page * @param $name * @return String */ function getUserNameFilter($name){ $name_filter = "(" . $GLOBALS['ldap_config']->settings['ldap_login_attr']. "=" . $name . ")"; //add the additional user filter if it is specified if(!empty($GLOBALS['ldap_config']->settings['ldap_login_filter'])){ $add_filter = $GLOBALS['ldap_config']->settings['ldap_login_filter']; if(substr($add_filter, 0, 1) !== "("){ $add_filter = "(" . $add_filter . ")"; } $name_filter = "(&" . $name_filter . $add_filter . ")"; } return $name_filter; } /** * Creates a user with the given User Name and returns the id of that new user * populates the user with what was set in ldapUserInfo * * @param STRING $name * @return STRING $id */ function createUser($name){ $user = new User(); $user->user_name = $name; foreach($this->ldapUserInfo as $key=>$value){ $user->$key = $value; } $user->employee_status = 'Active'; $user->status = 'Active'; $user->is_admin = 0; $user->external_auth_only = 1; $user->save(); return $user->id; } /** * this is called when a user logs in * * @param STRING $name * @param STRING $password * @return boolean */ function loadUserOnLogin($name, $password) { global $mod_strings; // Check if the LDAP extensions are loaded if(!function_exists('ldap_connect')) { $error = $mod_strings['LBL_LDAP_EXTENSION_ERROR']; $GLOBALS['log']->fatal($error); $_SESSION['login_error'] = $error; return false; } global $login_error; $GLOBALS['ldap_config'] = new Administration(); $GLOBALS['ldap_config']->retrieveSettings('ldap'); $GLOBALS['log']->debug("Starting user load for ". $name); if(empty($name) || empty($password)) return false; checkAuthUserStatus(); $user_id = $this->authenticateUser($name, $password); if(empty($user_id)) { //check if the user can login as a normal sugar user $GLOBALS['log']->fatal('SECURITY: User authentication for '.$name.' failed'); return false; } $this->loadUserOnSession($user_id); return true; } /** * Called with the error number of the last call if the error number is 0 * there was no error otherwise it converts the error to a string and logs it as fatal * * @param INT $error * @return boolean */ function loginError($error){ if(empty($error)) return false; $errorstr = ldap_err2str($error); // BEGIN SUGAR INT $_SESSION['login_error'] = $errorstr; /* // END SUGAR INT $_SESSION['login_error'] = translate('ERR_INVALID_PASSWORD', 'Users'); // BEGIN SUGAR INT */ // END SUGAR INT $GLOBALS['log']->fatal('[LDAP ERROR]['. $error . ']'.$errorstr); return true; } /** * @return string appropriate value for username when binding to directory server. * @param string $user_name the value provided in login form * @desc Take the login username and return either said username for AD or lookup * distinguished name using anonymous credentials for OpenLDAP. * Contributions by Erik Mitchell erikm@logicpd.com */ function ldap_rdn_lookup($user_name, $password) { // MFH BUG# 14547 - Added htmlspecialchars_decode() $server = $GLOBALS['ldap_config']->settings['ldap_hostname']; $base_dn = htmlspecialchars_decode($GLOBALS['ldap_config']->settings['ldap_base_dn']); if(!empty($GLOBALS['ldap_config']->settings['ldap_authentication'])){ $admin_user = htmlspecialchars_decode($GLOBALS['ldap_config']->settings['ldap_admin_user']); $admin_password = htmlspecialchars_decode($GLOBALS['ldap_config']->settings['ldap_admin_password']); }else{ $admin_user = ''; $admin_password = ''; } $user_attr = $GLOBALS['ldap_config']->settings['ldap_login_attr']; $bind_attr = $GLOBALS['ldap_config']->settings['ldap_bind_attr']; $port = $GLOBALS['ldap_config']->settings['ldap_port']; if(!$port) $port = DEFAULT_PORT; $ldapconn = ldap_connect($server, $port); $error = ldap_errno($ldapconn); if($this->loginError($error)){ return false; } ldap_set_option($ldapconn, LDAP_OPT_PROTOCOL_VERSION, 3); ldap_set_option($ldapconn, LDAP_OPT_REFERRALS, 0); // required for AD //if we are going to connect anonymously lets at least try to connect with the user connecting if(empty($admin_user)){ $bind = @ldap_bind($ldapconn, $user_name, $password); $error = ldap_errno($ldapconn); } if(empty($bind)){ $bind = @ldap_bind($ldapconn, $admin_user, $admin_password); $error = ldap_errno($ldapconn); } if($this->loginError($error)){ return false; } if (!$bind) { $GLOBALS['log']->warn("ldapauth.ldap_rdn_lookup: Could not bind with admin user, trying to bind anonymously"); $bind = @ldap_bind($ldapconn); $error = ldap_errno($ldapconn); if($this->loginError($error)){ return false; } if (!$bind) { $GLOBALS['log']->warn("ldapauth.ldap_rdn_lookup: Could not bind anonymously, returning username"); return $user_name; } } // If we get here we were able to bind somehow $search_filter = $this->getUserNameFilter($user_name); $GLOBALS['log']->info("ldapauth.ldap_rdn_lookup: Bind succeeded, searching for $user_attr=$user_name"); $GLOBALS['log']->debug("ldapauth.ldap_rdn_lookup: base_dn:$base_dn , search_filter:$search_filter"); $result = @ldap_search($ldapconn, $base_dn , $search_filter, array("dn", $bind_attr)); $error = ldap_errno($ldapconn); if($this->loginError($error)){ return false; } $info = ldap_get_entries($ldapconn, $result); if($info['count'] == 0){ return false; } ldap_unbind($ldapconn); $GLOBALS['log']->info("ldapauth.ldap_rdn_lookup: Search result:\nldapauth.ldap_rdn_lookup: " . count($info)); if ($bind_attr == "dn") { $found_bind_user = $info[0]['dn']; } else { $found_bind_user = $info[0][strtolower($bind_attr)][0]; } $GLOBALS['log']->info("ldapauth.ldap_rdn_lookup: found_bind_user=" . $found_bind_user); if (!empty($found_bind_user)) { return $found_bind_user; } elseif ($user_attr == $bind_attr) { return $user_name; } else { return false; } } } ?>