mirror of
https://github.com/librenms/librenms.git
synced 2024-10-07 16:52:45 +00:00
AD Authorization fixes (#9216)
* AD Authorization fixes Remove mres() and $_SESSION usage. Remove broken addUser function and use Mysql addUser. * AD Authorization fixes Remove mres() and $_SESSION usage. Remove broken addUser function and use Mysql addUser. Extract common AD auth code to ADUtils * AD Authorization fixes Remove mres() and $_SESSION usage. Remove broken addUser function and use Mysql addUser. Extract common AD auth code to ADUtils * Send no user info to log instead of toast. * Remove commented code * add abstract getConnection() method that is required. * Actually return the value
This commit is contained in:
@@ -7,6 +7,9 @@ use LibreNMS\Exceptions\AuthenticationException;
|
||||
|
||||
class ADAuthorizationAuthorizer extends MysqlAuthorizer
|
||||
{
|
||||
use LdapSessionCache;
|
||||
use ActiveDirectoryCommon;
|
||||
|
||||
protected static $AUTH_IS_EXTERNAL = 1;
|
||||
protected static $CAN_UPDATE_PASSWORDS = 0;
|
||||
|
||||
@@ -14,10 +17,6 @@ class ADAuthorizationAuthorizer extends MysqlAuthorizer
|
||||
|
||||
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.");
|
||||
}
|
||||
@@ -54,39 +53,17 @@ class ADAuthorizationAuthorizer extends MysqlAuthorizer
|
||||
|
||||
public function authenticate($username, $password)
|
||||
{
|
||||
if (isset($_SERVER['REMOTE_USER'])) {
|
||||
$_SESSION['username'] = mres($_SERVER['REMOTE_USER']);
|
||||
if ($this->userExists($username)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->userExists($_SESSION['username'])) {
|
||||
$this->addUser($username, null);
|
||||
return true;
|
||||
}
|
||||
|
||||
$_SESSION['username'] = Config::get('http_auth_guest');
|
||||
if (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;
|
||||
}
|
||||
}
|
||||
|
||||
public function userExists($username, $throw_exception = false)
|
||||
{
|
||||
if ($this->authLdapSessionCacheGet('user_exists')) {
|
||||
@@ -96,7 +73,7 @@ class ADAuthorizationAuthorizer extends MysqlAuthorizer
|
||||
$search = ldap_search(
|
||||
$this->ldap_connection,
|
||||
Config::get('auth_ad_base_dn'),
|
||||
ActiveDirectoryAuthorizer::userFilter($username),
|
||||
$this->userFilter($username),
|
||||
array('samaccountname')
|
||||
);
|
||||
$entries = ldap_get_entries($this->ldap_connection, $search);
|
||||
@@ -127,7 +104,7 @@ class ADAuthorizationAuthorizer extends MysqlAuthorizer
|
||||
$search = ldap_search(
|
||||
$this->ldap_connection,
|
||||
Config::get('auth_ad_base_dn'),
|
||||
ActiveDirectoryAuthorizer::userFilter($username),
|
||||
$this->userFilter($username),
|
||||
array('memberOf')
|
||||
);
|
||||
$entries = ldap_get_entries($this->ldap_connection, $search);
|
||||
@@ -159,171 +136,21 @@ class ADAuthorizationAuthorizer extends MysqlAuthorizer
|
||||
$search = ldap_search(
|
||||
$this->ldap_connection,
|
||||
Config::get('auth_ad_base_dn'),
|
||||
ActiveDirectoryAuthorizer::userFilter($username),
|
||||
$this->userFilter($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]));
|
||||
$user_id = $this->getUseridFromSid($this->sidFromLdap($entries[0]['objectsid'][0]));
|
||||
}
|
||||
|
||||
$this->authLdapSessionCacheSet('userid', $user_id);
|
||||
return $user_id;
|
||||
}
|
||||
|
||||
public function getUserlist()
|
||||
protected function getConnection()
|
||||
{
|
||||
$userlist = array();
|
||||
$userhash = array();
|
||||
|
||||
$ldap_groups = $this->getGroupList();
|
||||
|
||||
foreach ($ldap_groups as $ldap_group) {
|
||||
$search_filter = "(&(memberOf:1.2.840.113556.1.4.1941:=$ldap_group)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))";
|
||||
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;
|
||||
}
|
||||
|
||||
protected function getFullname($username)
|
||||
{
|
||||
$attributes = array('name');
|
||||
$result = ldap_search(
|
||||
$this->ldap_connection,
|
||||
Config::get('auth_ad_base_dn'),
|
||||
ActiveDirectoryAuthorizer::userFilter($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'),
|
||||
ActiveDirectoryAuthorizer::groupFilter($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();
|
||||
return $this->ldap_connection;
|
||||
}
|
||||
}
|
||||
|
@@ -10,6 +10,8 @@ use LibreNMS\Exceptions\AuthenticationException;
|
||||
|
||||
class ActiveDirectoryAuthorizer extends AuthorizerBase
|
||||
{
|
||||
use ActiveDirectoryCommon;
|
||||
|
||||
protected static $CAN_UPDATE_PASSWORDS = 0;
|
||||
|
||||
protected $ldap_connection;
|
||||
@@ -58,8 +60,6 @@ class ActiveDirectoryAuthorizer extends AuthorizerBase
|
||||
protected function userInGroup($username, $groupname)
|
||||
{
|
||||
// check if user is member of the given group or nested groups
|
||||
|
||||
|
||||
$search_filter = "(&(objectClass=group)(cn=$groupname))";
|
||||
|
||||
// get DN for auth_ad_group
|
||||
@@ -93,7 +93,7 @@ class ActiveDirectoryAuthorizer extends AuthorizerBase
|
||||
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
|
||||
"(&" . static::userFilter($username) . "(memberOf:1.2.840.113556.1.4.1941:=$group_dn))",
|
||||
"(&" . $this->userFilter($username) . "(memberOf:1.2.840.113556.1.4.1941:=$group_dn))",
|
||||
array("DN")
|
||||
);
|
||||
$entries = ldap_get_entries($this->ldap_connection, $search);
|
||||
@@ -108,7 +108,7 @@ class ActiveDirectoryAuthorizer extends AuthorizerBase
|
||||
$search = ldap_search(
|
||||
$this->ldap_connection,
|
||||
Config::get('auth_ad_base_dn'),
|
||||
static::userFilter($username),
|
||||
$this->userFilter($username),
|
||||
array('samaccountname')
|
||||
);
|
||||
$entries = ldap_get_entries($this->ldap_connection, $search);
|
||||
@@ -155,7 +155,7 @@ class ActiveDirectoryAuthorizer extends AuthorizerBase
|
||||
$search = ldap_search(
|
||||
$this->ldap_connection,
|
||||
Config::get('auth_ad_base_dn'),
|
||||
static::userFilter($username),
|
||||
$this->userFilter($username),
|
||||
$attributes
|
||||
);
|
||||
$entries = ldap_get_entries($this->ldap_connection, $search);
|
||||
@@ -167,198 +167,6 @@ class ActiveDirectoryAuthorizer extends AuthorizerBase
|
||||
return -1;
|
||||
}
|
||||
|
||||
protected function getDomainSid()
|
||||
{
|
||||
$this->bind(); // make sure we called bind
|
||||
|
||||
// Extract only the domain components
|
||||
$dn_candidate = preg_replace('/^.*?DC=/i', 'DC=', Config::get('auth_ad_base_dn'));
|
||||
|
||||
$search = ldap_read(
|
||||
$this->ldap_connection,
|
||||
$dn_candidate,
|
||||
'(objectClass=*)',
|
||||
array('objectsid')
|
||||
);
|
||||
$entry = ldap_get_entries($this->ldap_connection, $search);
|
||||
return substr($this->sidFromLdap($entry[0]['objectsid'][0]), 0, 41);
|
||||
}
|
||||
|
||||
public function getUser($user_id)
|
||||
{
|
||||
$this->bind(); // make sure we called bind
|
||||
|
||||
$domain_sid = $this->getDomainSid();
|
||||
|
||||
$search_filter = "(&(objectcategory=person)(objectclass=user)(objectsid=$domain_sid-$user_id))";
|
||||
$attributes = array('samaccountname', 'displayname', 'objectsid', 'mail');
|
||||
$search = ldap_search($this->ldap_connection, Config::get('auth_ad_base_dn'), $search_filter, $attributes);
|
||||
$entry = ldap_get_entries($this->ldap_connection, $search);
|
||||
|
||||
if (isset($entry[0]['samaccountname'][0])) {
|
||||
return $this->userFromAd($entry[0]);
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
|
||||
public function getUserlist()
|
||||
{
|
||||
$this->bind(); // make sure we called bind
|
||||
|
||||
$userlist = array();
|
||||
$ldap_groups = $this->getGroupList();
|
||||
|
||||
foreach ($ldap_groups as $ldap_group) {
|
||||
$search_filter = "(&(memberOf:1.2.840.113556.1.4.1941:=$ldap_group)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))";
|
||||
if (Config::get('auth_ad_user_filter')) {
|
||||
$search_filter = "(&" . Config::get('auth_ad_user_filter') . $search_filter .")";
|
||||
}
|
||||
$attributes = array('samaccountname', 'displayname', 'objectsid', 'mail');
|
||||
$search = ldap_search($this->ldap_connection, Config::get('auth_ad_base_dn'), $search_filter, $attributes);
|
||||
$results = ldap_get_entries($this->ldap_connection, $search);
|
||||
|
||||
foreach ($results as $result) {
|
||||
if (isset($result['samaccountname'][0])) {
|
||||
$userlist[$result['samaccountname'][0]] = $this->userFromAd($result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($userlist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a user array from an AD LDAP entry
|
||||
* Must have the attributes: objectsid, samaccountname, displayname, mail
|
||||
* @internal
|
||||
*
|
||||
* @param $entry
|
||||
* @return array
|
||||
*/
|
||||
protected function userFromAd($entry)
|
||||
{
|
||||
return array(
|
||||
'user_id' => $this->getUseridFromSid($this->sidFromLdap($entry['objectsid'][0])),
|
||||
'username' => $entry['samaccountname'][0],
|
||||
'realname' => $entry['displayname'][0],
|
||||
'email' => isset($entry['mail'][0]) ? $entry['mail'][0] : null,
|
||||
'descr' => '',
|
||||
'level' => $this->getUserlevel($entry['samaccountname'][0]),
|
||||
'can_modify_passwd' => 0,
|
||||
);
|
||||
}
|
||||
|
||||
protected function getEmail($username)
|
||||
{
|
||||
$this->bind(); // make sure we called bind
|
||||
|
||||
$attributes = array('mail');
|
||||
$search = ldap_search(
|
||||
$this->ldap_connection,
|
||||
Config::get('auth_ad_base_dn'),
|
||||
static::userFilter($username),
|
||||
$attributes
|
||||
);
|
||||
$result = ldap_get_entries($this->ldap_connection, $search);
|
||||
unset($result[0]['mail']['count']);
|
||||
return current($result[0]['mail']);
|
||||
}
|
||||
|
||||
protected function getFullname($username)
|
||||
{
|
||||
$this->bind(); // make sure we called bind
|
||||
|
||||
$attributes = array('name');
|
||||
$result = ldap_search(
|
||||
$this->ldap_connection,
|
||||
Config::get('auth_ad_base_dn'),
|
||||
static::userFilter($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)
|
||||
{
|
||||
$this->bind(); // make sure we called bind
|
||||
|
||||
$attributes = array('dn');
|
||||
$result = ldap_search(
|
||||
$this->ldap_connection,
|
||||
Config::get('auth_ad_base_dn'),
|
||||
static::groupFilter($samaccountname),
|
||||
$attributes
|
||||
);
|
||||
$entries = ldap_get_entries($this->ldap_connection, $result);
|
||||
if ($entries['count'] > 0) {
|
||||
return $entries[0]['dn'];
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
protected function getCn($dn)
|
||||
{
|
||||
$dn = str_replace('\\,', '~C0mmA~', $dn);
|
||||
preg_match('/[^,]*/', $dn, $matches, PREG_OFFSET_CAPTURE, 3);
|
||||
return str_replace('~C0mmA~', ',', $matches[0][0]);
|
||||
}
|
||||
|
||||
protected function getUseridFromSid($sid)
|
||||
{
|
||||
return preg_replace('/.*-(\d+)$/', '$1', $sid);
|
||||
}
|
||||
|
||||
protected function sidFromLdap($sid)
|
||||
{
|
||||
$sidUnpacked = unpack('H*hex', $sid);
|
||||
$sidHex = array_shift($sidUnpacked);
|
||||
$subAuths = unpack('H2/H2/n/N/V*', $sid);
|
||||
if (PHP_INT_SIZE <= 4) {
|
||||
for ($i = 1; $i <= count($subAuths); $i++) {
|
||||
if ($subAuths[$i] < 0) {
|
||||
$subAuths[$i] = $subAuths[$i] + 0x100000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
$revLevel = hexdec(substr($sidHex, 0, 2));
|
||||
$authIdent = hexdec(substr($sidHex, 4, 12));
|
||||
return 'S-'.$revLevel.'-'.$authIdent.'-'.implode('-', $subAuths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Bind to AD with the bind user if available, otherwise anonymous bind
|
||||
@@ -434,29 +242,9 @@ class ActiveDirectoryAuthorizer extends AuthorizerBase
|
||||
ldap_set_option($this->ldap_connection, LDAP_OPT_PROTOCOL_VERSION, 3);
|
||||
}
|
||||
|
||||
public static function userFilter($username)
|
||||
protected function getConnection()
|
||||
{
|
||||
// don't return disabled users
|
||||
$user_filter = "(&(samaccountname=$username)(!(useraccountcontrol:1.2.840.113556.1.4.803:=2))";
|
||||
|
||||
$extra = Config::get('auth_ad_user_filter');
|
||||
if ($extra) {
|
||||
$user_filter .= $extra;
|
||||
}
|
||||
$user_filter .= ')';
|
||||
|
||||
return $user_filter;
|
||||
}
|
||||
|
||||
public static function groupFilter($groupname)
|
||||
{
|
||||
$group_filter = "(samaccountname=$groupname)";
|
||||
|
||||
$extra = Config::get('auth_ad_group_filter');
|
||||
if ($extra) {
|
||||
$group_filter = "(&$extra$group_filter)";
|
||||
}
|
||||
|
||||
return $group_filter;
|
||||
$this->bind(); // make sure connected and bound
|
||||
return $this->ldap_connection;
|
||||
}
|
||||
}
|
||||
|
236
LibreNMS/Authentication/ActiveDirectoryCommon.php
Normal file
236
LibreNMS/Authentication/ActiveDirectoryCommon.php
Normal file
@@ -0,0 +1,236 @@
|
||||
<?php
|
||||
/**
|
||||
* ActiveDirectoryCommonirectoryCommon.php
|
||||
*
|
||||
* Common code from AD auth modules
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @package LibreNMS
|
||||
* @link http://librenms.org
|
||||
* @copyright 2018 Tony Murray
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
namespace LibreNMS\Authentication;
|
||||
|
||||
use LibreNMS\Config;
|
||||
|
||||
trait ActiveDirectoryCommon
|
||||
{
|
||||
protected function getUseridFromSid($sid)
|
||||
{
|
||||
return preg_replace('/.*-(\d+)$/', '$1', $sid);
|
||||
}
|
||||
|
||||
protected function sidFromLdap($sid)
|
||||
{
|
||||
$sidUnpacked = unpack('H*hex', $sid);
|
||||
$sidHex = array_shift($sidUnpacked);
|
||||
$subAuths = unpack('H2/H2/n/N/V*', $sid);
|
||||
if (PHP_INT_SIZE <= 4) {
|
||||
for ($i = 1; $i <= count($subAuths); $i++) {
|
||||
if ($subAuths[$i] < 0) {
|
||||
$subAuths[$i] = $subAuths[$i] + 0x100000000;
|
||||
}
|
||||
}
|
||||
}
|
||||
$revLevel = hexdec(substr($sidHex, 0, 2));
|
||||
$authIdent = hexdec(substr($sidHex, 4, 12));
|
||||
return 'S-'.$revLevel.'-'.$authIdent.'-'.implode('-', $subAuths);
|
||||
}
|
||||
|
||||
protected function getCn($dn)
|
||||
{
|
||||
$dn = str_replace('\\,', '~C0mmA~', $dn);
|
||||
preg_match('/[^,]*/', $dn, $matches, PREG_OFFSET_CAPTURE, 3);
|
||||
return str_replace('~C0mmA~', ',', $matches[0][0]);
|
||||
}
|
||||
|
||||
protected function getDn($samaccountname)
|
||||
{
|
||||
$link_identifier = $this->getConnection();
|
||||
$attributes = ['dn'];
|
||||
$result = ldap_search(
|
||||
$link_identifier,
|
||||
Config::get('auth_ad_base_dn'),
|
||||
$this->groupFilter($samaccountname),
|
||||
$attributes
|
||||
);
|
||||
$entries = ldap_get_entries($link_identifier, $result);
|
||||
if ($entries['count'] > 0) {
|
||||
return $entries[0]['dn'];
|
||||
} else {
|
||||
return '';
|
||||
}
|
||||
}
|
||||
|
||||
protected function userFilter($username)
|
||||
{
|
||||
// don't return disabled users
|
||||
$user_filter = "(&(samaccountname=$username)(!(useraccountcontrol:1.2.840.113556.1.4.803:=2))";
|
||||
|
||||
$extra = Config::get('auth_ad_user_filter');
|
||||
if ($extra) {
|
||||
$user_filter .= $extra;
|
||||
}
|
||||
$user_filter .= ')';
|
||||
|
||||
return $user_filter;
|
||||
}
|
||||
|
||||
protected function groupFilter($groupname)
|
||||
{
|
||||
$group_filter = "(samaccountname=$groupname)";
|
||||
|
||||
$extra = Config::get('auth_ad_group_filter');
|
||||
if ($extra) {
|
||||
$group_filter = "(&$extra$group_filter)";
|
||||
}
|
||||
|
||||
return $group_filter;
|
||||
}
|
||||
|
||||
protected function getFullname($username)
|
||||
{
|
||||
$connection = $this->getConnection();
|
||||
$attributes = ['name'];
|
||||
$result = ldap_search(
|
||||
$connection,
|
||||
Config::get('auth_ad_base_dn'),
|
||||
$this->userFilter($username),
|
||||
$attributes
|
||||
);
|
||||
$entries = ldap_get_entries($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;
|
||||
}
|
||||
|
||||
public function getUserlist()
|
||||
{
|
||||
$connection = $this->getConnection();
|
||||
|
||||
$userlist = array();
|
||||
$ldap_groups = $this->getGroupList();
|
||||
|
||||
foreach ($ldap_groups as $ldap_group) {
|
||||
$search_filter = "(&(memberOf:1.2.840.113556.1.4.1941:=$ldap_group)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))";
|
||||
if (Config::get('auth_ad_user_filter')) {
|
||||
$search_filter = "(&" . Config::get('auth_ad_user_filter') . $search_filter .")";
|
||||
}
|
||||
$attributes = array('samaccountname', 'displayname', 'objectsid', 'mail');
|
||||
$search = ldap_search($connection, Config::get('auth_ad_base_dn'), $search_filter, $attributes);
|
||||
$results = ldap_get_entries($connection, $search);
|
||||
|
||||
foreach ($results as $result) {
|
||||
if (isset($result['samaccountname'][0])) {
|
||||
$userlist[$result['samaccountname'][0]] = $this->userFromAd($result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return array_values($userlist);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a user array from an AD LDAP entry
|
||||
* Must have the attributes: objectsid, samaccountname, displayname, mail
|
||||
* @internal
|
||||
*
|
||||
* @param $entry
|
||||
* @return array
|
||||
*/
|
||||
protected function userFromAd($entry)
|
||||
{
|
||||
return array(
|
||||
'user_id' => $this->getUseridFromSid($this->sidFromLdap($entry['objectsid'][0])),
|
||||
'username' => $entry['samaccountname'][0],
|
||||
'realname' => $entry['displayname'][0],
|
||||
'email' => isset($entry['mail'][0]) ? $entry['mail'][0] : null,
|
||||
'descr' => '',
|
||||
'level' => $this->getUserlevel($entry['samaccountname'][0]),
|
||||
'can_modify_passwd' => 0,
|
||||
);
|
||||
}
|
||||
|
||||
public function getUser($user_id)
|
||||
{
|
||||
$connection = $this->getConnection();
|
||||
$domain_sid = $this->getDomainSid();
|
||||
|
||||
$search_filter = "(&(objectcategory=person)(objectclass=user)(objectsid=$domain_sid-$user_id))";
|
||||
$attributes = array('samaccountname', 'displayname', 'objectsid', 'mail');
|
||||
$search = ldap_search($connection, Config::get('auth_ad_base_dn'), $search_filter, $attributes);
|
||||
$entry = ldap_get_entries($connection, $search);
|
||||
|
||||
if (isset($entry[0]['samaccountname'][0])) {
|
||||
return $this->userFromAd($entry[0]);
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
protected function getDomainSid()
|
||||
{
|
||||
$connection = $this->getConnection();
|
||||
|
||||
// Extract only the domain components
|
||||
$dn_candidate = preg_replace('/^.*?DC=/i', 'DC=', Config::get('auth_ad_base_dn'));
|
||||
|
||||
$search = ldap_read(
|
||||
$connection,
|
||||
$dn_candidate,
|
||||
'(objectClass=*)',
|
||||
array('objectsid')
|
||||
);
|
||||
$entry = ldap_get_entries($connection, $search);
|
||||
return substr($this->sidFromLdap($entry[0]['objectsid'][0]), 0, 41);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provide a connected and bound ldap connection resource
|
||||
*
|
||||
* @return resource
|
||||
*/
|
||||
abstract protected function getConnection();
|
||||
}
|
@@ -47,6 +47,8 @@ use Session;
|
||||
|
||||
class LdapAuthorizationAuthorizer extends AuthorizerBase
|
||||
{
|
||||
use LdapSessionCache;
|
||||
|
||||
protected $ldap_connection;
|
||||
protected static $AUTH_IS_EXTERNAL = 1;
|
||||
|
||||
@@ -244,48 +246,6 @@ class LdapAuthorizationAuthorizer extends AuthorizerBase
|
||||
return $membername;
|
||||
}
|
||||
|
||||
|
||||
protected function authLdapSessionCacheGet($attr)
|
||||
{
|
||||
$ttl = Config::get('auth_ldap_cache_ttl', 300);
|
||||
|
||||
// no session, don't cache
|
||||
if (!class_exists('Session')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// auth_ldap cache present in this session?
|
||||
if (!Session::has('auth_ldap')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$cache = Session::get('auth_ldap');
|
||||
|
||||
// $attr present in cache?
|
||||
if (! isset($cache[$attr])) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// Value still valid?
|
||||
if (time() - $cache[$attr]['last_updated'] >= $ttl) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$cache[$attr]['value'];
|
||||
}
|
||||
|
||||
|
||||
protected function authLdapSessionCacheSet($attr, $value)
|
||||
{
|
||||
if (class_exists('Session')) {
|
||||
Session::put($attr, [
|
||||
'value' => $value,
|
||||
'last_updated' => Carbon::now(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public function getGroupList()
|
||||
{
|
||||
$ldap_groups = array();
|
||||
|
73
LibreNMS/Authentication/LdapSessionCache.php
Normal file
73
LibreNMS/Authentication/LdapSessionCache.php
Normal file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
/**
|
||||
* LdapSessionCache.php
|
||||
*
|
||||
* Session cache for ldap queries
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
* @package LibreNMS
|
||||
* @link http://librenms.org
|
||||
* @copyright 2018 Tony Murray
|
||||
* @author Tony Murray <murraytony@gmail.com>
|
||||
*/
|
||||
|
||||
namespace LibreNMS\Authentication;
|
||||
|
||||
use Carbon\Carbon;
|
||||
use LibreNMS\Config;
|
||||
use Session;
|
||||
|
||||
trait LdapSessionCache
|
||||
{
|
||||
protected function authLdapSessionCacheGet($attr)
|
||||
{
|
||||
$ttl = Config::get('auth_ldap_cache_ttl', 300);
|
||||
|
||||
// no session, don't cache
|
||||
if (!class_exists('Session')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// auth_ldap cache present in this session?
|
||||
if (!Session::has('auth_ldap')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$cache = Session::get('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)
|
||||
{
|
||||
if (class_exists('Session')) {
|
||||
Session::put($attr, [
|
||||
'value' => $value,
|
||||
'last_updated' => Carbon::now(),
|
||||
]);
|
||||
}
|
||||
}
|
||||
}
|
@@ -104,7 +104,7 @@ class MysqlAuthorizer extends AuthorizerBase
|
||||
$user_id = $new_user->user_id;
|
||||
|
||||
// set auth_id
|
||||
$new_user->auth_id = $user_id;
|
||||
$new_user->auth_id = $this->getUserid($username);
|
||||
$new_user->save();
|
||||
|
||||
if ($user_id) {
|
||||
|
@@ -32,6 +32,7 @@ use Illuminate\Contracts\Auth\Authenticatable;
|
||||
use Illuminate\Contracts\Auth\UserProvider;
|
||||
use LibreNMS\Authentication\LegacyAuth;
|
||||
use LibreNMS\Exceptions\AuthenticationException;
|
||||
use Log;
|
||||
use Request;
|
||||
use Session;
|
||||
use Toastr;
|
||||
@@ -199,7 +200,7 @@ class LegacyUserProvider implements UserProvider
|
||||
}
|
||||
|
||||
if (empty($new_user)) {
|
||||
Toastr::info("No user ($auth_id) [$username]");
|
||||
Log::error("Auth Error ($type): No user ($auth_id) [$username]");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user