summaryrefslogtreecommitdiffstats
path: root/src/providers/ldap/sdap_async_accounts.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/providers/ldap/sdap_async_accounts.c')
-rw-r--r--src/providers/ldap/sdap_async_accounts.c386
1 files changed, 374 insertions, 12 deletions
diff --git a/src/providers/ldap/sdap_async_accounts.c b/src/providers/ldap/sdap_async_accounts.c
index b71e6d596..af1f5c53a 100644
--- a/src/providers/ldap/sdap_async_accounts.c
+++ b/src/providers/ldap/sdap_async_accounts.c
@@ -4,6 +4,7 @@
Async LDAP Helper routines
Copyright (C) Simo Sorce <ssorce@redhat.com> - 2009
+ Copyright (C) 2010, Ralf Haferkamp <rhafer@suse.de>, Novell Inc.
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
@@ -917,6 +918,329 @@ done:
}
+/* ==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_ctx *sysdb;
+
+ struct sysdb_attrs *group;
+ struct sysdb_attrs **new_members;
+ struct ldb_message_element* sysdb_dns;
+ char **queued_members;
+ const char **attrs;
+ const char *filter;
+ size_t member_idx;
+ size_t queue_idx;
+ size_t count;
+ size_t check_count;
+};
+
+#define GROUPMEMBER_REQ_PARALLEL 50
+static void sdap_process_group_members(struct tevent_req *subreq);
+
+struct tevent_req *sdap_process_group_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sss_domain_info *dom,
+ struct sysdb_ctx *sysdb,
+ struct sdap_options *opts,
+ struct sdap_handle *sh,
+ struct sysdb_attrs *group)
+{
+ struct ldb_message_element *el;
+ struct sdap_process_group_state *grp_state;
+ struct tevent_req *req = NULL;
+ const char **attrs;
+ char* filter;
+ int queue_len;
+ int ret;
+ int i;
+
+ req = tevent_req_create(memctx, &grp_state,
+ struct sdap_process_group_state);
+ if (!req) return NULL;
+
+ ret = build_attrs_from_map(grp_state, opts->user_map, SDAP_OPTS_USER, &attrs);
+ if (ret) {
+ goto done;
+ }
+
+ /* FIXME: we ignore nested rfc2307bis groups for now */
+ filter = talloc_asprintf(grp_state, "(objectclass=%s)",
+ opts->user_map[SDAP_OC_USER].name);
+ if (!filter) {
+ talloc_zfree(req);
+ return NULL;
+ }
+
+ grp_state->ev = ev;
+ grp_state->opts = opts;
+ grp_state->dom = dom;
+ grp_state->sh = sh;
+ grp_state->sysdb = sysdb;
+ grp_state->group = group;
+ grp_state->check_count = 0;
+ grp_state->new_members = NULL;
+ grp_state->member_idx = 0;
+ grp_state->queue_idx = 0;
+ grp_state->queued_members = NULL;
+ grp_state->filter = filter;
+ grp_state->attrs = attrs;
+
+ ret = sysdb_attrs_get_el(group,
+ opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name,
+ &el);
+ if (ret) {
+ goto done;
+ }
+
+ /* Group without members */
+ if (el->num_values == 0) {
+ DEBUG(2, ("No Members. Done!\n"));
+ ret = EOK;
+ goto done;
+ }
+
+ grp_state->sysdb_dns = talloc(grp_state, struct ldb_message_element);
+ if (!grp_state->sysdb_dns) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ grp_state->sysdb_dns->values = talloc_array(grp_state, struct ldb_val,
+ el->num_values);
+ if (!grp_state->sysdb_dns->values) {
+ talloc_zfree(req);
+ return NULL;
+ }
+ grp_state->sysdb_dns->num_values = 0;
+ queue_len = 0;
+
+ /*
+ * For each member check if it is already present in sysdb,
+ * if it isn't read it from LDAP
+ */
+ for (i = 0; i < el->num_values; i++) {
+ char *sysdb_dn;
+ DEBUG(7, ("checking member: %s\n", (const char*)el->values[i].data));
+ ret = sdap_find_entry_by_origDN(grp_state->sysdb_dns->values, sysdb,
+ dom, (const char *)el->values[i].data,
+ &sysdb_dn);
+ if (ret == ENOENT) {
+ if (opts->schema_type != SDAP_SCHEMA_RFC2307BIS) {
+ continue;
+ }
+
+ DEBUG(7, ("member #%d (%s): not found in sysdb, searching LDAP\n",
+ i, (char *)el->values[i].data));
+
+ /*
+ * 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->check_count > GROUPMEMBER_REQ_PARALLEL) {
+ DEBUG(7, (" queueing search for: %s\n",
+ (char*)el->values[i].data));
+ if (!grp_state->queued_members) {
+ DEBUG(7,("Allocating queue for %d members\n",
+ el->num_values - grp_state->check_count));
+ grp_state->queued_members = talloc_array(grp_state, char *,
+ el->num_values - grp_state->check_count + 1);
+ if (!grp_state->queued_members) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ grp_state->queued_members[queue_len] = (char*)el->values[i].data;
+ queue_len++;
+ } else {
+ struct tevent_req *subreq =
+ sdap_get_generic_send(grp_state,
+ ev, opts, sh,
+ (char *)el->values[i].data,
+ LDAP_SCOPE_BASE,
+ filter, attrs,
+ opts->user_map,
+ SDAP_OPTS_USER);
+ if (!subreq) {
+ ret = ENOMEM;
+ goto done;
+ }
+ tevent_req_set_callback(subreq, sdap_process_group_members, req);
+ }
+ grp_state->check_count++;
+ } else {
+ /*
+ * User already cached in sysdb. Remember the sysdb DN for later
+ * use by sdap_save_groups()
+ */
+ DEBUG(7, ("sysdbdn: %s\n", sysdb_dn));
+ grp_state->sysdb_dns->values[grp_state->sysdb_dns->num_values].data =
+ (uint8_t*)sysdb_dn;
+ grp_state->sysdb_dns->values[grp_state->sysdb_dns->num_values].length =
+ strlen(sysdb_dn);
+ grp_state->sysdb_dns->num_values++;
+ }
+ }
+ if (queue_len > 0) {
+ grp_state->queued_members[queue_len] = NULL;
+ }
+
+ if (grp_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.
+ */
+ el->values = talloc_steal(grp_state->group, grp_state->sysdb_dns->values);
+ el->num_values = grp_state->sysdb_dns->num_values;
+ ret = EOK;
+ goto done;
+ } else {
+ grp_state->count = grp_state->check_count;
+ grp_state->new_members = talloc_zero_array(grp_state,
+ struct sysdb_attrs *,
+ grp_state->count + 1);
+ if (!grp_state->new_members) {
+ ret = ENOMEM;
+ goto done;
+ }
+ }
+ return req;
+
+done:
+ if (ret) {
+ tevent_req_error(req, ret);
+ } else {
+ tevent_req_done(req);
+ }
+ tevent_req_post(req,ev);
+ return req;
+}
+
+static void sdap_process_group_members(struct tevent_req *subreq)
+{
+ struct sysdb_attrs **usr_attrs;
+ size_t count;
+ int ret;
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct sdap_process_group_state *state =
+ tevent_req_data(req, struct sdap_process_group_state);
+ struct ldb_message_element *el;
+ struct ldb_dn *dn;
+ char* dn_string;
+
+ state->check_count--;
+ DEBUG(9, ("Members remaining: %d\n", state->check_count));
+
+ ret = sdap_get_generic_recv(subreq, state, &count, &usr_attrs);
+ talloc_zfree(subreq);
+ if (ret) {
+ goto next;
+ }
+ if (count != 1) {
+ ret = EINVAL;
+ DEBUG(7, ("Expected one user entry and got %d\n", count));
+ goto next;
+ }
+ ret = sysdb_attrs_get_el(usr_attrs[0],
+ state->opts->user_map[SDAP_AT_USER_NAME].sys_name, &el);
+ if (el->num_values == 0) {
+ ret = EINVAL;
+ }
+ if (ret) {
+ DEBUG(2, ("Failed to get the member's name\n"));
+ goto next;
+ }
+
+ /*
+ * Convert the just received DN into the corresponding sysdb DN
+ * for later usage by sdap_save_groups()
+ */
+ dn = sysdb_user_dn(state->sysdb, state, state->dom->name,
+ (char*)el[0].values[0].data);
+ if (!dn) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ dn_string = ldb_dn_alloc_linearized(state->group, dn);
+ if (!dn_string) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ state->sysdb_dns->values[state->sysdb_dns->num_values].data =
+ (uint8_t*)dn_string;
+ state->sysdb_dns->values[state->sysdb_dns->num_values].length =
+ strlen(dn_string);
+ state->sysdb_dns->num_values++;
+
+ state->new_members[state->member_idx] = usr_attrs[0];
+ state->member_idx++;
+
+next:
+ if (ret) {
+ DEBUG(7, ("Error reading group member. Skipping\n", ret));
+ state->count--;
+ }
+ /* Are there more searches for uncached users to submit ? */
+ if (state->queued_members && state->queued_members[state->queue_idx]) {
+ subreq = sdap_get_generic_send(state,
+ state->ev, state->opts, state->sh,
+ state->queued_members[state->queue_idx],
+ LDAP_SCOPE_BASE,
+ state->filter,
+ state->attrs,
+ state->opts->user_map,
+ SDAP_OPTS_USER);
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+
+ tevent_req_set_callback(subreq,
+ sdap_process_group_members, req);
+ state->queue_idx++;
+ }
+
+ if (state->check_count == 0) {
+ ret = sdap_save_users(state, state->sysdb, state->dom, state->opts,
+ state->new_members, state->count, NULL);
+ if (ret) {
+ DEBUG(2, ("Failed to store users.\n"));
+ tevent_req_error(req, ret);
+ return;
+ }
+
+ /*
+ * To avoid redundant sysdb lookups, populate the "member" attribute
+ * of the group entry with the sysdb DNs of the members.
+ */
+ ret = sysdb_attrs_get_el(state->group,
+ state->opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name, &el);
+ el->values = talloc_steal(state->group, state->sysdb_dns->values);
+ el->num_values = state->sysdb_dns->num_values;
+ DEBUG(9, ("Processed Group - Done\n"));
+ tevent_req_done(req);
+ }
+}
+
+static int sdap_process_group_recv(struct tevent_req *req)
+{
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ return EOK;
+}
+
+
/* ==Search-Groups-with-filter============================================ */
struct sdap_get_groups_state {
@@ -931,9 +1255,11 @@ struct sdap_get_groups_state {
char *higher_timestamp;
struct sysdb_attrs **groups;
size_t count;
+ size_t check_count;
};
static void sdap_get_groups_process(struct tevent_req *subreq);
+static void sdap_get_groups_done(struct tevent_req *subreq);
struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx,
struct tevent_context *ev,
@@ -978,11 +1304,12 @@ struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx,
static void sdap_get_groups_process(struct tevent_req *subreq)
{
- struct tevent_req *req = tevent_req_callback_data(subreq,
- struct tevent_req);
- struct sdap_get_groups_state *state = tevent_req_data(req,
- struct sdap_get_groups_state);
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct sdap_get_groups_state *state =
+ tevent_req_data(req, struct sdap_get_groups_state);
int ret;
+ int i;
ret = sdap_get_generic_recv(subreq, state,
&state->count, &state->groups);
@@ -999,20 +1326,55 @@ static void sdap_get_groups_process(struct tevent_req *subreq)
return;
}
- ret = sdap_save_groups(state, state->sysdb,
- state->dom, state->opts,
- state->groups, state->count,
- false,
- &state->higher_timestamp);
+ state->check_count = state->count;
+
+ for (i = 0; i < state->count; i++) {
+ subreq = sdap_process_group_send(state, state->ev, state->dom,
+ state->sysdb, state->opts,
+ state->sh, state->groups[i]);
+
+ if (!subreq) {
+ tevent_req_error(req, ENOMEM);
+ return;
+ }
+ tevent_req_set_callback(subreq, sdap_get_groups_done, req);
+ }
+}
+
+static void sdap_get_groups_done(struct tevent_req *subreq)
+{
+ struct tevent_req *req =
+ tevent_req_callback_data(subreq, struct tevent_req);
+ struct sdap_get_groups_state *state =
+ tevent_req_data(req, struct sdap_get_groups_state);
+
+ int ret;
+
+ ret = sdap_process_group_recv(subreq);
+ talloc_zfree(subreq);
if (ret) {
- DEBUG(2, ("Failed to store groups.\n"));
tevent_req_error(req, ret);
return;
}
- DEBUG(9, ("Saving %d Groups - Done\n", state->count));
+ state->check_count--;
+ DEBUG(9, ("Groups remaining: %d\n", state->check_count));
- tevent_req_done(req);
+
+ if (state->check_count == 0) {
+ DEBUG(9, ("All groups processed\n"));
+
+ ret = sdap_save_groups(state, state->sysdb, state->dom, state->opts,
+ state->groups, state->count, true,
+ &state->higher_timestamp);
+ if (ret) {
+ DEBUG(2, ("Failed to store groups.\n"));
+ tevent_req_error(req, ret);
+ return;
+ }
+ DEBUG(9, ("Saving %d Groups - Done\n", state->count));
+ tevent_req_done(req);
+ }
}
int sdap_get_groups_recv(struct tevent_req *req,