/*
SSSD
Authors:
Pavel Březina <pbrezina@redhat.com>
Copyright (C) 2013 Red Hat
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/>.
*/
#include <errno.h>
#include <string.h>
#include <tevent.h>
#include <talloc.h>
#include <ldb.h>
#include <dhash.h>
#include <stdint.h>
#include <time.h>
#include "providers/ldap/ldap_common.h"
#include "providers/ldap/sdap_async.h"
#include "providers/ldap/sdap_async_private.h"
#include "providers/ldap/sdap_idmap.h"
enum sdap_nested_group_dn_type {
};
struct sdap_nested_group_member {
const char *dn;
const char *user_filter;
const char *group_filter;
};
#ifndef EXTERNAL_MEMBERS_CHUNK
#endif /* EXTERNAL_MEMBERS_CHUNK */
struct sdap_external_missing_member {
const char **parent_group_dns;
};
struct sdap_nested_group_ctx {
bool try_deref;
int deref_treshold;
int max_nesting_level;
};
static struct tevent_req *
struct tevent_context *ev,
struct sdap_nested_group_ctx *group_ctx,
int nesting_level,
struct sysdb_attrs *group);
static struct tevent_req *
struct tevent_context *ev,
struct sdap_nested_group_ctx *group_ctx,
struct sdap_nested_group_member *members,
int num_members,
int num_groups_max,
int nesting_level);
static struct tevent_req *
struct tevent_context *ev,
struct sdap_nested_group_ctx *group_ctx,
struct sdap_nested_group_member *member);
struct tevent_req *req,
struct sysdb_attrs **_user);
static struct tevent_req *
struct tevent_context *ev,
struct sdap_nested_group_ctx *group_ctx,
struct sdap_nested_group_member *member);
struct tevent_req *req,
struct sysdb_attrs **_group);
static struct tevent_req *
struct tevent_context *ev,
struct sdap_nested_group_ctx *group_ctx,
struct sdap_nested_group_member *member);
static errno_t
struct tevent_req *req,
struct sysdb_attrs **_entry,
enum sdap_nested_group_dn_type *_type);
static struct tevent_req *
struct tevent_context *ev,
struct sdap_nested_group_ctx *group_ctx,
struct ldb_message_element *members,
const char *group_dn,
int nesting_level);
static errno_t
unsigned long *_num_entries,
struct sysdb_attrs ***_entries)
{
unsigned long num_entries;
unsigned int i;
bool hret;
if (hret != HASH_SUCCESS) {
goto done;
}
if (num_entries > 0) {
goto done;
}
for (i = 0; i < num_entries; i++) {
}
}
if (_num_entries != NULL) {
}
}
done:
}
return ret;
}
const char *entry_key,
void *entry_value,
bool overwrite,
const char *table_name)
{
int hret;
return ENOMEM;
}
return EEXIST;
}
if (hret != HASH_SUCCESS) {
return EIO;
}
return EOK;
}
struct sysdb_attrs *entry,
const char *table_name)
{
return ret;
}
}
static errno_t
struct sysdb_attrs *user)
{
}
static errno_t
struct sysdb_attrs *group)
{
bool posix_group = true;
bool use_id_mapping;
bool can_find_gid;
bool need_filter;
return ret;
}
if (need_filter) {
posix_group = false;
gid = 0;
}
if (can_find_gid) {
&gid);
}
"Marking group as non-posix and setting GID=0!\n");
"Failed to add a GID to non-posix group!\n");
return ret;
}
}
"Error: Failed to mark group as non-posix!\n");
return ret;
}
return ret;
}
}
const char *ext_member,
const char *parent_group_dn)
{
int hret;
int ret;
"Inserting external member [%s] into external members hash table\n",
switch (hret) {
case HASH_ERROR_KEY_NOT_FOUND:
return ENOMEM;
}
const char *,
return ENOMEM;
}
true, "missing external users");
return ret;
}
break;
case HASH_SUCCESS:
struct sdap_external_missing_member);
if (ext_mem->parent_dn_idx == \
const char *,
ext_mem->parent_dn_idx + \
return ENOMEM;
}
}
break;
default:
return EIO;
}
return ENOMEM;
}
ext_mem->parent_dn_idx++;
return EOK;
}
const char *filter,
bool user)
{
NULL};
if (user) {
} else {
}
goto done;
}
if (count != 1) {
goto done;
}
/* we found an object with this origDN in the sysdb,
* check if it is valid */
if (user) {
if (uid == 0) {
goto done;
}
}
/* needs refresh */
goto done;
}
/* valid object */
done:
return ret;
}
static errno_t
struct sss_domain_info *domain,
const char *member_dn,
enum sdap_nested_group_dn_type *_type)
{
return ENOMEM;
}
goto done;
}
goto done;
}
/* determine correct domain of this member */
/* search in users */
/* user found */
goto done;
/* error */
goto done;
}
/* search in groups */
/* group found */
goto done;
/* error */
goto done;
}
/* not found in the sysdb */
done:
return ret;
}
static bool
{
bool ret = false;
filter);
if (ret == true) {
break;
}
}
return ret;
}
static inline bool
{
}
static inline bool
{
}
static errno_t
struct sdap_nested_group_ctx *group_ctx,
int threshold,
int nesting_level,
struct ldb_message_element *members,
struct sdap_nested_group_member **_missing,
int *_num_missing,
int *_num_groups)
{
int num_missing = 0;
int num_groups = 0;
bool bret;
bool is_user;
bool is_group;
int i;
*_num_missing = 0;
*_num_groups = 0;
return EOK;
}
return ENOMEM;
}
goto done;
}
/* create list of missing members
* skip dn if:
* - is present in user or group hash table
* - is present in sysdb and not expired
* - it is a group and we have reached the maximal nesting level
* - it is not under user nor group search bases
*
* if dn is in sysdb but expired
* - we know what object type it is
*
* if dn is not in hash table or sysdb
* - try to determine type of object by search base that match dn
*/
for (i = 0; i < members->num_values; i++) {
/* check hash tables */
if (bret) {
continue;
}
if (bret) {
continue;
}
/* check sysdb */
/* found and valid */
continue;
/* error */
goto done;
}
/* try to determine type by dn */
if (type == SDAP_NESTED_GROUP_DN_UNKNOWN) {
/* user */
&user_filter);
&group_filter);
/* search bases overlap */
} else if (is_user) {
} else if (is_group) {
} else {
/* dn is outside search bases */
"search bases, skipping\n", dn);
continue;
}
}
/* check nesting level */
if (type == SDAP_NESTED_GROUP_DN_GROUP) {
continue;
}
}
goto done;
}
num_missing++;
if (_num_missing) {
}
goto done;
}
if (type != SDAP_NESTED_GROUP_DN_USER) {
num_groups++;
}
}
struct sdap_nested_group_member, num_missing);
/* talloc_realloc behaves as talloc_free if 3rd parameter (count) is 0,
* so it's OK to return NULL then
*/
goto done;
}
if (_missing) {
}
if (_num_missing) {
}
if (_num_groups) {
}
done:
return ret;
}
static errno_t
struct sysdb_attrs *group,
struct ldb_message_element *ext_members)
{
const char *ext_member_attr;
const char *orig_dn;
if (ext_members == NULL) {
return EOK;
}
return ret;
}
orig_dn);
"Cannot add %s into external members [%d]: %s\n",
return ret;
}
}
return EOK;
}
static struct ldb_message_element *
struct sysdb_attrs *group)
{
return NULL;
}
false, &ext_members);
}
return ext_members;
}
struct sdap_nested_group_state {
};
struct tevent_req *
struct tevent_context *ev,
struct sdap_domain *sdom,
struct sdap_options *opts,
struct sdap_handle *sh,
struct sysdb_attrs *group)
{
int i;
return NULL;
}
/* create main nested group context */
goto immediately;
}
goto immediately;
}
goto immediately;
}
goto immediately;
}
/* disable deref if threshold <= 0 */
}
/* if any search base contains filter, disable dereference. */
"dereference will be disabled\n");
break;
}
}
}
"dereference will be disabled\n");
break;
}
}
}
/* insert initial group into hash table */
goto immediately;
}
/* resolve group */
0, group);
goto immediately;
}
return req;
} else {
}
return req;
}
{
return;
}
}
struct tevent_req *req,
unsigned long *_num_users,
struct sysdb_attrs ***_users,
unsigned long *_num_groups,
struct sysdb_attrs ***_groups,
{
unsigned long num_users;
unsigned long num_groups;
return ret;
}
&num_groups, &groups);
return ret;
}
if (_num_users != NULL) {
*_num_users = num_users;
}
}
if (_num_groups!= NULL) {
}
}
if (_missing_external) {
}
return EOK;
}
struct sdap_nested_group_process_state {
int num_missing_total;
int num_missing_groups;
int nesting_level;
char *group_dn;
bool deref;
bool deref_shortcut;
};
static struct tevent_req *
struct tevent_context *ev,
struct sdap_nested_group_ctx *group_ctx,
int nesting_level,
struct sysdb_attrs *group)
{
int split_threshold;
struct sdap_nested_group_process_state);
return NULL;
}
/* get original dn */
goto immediately;
}
goto immediately;
}
/* get member list, both direct and external */
group);
goto immediately;
goto immediately;
}
-1;
/* get members that need to be refreshed */
if (ret == ERR_DEREF_THRESHOLD) {
"More members were missing than the deref threshold\n");
state->deref_shortcut = true;
goto immediately;
}
state->ext_members);
goto immediately;
}
if (state->num_missing_total == 0
goto immediately;
}
/* If there are only indirect members of the group, it's still safe to
* proceed and let the direct lookup code just fall through.
*/
"Looking up %d/%d members of group [%s]\n",
orig_dn);
/* process members */
orig_dn);
} else {
"processed individually\n", orig_dn);
}
goto immediately;
}
return req;
} else {
}
return req;
}
{
/* dereference is not supported, try again without dereference */
if (state->deref_shortcut == true) {
/* If we previously short-cut dereference, we need to split the
* members again to get full list of missing member types
*/
-1,
"[%d]: %s\n",
goto done;
}
}
goto done;
}
req);
}
} else {
}
done:
}
}
{
#ifdef HAVE_SYSTEMTAP
#endif
return EOK;
}
struct sdap_nested_group_recurse_state {
int num_groups;
int index;
int nesting_level;
};
static struct tevent_req *
struct tevent_context *ev,
struct sdap_nested_group_ctx *group_ctx,
struct sysdb_attrs **nested_groups,
int num_groups,
int nesting_level)
{
struct sdap_nested_group_recurse_state);
return NULL;
}
/* process each group individually */
goto immediately;
}
return req;
} else {
}
return req;
}
{
/* we're done */
return EOK;
}
return ENOMEM;
}
return EAGAIN;
}
{
goto done;
}
done:
}
return;
}
{
return EOK;
}
struct sdap_nested_group_single_state {
int nesting_level;
int num_members;
int member_index;
int num_groups;
};
static struct tevent_req *
struct tevent_context *ev,
struct sdap_nested_group_ctx *group_ctx,
struct sdap_nested_group_member *members,
int num_members,
int num_groups_max,
int nesting_level)
{
struct sdap_nested_group_single_state);
return NULL;
}
state->member_index = 0;
goto immediately;
}
/* process each member individually */
goto immediately;
}
return req;
} else {
}
return req;
}
{
/* we're done */
return EOK;
}
state->member_index++;
break;
break;
break;
}
return ENOMEM;
}
return EAGAIN;
}
static errno_t
{
/* set correct type if possible */
goto done;
}
}
}
/* type was not unknown, receive data */
goto done;
}
/* user not found, continue */
break;
}
}
/* save user in hash table */
/* the user is already present, skip it */
goto done;
goto done;
}
break;
/* type was not unknown, receive data */
goto done;
}
/* group not found, continue */
break;
}
} else {
/* the type was unknown so we had to pull the group,
* but we don't want to process it if we have reached
* the nesting level */
"The entry has no originalDN\n");
orig_dn = "invalid";
}
break;
}
}
/* save group in hash table */
/* the group is already present, skip it */
goto done;
goto done;
}
/* remember the group for later processing */
state->num_groups++;
break;
/* not found in users nor nested_groups, continue */
break;
}
done:
return ret;
}
{
/* process direct members */
goto done;
}
/* we have processed all direct members,
* now recurse and process nested groups */
goto done;
}
/* error */
goto done;
}
/* we're not done yet */
done:
/* tevent_req_error() cannot cope with EOK */
}
return;
}
{
/* all nested groups are completed */
return;
}
return;
}
{
return EOK;
}
const char *user_dn,
struct sysdb_attrs **_user)
{
char *name;
return ENOMEM;
}
"cn", "users", "cn", "accounts");
goto done;
}
goto done;
}
goto done;
}
goto done;
}
goto done;
}
done:
return ret;
}
struct sdap_nested_group_lookup_user_state {
};
static struct tevent_req *
struct tevent_context *ev,
struct sdap_nested_group_ctx *group_ctx,
struct sdap_nested_group_member *member)
{
struct sdap_nested_group_lookup_user_state);
return NULL;
}
/* if the schema is IPA, then just shortcut and guess the name */
goto immediately;
}
}
/* only pull down username and originalDN */
goto immediately;
}
attrs[0] = "objectClass";
/* create filter */
if (base_filter == NULL) {
goto immediately;
}
/* use search base filter if needed */
goto immediately;
}
/* search */
false);
goto immediately;
}
return req;
} else {
}
return req;
}
{
count = 0;
goto done;
}
if (count == 1) {
} else if (count == 0) {
/* group not found */
} else {
"BASE search returned more than one records\n");
goto done;
}
done:
return;
}
}
struct tevent_req *req,
struct sysdb_attrs **_user)
{
}
return EOK;
}
struct sdap_nested_group_lookup_group_state {
};
static struct tevent_req *
struct tevent_context *ev,
struct sdap_nested_group_ctx *group_ctx,
struct sdap_nested_group_member *member)
{
char *oc_list;
struct sdap_nested_group_lookup_group_state);
return NULL;
}
goto immediately;
}
/* create filter */
goto immediately;
}
if (base_filter == NULL) {
goto immediately;
}
/* use search base filter if needed */
goto immediately;
}
/* search */
false);
goto immediately;
}
return req;
} else {
}
return req;
}
{
count = 0;
goto done;
}
if (count == 1) {
} else if (count == 0) {
/* group not found */
} else {
"BASE search returned more than one records\n");
goto done;
}
done:
return;
}
}
struct tevent_req *req,
struct sysdb_attrs **_group)
{
}
return EOK;
}
};
static void
static void
static struct tevent_req *
struct tevent_context *ev,
struct sdap_nested_group_ctx *group_ctx,
struct sdap_nested_group_member *member)
{
return NULL;
}
/* try users first */
goto immediately;
}
req);
return req;
} else {
}
return req;
}
static void
{
goto done;
}
/* found in users */
goto done;
}
/* not found in users, try group */
goto done;
}
req);
done:
}
return;
}
static void
{
goto done;
}
/* not found, end request */
} else {
/* found in groups */
}
done:
return;
}
}
static errno_t
struct tevent_req *req,
struct sysdb_attrs **_entry,
enum sdap_nested_group_dn_type *_type)
{
}
}
return EOK;
}
struct sdap_nested_group_deref_state {
int nesting_level;
int num_groups;
};
static struct tevent_req *
struct tevent_context *ev,
struct sdap_nested_group_ctx *group_ctx,
struct ldb_message_element *members,
const char *group_dn,
int nesting_level)
{
struct sdap_nested_group_deref_state);
return NULL;
}
goto immediately;
}
/* pull down the whole group map,
* but only pull down username and originalDN for users */
goto immediately;
}
goto immediately;
}
/* send request */
goto immediately;
}
return req;
} else {
}
return req;
}
static errno_t
{
size_t i, j;
bool member_found;
goto done;
}
"about to process them\n", num_entries);
/*
* We don't have any knowledge about possible number of groups when
* dereferencing. We expect that every member is a group and we will
* allocate enough space to hold it. We will shrink the memory later.
*/
goto done;
}
for (i = 0; i < num_entries; i++) {
SYSDB_ORIG_DN, &orig_dn);
goto done;
}
/* Ensure that all members returned from the deref request are included
* in the member processing. Sometimes we will get more results back
* with Active Directory and its range retrieval mechanism.
*/
member_found = false;
for (j = 0; j < members->num_values; j++) {
/* FIXME: This is inefficient for very large sets of groups */
member_found = true;
break;
}
}
if (!member_found) {
/* Append newly found member to member list.
* Changes in state->members will propagate into sysdb_attrs of
* the group. */
struct ldb_val,
goto done;
}
goto done;
}
members->num_values++;
}
/* we found a user */
/* skip the user if it is not amongst configured search bases */
continue;
}
/* save user in hash table */
"Unable to save user in hash table "
goto done;
}
/* we found a group */
/* skip the group if we have reached the nesting limit */
continue;
}
/* skip the group if it is not amongst configured search bases */
continue;
}
/* save group in hash table */
continue;
"Unable to save group in hash table "
goto done;
}
/* remember the group for later processing */
state->num_groups++;
} else {
/* this should never happen, but if it does, do not loop forever */
"Entry does not match any known map, skipping\n");
continue;
}
}
/* adjust size of nested groups array */
if (state->num_groups > 0) {
struct sysdb_attrs *,
state->num_groups);
goto done;
}
} else {
}
done:
return ret;
}
{
/* process direct members */
goto done;
}
/* we have processed all direct members,
* now recurse and process nested groups */
goto done;
}
done:
/* tevent_req_error() cannot cope with EOK */
}
return;
}
{
/* process nested groups */
} else {
}
return;
}
{
return EOK;
}
struct sdap_ext_member {
const char *ext_member_attr;
};
unsigned long n_entries;
unsigned long eniter;
};
static errno_t
static void
static errno_t
static errno_t
struct sdap_ext_member *member);
static errno_t
struct sss_domain_info *group_dom,
const char *original_dn,
const char ***_parents);
struct tevent_req *
struct tevent_context *ev,
struct sss_domain_info *group_dom,
struct sdap_ext_member_ctx *ext_ctx,
{
return NULL;
}
goto immediately;
}
if (ret != HASH_SUCCESS) {
goto immediately;
}
struct sdap_ext_member,
goto immediately;
}
goto immediately;
}
return req;
} else {
}
return req;
}
static errno_t
{
return ENOMEM;
}
req);
return EAGAIN;
}
static void
{
&member);
}
return;
}
return;
}
return;
}
return;
}
static errno_t
{
bool in_transaction = false;
goto fail;
}
in_transaction = true;
continue;
}
&state->ext_members[i]);
goto fail;
}
}
goto fail;
}
in_transaction = false;
return EOK;
fail:
if (in_transaction) {
}
}
return EFAULT;
}
static errno_t
struct sdap_ext_member *member)
{
const char *name;
int ret;
size_t i;
const char *orig_dn;
return ENOMEM;
}
goto done;
}
/* This only works because the groups were saved in a previous
* transaction */
"Linking external members %s from domain %s to parents of %s\n",
&parents);
"Cannot find parents of %s\n", orig_dn);
continue;
}
/* We don't have to remove the members here, since all members attributes
* are always written anew
*/
goto done;
}
}
done:
return ret;
}
static errno_t
struct sss_domain_info *group_dom,
const char *original_dn,
const char ***_parents)
{
char *sanitized_dn;
char *filter;
NULL };
const char **parents;
return ENOMEM;
}
"Cannot sanitize originalDN [%s]\n", original_dn);
goto done;
}
goto done;
}
goto done;
}
if (count != 1) {
"More than one entry found by originalDN?\n");
goto done;
}
"The external group is not a member of any groups\n");
goto done;
}
const char *,
goto done;
}
goto done;
}
}
done:
return ret;
}
struct tevent_req *req)
{
return EOK;
}