sdap_async_groups.c revision 2b7ee2a760e7fcc70f4970a3bbee6fbf8f2ccb9d
/*
SSSD
Async LDAP Helper routines - retrieving groups
Copyright (C) Simo Sorce <ssorce@redhat.com> - 2009
Copyright (C) 2010, Ralf Haferkamp <rhafer@suse.de>, Novell Inc.
Copyright (C) Jan Zeleny <jzeleny@redhat.com> - 2011
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 "providers/ldap/sdap_async_private.h"
#include "providers/ldap/ldap_common.h"
#include "providers/ldap/sdap_idmap.h"
/* ==Group-Parsing Routines=============================================== */
struct sss_domain_info *domain,
const char *orig_dn,
char **localdn)
{
char *filter;
struct ldb_message **msgs;
int ret;
char *sanitized_dn;
if (!tmpctx) {
return ENOMEM;
}
goto done;
}
if (!filter) {
goto done;
}
if (!base_dn) {
goto done;
}
if (ret) {
goto done;
}
if (num_msgs != 1) {
goto done;
}
if (!*localdn) {
goto done;
}
done:
return ret;
}
static errno_t
struct sss_domain_info *domain,
{
char *filter;
struct ldb_message **msgs;
size_t i;
char **localdn;
/* Don't search if the group is non-posix */
(unsigned long long) gid);
if (!filter) {
return ENOMEM;
}
*_ndn = 0;
return EOK;
return ret;
}
if (!localdn) {
return ENOMEM;
}
for (i=0; i < count; i++) {
if (!localdn[i]) {
return ENOMEM;
}
}
return EOK;
}
static errno_t
struct sdap_options *opts,
{
&gid);
/* Non-posix AD group. Skip. */
*_count = 0;
return EOK;
return ret;
}
return EOK;
}
struct sss_domain_info *domain,
int num_values,
char **userdns,
{
struct ldb_message_element *el;
int i, j;
int ret;
if (ret) {
goto done;
}
/* Just allocate both big enough to contain all members for now */
goto done;
}
j = el->num_values;
for (i = 0; i < num_values; i++) {
} else {
}
if (hret == HASH_ERROR_KEY_NOT_FOUND) {
/* sync search entry with this as origDN */
/* This should never return ENOENT
* -> fail if it does
*/
goto done;
}
j++;
} else if (hret != HASH_SUCCESS) {
goto done;
}
/* If the member is in ghost table, it has
* already been processed - just skip it */
}
el->num_values = j;
for (i=0; i < nuserdns; i++) {
}
done:
return ret;
}
/* ==Save-Group-Entry===================================================== */
/* FIXME: support non legacy */
/* FIXME: support storing additional attributes */
static errno_t
struct sss_domain_info *domain,
const char *name,
struct sysdb_attrs *group_attrs,
bool posix_group,
{
/* make sure that non-posix (empty or explicit gid=0) groups have the
* gidNumber set to zero even if updating existing group */
if (!posix_group) {
if (ret) {
return ret;
}
}
if (ret) {
return ret;
}
return ret;
}
static errno_t
struct sdap_options *opts,
bool populate_members,
bool store_original_member,
struct sysdb_attrs *sysdb_attrs)
{
struct ldb_message_element *gh;
struct ldb_message_element *memberel;
struct ldb_message_element *sysdb_memberel;
struct ldb_message_element *ghostel;
int i;
int hret;
("Error reading ghost attributes: [%s]\n",
return ret;
}
&memberel);
return ret;
}
if (store_original_member) {
for (i = 0; i < memberel->num_values; i++) {
if (ret) {
return ret;
}
}
}
if (populate_members) {
("Error reading group members from group_attrs: [%s]\n",
return ret;
}
}
return ret;
}
/* Now process RFC2307bis ghost hash table */
return ENOMEM;
}
for (i = 0; i < memberel->num_values; i++) {
if (hret == HASH_ERROR_KEY_NOT_FOUND) {
continue;
} else if (hret != HASH_SUCCESS) {
("Error checking hash table: [%s]\n",
return EFAULT;
}
return ENOMEM;
}
ghostel->num_values++;
}
}
return EOK;
}
struct sdap_options *opts,
struct sss_domain_info *dom,
struct sysdb_attrs *attrs,
bool populate_members,
bool store_original_member,
char **_usn_value,
{
struct ldb_message_element *el;
struct sysdb_attrs *group_attrs;
bool posix_group;
char *sid_str;
if (!tmpctx) {
goto done;
}
if (group_attrs == NULL) {
goto done;
}
&name);
goto done;
}
if (use_id_mapping) {
posix_group = true;
("Mapping group [%s] objectSID to unix ID\n", name));
&sid_str);
("Could not identify objectSID: [%s]\n",
goto done;
}
/* Add string representation to the cache for easier
* debugging
*/
("Could not add SID string: [%s]\n",
goto done;
}
/* Convert the SID into a UNIX group ID */
/* ENOTSUP is returned if built-in SID was provided
* => do not store the group, but return EOK */
goto done;
("Could not convert SID string: [%s]\n",
goto done;
}
/* Store the GID in the ldap_attrs so it doesn't get
* treated as a missing attribute from LDAP and removed.
*/
if (ret) {
goto done;
}
} else {
posix_group = true;
("Error reading posix attribute: [%s]\n",
goto done;
}
("Error setting posix attribute: [%s]\n",
goto done;
}
&gid);
goto done;
}
}
/* check that the gid is valid for this domain */
if (posix_group) {
name));
goto done;
}
/* Group ID OK */
}
name, group_attrs);
("Error setting original DN: [%s]\n",
goto done;
}
"original mod-Timestamp",
name, group_attrs);
("Error setting mod timestamp: [%s]\n",
goto done;
}
if (ret) {
("Error looking up group USN: [%s]\n",
goto done;
}
if (el->num_values == 0) {
name));
} else {
if (ret) {
("Error setting group USN: [%s]\n",
goto done;
}
if (!usn_value) {
goto done;
}
}
goto done;
}
goto done;
}
posix_group, now);
if (ret) {
("Could not store group with GID: [%s]\n",
goto done;
}
if (_usn_value) {
}
done:
if (ret) {
("Failed to save group [%s]: [%s]\n",
}
return ret;
}
/* ==Save-Group-Memebrs=================================================== */
/* FIXME: support non legacy */
/* FIXME: support storing additional attributes */
struct sdap_options *opts,
struct sss_domain_info *dom,
struct sysdb_attrs *attrs,
{
struct ldb_message_element *el;
const char *name;
int ret;
&name);
goto fail;
}
/* With AD we also want to merge in parent groups of primary GID as they
* are reported with tokenGroups, too
*/
goto fail;
}
}
goto fail;
}
} else {
if (!group_attrs) {
goto fail;
}
if (ret) {
goto fail;
}
}
return EOK;
fail:
return ret;
}
/* ==Generic-Function-to-save-multiple-groups============================= */
struct sss_domain_info *dom,
struct sdap_options *opts,
struct sysdb_attrs **groups,
int num_groups,
bool populate_members,
char **_usn_value)
{
char *higher_usn = NULL;
char *usn_value;
bool twopass;
bool has_nesting = false;
int ret;
int i;
int nsaved_groups = 0;
bool in_transaction = false;
switch (opts->schema_type) {
case SDAP_SCHEMA_RFC2307:
twopass = false;
break;
case SDAP_SCHEMA_RFC2307BIS:
case SDAP_SCHEMA_IPA_V1:
case SDAP_SCHEMA_AD:
twopass = true;
has_nesting = true;
break;
default:
return EINVAL;
}
if (!tmpctx) {
return ENOMEM;
}
if (ret) {
goto done;
}
in_transaction = true;
if (twopass && !populate_members) {
if (!saved_groups) {
goto done;
}
}
for (i = 0; i < num_groups; i++) {
/* if 2 pass savemembers = false */
/* Do not fail completely on errors.
* Just report the failure to save and go on */
if (ret) {
} else {
if (twopass && !populate_members) {
}
}
if (usn_value) {
if (higher_usn) {
} else {
}
} else {
}
}
}
if (twopass && !populate_members) {
for (i = 0; i < nsaved_groups; i++) {
/* Do not fail completely on errors.
* Just report the failure to save and go on */
if (ret) {
} else {
}
}
}
if (ret) {
goto done;
}
in_transaction = false;
if (_usn_value) {
}
done:
if (in_transaction) {
}
}
return ret;
}
/* ==Process-Groups======================================================= */
struct sdap_process_group_state {
struct tevent_context *ev;
struct sdap_options *opts;
struct sdap_handle *sh;
struct sss_domain_info *dom;
struct sysdb_attrs *group;
struct ldb_message_element* sysdb_dns;
struct ldb_message_element* ghost_dns;
char **queued_members;
int queue_len;
const char **attrs;
const char *filter;
bool enumeration;
};
#define GROUPMEMBER_REQ_PARALLEL 50
struct sdap_process_group_state *state,
struct ldb_message_element *memberel);
struct ldb_message_element *memberel,
struct ldb_message_element *ghostel);
struct ldb_message_element **_dns)
{
struct ldb_message_element *dns;
return ENOMEM;
}
dns->num_values = 0;
return ENOMEM;
}
return EOK;
}
struct tevent_context *ev,
struct sss_domain_info *dom,
struct sdap_options *opts,
struct sdap_handle *sh,
struct sysdb_attrs *group,
bool enumeration)
{
struct ldb_message_element *el;
struct ldb_message_element *ghostel;
struct sdap_process_group_state *grp_state;
const char **attrs;
char* filter;
int ret;
struct sdap_process_group_state);
if (ret) {
goto done;
}
/* FIXME: we ignore nested rfc2307bis groups for now */
if (!filter) {
return NULL;
}
grp_state->check_count = 0;
&el);
if (ret) {
goto done;
}
/* Group without members */
if (el->num_values == 0) {
goto done;
}
&ghostel);
if (ret) {
goto done;
}
if (ghostel->num_values == 0) {
/* Element was probably newly created, look for "member" again */
&el);
goto done;
}
}
goto done;
}
goto done;
}
switch (opts->schema_type) {
case SDAP_SCHEMA_RFC2307:
break;
case SDAP_SCHEMA_IPA_V1:
case SDAP_SCHEMA_AD:
case SDAP_SCHEMA_RFC2307BIS:
/* Note that this code branch will be used only if
* ldap_nesting_level = 0 is set in config file
*/
break;
default:
break;
}
done:
/* We managed to process all the entries */
/* EBUSY means we need to wait for entries in LDAP */
}
}
return req;
}
static int
char *user_dn,
int num_users)
{
struct sdap_process_group_state *grp_state =
struct tevent_req *subreq;
/*
* Issue at most GROUPMEMBER_REQ_PARALLEL LDAP searches at once.
* The rest is sent while the results are being processed.
* We limit the number as of request here, as the Server might
* enforce limits on the number of pending operations per
* connection.
*/
if (!grp_state->queued_members) {
if (!grp_state->queued_members) {
return ENOMEM;
}
}
} else {
false);
if (!subreq) {
return ENOMEM;
}
}
grp_state->check_count++;
return EOK;
}
static int
struct sdap_process_group_state *state,
struct ldb_message_element *memberel)
{
char *member_dn;
char *strdn;
int ret;
int i;
for (i=0; i < memberel->num_values; i++) {
&strdn);
/*
* User already cached in sysdb. Remember the sysdb DN for later
* use by sdap_save_groups()
*/
if (!state->enumeration) {
/* The user is not in sysdb, need to add it
* We don't need to do this if we're in an enumeration,
* because all real members should all be populated
* already by the first pass of the enumeration.
* Also, we don't want to be holding the sysdb
* transaction while we're performing LDAP lookups.
*/
i, member_dn));
return ret;
}
}
} else {
return ret;
}
}
}
if (state->check_count == 0) {
/*
* All group members are already cached in sysdb, we are done
* with this group. To avoid redundant sysdb lookups, populate the
* "member" attribute of the group entry with the sysdb DNs of
* the members.
*/
} else {
}
return ret;
}
static int
struct sss_domain_info *dom,
const char *username)
{
return ENOMEM;
}
sysdb_dns->num_values++;
return EOK;
}
static int
{
int ret;
const char *filter;
const char *username;
const char *user_dn;
/* Check for the alias in the sysdb */
if (!filter) {
goto done;
}
/* Entry exists but the group references it with an alias. */
if (count != 1) {
goto done;
}
/* fill username with primary name */
"without primary name?\n"));
goto done;
}
return ENOMEM;
}
}
/* The entry really does not exist, add a ghost */
}
} else {
}
done:
return ret;
}
static int
struct ldb_message_element *memberel,
struct ldb_message_element *ghostel)
{
struct ldb_message *msg;
char *member_name;
char *userdn;
int ret;
int i;
for (i=0; i < memberel->num_values; i++) {
/* We need to skip over zero-length usernames */
if (member_name[0] == '\0') continue;
/*
* User already cached in sysdb. Remember the sysdb DN for later
* use by sdap_save_groups()
*/
return ENOMEM;
}
goto done;
}
/* The user is not in sysdb, need to add it */
i, member_name));
i, member_name));
goto done;
}
} else {
goto done;
}
}
done:
return ret;
}
{
struct sysdb_attrs **usr_attrs;
int ret;
struct tevent_req *req =
struct sdap_process_group_state *state =
struct ldb_message_element *el;
state->check_count--;
if (ret) {
goto next;
}
if (count != 1) {
goto next;
}
if (el->num_values == 0) {
}
if (ret) {
goto next;
}
strlen((char *)name_string);
next:
if (ret) {
}
/* Are there more searches for uncached users to submit ? */
false);
if (!subreq) {
return;
}
}
if (state->check_count == 0) {
/*
* To avoid redundant sysdb lookups, populate the "member" attribute
* of the group entry with the sysdb DNs of the members.
*/
&el);
("Failed to get the group member attribute [%d]: %s\n",
return;
}
return;
}
}
}
{
return EOK;
}
/* ==Search-Groups-with-filter============================================ */
struct sdap_get_groups_state {
struct tevent_context *ev;
struct sdap_options *opts;
struct sdap_handle *sh;
struct sss_domain_info *dom;
const char **attrs;
const char *base_filter;
char *filter;
int timeout;
bool enumeration;
char *higher_usn;
struct sysdb_attrs **groups;
struct sdap_search_base **search_bases;
};
struct tevent_context *ev,
struct sss_domain_info *dom,
struct sdap_options *opts,
struct sdap_search_base **search_bases,
struct sdap_handle *sh,
const char **attrs,
const char *filter,
int timeout,
bool enumeration)
{
struct tevent_req *req;
struct sdap_get_groups_state *state;
if (!search_bases) {
("Group lookup request without a search base\n"));
goto done;
}
done:
}
return req;
}
{
struct tevent_req *subreq;
struct sdap_get_groups_state *state;
return ENOMEM;
}
("Searching for groups with base [%s]\n",
if (!subreq) {
return ENOMEM;
}
return EOK;
}
static struct tevent_req *sdap_nested_group_process_send(
struct sss_domain_info *domain,
{
struct tevent_req *req =
struct sdap_get_groups_state *state =
int ret;
int i;
bool next_base = false;
struct sysdb_attrs **groups;
bool enable_deref = true;
if (ret) {
return;
}
("Individual group search returned multiple results\n"));
return;
}
next_base = true;
}
/* Add this batch of groups to the list */
if (count > 0) {
struct sysdb_attrs *,
return;
}
/* Copy the new groups into the list
*/
for (i = 0; i < count; i++) {
}
}
if (next_base) {
/* There are more search bases to try */
}
return;
}
}
/* No more search bases
* Return ENOENT if no groups were found
*/
return;
}
/* Check whether we need to do nested searches
* for RFC2307bis/FreeIPA/ActiveDirectory
* We don't need to do this for enumeration,
* because all groups will be picked up anyway.
*
* We can also skip this if we're using the
* LDAP_MATCHING_RULE_IN_CHAIN available in
* AD 2008 and later
*/
if (!state->enumeration) {
/* Prepare hashes for nested user processing */
return;
}
return;
}
/*
* If any search base contains filter, disable dereference.
*/
enable_deref = true;
("User search base contains filter, "
"dereference will be disabled\n"));
enable_deref = false;
break;
}
}
if (enable_deref) {
("Group search base contains filter, "
"dereference will be disabled\n"));
enable_deref = false;
break;
}
}
}
0);
if (!subreq) {
return;
}
return;
}
}
/* We have all of the groups. Save them to the sysdb */
/* If we're using LDAP_MATCHING_RULE_IN_CHAIN, start a subreq to
* retrieve the members so we can save them in a single step.
*/
if (!state->enumeration
if (!subreq) {
return;
}
req);
return;
}
DEBUG(0, ("Failed to start transaction\n"));
return;
}
if (state->enumeration
"to allow unrolling of nested groups.\n"));
if (ret) {
return;
}
}
state->enumeration);
if (!subreq) {
return;
}
}
}
{
struct tevent_req *req =
struct sdap_get_groups_state *state =
int ret;
if (ret) {
DEBUG(0, ("Could not cancel sysdb transaction\n"));
}
return;
}
state->check_count--;
if (state->check_count == 0) {
/* If ignore_group_members is set for the domain, don't update
* group memberships in the cache.
*/
&state->higher_usn);
if (ret) {
return;
}
DEBUG(0, ("Couldn't commit transaction\n"));
} else {
}
}
}
struct sss_domain_info *domain,
struct sdap_options *opts,
struct sysdb_attrs **users,
int num_users,
hash_table_t **_ghosts);
{
struct tevent_req *req =
struct sdap_get_groups_state);
struct sysdb_attrs **users;
struct ldb_message_element *member_el;
struct ldb_message_element *orig_dn_el;
size_t i;
("Could not retrieve members using AD match rule. [%s]\n",
goto done;
}
/* Save the group and users to the cache */
/* Truncate the member attribute of the group.
* It will be repopulated below, and it may currently
* be incomplete anyway, thanks to the range extension.
*/
goto done;
}
member_el->num_values = 0;
if (!tmp_ctx) {
goto done;
}
/* Figure out which users are already cached in the sysdb and
* which ones need to be added as ghost users.
*/
&ghosts);
("Could not determine which users are ghosts: [%s]\n",
goto done;
}
/* Add any entries that aren't in the ghost hash table to the
* member element of the group. This will get converted to a
* native sysdb representation later in sdap_save_groups().
*/
/* Add all of the users as members
*/
goto done;
}
/* Copy the origDN values of the users into the member element */
for (i = 0; i < count; i++) {
&orig_dn_el);
/* This should never happen. Every entry should have
* an originalDN.
*/
("BUG: Missing originalDN for user?\n"));
goto done;
}
/* These values will have the same lifespan, so instead
* of copying them, just point at the data.
*/
}
/* Now save the group, users and ghosts to the cache */
("Could not save group to the cache: [%s]\n",
goto done;
}
done:
} else {
}
}
{
struct sdap_get_groups_state);
if (usn_value) {
}
return EOK;
}
{
int hret;
unsigned long i;
unsigned long user_count;
unsigned long group_count;
bool in_transaction = false;
struct tevent_req);
struct sdap_get_groups_state);
goto fail;
}
if (hret != HASH_SUCCESS) {
goto fail;
}
if (user_count) {
if (!users) {
goto fail;
}
for (i = 0; i < user_count; i++) {
}
}
/* Users are all retrieved. Now retrieve groups */
if (hret != HASH_SUCCESS) {
goto fail;
}
if (!groups) {
goto fail;
}
for (i = 0; i < group_count; i++) {
}
/* Save all of the users first so that they are in
* place for the groups to add them.
*/
goto fail;
}
in_transaction = true;
goto fail;
}
&state->higher_usn);
goto fail;
}
goto fail;
}
in_transaction = false;
/* Processing complete */
return;
fail:
if (in_transaction) {
}
}
}
struct sss_domain_info *domain,
struct sdap_options *opts,
struct sysdb_attrs **users,
int num_users,
{
int i;
struct ldb_message_element *el;
const char *username;
char *clean_orig_dn;
const char *original_dn;
struct ldb_message **msgs;
char *filter;
const char *sysdb_name;
struct sysdb_attrs *attrs;
bool in_transaction = false;
return EINVAL;
}
if (num_users == 0) {
/* Nothing to do if there are no users */
return EOK;
}
if (ret != HASH_SUCCESS) {
goto done;
}
if (ret) {
goto done;
}
in_transaction = true;
for (i = 0; i < num_users; i++) {
&username);
continue;
}
if (el->num_values == 0) {
}
goto done;
}
goto done;
}
/* Check for the specified origDN in the sysdb */
if (!filter) {
goto done;
}
goto done;
/* The entry is cached but expired. Update the username
* if needed. */
if (count != 1) {
continue;
}
/* Username is correct, continue */
continue;
}
if (!attrs) {
goto done;
}
} else {
if (ret != HASH_SUCCESS) {
goto done;
}
}
}
if (ret) {
goto done;
}
in_transaction = false;
done:
if (in_transaction) {
}
}
} else {
}
return ret;
}
/*
* Get user based on a user DN. Optimized for environments where the containers
* are strictly defined, such as IPA.
*/
static void
static errno_t
struct sdap_nested_get_user_state {
struct sysdb_attrs **replies;
};
static struct tevent_req *
struct sss_domain_info *domain,
struct sdap_options *opts,
struct sdap_handle *sh,
const char *user_dn,
const char *search_bases_filter)
{
struct tevent_req *req;
struct tevent_req *subreq;
const char **sdap_attrs;
const char *filter;
struct sdap_nested_get_user_state *state;
struct sdap_nested_get_user_state);
if (!req) {
return NULL;
}
/* If the schema is IPA, then just shortcut and guess the name */
goto immediate;
} else {
"based on DN %s, falling back to an LDAP lookup\n"));
}
}
/* Only pull down username and originalDN */
if (!sdap_attrs) {
goto immediate;
}
sdap_attrs[0] = "objectClass";
if (search_bases_filter != NULL) {
} else {
}
if (!filter) {
goto immediate;
}
false);
if (!subreq) {
goto immediate;
}
return req;
if (ret) {
} else {
}
return req;
}
/* This should be a function pointer set from the IPA provider */
static errno_t
{
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;
goto done;
}
/* RDN, users, accounts, and at least one DC=
* For example:
* uid=admin,cn=users,cn=accounts,dc=example,dc=com */
/* If it's fewer, it's not a user DN */
goto done;
}
/* If the RDN attribute name is 'uid' */
/* Shouldn't happen if ldb_dn_validate()
* passed, but we'll be careful.
*/
goto done;
}
/* RDN has the wrong attribute name.
* It's not a service.
*/
goto done;
}
/* and the second component is "cn=users" */
/* The second component name is not "cn" */
goto done;
}
if (strncasecmp("users",
(const char *) users_comp_val->data,
users_comp_val->length) != 0) {
/* The second component value is not "users" */
goto done;
}
/* and the third component is "cn=accounts" */
/* The third component name is not "cn" */
goto done;
}
if (strncasecmp("accounts",
(const char *) acct_comp_val->data,
acct_comp_val->length) != 0) {
/* The third component value is not "accounts" */
goto done;
}
/* Then the value of the RDN is the group name */
if (!reply) {
goto done;
}
if (!reply[0]) {
goto done;
}
goto done;
}
goto done;
}
goto done;
}
goto done;
}
done:
return ret;
}
static void
{
struct tevent_req *req =
struct sdap_nested_get_user_state);
return;
}
}
static errno_t
{
struct sdap_nested_get_user_state);
if (_count) {
}
if (_replies) {
}
return EOK;
}
struct sdap_deref_ctx {
const char *orig_dn;
char **expired_users;
char **expired_groups;
char **missing_dns;
struct sdap_deref_attrs **deref_result;
int deref_threshold;
};
struct sdap_nested_group_ctx {
struct tevent_context *ev;
struct sss_domain_info *domain;
struct sdap_options *opts;
struct sdap_handle *sh;
struct ldb_message_element *members;
char *member_dn;
bool enable_deref;
struct sdap_deref_ctx *derefctx;
};
static struct tevent_req *sdap_nested_group_process_send(
struct sss_domain_info *domain,
{
int hret;
struct tevent_req *req;
struct sdap_nested_group_ctx *state;
const char *groupname;
if (!req) {
return NULL;
}
/* If this is too many levels deep, just return success */
goto immediate;
}
/* Add the current group to the groups hash so we don't
* look it up more than once
*/
&groupname);
goto immediate;
}
goto immediate;
}
/* This group has already been processed
* (or is in progress)
* Skip it and just return success
*/
goto immediate;
}
&gid);
0);
goto immediate;
}
}
goto immediate;
}
} else if (ret) {
goto immediate;
}
if (hret != HASH_SUCCESS) {
goto immediate;
}
/* Process group memberships */
/* TODO: future enhancement, check for memberuid as well
*/
/* No members to process */
}
goto immediate;
}
state->member_index = 0;
goto immediate;
}
} else {
}
return req;
} else {
}
return req;
}
struct sss_domain_info *dom,
struct sdap_options *opts,
char *member_dn,
struct ldb_message ***_msgs,
enum sysdb_member_type *_mtype);
{
struct sdap_nested_group_ctx *state =
enum sysdb_member_type mtype;
if (!dctx->expired_users ||
!dctx->expired_groups ||
while (true) {
/* No more entries to check. Return success */
break;
}
/* Continue to loop through until all entries have been
* processed.
*/
break; /* All remaining members in hash, check missing */
goto done; /* Unexpected error */
}
/* The entry is cached and valid */
state->member_index++;
continue;
/* The entry is cached but needs refresh */
switch(mtype) {
case SYSDB_MEMBER_GROUP:
missing++;
state->member_index++;
continue;
case SYSDB_MEMBER_USER:
missing++;
state->member_index++;
continue;
default:
goto done;
}
/* The entry is missing. It is unclear whether it
* is a user or a group so we'll need to try looking
* it up */
missing++;
dctx->missing_dns_num++;
state->member_index++;
continue;
}
/* Unexpected error, skip this entry */
state->member_index++;
continue;
} /* while (true) */
if (missing == 0) {
goto done;
}
} else {
}
return EAGAIN;
done:
return ret;
}
{
struct sdap_nested_group_ctx *state =
enum sysdb_member_type mtype;
while (true) {
/* Continue to loop through until all entries have been
* processed.
*/
return EOK; /* All members in hash */
goto error; /* Unexpected error */
}
/* The entry is cached and valid */
state->member_index++;
continue;
/* The entry is cached but needs refresh */
switch(mtype) {
case SYSDB_MEMBER_GROUP:
break;
case SYSDB_MEMBER_USER:
break;
default:
goto error;
}
return EAGAIN;
/* It wasn't found in the cache either
* We'll have to do a blind lookup in LDAP
*/
/* Try users first */
goto error;
}
return EAGAIN;
}
/* Unexpected error, skip this entry */
state->member_index++;
continue;
} /* while (true) */
return ret;
}
static errno_t
{
bool has_key = false;
do {
/* No more entries to check. Return success */
return EOK;
}
return ENOMEM;
}
/* Check the user hash
* If it's there, we can save ourselves a trip to the
* sysdb and possibly LDAP as well
*/
if (has_key) {
state->member_index++;
continue;
}
} while (has_key);
return ENOENT;
}
static errno_t
struct sss_domain_info *dom,
struct sdap_options *opts,
char *dn,
struct ldb_message ***_msgs,
enum sysdb_member_type *_mtype)
{
char *member_dn;
NULL };
char *filter;
enum sysdb_member_type mtype;
goto fail;
}
/* Check for the specified origDN in the sysdb */
if (!filter) {
goto fail;
}
/* Try users first */
goto fail;
/* We found a user with this origDN in the sysdb. Check if it is valid
*/
/* Check whether the entry is valid */
if (count != 1) {
goto fail;
}
if (!user_uid) {
goto fail;
} else {
/* Regular user, check if we need a refresh */
0);
}
goto done;
}
/* Refresh the user from LDAP */
goto done;
}
/* It wasn't a user. Check whether it's a group */
goto fail;
/* We found a group with this origDN in the sysdb */
/* Check whether the entry is valid */
if (count != 1) {
goto fail;
}
0);
goto done;
}
/* Refresh the group from LDAP */
goto done;
}
/* It wasn't found in the groups either */
done:
}
return ret;
fail:
return ret;
}
static errno_t
{
struct tevent_req *subreq;
struct sdap_attr_map_info *maps;
const char **sdap_attrs;
int ret;
int timeout;
const int num_maps = 2;
struct sdap_nested_group_ctx *state =
/* Pull down the whole group map, but only pull down username
* and originalDN for users. */
attr_count + 2);
if (!sdap_attrs) {
goto fail;
}
sdap_attrs[attr_count] = \
if (!subreq) {
goto fail;
}
return EOK;
fail:
return ret;
}
{
struct sdap_nested_group_ctx *state =
return EAGAIN;
}
return EAGAIN;
}
/* Try users first for generic missing DNs */
return EAGAIN;
}
done:
return ret;
}
{
char *search_bases_filter = NULL;
struct tevent_req *subreq;
struct sdap_nested_group_ctx *state =
/*
* If dn is not in user search base and object may be group
* continue with group lookup. If it can't be group, skip it.
*/
&search_bases_filter)) {
if (fn == sdap_nested_group_process_ldap_user) {
return sdap_nested_group_lookup_group(req);
} else if (fn == sdap_nested_group_process_user) {
} else {
state->member_index++;
}
return ret;
}
return EOK;
}
/*
* Something else? Continue.
*/
}
if (!subreq) {
return EIO;
}
return EOK;
}
{
const char **sdap_attrs;
char *filter;
char *search_bases_filter = NULL;
struct tevent_req *subreq;
struct sdap_nested_group_ctx *state =
/*
* If dn is not in group search base, skip it.
*/
&search_bases_filter)) {
} else {
}
} else {
state->member_index++;
}
return ret;
}
return EOK;
}
return ret;
}
if (search_bases_filter != NULL) {
} else {
}
if (!filter) {
return ENOMEM;
}
false);
if (!subreq) {
return EIO;
}
return EOK;
}
{
struct tevent_req *req =
struct sdap_nested_group_ctx *state =
int hret;
if (!tmp_ctx) {
return;
}
goto done;
/* Nothing to do if the user doesn't exist */
goto skip;
}
if (count != 1) {
/* There should only ever be one reply for a
* BASE search. If otherwise, it's a serious
* error.
*/
goto done;
}
/* Save the user attributes to the user hash so we can store
* them all at once later.
*/
if (hret != HASH_SUCCESS) {
goto done;
}
skip:
} else {
state->member_index++;
}
/* EOK means it's complete */
}
/* EAGAIN means that we should re-enter
* the mainloop
*/
done:
}
{
struct tevent_req *req =
struct sdap_nested_group_ctx *state =
struct sysdb_attrs **replies;
if (!tmp_ctx) {
return;
}
goto done;
/* Nothing to do if the group doesn't exist */
goto skip;
}
if (count != 1) {
/* There should only ever be one reply for a
* BASE search. If otherwise, it's a serious
* error.
*/
goto done;
}
/* Recurse down into the member group */
if (!subreq) {
goto done;
}
return;
skip:
} else {
}
} else {
state->member_index++;
}
/* EOK means it's complete */
}
/* EAGAIN means that we should re-enter
* the mainloop
*/
done:
}
{
struct tevent_req *req =
struct sdap_nested_group_ctx *state =
return;
}
} else {
}
} else {
state->member_index++;
}
/* EOK means it's complete */
}
/* EAGAIN means that we should re-enter
* the mainloop
*/
}
{
struct tevent_req *req =
struct sdap_nested_group_ctx *state =
int hret;
if (!tmp_ctx) {
return;
}
goto done;
/* No user found. Assume it's a group */
}
goto done;
}
if (count != 1) {
/* There should only ever be one reply for a
* BASE search. If otherwise, it's a serious
* error.
*/
goto done;
}
/* Save the user attributes to the user hash so we can store
* them all at once later.
*/
if (hret != HASH_SUCCESS) {
goto done;
}
/* Move on to the next member */
} else {
state->member_index++;
}
/* EOK means it's complete */
}
/* EAGAIN means that we should re-enter
* the mainloop
*/
done:
}
static errno_t
{
struct tevent_req *req =
struct sdap_nested_group_ctx *state =
return;
/* Nothing could be dereferenced. Done. */
return;
}
}
/* EAGAIN means a recursive search is in progress */
}
static void
static errno_t
{
struct sdap_nested_group_ctx *state =
struct tevent_req *subreq;
int hret;
const char *orig_dn;
const char *tmp_name;
size_t i;
/* Add to appropriate hash table */
SYSDB_ORIG_DN, &orig_dn);
return ret;
}
/* Ensure that all members returned from the deref request are included
* in the member processing. Sometimes we will get more results back from
* Active Directory and its range retrieval mechanism.
*/
/* FIXME: This is inefficient for very large sets of groups */
orig_dn) == 0) break;
}
struct ldb_val,
return ENOMEM;
}
return ENOMEM;
}
}
/* check if the user is in search base */
NULL)) {
dctx->result_index++;
continue;
}
dctx->result_index++;
&tmp_name);
} else if (ret) {
return EIO;
}
/* check if the group is in search base */
NULL)) {
dctx->result_index++;
continue;
}
req);
return EAGAIN;
} else {
/* This should never happen, but if it does,
* do not loop forever */
dctx->result_index++;
continue;
}
}
/* All deref results processed */
return EOK;
}
static void
{
struct tevent_req *req =
struct sdap_nested_group_ctx *state =
return;
}
}
/* EAGAIN means a recursive search is in progress */
}
{
return EOK;
}