683 lines
24 KiB
PHP
683 lines
24 KiB
PHP
<?php
|
|
|
|
/**
|
|
* Observium
|
|
*
|
|
* This file is part of Observium.
|
|
*
|
|
* @package observium
|
|
* @subpackage authentication
|
|
* @copyright (C) Adam Armstrong
|
|
*
|
|
*/
|
|
|
|
// Load common LDAP functions
|
|
include_once($config['html_dir'] . '/includes/ldap-functions.inc.php');
|
|
|
|
// Warn if authentication will be impossible.
|
|
check_extension_exists('ldap', 'AD selected as authentication module, but PHP does not have LDAP support! Please load the PHP LDAP module.', TRUE);
|
|
|
|
// Set LDAP debugging level to 7 (dumped to Apache daemon error log) (not virtualhost error log!)
|
|
if (OBS_DEBUG > 1) { // FIXME Currently OBS_DEBUG > 1 for WUI is not supported ;)
|
|
// Disabled by default, VERY chatty.
|
|
ldap_set_option(NULL, LDAP_OPT_DEBUG_LEVEL, 7);
|
|
}
|
|
|
|
// If a single server is specified, convert it to array anyway for use in functions below
|
|
if (!is_array($config['auth_ad_server'])) {
|
|
// If no server set and AD domain is specified, get domain controllers from SRV records
|
|
if ($config['auth_ad_server'] == '' && $config['auth_ad_domain'] != '') {
|
|
$config['auth_ad_server'] = ldap_domain_servers_from_dns($config['auth_ad_domain']);
|
|
} else {
|
|
$config['auth_ad_server'] = array($config['auth_ad_server']);
|
|
}
|
|
}
|
|
|
|
// If no access control groups are specified, use the defined level groups as access control
|
|
if (!is_array($config['auth_ad_group'])) {
|
|
$config['auth_ad_group'] = array_keys($config['auth_ad_groups']);
|
|
}
|
|
|
|
// Synthesize Base DN from configured domain name, if it is not set
|
|
if (!isset($config['auth_ad_basedn'])) {
|
|
if (isset($config['auth_ad_domain'])) {
|
|
$config['auth_ad_basedn'] = ad_internal_basedn_from_domain($config['auth_ad_domain']);
|
|
print_debug("Synthesized base DN " . $config['auth_ad_basedn'] . " from " . $config['auth_ad_domain']);
|
|
} else {
|
|
print_error("AD authentication selected, but AD domain AND BaseDN are not set. Authentication will fail.");
|
|
}
|
|
}
|
|
|
|
// If we have "hard" TLS, don't also set STARTTLS, and mangle the server names for ldap_connect
|
|
if ($config['auth_ad_tls']) {
|
|
unset($config['auth_ad_starttls']);
|
|
|
|
// Add ldaps:// in front of every hostname
|
|
$config['auth_ad_server'] = array_map(function($value) { return "ldaps://$value"; }, $config['auth_ad_server']);
|
|
}
|
|
|
|
// We need a bind DN, as we only get the user's password on initial login and not on successive page loads, meaning that
|
|
// we are not able to connect to AD anymore without a dedicated bind user.
|
|
if (!isset($config['auth_ad_binddn']) || !isset($config['auth_ad_bindpw'])) {
|
|
print_error("AD authentication selected, but AD bind user is not correctly set (auth_ad_binddn and auth_ad_bindpwd). Authentication will fail.");
|
|
}
|
|
|
|
// TESTME
|
|
/**
|
|
* Synthesize base DN from AD domain ('fqdn') name.
|
|
* Private function for this auth module only.
|
|
*
|
|
* @param string $ad_domain AD domain fqdn
|
|
*
|
|
* @return string Base DN
|
|
*/
|
|
function ad_internal_basedn_from_domain($ad_domain) {
|
|
// ad.observium.org -> DC=ad,DC=observium,DC=org
|
|
// contoso.com -> DC=contoso,DC=com
|
|
return 'DC=' . implode(',DC=', explode('.', $ad_domain));
|
|
}
|
|
|
|
/**
|
|
* Finds if user belongs to group, recursively.
|
|
* Private function for this auth module only.
|
|
*
|
|
* @param string $ldap_group LDAP group to check
|
|
* @param string $userdn User Distinguished Name
|
|
* @return bool TRUE when user is member of the group, FALSE if not
|
|
*/
|
|
function ad_internal_search_user($ldap_group, $userdn) {
|
|
global $ds, $config;
|
|
|
|
$filter_params = [];
|
|
$filter_params[] = ldap_filter_create('objectCategory', 'person');
|
|
$filter_params[] = ldap_filter_create('objectClass', 'user');
|
|
$filter_params[] = ldap_filter_create('distinguishedName', $userdn);
|
|
$filter_params[] = ldap_filter_create('memberOf:1.2.840.113556.1.4.1941:', ad_internal_dn_from_groupname($ldap_group));
|
|
$filter = ldap_filter_combine($filter_params);
|
|
|
|
print_debug("ad_internal_search_user: Searching for user [$userdn] Group membership: [$ldap_group] using filter: [$filter] on base " . $config['auth_ad_basedn']);
|
|
|
|
$ldap_search = ldap_search($ds, $config['auth_ad_basedn'], $filter, [ 'distinguishedName' ]);
|
|
$entries = ldap_get_entries($ds, $ldap_search);
|
|
|
|
return ($entries['count'] != 0);
|
|
}
|
|
|
|
/**
|
|
* Initializes the LDAP connection to the specified server(s). Cycles through all servers, throws error when no server can be reached.
|
|
* Private function for this auth module only.
|
|
*/
|
|
function ad_internal_init() {
|
|
global $ds, $config;
|
|
|
|
if ($ad_valid = ldap_internal_is_valid($ds)) {
|
|
// Already initiated
|
|
return TRUE;
|
|
}
|
|
|
|
print_debug('ad_internal_init: connecting to ' . implode(' ', $config['auth_ad_server']));
|
|
|
|
if ($config['auth_ad_validatecert'] === FALSE) {
|
|
// FIXME. Sync names with auth_ldap_tls_require_cert
|
|
//ldap_set_option($ds, LDAP_OPT_X_TLS_REQUIRE_CERT, LDAP_OPT_X_TLS_NEVER);
|
|
putenv('LDAPTLS_REQCERT=never');
|
|
}
|
|
|
|
foreach ((array)$config['auth_ad_server'] as $ad_server) {
|
|
$ds = @ldap_connect($ad_server, $config['auth_ad_port']);
|
|
if ($ad_valid = ldap_internal_is_valid($ds)) { break; }
|
|
}
|
|
|
|
if ($ad_valid) {
|
|
print_debug('ad_internal_init: Connected');
|
|
|
|
ldap_set_option($ds, LDAP_OPT_REFERRALS, FALSE);
|
|
print_debug('ad_internal_init: Referrals disabled');
|
|
|
|
ldap_set_option($ds, LDAP_OPT_PROTOCOL_VERSION, 3);
|
|
print_debug('ad_internal_init: Version set to 3');
|
|
|
|
if ($config['auth_ad_starttls'] &&
|
|
($config['auth_ad_starttls'] === 'optional' || $config['auth_ad_starttls'] === 'require')) {
|
|
$tls = ldap_start_tls($ds);
|
|
if ($config['auth_ad_starttls'] === 'require' && $tls === FALSE) {
|
|
session_logout();
|
|
print_error('Fatal error: AD TLS required but not successfully negotiated. ' . ldap_internal_error($ds));
|
|
exit;
|
|
}
|
|
}
|
|
}
|
|
|
|
return $ad_valid;
|
|
}
|
|
|
|
/**
|
|
* Check username and password against LDAP authentication backend.
|
|
* Cut short if remote_user setting is on, as we assume the user has already authed against Apache.
|
|
* We still need to check for certain group memberships however, so we can not simply bail out with TRUE in such case.
|
|
*
|
|
* @param string $username User name to check
|
|
* @param string $password User password to check
|
|
* @return int Authentication success (0 = fail, 1 = success) FIXME bool
|
|
*/
|
|
function ad_authenticate($username, $password) {
|
|
global $config, $ds;
|
|
|
|
ad_internal_init();
|
|
if ($username && ldap_internal_is_valid($ds)) {
|
|
// Bind using bindDN, fail early if this fails
|
|
if (!ad_bind_dn()) {
|
|
return 0;
|
|
}
|
|
|
|
// Find DN for username trying to log in
|
|
$binduser = ad_internal_dn_from_username($username);
|
|
|
|
// If user is found, try authenticating using their password
|
|
if ($binduser) {
|
|
print_debug("ad_authenticate: User: $username - LDAP bind user: $binduser");
|
|
|
|
// Auth via Apache + LDAP fallback -> automatically authenticated, fall through to group permission check
|
|
if ($config['auth']['remote_user'] || ldap_bind($ds, $binduser, $password)) {
|
|
if (!$config['auth_ad_group']) {
|
|
// No groups defined, auth is sufficient
|
|
return 1;
|
|
}
|
|
|
|
foreach ($config['auth_ad_group'] as $ldap_group) {
|
|
print_debug("ad_authenticate: Searching $ldap_group for $binduser");
|
|
$compare = ad_internal_search_user($ldap_group, $binduser);
|
|
|
|
if ($compare === -1) {
|
|
print_debug("ad_authenticate: Compare LDAP error: " . ldap_internal_error($ds));
|
|
continue;
|
|
} elseif ($compare === FALSE) {
|
|
print_debug("ad_authenticate: Processing group: $ldap_group - Not matched");
|
|
} else {
|
|
// $compare === TRUE
|
|
print_debug("ad_authenticate: Processing group: $ldap_group - Matched");
|
|
return 1;
|
|
}
|
|
}
|
|
} else {
|
|
print_debug(ldap_internal_error($ds));
|
|
}
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Check if the backend allows users to log out.
|
|
* We don't check for Apache authentication (remote_user) as this is done already before calling into this function.
|
|
*
|
|
* @return bool TRUE if logout is possible, FALSE if it is not
|
|
*/
|
|
function ad_auth_can_logout() {
|
|
return TRUE;
|
|
}
|
|
|
|
/**
|
|
* Check if the backend allows a specific user to change their password.
|
|
* This is not possible using the AD backend.
|
|
*
|
|
* @param string $username Username to check
|
|
* @return bool TRUE if password change is possible, FALSE if it is not
|
|
*/
|
|
function ad_auth_can_change_password($username = "") {
|
|
// Not supported
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Changes a user's password.
|
|
* This is not possible using the AD backend.
|
|
*
|
|
* @param string $username Username to modify the password for
|
|
* @param string $password New password
|
|
* @return bool TRUE if password change is successful, FALSE if it is not
|
|
*/
|
|
function ad_auth_change_password($username, $newpassword) {
|
|
// Not supported
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Check if the backend allows user management at all (create/delete/modify users).
|
|
* This is not possible using the AD backend.
|
|
*
|
|
* @return bool TRUE if user management is possible, FALSE if it is not
|
|
*/
|
|
function ad_auth_usermanagement() {
|
|
// Not supported
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Adds a new user to the user backend.
|
|
* This is not possible using the AD backend.
|
|
*
|
|
* @param string $username User's username
|
|
* @param string $password User's password (plain text)
|
|
* @param int $level User's auth level
|
|
* @param string $email User's e-mail address
|
|
* @param string $realname User's real name
|
|
* @param bool $can_modify_passwd TRUE if user can modify their own password, FALSE if not
|
|
* @param string $description User's description
|
|
* @return bool TRUE if user addition is successful, FALSE if it is not
|
|
*/
|
|
function ad_adduser($username, $password, $level, $email = "", $realname = "", $can_modify_passwd = '1') {
|
|
// Not supported
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Check if a user, specified by username, exists in the user backend.
|
|
*
|
|
* @param string $username Username to check
|
|
* @return bool TRUE if the user exists, FALSE if they do not
|
|
*/
|
|
function ad_auth_user_exists($username) {
|
|
global $config, $ds;
|
|
|
|
ad_internal_init();
|
|
if (!ad_bind_dn()) {
|
|
// Will not work without bind user
|
|
return 0;
|
|
}
|
|
|
|
// Find user's DN which will reveal if it exists or not
|
|
if (ad_internal_dn_from_username($username)) {
|
|
return TRUE;
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Retrieve user auth level for specified user.
|
|
*
|
|
* @param string $username Username to retrieve the auth level for
|
|
* @return int User's auth level
|
|
*/
|
|
function ad_auth_user_level($username) {
|
|
global $config, $ds, $cache;
|
|
|
|
if (!isset($cache['ldap']['level'][$username])) {
|
|
$userlevel = 0;
|
|
|
|
ad_internal_init();
|
|
ad_bind_dn();
|
|
|
|
// Find all defined groups $username is in
|
|
$userdn = ad_internal_dn_from_username($username);
|
|
print_debug("ad_auth_user_level: UserDN: $userdn");
|
|
|
|
// Check membership of each of our groups for the requested user
|
|
foreach ($config['auth_ad_groups'] as $ldap_group => $ldap_group_info) {
|
|
$compare = ad_internal_search_user(ad_internal_dn_from_groupname($ldap_group), $userdn);
|
|
|
|
if ($compare === -1) {
|
|
print_debug("ad_user_level: Compare LDAP error: " . ldap_internal_error($ds));
|
|
continue;
|
|
} elseif ($compare === FALSE) {
|
|
print_debug("ad_user_level: Processing group: $ldap_group - Not matched");
|
|
} else { // $compare === TRUE
|
|
print_debug("ad_user_level: Processing group: $ldap_group - level: " . $ldap_group_info['level']);
|
|
if ($ldap_group_info['level'] > $userlevel) {
|
|
$userlevel = $ldap_group_info['level'];
|
|
print_debug('ad_user_level: Accepted group level as new highest level');
|
|
} else {
|
|
print_debug("ad_user_level: Ignoring group level as it's lower than what we have already");
|
|
}
|
|
}
|
|
}
|
|
|
|
print_debug('ad_user_level: Final level: '.$userlevel);
|
|
|
|
$cache['ldap']['level'][$username] = $userlevel;
|
|
}
|
|
|
|
return $cache['ldap']['level'][$username];
|
|
}
|
|
|
|
/**
|
|
* Retrieve user id for specified user.
|
|
*
|
|
* @param string $username Username to retrieve the ID for
|
|
* @return int User's ID
|
|
*/
|
|
function ad_auth_user_id($username) {
|
|
global $config, $ds;
|
|
|
|
$userid = -1;
|
|
|
|
ad_internal_init();
|
|
ad_bind_dn();
|
|
|
|
//$userdn = ad_internal_dn_from_username($username);
|
|
|
|
$filter_params = [];
|
|
$filter_params[] = ldap_filter_create('objectClass', 'person');
|
|
$filter_params[] = ldap_filter_create('sAMAccountName', $username);
|
|
$filter = ldap_filter_combine($filter_params);
|
|
|
|
print_debug("ad_auth_user_id: Filter $filter");
|
|
$search = ldap_search($ds, $config['auth_ad_basedn'], $filter);
|
|
$entries = ldap_internal_is_valid($search) ? ldap_get_entries($ds, $search) : [];
|
|
|
|
if ($entries['count']) {
|
|
$userid = ad_internal_auth_user_id($entries[0]);
|
|
print_debug('ad_auth_user_id: UserID '.$userid);
|
|
} else {
|
|
print_debug('ad_auth_user_id: User not found through filter');
|
|
}
|
|
|
|
return $userid;
|
|
}
|
|
|
|
/**
|
|
* Deletes a user from the user database.
|
|
* This is not possible using the AD backend.
|
|
*
|
|
* @param string $username Username to delete
|
|
* @return bool TRUE if user deletion is successful, FALSE if it is not
|
|
*/
|
|
function ad_deluser($username) {
|
|
// Call into mysql database functions to make sure user is gone from the database for legacy setups
|
|
mysql_deluser($username, 'ad');
|
|
|
|
// Not supported
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Find the user's username by specifying their user ID.
|
|
* Can't search objectSid object for the RID using LDAP filters, so fetch all users and compare.
|
|
*
|
|
* @param int $user_id The user's ID to look up the username for
|
|
* @return string The user's user name, or FALSE if the user ID is not found
|
|
*/
|
|
function ad_auth_username_by_id($user_id) {
|
|
|
|
foreach(ad_auth_user_list() as $user) {
|
|
if ($user['user_id'] == $user_id) {
|
|
return $user['username'];
|
|
}
|
|
}
|
|
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Get the user information by username
|
|
*
|
|
* @param string $username Username
|
|
* @return array The user's user name, or FALSE if the user ID is not found
|
|
*/
|
|
function ad_auth_user_info($username) {
|
|
|
|
if (empty($username)) {
|
|
return [];
|
|
}
|
|
|
|
// Delegate fetching of user details to user list function
|
|
foreach(ad_auth_user_list($username) as $user) {
|
|
if ($user['username'] == $username) {
|
|
return $user;
|
|
}
|
|
}
|
|
|
|
return [];
|
|
}
|
|
|
|
/**
|
|
* Retrieve list of users with all details.
|
|
* If we specify a username, only fetch details for that specific use.
|
|
*
|
|
* @param string $username Username (optional)
|
|
* @return array Rows of user data
|
|
*/
|
|
function ad_auth_user_list($username = NULL) {
|
|
global $config, $ds;
|
|
|
|
// Use caching for reduce queries to LDAP
|
|
if (isset($GLOBALS['cache']['ldap']['userlist'])) {
|
|
if ((get_time() - $GLOBALS['cache']['ldap']['userlist']['unixtime']) <= 300) { // Cache valid for 5 min
|
|
return $GLOBALS['cache']['ldap']['userlist']['entries'];
|
|
}
|
|
unset($GLOBALS['cache']['ldap']['userlist']);
|
|
}
|
|
|
|
ad_internal_init();
|
|
ad_bind_dn();
|
|
|
|
$filter_params = [];
|
|
$filter_params[] = ldap_filter_create('objectClass', 'person');
|
|
|
|
if (!empty($username)) {
|
|
// Filter users by username, if passed to the function
|
|
$filter_params[] = ldap_filter_create('sAMAccountName', $username);
|
|
}
|
|
|
|
// Filter user(s) by group(s) as configured in auth_ad_group, if any
|
|
if (count($config['auth_ad_group']) > 0) {
|
|
$group_params = [];
|
|
|
|
// Add all configured LDAP groups as an OR filter to build the user list
|
|
foreach($config['auth_ad_group'] as $group) {
|
|
$group_params[] = ldap_filter_create('memberOf:1.2.840.113556.1.4.1941:', ad_internal_dn_from_groupname($group));
|
|
}
|
|
|
|
$filter_params[] = ldap_filter_combine($group_params, '|');
|
|
}
|
|
|
|
$filter = ldap_filter_combine($filter_params);
|
|
|
|
// Limit fetched attributes, to reduce network transfer size
|
|
$attributes = [ 'samaccountname', 'name', 'objectsid', 'description', 'mail', 'dn' ];
|
|
|
|
$entries = ldap_paged_entries($filter, $attributes, $config['auth_ad_basedn']);
|
|
|
|
// Process array in separate function
|
|
ad_internal_user_entries($entries, $userlist);
|
|
unset($entries);
|
|
|
|
// Store userlist in cache
|
|
$GLOBALS['cache']['ldap']['userlist'] = [ 'unixtime' => get_time(),
|
|
'entries' => $userlist ];
|
|
return $userlist;
|
|
}
|
|
|
|
/**
|
|
* Parse user entries in ad_auth_user_list()
|
|
*
|
|
* @param array $entries LDAP entries by ldap_get_entries()
|
|
* @param array $userlist Users list
|
|
*/
|
|
function ad_internal_user_entries($entries, &$userlist) {
|
|
global $config, $ds;
|
|
|
|
if (!is_array($userlist)) {
|
|
$userlist = [];
|
|
}
|
|
|
|
if ($entries['count']) {
|
|
unset($entries['count']);
|
|
|
|
foreach ($entries as $i => $entry) {
|
|
$username = $entry['samaccountname'][0];
|
|
$realname = $entry['name'][0];
|
|
$user_id = ad_internal_auth_user_id($entry);
|
|
$email = $entry['mail'][0];
|
|
$description = $entry['description'][0];
|
|
$userdn = $entry['dn'];
|
|
|
|
print_debug("ad_internal_user_entries: Compare: " . implode('|',$config['auth_ad_group']) . " to fulldn ($userdn)");
|
|
|
|
foreach ($config['auth_ad_group'] as $ldap_group) {
|
|
$authorized = 0;
|
|
|
|
$compare = ad_internal_search_user($ldap_group, $userdn);
|
|
|
|
if ($compare === -1) {
|
|
print_debug("ad_internal_user_entries: Compare LDAP error: " . ldap_internal_error($ds));
|
|
continue;
|
|
} elseif ($compare === FALSE) {
|
|
print_debug("ad_internal_user_entries: Processing group: $ldap_group - Not matched");
|
|
} else {
|
|
// $$compare === TRUE
|
|
print_debug("ad_internal_user_entries: Authorized: $userdn for group $ldap_group");
|
|
$authorized = 1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!isset($config['auth_ad_group']) || $authorized) {
|
|
$user_level = ad_auth_user_level($username);
|
|
$userlist[] = [ 'username' => $username, 'realname' => $realname, 'user_id' => $user_id,
|
|
'level' => $user_level, 'email' => $email, 'descr' => $description ];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Bind with the configured bind DN
|
|
* Private function for this auth module only.
|
|
*
|
|
* @return bool TRUE if bind succeeded, FALSE if not
|
|
*/
|
|
function ad_bind_dn() {
|
|
global $config, $ds, $cache;
|
|
|
|
// Avoid binding multiple times on one resource, this upsets some LDAP servers.
|
|
if (isset($cache['ldap_bind_result'])) {
|
|
return $cache['ldap_bind_result'];
|
|
}
|
|
|
|
print_debug("ad_bind_dn: Binding to server with DN [" . $config['auth_ad_binddn'] . "]");
|
|
$bind = ldap_bind($ds, $config['auth_ad_binddn'], $config['auth_ad_bindpw']);
|
|
|
|
if ($bind) {
|
|
$cache['ldap_bind_result'] = 1;
|
|
print_debug("ad_bind_dn: Bound to AD server.");
|
|
return TRUE;
|
|
}
|
|
|
|
$cache['ldap_bind_result'] = 0;
|
|
ldap_get_option($ds, LDAP_OPT_DIAGNOSTIC_MESSAGE, $err);
|
|
print_debug("ad_bind_dn: Error binding to AD server: " . implode(' ', $config['auth_ad_server']) . ': ' . ldap_error($ds) . " ($err)");
|
|
session_logout();
|
|
return FALSE;
|
|
}
|
|
|
|
/**
|
|
* Find user's Distinguished Name based on their username.
|
|
* Private function for this auth module only.
|
|
*
|
|
* @param string $username Username to retrieve DN for
|
|
*
|
|
* @return string User's Distinguished Name
|
|
*/
|
|
function ad_internal_dn_from_username($username) {
|
|
global $config, $ds, $cache;
|
|
|
|
// If not cached, retrieve DN from AD by searching for the sAMAccountName
|
|
if (!isset($cache['ldap']['dn'][$username])) {
|
|
ad_internal_init();
|
|
$filter_params[] = ldap_filter_create('objectClass', 'person');
|
|
$filter_params[] = ldap_filter_create('sAMAccountName', $username);
|
|
$filter = ldap_filter_combine($filter_params);
|
|
print_debug("ad_internal_dn_from_username: Searching for user $username using filter: $filter on base " . $config['auth_ad_basedn']);
|
|
|
|
$search = ldap_search($ds, $config['auth_ad_basedn'], $filter);
|
|
$entries = ldap_get_entries($ds, $search);
|
|
|
|
if ($entries['count']) {
|
|
[ $cache['ldap']['dn'][$username], ] = ldap_escape_filter_value($entries[0]['dn']);
|
|
print_debug("ad_internal_dn_from_username: retrieved DN for $username from AD: " . $cache['ldap']['dn'][$username]);
|
|
} else {
|
|
$cache['ldap']['dn'][$username] = FALSE;
|
|
print_debug("ad_internal_dn_from_username: unable to retrieve DN for $username from AD");
|
|
}
|
|
} else {
|
|
print_debug("ad_internal_dn_from_username: retrieved DN for $username from cache: " . $cache['ldap']['dn'][$username]);
|
|
}
|
|
|
|
return $cache['ldap']['dn'][$username];
|
|
}
|
|
|
|
/**
|
|
* Calculate User's numeric ID from LDAP.
|
|
* Fetches objectSID from AD, and grab the RID to use as uid number. If RFC2307
|
|
* (unix attributes) are used in your AD schema, we will use this instead.
|
|
* Some systems (SSSD, Samba rid idmap) use an offset to distinguish multiple
|
|
* domains, Observium does not currently support this.
|
|
*
|
|
* Private function for this auth module only.
|
|
*
|
|
* @param object LDAP search result for the user
|
|
*
|
|
* @return int User ID.
|
|
*/
|
|
function ad_internal_auth_user_id($result) {
|
|
// If RFC2307 UidNumber is set up, use it.
|
|
if (isset($result['uidnumber'][0])) {
|
|
return $result['uidnumber'][0];
|
|
}
|
|
|
|
// No RFC2307 UID found, convert SID S-1-5-21-4113566099-323201010-15454308-1104 to 1104 as our numeric unique ID
|
|
$sid = explode('-', ldap_bin_to_str_sid($result['objectsid'][0]));
|
|
$userid = $sid[count($sid)-1];
|
|
print_debug("ad_internal_auth_user_id: Converted objectSid " . ldap_bin_to_str_sid($result['objectsid'][0]) . " to numeric user ID $userid");
|
|
|
|
return $userid;
|
|
}
|
|
|
|
/**
|
|
* Returns group DN for group name, if it is not yet a DN.
|
|
* Private function for this auth module only.
|
|
*
|
|
* @param string $groupname Group name
|
|
*
|
|
* @return array Group DN
|
|
*/
|
|
function ad_internal_dn_from_groupname($groupname) {
|
|
global $ds, $config, $cache;
|
|
|
|
if (!isset($cache['ldap']['dn'][$groupname])) {
|
|
// If the Base DN is not found in the name, we assume it's just the group name and not the DN, and search for the DN.
|
|
// If it is, we just return the given string as it should be correct.
|
|
if (strpos($groupname,$config['auth_ad_basedn']) === FALSE) {
|
|
$filter_params = [];
|
|
$filter_params[] = ldap_filter_create('objectClass', 'group');
|
|
$filter_params[] = ldap_filter_create('name', $groupname);
|
|
$filter = ldap_filter_combine($filter_params);
|
|
|
|
print_debug("ad_internal_dn_from_groupname: Searching for group $groupname using filter: $filter on base " . $config['auth_ad_basedn']);
|
|
|
|
$ldap_search = ldap_search($ds, $config['auth_ad_basedn'], $filter, [ 'distinguishedName' ]);
|
|
$entries = ldap_get_entries($ds, $ldap_search);
|
|
|
|
if ($entries['count']) {
|
|
[ $cache['ldap']['dn'][$groupname], ] = ldap_escape_filter_value($entries[0]['dn']);
|
|
print_debug("ad_internal_dn_from_groupname: retrieved DN for $groupname from AD: " . $cache['ldap']['dn'][$groupname]);
|
|
} else {
|
|
$cache['ldap']['dn'][$groupname] = FALSE;
|
|
print_debug("ad_internal_dn_from_groupname: unable to retrieve DN for $groupname from AD");
|
|
}
|
|
} else {
|
|
$cache['ldap']['dn'][$groupname] = $groupname;
|
|
print_debug("ad_internal_dn_from_groupname: retrieved DN for $groupname from group name itself: " . $cache['ldap']['dn'][$groupname]);
|
|
}
|
|
} else {
|
|
print_debug("ad_internal_dn_from_groupname: retrieved DN for $groupname from cache: " . $cache['ldap']['dn'][$groupname]);
|
|
}
|
|
|
|
return $cache['ldap']['dn'][$groupname];
|
|
}
|
|
|
|
// EOF
|