mirror of
https://github.com/librenms/librenms.git
synced 2024-10-07 16:52:45 +00:00
* refactor: AD Auth defer connection until it is needed Nice error if php-ldap is missing instead of http 500. * Add the same error when ldap is missing to other auth methods. Not as graceful looking in the authorizers since they do not defer connection.
358 lines
12 KiB
PHP
358 lines
12 KiB
PHP
<?php
|
|
|
|
namespace LibreNMS\Authentication;
|
|
|
|
use LibreNMS\Config;
|
|
use LibreNMS\Exceptions\AuthenticationException;
|
|
|
|
class ADAuthorizationAuthorizer extends AuthorizerBase
|
|
{
|
|
protected $ldap_connection;
|
|
|
|
public function __construct()
|
|
{
|
|
if (! isset($_SESSION['username'])) {
|
|
$_SESSION['username'] = '';
|
|
}
|
|
|
|
if (!function_exists('ldap_connect')) {
|
|
throw new AuthenticationException("PHP does not support LDAP, please install or enable the PHP LDAP extension.");
|
|
}
|
|
|
|
// Disable certificate checking before connect if required
|
|
if (Config::has('auth_ad_check_certificates') &&
|
|
Config::get('auth_ad_check_certificates') == 0) {
|
|
putenv('LDAPTLS_REQCERT=never');
|
|
};
|
|
|
|
// Set up connection to LDAP server
|
|
$this->ldap_connection = @ldap_connect(Config::get('auth_ad_url'));
|
|
if (! $this->ldap_connection) {
|
|
throw new AuthenticationException('Fatal error while connecting to AD url ' . Config::get('auth_ad_url') . ': ' . ldap_error($this->ldap_connection));
|
|
}
|
|
|
|
// disable referrals and force ldap version to 3
|
|
ldap_set_option($this->ldap_connection, LDAP_OPT_REFERRALS, 0);
|
|
ldap_set_option($this->ldap_connection, LDAP_OPT_PROTOCOL_VERSION, 3);
|
|
|
|
// Bind to AD
|
|
if (Config::has('auth_ad_binduser') && Config::has('auth_ad_bindpassword')) {
|
|
// With specified bind user
|
|
if (! ldap_bind($this->ldap_connection, Config::get('auth_ad_binduser') . '@' . Config::get('auth_ad_domain'), Config::get('auth_ad_bindpassword'))) {
|
|
echo ldap_error($this->ldap_connection);
|
|
}
|
|
} else {
|
|
// Anonymous
|
|
if (! ldap_bind($this->ldap_connection)) {
|
|
echo ldap_error($this->ldap_connection);
|
|
}
|
|
}
|
|
}
|
|
|
|
public function authenticate($username, $password)
|
|
{
|
|
if (isset($_SERVER['REMOTE_USER'])) {
|
|
$_SESSION['username'] = mres($_SERVER['REMOTE_USER']);
|
|
|
|
if ($this->userExists($_SESSION['username'])) {
|
|
$this->addUser($username, null);
|
|
return true;
|
|
}
|
|
|
|
$_SESSION['username'] = Config::get('http_auth_guest');
|
|
return true;
|
|
}
|
|
|
|
throw new AuthenticationException();
|
|
}
|
|
|
|
public function addUser($username, $password, $level = 0, $email = '', $realname = '', $can_modify_passwd = 0, $description = '')
|
|
{
|
|
// Check to see if user is already added in the database
|
|
if (!$this->userExists($username)) {
|
|
$userid = dbInsert(array('username' => $username, 'realname' => $realname, 'email' => $email, 'descr' => $description, 'level' => $level, 'can_modify_passwd' => $can_modify_passwd, 'user_id' => $this->getUserid($username)), 'users');
|
|
if ($userid == false) {
|
|
return false;
|
|
} else {
|
|
foreach (dbFetchRows('select notifications.* from notifications where not exists( select 1 from notifications_attribs where notifications.notifications_id = notifications_attribs.notifications_id and notifications_attribs.user_id = ?) order by notifications.notifications_id desc', array($userid)) as $notif) {
|
|
dbInsert(array('notifications_id'=>$notif['notifications_id'],'user_id'=>$userid,'key'=>'read','value'=>1), 'notifications_attribs');
|
|
}
|
|
}
|
|
return $userid;
|
|
} else {
|
|
return false;
|
|
}
|
|
}
|
|
|
|
protected function userExistsInDb($username)
|
|
{
|
|
$return = dbFetchCell('SELECT COUNT(*) FROM users WHERE username = ?', array($username), true);
|
|
return $return;
|
|
}
|
|
|
|
public function userExists($username, $throw_exception = false)
|
|
{
|
|
if ($this->authLdapSessionCacheGet('user_exists')) {
|
|
return 1;
|
|
}
|
|
|
|
$search = ldap_search(
|
|
$this->ldap_connection,
|
|
Config::get('auth_ad_base_dn'),
|
|
get_auth_ad_user_filter($username),
|
|
array('samaccountname')
|
|
);
|
|
$entries = ldap_get_entries($this->ldap_connection, $search);
|
|
|
|
if ($entries['count']) {
|
|
/*
|
|
* Cache positiv result as this will result in more queries which we
|
|
* want to speed up.
|
|
*/
|
|
$this->authLdapSessionCacheSet('user_exists', 1);
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
public function getUserlevel($username)
|
|
{
|
|
$userlevel = $this->authLdapSessionCacheGet('userlevel');
|
|
if ($userlevel) {
|
|
return $userlevel;
|
|
} else {
|
|
$userlevel = 0;
|
|
}
|
|
|
|
// Find all defined groups $username is in
|
|
$search = ldap_search(
|
|
$this->ldap_connection,
|
|
Config::get('auth_ad_base_dn'),
|
|
get_auth_ad_user_filter($username),
|
|
array('memberOf')
|
|
);
|
|
$entries = ldap_get_entries($this->ldap_connection, $search);
|
|
|
|
// Loop the list and find the highest level
|
|
foreach ($entries[0]['memberof'] as $entry) {
|
|
$group_cn = $this->getCn($entry);
|
|
$auth_ad_groups = Config::get('auth_ad_groups');
|
|
if ($auth_ad_groups[$group_cn]['level'] > $userlevel) {
|
|
$userlevel = $auth_ad_groups[$group_cn]['level'];
|
|
}
|
|
}
|
|
|
|
$this->authLdapSessionCacheSet('userlevel', $userlevel);
|
|
return $userlevel;
|
|
}
|
|
|
|
|
|
public function getUserid($username)
|
|
{
|
|
$user_id = $this->authLdapSessionCacheGet('userid');
|
|
if (isset($user_id)) {
|
|
return $user_id;
|
|
} else {
|
|
$user_id = -1;
|
|
}
|
|
|
|
$attributes = array('objectsid');
|
|
$search = ldap_search(
|
|
$this->ldap_connection,
|
|
Config::get('auth_ad_base_dn'),
|
|
get_auth_ad_user_filter($username),
|
|
$attributes
|
|
);
|
|
$entries = ldap_get_entries($this->ldap_connection, $search);
|
|
|
|
if ($entries['count']) {
|
|
$user_id = preg_replace('/.*-(\d+)$/', '$1', $this->sidFromLdap($entries[0]['objectsid'][0]));
|
|
}
|
|
|
|
$this->authLdapSessionCacheSet('userid', $user_id);
|
|
return $user_id;
|
|
}
|
|
|
|
|
|
public function deleteUser($userid)
|
|
{
|
|
dbDelete('bill_perms', '`user_id` = ?', array($userid));
|
|
dbDelete('devices_perms', '`user_id` = ?', array($userid));
|
|
dbDelete('ports_perms', '`user_id` = ?', array($userid));
|
|
dbDelete('users_prefs', '`user_id` = ?', array($userid));
|
|
dbDelete('users', '`user_id` = ?', array($userid));
|
|
return dbDelete('users', '`user_id` = ?', array($userid));
|
|
}
|
|
|
|
|
|
public function getUserlist()
|
|
{
|
|
$userlist = array();
|
|
$userhash = array();
|
|
|
|
$ldap_groups = $this->getGroupList();
|
|
|
|
foreach ($ldap_groups as $ldap_group) {
|
|
$search_filter = "(memberOf=$ldap_group)";
|
|
if (Config::get('auth_ad_user_filter')) {
|
|
$search_filter = "(&{" . Config::get('auth_ad_user_filter') . $search_filter . ")";
|
|
}
|
|
$search = ldap_search($this->ldap_connection, Config::get('auth_ad_base_dn'), $search_filter, array('samaccountname','displayname','objectsid','mail'));
|
|
$results = ldap_get_entries($this->ldap_connection, $search);
|
|
|
|
foreach ($results as $result) {
|
|
if (isset($result['samaccountname'][0])) {
|
|
$userid = preg_replace(
|
|
'/.*-(\d+)$/',
|
|
'$1',
|
|
$this->sidFromLdap($result['objectsid'][0])
|
|
);
|
|
|
|
// don't make duplicates, user may be member of more than one group
|
|
$userhash[$result['samaccountname'][0]] = array(
|
|
'realname' => $result['displayName'][0],
|
|
'user_id' => $userid,
|
|
'email' => $result['mail'][0]
|
|
);
|
|
}
|
|
}
|
|
}
|
|
|
|
foreach (array_keys($userhash) as $key) {
|
|
$userlist[] = array(
|
|
'username' => $key,
|
|
'realname' => $userhash[$key]['realname'],
|
|
'user_id' => $userhash[$key]['user_id'],
|
|
'email' => $userhash[$key]['email']
|
|
);
|
|
}
|
|
|
|
return $userlist;
|
|
}
|
|
|
|
public function getUser($user_id)
|
|
{
|
|
// not supported so return 0
|
|
return dbFetchRow('SELECT * FROM `users` WHERE `user_id` = ?', array($user_id), true);
|
|
}
|
|
|
|
|
|
public function updateUser($user_id, $realname, $level, $can_modify_passwd, $email)
|
|
{
|
|
dbUpdate(array('realname' => $realname, 'can_modify_passwd' => $can_modify_passwd, 'email' => $email), 'users', '`user_id` = ?', array($user_id));
|
|
}
|
|
|
|
|
|
protected function getFullname($username)
|
|
{
|
|
$attributes = array('name');
|
|
$result = ldap_search(
|
|
$this->ldap_connection,
|
|
Config::get('auth_ad_base_dn'),
|
|
get_auth_ad_user_filter($username),
|
|
$attributes
|
|
);
|
|
$entries = ldap_get_entries($this->ldap_connection, $result);
|
|
if ($entries['count'] > 0) {
|
|
$membername = $entries[0]['name'][0];
|
|
} else {
|
|
$membername = $username;
|
|
}
|
|
|
|
return $membername;
|
|
}
|
|
|
|
|
|
public function getGroupList()
|
|
{
|
|
$ldap_groups = array();
|
|
|
|
// show all Active Directory Users by default
|
|
$default_group = 'Users';
|
|
|
|
if (Config::has('auth_ad_group')) {
|
|
if (Config::get('auth_ad_group') !== $default_group) {
|
|
$ldap_groups[] = Config::get('auth_ad_group');
|
|
}
|
|
}
|
|
|
|
if (!Config::has('auth_ad_groups') && !Config::has('auth_ad_group')) {
|
|
$ldap_groups[] = $this->getDn($default_group);
|
|
}
|
|
|
|
foreach (Config::get('auth_ad_groups') as $key => $value) {
|
|
$ldap_groups[] = $this->getDn($key);
|
|
}
|
|
|
|
return $ldap_groups;
|
|
}
|
|
|
|
protected function getDn($samaccountname)
|
|
{
|
|
$attributes = array('dn');
|
|
$result = ldap_search(
|
|
$this->ldap_connection,
|
|
Config::get('auth_ad_base_dn'),
|
|
get_auth_ad_group_filter($samaccountname),
|
|
$attributes
|
|
);
|
|
$entries = ldap_get_entries($this->ldap_connection, $result);
|
|
if ($entries['count'] > 0) {
|
|
return $entries[0]['dn'];
|
|
} else {
|
|
return '';
|
|
}
|
|
}
|
|
|
|
protected function getCn($dn)
|
|
{
|
|
preg_match('/[^,]*/', $dn, $matches, PREG_OFFSET_CAPTURE, 3);
|
|
return $matches[0][0];
|
|
}
|
|
|
|
protected function sidFromLdap($sid)
|
|
{
|
|
$sidHex = unpack('H*hex', $sid);
|
|
$subAuths = unpack('H2/H2/n/N/V*', $sid);
|
|
$revLevel = hexdec(substr($sidHex, 0, 2));
|
|
$authIdent = hexdec(substr($sidHex, 4, 12));
|
|
return 'S-'.$revLevel.'-'.$authIdent.'-'.implode('-', $subAuths);
|
|
}
|
|
|
|
protected function authLdapSessionCacheGet($attr)
|
|
{
|
|
$ttl = 300;
|
|
if (Config::get('auth_ldap_cache_ttl')) {
|
|
$ttl = Config::get('auth_ldap_cache_ttl');
|
|
}
|
|
|
|
// auth_ldap cache present in this session?
|
|
if (! isset($_SESSION['auth_ldap'])) {
|
|
return null;
|
|
}
|
|
|
|
$cache = $_SESSION['auth_ldap'];
|
|
|
|
// $attr present in cache?
|
|
if (! isset($cache[$attr])) {
|
|
return null;
|
|
}
|
|
|
|
// Value still valid?
|
|
if (time() - $cache[$attr]['last_updated'] >= $ttl) {
|
|
return null;
|
|
}
|
|
|
|
return $cache[$attr]['value'];
|
|
}
|
|
|
|
|
|
protected function authLdapSessionCacheSet($attr, $value)
|
|
{
|
|
$_SESSION['auth_ldap'][$attr]['value'] = $value;
|
|
$_SESSION['auth_ldap'][$attr]['last_updated'] = time();
|
|
}
|
|
}
|