sdap_async_nested_groups.c revision 759fd29a597533a3f5489246c0d2b658d8bee417
/*
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"
enum sdap_nested_group_dn_type {
};
struct sdap_nested_group_member {
const char *dn;
const char *user_filter;
const char *group_filter;
};
struct sdap_nested_group_ctx {
struct sss_domain_info *domain;
struct sdap_options *opts;
struct sdap_search_base **user_search_bases;
struct sdap_search_base **group_search_bases;
struct sdap_handle *sh;
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;
}
struct sysdb_attrs *entry,
const char *table_name)
{
int hret;
return ret;
}
name, table_name);
return ENOMEM;
}
return EEXIST;
}
if (hret != HASH_SUCCESS) {
return EIO;
}
return EOK;
}
static errno_t
struct sysdb_attrs *user)
{
}
static errno_t
struct sysdb_attrs *group)
{
bool posix_group = true;
return ret;
}
/* Only security groups from AD are considered for POSIX groups.
* Additionally only global and universal group are taken to account
* for trusted domains. */
if (!(ad_group_type & SDAP_AD_GROUP_TYPE_SECURITY)
&& (!((ad_group_type & SDAP_AD_GROUP_TYPE_GLOBAL)
|| (ad_group_type & SDAP_AD_GROUP_TYPE_UNIVERSAL))))) {
posix_group = false;
gid = 0;
}
}
&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 *filter,
bool user)
{
static const char *attrs[] = {SYSDB_CACHE_EXPIRE,
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)
{
char *sanitized_dn = NULL;
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;
struct sdap_search_base **search_bases;
filter);
if (ret == true) {
break;
}
}
return ret;
}
static inline bool
{
}
static inline bool
{
}
static errno_t
struct sdap_nested_group_ctx *group_ctx,
int nesting_level,
struct ldb_message_element *members,
struct sdap_nested_group_member **_missing,
int *_num_missing,
int *_num_groups)
{
char *user_filter = NULL;
char *group_filter = NULL;
int num_missing = 0;
int num_groups = 0;
bool bret;
bool is_user;
bool is_group;
int i;
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 (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;
}
struct sdap_nested_group_state {
struct sdap_nested_group_ctx *group_ctx;
};
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;
}
/* 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) {
}
}
return EOK;
}
struct sdap_nested_group_process_state {
struct tevent_context *ev;
struct sdap_nested_group_ctx *group_ctx;
struct sdap_nested_group_member *missing;
int num_missing_total;
int num_missing_groups;
int nesting_level;
char *group_dn;
bool deref;
};
static struct tevent_req *
struct tevent_context *ev,
struct sdap_nested_group_ctx *group_ctx,
int nesting_level,
struct sysdb_attrs *group)
{
struct sdap_nested_group_process_state);
return NULL;
}
/* get original dn */
goto immediately;
}
goto immediately;
}
/* get member list */
&members);
goto immediately;
goto immediately;
}
/* get members that need to be refreshed */
if (state->num_missing_total == 0) {
goto immediately;
}
/* 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 */
goto done;
}
req);
}
} else {
}
done:
}
}
{
return EOK;
}
struct sdap_nested_group_recurse_state {
struct tevent_context *ev;
struct sdap_nested_group_ctx *group_ctx;
struct sysdb_attrs **groups;
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 {
struct tevent_context *ev;
struct sdap_nested_group_ctx *group_ctx;
struct sdap_nested_group_member *members;
int nesting_level;
struct sdap_nested_group_member *current_member;
int num_members;
int member_index;
struct sysdb_attrs **nested_groups;
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;
}
/* This should be a function pointer set from the IPA provider */
const char *user_dn,
struct sysdb_attrs **_user)
{
char *name;
const char *rdn_name;
const char *users_comp_name;
const char *acct_comp_name;
const struct ldb_val *users_comp_val;
const struct ldb_val *acct_comp_val;
/* return username if dn is in form:
* uid=username,cn=users,cn=accounts,dc=example,dc=com */
goto done;
}
/* rdn, users, accounts and least one domain component */
goto done;
}
goto done;
}
/* rdn must be 'uid' */
goto done;
}
/* second component must be 'cn=users' */
goto done;
}
users_comp_val->length) != 0) {
goto done;
}
/* third component must be 'cn=accounts' */
goto done;
}
acct_comp_val->length) != 0) {
goto done;
}
/* value of rdn is username */
goto done;
}
goto done;
}
goto done;
}
goto done;
}
goto done;
}
done:
return ret;
}
struct sdap_nested_group_lookup_user_state {
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)
{
const char *base_filter = NULL;
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 {
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)
{
const char *base_filter = NULL;
struct sdap_nested_group_lookup_group_state);
return NULL;
}
goto immediately;
}
/* 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 **_group)
{
}
return EOK;
}
struct tevent_context *ev;
struct sdap_nested_group_ctx *group_ctx;
struct sdap_nested_group_member *member;
struct sysdb_attrs *entry;
};
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 {
struct tevent_context *ev;
struct sdap_nested_group_ctx *group_ctx;
struct ldb_message_element *members;
int nesting_level;
struct sysdb_attrs **nested_groups;
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)
{
static const int num_maps = 2;
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 num_entries = 0;
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;
}