diff --git a/LibreNMS/Authentication/ActiveDirectoryAuthorizer.php b/LibreNMS/Authentication/ActiveDirectoryAuthorizer.php index 387760ea29..5df38bbb99 100644 --- a/LibreNMS/Authentication/ActiveDirectoryAuthorizer.php +++ b/LibreNMS/Authentication/ActiveDirectoryAuthorizer.php @@ -59,17 +59,19 @@ class ActiveDirectoryAuthorizer extends AuthorizerBase protected function userInGroup($username, $groupname) { + $connection = $this->getConnection(); + // check if user is member of the given group or nested groups $search_filter = "(&(objectClass=group)(cn=$groupname))"; // get DN for auth_ad_group $search = ldap_search( - $this->ldap_connection, + $connection, Config::get('auth_ad_base_dn'), $search_filter, array("cn") ); - $result = ldap_get_entries($this->ldap_connection, $search); + $result = ldap_get_entries($connection, $search); if ($result == false || $result['count'] !== 1) { if (Config::get('auth_ad_debug', false)) { @@ -89,29 +91,29 @@ class ActiveDirectoryAuthorizer extends AuthorizerBase $group_dn = $result[0]["dn"]; $search = ldap_search( - $this->ldap_connection, + $connection, Config::get('auth_ad_base_dn'), // add 'LDAP_MATCHING_RULE_IN_CHAIN to the user filter to search for $username in nested $group_dn // limiting to "DN" for shorter array "(&" . $this->userFilter($username) . "(memberOf:1.2.840.113556.1.4.1941:=$group_dn))", array("DN") ); - $entries = ldap_get_entries($this->ldap_connection, $search); + $entries = ldap_get_entries($connection, $search); return ($entries["count"] > 0); } public function userExists($username, $throw_exception = false) { - $this->bind(); // make sure we called bind + $connection = $this->getConnection(); $search = ldap_search( - $this->ldap_connection, + $connection, Config::get('auth_ad_base_dn'), $this->userFilter($username), array('samaccountname') ); - $entries = ldap_get_entries($this->ldap_connection, $search); + $entries = ldap_get_entries($connection, $search); if ($entries['count']) { @@ -124,8 +126,6 @@ class ActiveDirectoryAuthorizer extends AuthorizerBase public function getUserlevel($username) { - $this->bind(); // make sure we called bind - $userlevel = 0; if (!Config::get('auth_ad_require_groupmembership', true)) { if (Config::get('auth_ad_global_read', false)) { @@ -149,16 +149,16 @@ class ActiveDirectoryAuthorizer extends AuthorizerBase public function getUserid($username) { - $this->bind(); // make sure we called bind + $connection = $this->getConnection(); $attributes = array('objectsid'); $search = ldap_search( - $this->ldap_connection, + $connection, Config::get('auth_ad_base_dn'), $this->userFilter($username), $attributes ); - $entries = ldap_get_entries($this->ldap_connection, $search); + $entries = ldap_get_entries($connection, $search); if ($entries['count']) { return $this->getUseridFromSid($this->sidFromLdap($entries[0]['objectsid'][0])); @@ -170,49 +170,15 @@ class ActiveDirectoryAuthorizer extends AuthorizerBase /** * Bind to AD with the bind user if available, otherwise anonymous bind - * @internal - * - * @param bool $allow_anonymous attempt anonymous bind if bind user isn't available - * @param bool $force force rebind - * @return bool success or failure */ - protected function bind($allow_anonymous = true, $force = false) + protected function init() { - if ($this->is_bound && !$force) { - return true; // bind already attempted + if ($this->ldap_connection) { + return; } - $this->connect(); // make sure we are connected - - // set timeout - ldap_set_option( - $this->ldap_connection, - LDAP_OPT_NETWORK_TIMEOUT, - Config::get('auth_ad_timeout', 5) - ); - - // With specified bind user - if (Config::has('auth_ad_binduser') && Config::has('auth_ad_bindpassword')) { - $this->is_bound = true; - $bind = ldap_bind( - $this->ldap_connection, - Config::get('auth_ad_binduser') . '@' . Config::get('auth_ad_domain'), - Config::get('auth_ad_bindpassword') - ); - ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, -1); // restore timeout - return $bind; - } - - $bind = false; - - // Anonymous - if ($allow_anonymous) { - $this->is_bound = true; - $bind = ldap_bind($this->ldap_connection); - } - - ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, -1); // restore timeout - return $bind; + $this->connect(); + $this->bind(); } protected function connect() @@ -242,9 +208,34 @@ class ActiveDirectoryAuthorizer extends AuthorizerBase ldap_set_option($this->ldap_connection, LDAP_OPT_PROTOCOL_VERSION, 3); } + public function bind($username = null, $password = null) + { + if (!$this->ldap_connection) { + $this->connect(); + } + + if (Config::has('auth_ad_binduser') && Config::has('auth_ad_bindpassword')) { + $username = Config::get('auth_ad_binduser'); + $password = Config::get('auth_ad_bindpassword'); + } + $username .= '@' . Config::get('auth_ad_domain'); + + ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, Config::get('auth_ad_timeout', 5)); + $bind_result = ldap_bind($this->ldap_connection, $username, $password); + ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, -1); // restore timeout + + if ($bind_result) { + return; + } + + ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, Config::get('auth_ad_timeout', 5)); + ldap_bind($this->ldap_connection); + ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, -1); // restore timeout + } + protected function getConnection() { - $this->bind(); // make sure connected and bound + $this->init(); // make sure connected and bound return $this->ldap_connection; } } diff --git a/LibreNMS/Authentication/LdapAuthorizer.php b/LibreNMS/Authentication/LdapAuthorizer.php index 22631d18b9..293ea6de29 100644 --- a/LibreNMS/Authentication/LdapAuthorizer.php +++ b/LibreNMS/Authentication/LdapAuthorizer.php @@ -267,6 +267,37 @@ class LdapAuthorizer extends AuthorizerBase return $this->ldap_connection; // bind already attempted } + if ($skip_bind) { + $this->connect(); + } else { + $this->bind(); + } + + return $this->ldap_connection; + } + + /** + * @param array $entry ldap entry array + * @return array + */ + private function ldapToUser($entry) + { + $uid_attr = strtolower(Config::get('auth_ldap_uid_attribute', 'uidnumber')); + return [ + 'username' => $entry['uid'][0], + 'realname' => $entry['cn'][0], + 'user_id' => (int)$entry[$uid_attr][0], + 'email' => $entry[Config::get('auth_ldap_emailattr', 'mail')][0], + 'level' => $this->getUserlevel($entry['uid'][0]), + ]; + } + + private function connect() + { + if ($this->ldap_connection) { + return; + } + if (!function_exists('ldap_connect')) { throw new AuthenticationException("PHP does not support LDAP, please install or enable the PHP LDAP extension."); } @@ -287,65 +318,43 @@ class LdapAuthorizer extends AuthorizerBase throw new AuthenticationException("Fatal error: LDAP TLS required but not successfully negotiated: $error"); } } + } - if ($skip_bind) { - return $this->ldap_connection; - } - + public function bind($username = null, $password = null) + { if (Config::get('auth_ldap_debug')) { ldap_set_option(null, LDAP_OPT_DEBUG_LEVEL, 7); } - // set timeout - ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, Config::get('auth_ldap_timeout', 5)); + $this->connect(); + + if ((Config::has('auth_ldap_binduser') || Config::has('auth_ldap_binddn')) && Config::has('auth_ldap_bindpassword')) { + $username = Config::get('auth_ldap_binddn', $this->getFullDn(Config::get('auth_ldap_binduser'))); + $password = Config::get('auth_ldap_bindpassword'); + } elseif ($username) { + $username = $this->getFullDn($username); + } // With specified bind user - if ((Config::has('auth_ldap_binduser') || Config::has('auth_ldap_binddn')) - && Config::has('auth_ldap_bindpassword') - ) { - if (Config::has('auth_ldap_binddn')) { - $bind_dn = Config::get('auth_ldap_binddn'); - } else { - $bind_dn = $this->getFullDn(Config::get('auth_ldap_binduser')); - } + ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, Config::get('auth_ldap_timeout', 5)); + $bind_result = ldap_bind($this->ldap_connection, $username, $password); + ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, -1); // restore timeout + if (Config::get('auth_ldap_debug')) { + echo "Bind result: " . ldap_error($this->ldap_connection) . PHP_EOL; + } - $bind_result = ldap_bind($this->ldap_connection, $bind_dn, Config::get('auth_ldap_bindpassword')); - - if (Config::get('auth_ldap_debug')) { - echo "Bind result: " . ldap_error($this->ldap_connection) . PHP_EOL; - } - - if ($bind_result) { - ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, -1); // restore timeout - return $this->ldap_connection; - } + if ($bind_result) { + return; } // Anonymous + ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, Config::get('auth_ldap_timeout', 5)); ldap_bind($this->ldap_connection); + ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, -1); // restore timeout if (Config::get('auth_ldap_debug')) { echo "Anonymous bind result: " . ldap_error($this->ldap_connection) . PHP_EOL; } - - ldap_set_option($this->ldap_connection, LDAP_OPT_NETWORK_TIMEOUT, -1); // restore timeout - return $this->ldap_connection; - } - - /** - * @param array $entry ldap entry array - * @return array - */ - private function ldapToUser($entry) - { - $uid_attr = strtolower(Config::get('auth_ldap_uid_attribute', 'uidnumber')); - return [ - 'username' => $entry['uid'][0], - 'realname' => $entry['cn'][0], - 'user_id' => (int)$entry[$uid_attr][0], - 'email' => $entry[Config::get('auth_ldap_emailattr', 'mail')][0], - 'level' => $this->getUserlevel($entry['uid'][0]), - ]; } } diff --git a/app/Providers/LegacyUserProvider.php b/app/Providers/LegacyUserProvider.php index 0415a23cb3..2217156a5b 100644 --- a/app/Providers/LegacyUserProvider.php +++ b/app/Providers/LegacyUserProvider.php @@ -171,6 +171,7 @@ class LegacyUserProvider implements UserProvider * Fetch user by username from legacy auth, update it or add it to the db then return it. * * @param string $username + * @param string $password * @return User|null */ protected function fetchUserByName($username, $password = null) @@ -180,6 +181,11 @@ class LegacyUserProvider implements UserProvider $auth = LegacyAuth::get(); $type = LegacyAuth::getType(); + // ldap based auth we should bind before using, otherwise searches may fail due to anonymous bind + if (method_exists($auth, 'bind')) { + $auth->bind($username, $password); + } + $auth_id = $auth->getUserid($username); $new_user = $auth->getUser($auth_id);