link = $host->get_link(); $this->host = $host; $this->base = new Auth_Base($this->link); $host->add_hook($host::HOOK_AUTH_USER, $this); } private function _log($msg, $level = E_USER_NOTICE, $file = '', $line = 0, $context = '') { $loggerFunction = Logger::get(); if (is_object($loggerFunction)) { $loggerFunction->log_error($level, $msg, $file, $line, $context); } else { trigger_error($msg, $level); } } /** * Logs login attempts * @param string $username Given username that attempts to log in to TTRSS * @param string $result "Logging message for type of result. (Success / Fail)" * @return boolean * @deprecated * * Now that _log support syslog and log levels and graceful fallback user. */ private function _logAttempt($username, $result) { return trigger_error('TT-RSS Login Attempt: user ' . (string) $username . ' attempted to login (' . (string) $result . ') from ' . (string) $ip, E_USER_NOTICE ); } /** * @param string $subject The subject string * @param string $ignore Set of characters to leave untouched * @param int $flags Any combination of LDAP_ESCAPE_* flags to indicate the * set(s) of characters to escape. * @return string **/ function ldap_escape($subject, $ignore = '', $flags = 0) { if (!function_exists('ldap_escape')) { define('LDAP_ESCAPE_FILTER', 0x01); define('LDAP_ESCAPE_DN', 0x02); static $charMaps = array( LDAP_ESCAPE_FILTER => array('\\', '*', '(', ')', "\x00"), LDAP_ESCAPE_DN => array('\\', ',', '=', '+', '<', '>', ';', '"', '#'), ); // Pre-process the char maps on first call if (!isset($charMaps[0])) { $charMaps[0] = array(); for ($i = 0; $i < 256; $i++) { $charMaps[0][chr($i)] = sprintf('\\%02x', $i);; } for ($i = 0, $l = count($charMaps[LDAP_ESCAPE_FILTER]); $i < $l; $i++) { $chr = $charMaps[LDAP_ESCAPE_FILTER][$i]; unset($charMaps[LDAP_ESCAPE_FILTER][$i]); $charMaps[LDAP_ESCAPE_FILTER][$chr] = $charMaps[0][$chr]; } for ($i = 0, $l = count($charMaps[LDAP_ESCAPE_DN]); $i < $l; $i++) { $chr = $charMaps[LDAP_ESCAPE_DN][$i]; unset($charMaps[LDAP_ESCAPE_DN][$i]); $charMaps[LDAP_ESCAPE_DN][$chr] = $charMaps[0][$chr]; } } // Create the base char map to escape $flags = (int)$flags; $charMap = array(); if ($flags & LDAP_ESCAPE_FILTER) { $charMap += $charMaps[LDAP_ESCAPE_FILTER]; } if ($flags & LDAP_ESCAPE_DN) { $charMap += $charMaps[LDAP_ESCAPE_DN]; } if (!$charMap) { $charMap = $charMaps[0]; } // Remove any chars to ignore from the list $ignore = (string)$ignore; for ($i = 0, $l = strlen($ignore); $i < $l; $i++) { unset($charMap[$ignore[$i]]); } // Do the main replacement $result = strtr($subject, $charMap); // Encode leading/trailing spaces if LDAP_ESCAPE_DN is passed if ($flags & LDAP_ESCAPE_DN) { if ($result[0] === ' ') { $result = '\\20' . substr($result, 1); } if ($result[strlen($result) - 1] === ' ') { $result = substr($result, 0, -1) . '\\20'; } } return $result; }else{ return ldap_escape($subject, $ignore, $flags); } } /** * Finds client's IP address * @return string */ private function _getClientIP() { if (!empty($_SERVER['HTTP_CLIENT_IP'])) { //check ip from share internet $ip = $_SERVER['HTTP_CLIENT_IP']; } elseif (!empty($_SERVER['HTTP_X_FORWARDED_FOR'])) { //to check ip is pass from proxy $ip = $_SERVER['HTTP_X_FORWARDED_FOR']; } else { $ip = $_SERVER['REMOTE_ADDR']; } return $ip; } private function _getBindDNWord() { return (strlen($this->_serviceBindDN) > 0 ) ? $this->_serviceBindDN : 'anonymous DN'; } private function _getTempDir() { if (!sys_get_temp_dir()) { $tmpFile = tempnam(); $tmpDir = dirname($tmpFile); unlink($tmpFile); unset($tmpFile); return $tmpDir; } else { return sys_get_temp_dir(); } } /** * Main Authentication method * Required for plugin interface * @param unknown $login User's username * @param unknown $password User's password * @return boolean */ function authenticate($login, $password) { if ($login && $password) { if (!function_exists('ldap_connect')) { trigger_error('auth_ldap requires PHP\'s PECL LDAP package installed.'); return FALSE; } //Loading configuration $this->_debugMode = defined('LDAP_AUTH_DEBUG') ? LDAP_AUTH_DEBUG : FALSE; $this->_anonBeforeBind = defined('LDAP_AUTH_ANONYMOUSBEFOREBIND') ? LDAP_AUTH_ANONYMOUSBEFOREBIND : FALSE; $this->_serviceBindDN = defined('LDAP_AUTH_BINDDN') ? LDAP_AUTH_BINDDN : null; $this->_serviceBindPass = defined('LDAP_AUTH_BINDPW') ? LDAP_AUTH_BINDPW : null; $this->_baseDN = defined('LDAP_AUTH_BASEDN') ? LDAP_AUTH_BASEDN : null; if (!defined('LDAP_AUTH_BASEDN')) { $this->_log('LDAP_AUTH_BASEDN is required and not defined.', E_USER_ERROR); return FALSE; } else { $this->_baseDN = LDAP_AUTH_BASEDN; } $parsedURI = parse_url(LDAP_AUTH_SERVER_URI); if ($parsedURI === FALSE) { $this->_log('Could not parse LDAP_AUTH_SERVER_URI in config.php', E_USER_ERROR); return FALSE; } $this->_host = $parsedURI['host']; $this->_scheme = $parsedURI['scheme']; if (is_int($parsedURI['port'])) { $this->_port = $parsedURI['port']; } else { $this->_port = ($this->_scheme === 'ldaps') ? 636 : 389; } $this->_useTLS = defined('LDAP_AUTH_USETLS') ? LDAP_AUTH_USETLS : FALSE; $this->_logAttempts = defined('LDAP_AUTH_LOG_ATTEMPTS') ? LDAP_AUTH_LOG_ATTEMPTS : FALSE; $this->_ldapLoginAttrib = defined('LDAP_AUTH_LOGIN_ATTRIB') ? LDAP_AUTH_LOGIN_ATTRIB : null; /** Building LDAP connection * */ $ldapConnParams = array( 'host' => $this->_host, 'basedn' => $this->_baseDN, 'port' => $this->_port, 'starttls' => $this->_useTLS ); if ($this->_debugMode) $this->_log(print_r($ldapConnParams, TRUE), E_USER_NOTICE); $ldapConn = @ldap_connect($this->_host, $this->_port); if ($ldapConn === FALSE) { $this->_log('Could not connect to LDAP Server: \'' . $this->_host . '\'', E_USER_ERROR); return false; } /* Enable LDAP protocol version 3. */ if (!@ldap_set_option($ldapConn, LDAP_OPT_PROTOCOL_VERSION, 3)) { $this->_log('Failed to set LDAP Protocol version (LDAP_OPT_PROTOCOL_VERSION) to 3', E_USER_ERROR); return false; } /* Set referral option */ if (!@ldap_set_option($ldapConn, LDAP_OPT_REFERRALS, FALSE)) { $this->_log('Failed to set LDAP Referrals (LDAP_OPT_REFERRALS) to TRUE', E_USER_ERROR); return false; } if (stripos($this->_host, "ldaps:") === FALSE and $this->_useTLS) { if (!@ldap_start_tls($ldapConn)) { $this->_log('Unable to force TLS', E_USER_ERROR); return false; } } $error = @ldap_bind($ldapConn, $this->_serviceBindDN, $this->_serviceBindPass); if ($error === FALSE) { $this->_log( 'LDAP bind(): Bind failed (' . $error . ')with DN ' . $this->_serviceBindDN, E_USER_ERROR ); return FALSE; } else { $this->_log( 'Connected to LDAP Server: ' . LDAP_AUTH_SERVER_URI . ' with ' . $this->_getBindDNWord()); } // Bind with service account if orignal connexion was anonymous /* if (($this->_anonBeforeBind) && (strlen($this->_bindDN > 0))) { $binding=$this->ldapObj->bind($this->_serviceBindDN, $this->_serviceBindPass); if (get_class($binding) !== 'Net_LDAP2') { $this->_log( 'Cound not bind service account: '.$binding->getMessage(),E_USER_ERROR); return FALSE; } else { $this->_log('Bind with '.$this->_serviceBindDN.' successful.',E_USER_NOTICE); } } */ //Searching for user $filterObj = str_replace('???', $this->ldap_escape($login), LDAP_AUTH_SEARCHFILTER); $searchResults = @ldap_search($ldapConn, $this->_baseDN, $filterObj, array('displayName', 'title', 'sAMAccountName', $this->_ldapLoginAttrib), 0, 0, 0); if ($searchResults === FALSE) { $this->_log('LDAP Search Failed on base \'' . $this->_baseDN . '\' for \'' . $filterObj . '\'', E_USER_ERROR); return FALSE; } $count = @ldap_count_entries($ldapConn, $searchResults); if ($count === FALSE) { } elseif ($count > 1) { $this->_log('Multiple DNs found for username ' . (string) $login, E_USER_WARNING); return FALSE; } elseif ($count === 0) { $this->_log((string) $login, 'Unknown User', E_USER_NOTICE); return FALSE; } //Getting user's DN from search $userEntry = @ldap_first_entry($ldapConn, $searchResults); if ($userEntry === FALSE) { $this->_log('LDAP search(): Unable to retrieve result after searching base \'' . $this->_baseDN . '\' for \'' . $filterObj . '\'', E_USER_WARNING); return false; } $userAttributes = @ldap_get_attributes($ldapConn, $userEntry); $userDN = @ldap_get_dn($ldapConn, $userEntry); if ($userDN == FALSE) { $this->_log('LDAP search(): Unable to get DN after searching base \'' . $this->_baseDN . '\' for \'' . $filterObj . '\'', E_USER_WARNING); return false; } //Binding with user's DN. if ($this->_debugMode) $this->_log('Try to bind with user\'s DN: ' . $userDN); $loginAttempt = @ldap_bind($ldapConn, $userDN, $password); if ($loginAttempt === TRUE) { $this->_log('User: ' . (string) $login . ' authentication successful'); if (strlen($this->_ldapLoginAttrib) > 0) { if ($this->_debugMode) $this->_log('Looking up TT-RSS username attribute in ' . $this->_ldapLoginAttrib); $ttrssUsername = $userAttributes[$this->_ldapLoginAttrib][0]; ; @ldap_close($ldapConn); if (!is_string($ttrssUsername)) { $this->_log('Could not find user name attribute ' . $this->_ldapLoginAttrib . ' in LDAP entry', E_USER_WARNING); return FALSE; } return $this->base->auto_create_user($ttrssUsername); } else { @ldap_close($ldapConn); return $this->base->auto_create_user($login); } } else { @ldap_close($ldapConn); $this->_log('User: ' . (string) $login . ' authentication failed'); return FALSE; } } return false; } /** * Returns plugin API version * Required for plugin interface * @return number */ function api_version() { return 2; } } ?>