diff --git a/application/config/constants.php b/application/config/constants.php index f21b6c962..cbbf55d8b 100644 --- a/application/config/constants.php +++ b/application/config/constants.php @@ -57,14 +57,6 @@ define('AUTH_SUCCESS', 0); define('AUTH_NOT_AUTHENTICATED', 1); define('AUTH_INVALID_CREDENTIALS', 2); -/* -|-------------------------------------------------------------------------- -| LDAP constants -|-------------------------------------------------------------------------- -*/ -define('LDAP_NO_USER_DN', 10); -define('LDAP_TOO_MANY_USER_DN', 11); - /* |-------------------------------------------------------------------------- | Language constants diff --git a/application/config/ldap.php b/application/config/ldap.php index b7134e3ce..be8474c1e 100644 --- a/application/config/ldap.php +++ b/application/config/ldap.php @@ -17,7 +17,8 @@ if (defined('LDAP_SERVER')) // 1st LDAP server 'basedn' => LDAP_BASE_DN, 'username' => LDAP_BIND_USER, 'password' => LDAP_BIND_PASSWORD, - 'usf' => LDAP_USER_SEARCH_FILTER + 'usf' => LDAP_USER_SEARCH_FILTER, + 'timeout' => 1 ); } @@ -30,7 +31,8 @@ if (defined('LDAP2_SERVER')) // 2nd LDAP server 'basedn' => LDAP2_BASE_DN, 'username' => LDAP2_BIND_USER, 'password' => LDAP2_BIND_PASSWORD, - 'usf' => LDAP2_USER_SEARCH_FILTER + 'usf' => LDAP2_USER_SEARCH_FILTER, + 'timeout' => 1 ); } @@ -45,7 +47,8 @@ if (defined('LDAP_SERVER')) // 1st LDAP server 'basedn' => LDAP_BASE_DN, 'username' => LDAP_BIND_USER, 'password' => LDAP_BIND_PASSWORD, - 'usf' => LDAP_USER_SEARCH_FILTER + 'usf' => LDAP_USER_SEARCH_FILTER, + 'timeout' => 1 ); } @@ -58,6 +61,8 @@ if (defined('LDAP2_SERVER')) // 2nd LDAP server 'basedn' => LDAP2_BASE_DN, 'username' => LDAP2_BIND_USER, 'password' => LDAP2_BIND_PASSWORD, - 'usf' => LDAP2_USER_SEARCH_FILTER + 'usf' => LDAP2_USER_SEARCH_FILTER, + 'timeout' => 1 ); } + diff --git a/application/libraries/AuthLDAPLib.php b/application/libraries/AuthLDAPLib.php new file mode 100644 index 000000000..711b7f5b9 --- /dev/null +++ b/application/libraries/AuthLDAPLib.php @@ -0,0 +1,309 @@ +_ci =& get_instance(); + + // Loads the LogLib + $this->_ci->load->library('LogLib'); + } + + //------------------------------------------------------------------------------------------------------------------ + // Public methods + + /** + * Checks if the given credentials are valid on one of the configured LDAP servers + */ + public function checkUsernamePassword($username, $password) + { + $authenticated = false; + + if (isEmptyString($username) || isEmptyString($password)) return error('Wrong username and password'); + + $ldapConfigArrays = $this->_loadConfig(); // NOTE: always the last to be called! + + // For each configured LDAP server + foreach ($ldapConfigArrays as $ldapConfigs) + { + // Check if the LDAP server is up and running + if (!$this->_servicePing($ldapConfigs)) + { + // If not available log debug and skip to the next configured server + $this->_ci->loglib->logError('This LDAP server is not available: '.$ldapConfigs[self::SERVER]); + continue; + } + + // Connection without username and passoword _or_ with the configured username and password + $noCredentialsConnectResult = $this->_connect($ldapConfigs); + if (isError($noCredentialsConnectResult)) // If an error occurred + { + // If the error is due to invalid credentials or + // the LDAP server does not support anonymous authentication + if (getCode($noCredentialsConnectResult) == AUTH_INVALID_CREDENTIALS) + { + $this->_ci->loglib->logDebug(getError($noCredentialsConnectResult).' on server '.$ldapConfigs[self::SERVER]); + } + else // otherwise if it was due to a fatal error + { + $this->_ci->loglib->logError(getError($noCredentialsConnectResult).' on server '.$ldapConfigs[self::SERVER]); + } + + continue; // anyway skip to the next configured server + } + + // If it is a success + $noCredentialsConnection = getData($noCredentialsConnectResult); + + // Check if the user exists on this LDAP server + $userDNResult = $this->_getUserDN( + $noCredentialsConnection, + $ldapConfigs[self::BASEDN], + $ldapConfigs[self::USF], + $username + ); + // If an error occurred or the user was not found or many users were found + if (isError($userDNResult)) + { + // Log debug and skip to the next configured server + // If the error is due to invalid credentials or + // the LDAP server does not support anonymous authentication + if (getCode($userDNResult) == self::LDAP_NO_USER_DN) + { + $this->_ci->loglib->logDebug(getError($userDNResult).' on server '.$ldapConfigs[self::SERVER]); + } + elseif (getCode($userDNResult) == self::LDAP_TOO_MANY_USER_DN) + { + $this->_ci->loglib->logDebug(getError($userDNResult).' on server '.$ldapConfigs[self::SERVER]); + } + else // otherwise if it was due to a fatal error + { + $this->_ci->loglib->logError(getError($userDNResult).' on server '.$ldapConfigs[self::SERVER]); + } + + $this->_close($noCredentialsConnection); // Close the current LDAP connection + continue; // anyway skip to the next configured server + } + + $this->_close($noCredentialsConnection); // Close the current LDAP connection + + // Connect to LDAP with the userDN and password + $credentialsConnectResult = $this->_connect($ldapConfigs, getData($userDNResult), $password); + if (isError($credentialsConnectResult)) // If an error occurred + { + // Log debug and skip to the next configured server + $this->_ci->loglib->logError(getError($credentialsConnectResult).' on server '.$ldapConfigs[self::SERVER]); + continue; + } + else // otherwise the user is authenticated + { + $this->_close(getData($credentialsConnectResult)); + $authenticated = true; + break; + } + } + + return $authenticated; + } + + //------------------------------------------------------------------------------------------------------------------ + // Private methods + + /** + * Loads the LDAP configuration file and returns the LDAP configuration array + */ + private function _loadConfig() + { + // Tries to require the LDAP configuration file... + // ...first in the ENVIRONMENT subdirectory... + if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/'.self::LDAP_CONF_FILE.'.php')) + { + require_once(APPPATH.'config/'.ENVIRONMENT.'/'.self::LDAP_CONF_FILE.'.php'); + } + else // ...then in the default config directory + { + require_once(APPPATH.'config/'.self::LDAP_CONF_FILE.'.php'); + } + + return $ldap[$ldap_active_group]; + } + + /** + * Establish a connection to LDAP with the given LDAP configuration array and eventually with + * with a given username and password + */ + private function _connect($ldapConfigs, $username = null, $password = null) + { + // Checks if the LDAP configuraion is empty + if (isEmptyArray($ldapConfigs)) return error('Wrong parameters given'); + + // LDAP connection + $ldapConnection = @ldap_connect($ldapConfigs[self::SERVER].':'.$ldapConfigs[self::PORT]); + if ($ldapConnection) // if success + { + // Sets the LDAP protocol version + if (!@ldap_set_option($ldapConnection, LDAP_OPT_PROTOCOL_VERSION, self::LDAP_PROTOCOL_VERSION)) + { + return error('Was not possible to set the protocol version using LDAP sever '.$ldapConfigs[self::SERVER]); + } + + // Enable/disable the LDAP referrals + if (!@ldap_set_option($ldapConnection, LDAP_OPT_REFERRALS, self::LDAP_REFERRALS)) + { + return error('Was not possible to enable referrals using LDAP sever '.$ldapConfigs[self::SERVER]); + } + + // Starts TLS if required + if ($ldapConfigs[self::STARTTLS] === true && !@ldap_start_tls($ldapConnection)) + { + return error('Was not possible to enable TLS using LDAP sever '.$ldapConfigs[self::SERVER]); + } + + // If username or password are not provided... + if (isEmptyString($username) || isEmptyString($password)) + { + // ...use those provided by the configuration + $username = $ldapConfigs[self::USERNAME]; + $password = $ldapConfigs[self::PASSWORD]; + } + + // Binds to LDAP directory + if (!@ldap_bind($ldapConnection, $username, $password)) + { + // Wrong username and/or password + if (ldap_errno($ldapConnection) == self::LDAP_INVALID_CREDENTIALS) + { + return error('Invalid credentials', AUTH_INVALID_CREDENTIALS); + } + else // Error + { + return error('Was not possible to bind to the LDAP sever '.$ldapConfigs[self::SERVER]); + } + } + + return success($ldapConnection); // connected!!! + } + else // Connection error + { + return error('An error occurred while connecting to the LDAP server '.$ldapConfigs[self::SERVER]); + } + } + + /** + * Check if the network service is up and running + */ + private function _servicePing($ldapConfigs) + { + // Set the default timeout + $timeout = self::LDAP_DEFAULT_TIMEOUT; + + // If a timeout was configured for this server then use it + if (isset($ldapConfigs[self::TIMEOUT])) $timeout = $ldapConfigs[self::TIMEOUT]; + + // The LDAP server name or URL + $host = $ldapConfigs[self::SERVER]; + + // If it is a URL + if (strpos($ldapConfigs[self::SERVER], 'ldap://') !== false + || strpos($ldapConfigs[self::SERVER], 'ldaps://') !== false) + { + // Get the host from the URL + $host = parse_url($ldapConfigs[self::SERVER], PHP_URL_HOST); + } + + // Check if the given host answers on the given port using the given timeout + if ($op = @fsockopen($host, $ldapConfigs[self::PORT], $errno, $errstr, $timeout)) + { + // If it works then close the socket connection + fclose($op); + return true; + } + + return false; // otherwise this server is not up or LDAP service is not running on the given port + } + + /** + * Close the current connection to LDAP if present + */ + private function _close($connection) + { + @ldap_unbind($connection); + } + + /** + * Get the user DN from LDAP using the given username + */ + private function _getUserDN($connection, $baseDN, $usf, $username) + { + $userDN = error('AuthLDAPLib->_getUserDN() failed'); + + // Tries to search for a user DN using the given username + $searchResultIdentifier = @ldap_search( + $connection, + $baseDN, + $usf.'='.$username + ); + if (!$searchResultIdentifier) // Error + { + $userDN = error(ldap_error($connection)); + } + + // Counts the number of found entries + $countEntries = @ldap_count_entries($connection, $searchResultIdentifier); + if ($countEntries === false) // Error + { + $userDN = error(ldap_error($connection)); + } + elseif ($countEntries == 0) + { + $userDN = error('No user DN were found with username: '.$username, self::LDAP_NO_USER_DN); + } + elseif ($countEntries > 1) + { + $userDN = error('Too many users DN were found with username: '.$username, self::LDAP_TOO_MANY_USER_DN); + } + else // One entry was found + { + $entries = @ldap_get_entries($connection, $searchResultIdentifier); + if (!$entries) // Error + { + $userDN = error(ldap_error($connection)); + } + else + { + $userDN = success($entries[0][self::DN]); + } + } + + return $userDN; + } +} + diff --git a/application/libraries/AuthLib.php b/application/libraries/AuthLib.php index 67a527655..ae30d41fa 100644 --- a/application/libraries/AuthLib.php +++ b/application/libraries/AuthLib.php @@ -385,7 +385,7 @@ class AuthLib // Invalid credentials // NOTE: this is a corner case because of the HTTP basic authentication if (getCode($hta) == AUTH_NOT_AUTHENTICATED || getCode($hta) == AUTH_INVALID_CREDENTIALS - || getCode($hta) == LDAP_NO_USER_DN || getCode($hta) == LDAP_TOO_MANY_USER_DN) + || getCode($hta) == AuthLDAPLib::LDAP_NO_USER_DN || getCode($hta) == AuthLDAPLib::LDAP_TOO_MANY_USER_DN) { $this->_showInvalidAuthentication(); // this also stop the execution } @@ -404,37 +404,12 @@ class AuthLib { $ldap = error('Not authenticated', AUTH_NOT_AUTHENTICATED); // by default is NOT authenticated - $this->_ci->load->library('LDAPLib'); // Loads the LDAP library + $this->_ci->load->library('AuthLDAPLib'); // Loads the LDAP library - $ldapConnection = $this->_ci->ldaplib->anonymousConnect(); // connect anonymously! - if (isSuccess($ldapConnection)) // connected!! + // If it is possible to authenticate on LDAP with the given username and password + if ($this->_ci->authldaplib->checkUsernamePassword($username, $password) === true) { - // Get the user DN from LDAP - $userDN = $this->_ci->ldaplib->getUserDN($username); - if (isSuccess($userDN)) // got it! - { - $this->_ci->ldaplib->close(); // close the previous LDAP anonymous connection - - // Connects to LDAP using the last working configuration + the retrieved user DN + the provided password - $ldapConnection = $this->_ci->ldaplib->connectUsernamePassword(getData($userDN), $password); - if (isSuccess($ldapConnection)) // connected! - { - $this->_ci->ldaplib->close(); // close the previous connection - $ldap = success('Authenticated', AUTH_SUCCESS); // authenticated! - } - else // blocking error - { - $ldap = $ldapConnection; - } - } - else // blocking error - { - $ldap = $userDN; - } - } - else // blocking error - { - $ldap = $ldapConnection; + $ldap = success('Authenticated', AUTH_SUCCESS); // authenticated! } return $ldap; diff --git a/application/libraries/LDAPLib.php b/application/libraries/LDAPLib.php deleted file mode 100644 index 195dddc88..000000000 --- a/application/libraries/LDAPLib.php +++ /dev/null @@ -1,228 +0,0 @@ -_connection = null; - $this->_workingConfigArray = null; - $this->_ldapConfigArray = null; - - $this->_loadConfig(); // NOTE: always the last to be called! - } - - //------------------------------------------------------------------------------------------------------------------ - // Public methods - - /** - * Tries to connect to LDAP using configurations in property _ldapConfigArray - * The first that works is used and stored in property _workingConfigArray - * The LDAP connection link is stored in _connection - */ - public function anonymousConnect() - { - $connect = error('Did not found a working LDAP configuration'); - - // Loops through LDAP configurations - foreach ($this->_ldapConfigArray as $ldapConfigs) - { - // Tries to establish a connection - $connect = $this->_connect($ldapConfigs); - if (isSuccess($connect)) - { - break; // found a working LDAP configuration and successfully connected! - } - else - { - $this->close(); // close the eventually established connection - } - } - - return $connect; - } - - /** - * Tries to connect using the given username and password and the last working configuration with anonymous connection - */ - public function connectUsernamePassword($username, $password) - { - if (isEmptyString($username) || isEmptyString($password)) return error('Wrong username and password'); - - return $this->_connect($this->_workingConfigArray, $username, $password); - } - - /** - * Close the current connection to LDAP if present - */ - public function close() - { - if ($this->_connection != null) @ldap_unbind($this->_connection); - } - - /** - * Get the user DN from LDAP using the given username - */ - public function getUserDN($username) - { - $userDN = error('No user DN were found', LDAP_NO_USER_DN); - - // Tries to search for a user DN using the given username - $searchResultIdentifier = @ldap_search( - $this->_connection, - $this->_workingConfigArray[self::BASEDN], - $this->_workingConfigArray[self::USF].'='.$username - ); - if (!$searchResultIdentifier) // Error - { - $userDN = error(ldap_error($this->_connection)); - } - - // Counts the number of found entries - $countEntries = @ldap_count_entries($this->_connection, $searchResultIdentifier); - if ($countEntries === false) // Error - { - $userDN = error(ldap_error($this->_connection)); - } - elseif ($countEntries == 0) - { - $userDN = error('No user DN were found', LDAP_NO_USER_DN); - } - elseif ($countEntries > 1) - { - $userDN = error('Too many users DN were found', LDAP_TOO_MANY_USER_DN); - } - else // One entry was found - { - $entries = @ldap_get_entries($this->_connection, $searchResultIdentifier); - if (!$entries) // Error - { - $userDN = error(ldap_error($this->_connection)); - } - else - { - $userDN = success($entries[0][self::DN]); - } - } - - return $userDN; - } - - //------------------------------------------------------------------------------------------------------------------ - // Private methods - - /** - * Loads the LDAP configuration file and store the LDAP configuration array into _ldapConfigArray property - */ - private function _loadConfig() - { - // Tries to require the LDAP configuration file... - // ...first in the ENVIRONMENT subdirectory... - if (file_exists(APPPATH.'config/'.ENVIRONMENT.'/'.self::LDAP_CONF_FILE.'.php')) - { - require_once(APPPATH.'config/'.ENVIRONMENT.'/'.self::LDAP_CONF_FILE.'.php'); - } - else // ...then in the default config directory - { - require_once(APPPATH.'config/'.self::LDAP_CONF_FILE.'.php'); - } - - $this->_ldapConfigArray = $ldap[$ldap_active_group]; // store the active LDAP configuration array - } - - /** - * Establish a connection to LDAP with the given LDAP configuration array and eventually with - * with a given username and password - */ - private function _connect($ldapConfigs, $username = null, $password = null) - { - // Checks if the LDAP configuraion is empty - if (isEmptyArray($ldapConfigs)) - { - return error('Wrong parameters given'); - } - - // LDAP connection - $ldapConnection = @ldap_connect($ldapConfigs[self::SERVER].':'.$ldapConfigs[self::PORT]); - if ($ldapConnection) // if success - { - // Sets the LDAP protocol version - if (!@ldap_set_option($ldapConnection, LDAP_OPT_PROTOCOL_VERSION, self::LDAP_PROTOCOL_VERSION)) - { - return error(ldap_error($ldapConnection)); - } - - // Enable/disable the LDAP referrals - if (!@ldap_set_option($ldapConnection, LDAP_OPT_REFERRALS, self::LDAP_REFERRALS)) - { - return error(ldap_error($ldapConnection)); - } - - // Starts TLS if required - if ($ldapConfigs[self::STARTTLS] === true) - { - if (!@ldap_start_tls($ldapConnection)) - { - return error(ldap_error($ldapConnection)); - } - } - - // If username and password are not provided... - if ($username == null || $password == null) - { - // ...uses those provided by the configuration - $username = $ldapConfigs[self::USERNAME]; - $password = $ldapConfigs[self::PASSWORD]; - } - - // Binds to LDAP directory - if (!@ldap_bind($ldapConnection, $username, $password)) - { - // Wrong username and/or password - if (ldap_errno($ldapConnection) == self::LDAP_INVALID_CREDENTIALS) - { - return error('Invalid credentials', AUTH_INVALID_CREDENTIALS); - } - else // Error - { - return error(ldap_error($ldapConnection)); - } - } - - $this->_connection = $ldapConnection; // save the connection into _connection property - $this->_workingConfigArray = $ldapConfigs; // save the working LDAP configuration into _workingConfigArray property - - return success('Connected'); // connected!!! - } - else // Connection error - { - return error( - 'An error occurred while connecting to the LDAP server: '.$ldapConfigs[self::SERVER].':'.$ldapConfigs[self::PORT] - ); - } - } -}