summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJan Zeleny <jzeleny@redhat.com>2011-11-16 04:19:57 -0500
committerStephen Gallagher <sgallagh@redhat.com>2011-11-23 14:19:53 -0500
commite526b608657f229f7486b3aa8c53b0f2c53b42b1 (patch)
tree69f73419d2435f32ba043de6f5e3f16e0f8f7a80
parent46d41cf95d520a1aab48dad2d577c885e69ef9f8 (diff)
downloadsssd_unused-e526b608657f229f7486b3aa8c53b0f2c53b42b1.tar.gz
sssd_unused-e526b608657f229f7486b3aa8c53b0f2c53b42b1.tar.xz
sssd_unused-e526b608657f229f7486b3aa8c53b0f2c53b42b1.zip
Added support for fetching netgroups in IPA provider
-rw-r--r--Makefile.am1
-rw-r--r--src/providers/ipa/ipa_netgroups.c992
2 files changed, 993 insertions, 0 deletions
diff --git a/Makefile.am b/Makefile.am
index 6f01cfbd..60895362 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -915,6 +915,7 @@ libsss_ipa_la_SOURCES = \
src/providers/ipa/ipa_init.c \
src/providers/ipa/ipa_common.c \
src/providers/ipa/ipa_utils.c \
+ src/providers/ipa/ipa_netgroups.c \
src/providers/ipa/ipa_auth.c \
src/providers/ipa/ipa_access.c \
src/providers/ipa/ipa_dyndns.c \
diff --git a/src/providers/ipa/ipa_netgroups.c b/src/providers/ipa/ipa_netgroups.c
new file mode 100644
index 00000000..ddebe61b
--- /dev/null
+++ b/src/providers/ipa/ipa_netgroups.c
@@ -0,0 +1,992 @@
+/*
+ SSSD
+
+ Async IPA Helper routines for netgroups
+
+ Authors:
+ Jan Zeleny <jzeleny@redhat.com>
+
+ Copyright (C) 2011 Red Hat
+
+ 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
+ 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 "util/util.h"
+#include "db/sysdb.h"
+#include "providers/ldap/sdap_async_private.h"
+#include "providers/ipa/ipa_id.h"
+#include "db/sysdb.h"
+#include <ctype.h>
+
+#define ENTITY_NG 1
+#define ENTITY_USER 2
+#define ENTITY_HOST 4
+
+struct ipa_get_netgroups_state {
+ struct tevent_context *ev;
+ struct sdap_options *opts;
+ struct ipa_options *ipa_opts;
+ struct sdap_handle *sh;
+ struct sysdb_ctx *sysdb;
+ const char **attrs;
+ int timeout;
+
+ char *filter;
+ const char *base_filter;
+
+ size_t netgr_base_iter;
+ size_t host_base_iter;
+ size_t user_base_iter;
+
+ /* Entities which have been already asked for
+ * and are scheduled for inspection */
+ hash_table_t *new_netgroups;
+ hash_table_t *new_users;
+ hash_table_t *new_hosts;
+
+ int current_entity;
+ int entities_found;
+
+ struct sysdb_attrs **netgroups;
+ int netgroups_count;
+};
+
+static errno_t ipa_save_netgroup(TALLOC_CTX *mem_ctx,
+ struct sysdb_ctx *ctx,
+ struct sdap_options *opts,
+ struct sysdb_attrs *attrs)
+{
+ struct ldb_message_element *el;
+ struct sysdb_attrs *netgroup_attrs;
+ const char *name = NULL;
+ int ret;
+ size_t c;
+
+ ret = sysdb_attrs_get_el(attrs,
+ opts->netgroup_map[IPA_AT_NETGROUP_NAME].sys_name,
+ &el);
+ if (ret) goto fail;
+ if (el->num_values == 0) {
+ ret = EINVAL;
+ goto fail;
+ }
+ name = (const char *)el->values[0].data;
+ DEBUG(SSSDBG_TRACE_INTERNAL, ("Storing netgroup %s\n", name));
+
+ netgroup_attrs = sysdb_new_attrs(mem_ctx);
+ if (!netgroup_attrs) {
+ ret = ENOMEM;
+ goto fail;
+ }
+
+ ret = sysdb_attrs_get_el(attrs, SYSDB_ORIG_DN, &el);
+ if (ret) {
+ goto fail;
+ }
+ if (el->num_values == 0) {
+ DEBUG(7, ("Original DN is not available for [%s].\n", name));
+ } else {
+ DEBUG(7, ("Adding original DN [%s] to attributes of [%s].\n",
+ el->values[0].data, name));
+ ret = sysdb_attrs_add_string(netgroup_attrs, SYSDB_ORIG_DN,
+ (const char *)el->values[0].data);
+ if (ret) {
+ goto fail;
+ }
+ }
+
+ ret = sysdb_attrs_get_el(attrs, SYSDB_NETGROUP_TRIPLE, &el);
+ if (ret) {
+ goto fail;
+ }
+ if (el->num_values == 0) {
+ DEBUG(7, ("No netgroup triples for netgroup [%s].\n", name));
+ } else {
+ for(c = 0; c < el->num_values; c++) {
+ ret = sysdb_attrs_add_string(netgroup_attrs,
+ SYSDB_NETGROUP_TRIPLE,
+ (const char*)el->values[c].data);
+ if (ret) {
+ goto fail;
+ }
+ }
+ }
+
+ ret = sysdb_attrs_get_el(attrs,
+ opts->netgroup_map[IPA_AT_NETGROUP_MEMBER].sys_name,
+ &el);
+ if (ret != EOK) {
+ goto fail;
+ }
+ if (el->num_values == 0) {
+ DEBUG(7, ("No original members for netgroup [%s]\n", name));
+
+ } else {
+ DEBUG(7, ("Adding original members to netgroup [%s]\n", name));
+ for(c = 0; c < el->num_values; c++) {
+ ret = sysdb_attrs_add_string(netgroup_attrs,
+ opts->netgroup_map[IPA_AT_NETGROUP_MEMBER].sys_name,
+ (const char*)el->values[c].data);
+ if (ret) {
+ goto fail;
+ }
+ }
+ }
+
+
+ ret = sysdb_attrs_get_el(attrs, SYSDB_NETGROUP_MEMBER, &el);
+ if (ret != EOK) {
+ goto fail;
+ }
+ if (el->num_values == 0) {
+ DEBUG(7, ("No members for netgroup [%s]\n", name));
+
+ } else {
+ DEBUG(7, ("Adding members to netgroup [%s]\n", name));
+ for(c = 0; c < el->num_values; c++) {
+ ret = sysdb_attrs_add_string(netgroup_attrs, SYSDB_NETGROUP_MEMBER,
+ (const char*)el->values[c].data);
+ if (ret) {
+ goto fail;
+ }
+ }
+ }
+
+ DEBUG(6, ("Storing info for netgroup %s\n", name));
+
+ ret = sysdb_add_netgroup(ctx, name, NULL, netgroup_attrs,
+ dp_opt_get_int(opts->basic,
+ SDAP_ENTRY_CACHE_TIMEOUT),
+ 0);
+ if (ret) goto fail;
+
+ return EOK;
+
+fail:
+ DEBUG(2, ("Failed to save netgroup %s\n", name));
+ return ret;
+}
+
+static errno_t ipa_netgr_next_base(struct tevent_req *req);
+static void ipa_get_netgroups_process(struct tevent_req *subreq);
+static int ipa_netgr_process_all(struct ipa_get_netgroups_state *state);
+
+struct tevent_req *ipa_get_netgroups_send(TALLOC_CTX *memctx,
+ struct tevent_context *ev,
+ struct sysdb_ctx *sysdb,
+ struct sdap_options *opts,
+ struct ipa_options *ipa_options,
+ struct sdap_handle *sh,
+ const char **attrs,
+ const char *filter,
+ int timeout)
+{
+ struct tevent_req *req;
+ struct ipa_get_netgroups_state *state;
+ int ret;
+
+ req = tevent_req_create(memctx, &state, struct ipa_get_netgroups_state);
+ if (!req) return NULL;
+
+ state->ev = ev;
+ state->opts = opts;
+ state->ipa_opts = ipa_options;
+ state->sh = sh;
+ state->sysdb = sysdb;
+ state->attrs = attrs;
+ state->timeout = timeout;
+ state->base_filter = filter;
+ state->netgr_base_iter = 0;
+
+ ret = sss_hash_create(state, 32, &state->new_netgroups);
+ if (ret != EOK) goto done;
+ ret = sss_hash_create(state, 32, &state->new_users);
+ if (ret != EOK) goto done;
+ ret = sss_hash_create(state, 32, &state->new_hosts);
+ if (ret != EOK) goto done;
+
+
+ ret = ipa_netgr_next_base(req);
+
+done:
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ tevent_req_post(req, ev);
+ }
+
+ return req;
+}
+
+static errno_t ipa_netgr_next_base(struct tevent_req *req)
+{
+ struct tevent_req *subreq;
+ struct ipa_get_netgroups_state *state;
+ struct sdap_search_base **netgr_bases;
+
+ state = tevent_req_data(req, struct ipa_get_netgroups_state);
+ netgr_bases = state->ipa_opts->id->netgroup_search_bases;
+
+ talloc_zfree(state->filter);
+ state->filter = sdap_get_id_specific_filter(
+ state,
+ state->base_filter,
+ netgr_bases[state->netgr_base_iter]->filter);
+ if (!state->filter) {
+ return ENOMEM;
+ }
+
+ DEBUG(SSSDBG_TRACE_FUNC,
+ ("Searching for netgroups with base [%s]\n",
+ netgr_bases[state->netgr_base_iter]->basedn));
+
+ subreq = sdap_get_generic_send(
+ state, state->ev, state->opts, state->sh,
+ netgr_bases[state->netgr_base_iter]->basedn,
+ netgr_bases[state->netgr_base_iter]->scope,
+ state->filter, state->attrs,
+ state->opts->netgroup_map, IPA_OPTS_NETGROUP,
+ state->timeout);
+ if (!subreq) {
+ return ENOMEM;
+ }
+ tevent_req_set_callback(subreq, ipa_get_netgroups_process, req);
+
+ return EOK;
+}
+
+static int ipa_netgr_fetch_netgroups(struct ipa_get_netgroups_state *state,
+ struct tevent_req *req);
+static int ipa_netgr_fetch_users(struct ipa_get_netgroups_state *state,
+ struct tevent_req *req);
+static int ipa_netgr_fetch_hosts(struct ipa_get_netgroups_state *state,
+ struct tevent_req *req);
+static void ipa_netgr_members_process(struct tevent_req *subreq);
+
+static void ipa_get_netgroups_process(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct ipa_get_netgroups_state *state = tevent_req_data(req,
+ struct ipa_get_netgroups_state);
+ int i, ret;
+ struct ldb_message_element *ng_found;
+ struct ldb_message_element *host_found;
+ struct ldb_message_element *user_found;
+ struct sdap_search_base **netgr_bases;
+ struct sysdb_attrs **netgroups;
+ size_t netgroups_count;
+ const char *orig_dn;
+ char *dn;
+ char *filter;
+ bool fetch_members = false;
+ hash_key_t key;
+ hash_value_t value;
+
+ netgr_bases = state->ipa_opts->id->netgroup_search_bases;
+
+ ret = sdap_get_generic_recv(subreq, state, &netgroups_count, &netgroups);
+ talloc_zfree(subreq);
+ if (ret) {
+ goto done;
+ }
+
+ DEBUG(6, ("Search for netgroups, returned %d results.\n", netgroups_count));
+
+ if (netgroups_count == 0) {
+ /* No netgroups found in this search */
+ state->netgr_base_iter++;
+ if (netgr_bases[state->netgr_base_iter]) {
+ /* There are more search bases to try */
+ ret = ipa_netgr_next_base(req);
+ if (ret != EOK) {
+ tevent_req_error(req, ENOENT);
+ }
+ return;
+ }
+
+ ret = ENOENT;
+ goto done;
+ }
+
+ filter = talloc_strdup(state, "(|");
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (i = 0; i < netgroups_count; i++) {
+ ret = sysdb_attrs_get_el(netgroups[i], SYSDB_ORIG_NETGROUP_MEMBER,
+ &ng_found);
+ if (ret != EOK) goto done;
+
+ ret = sysdb_attrs_get_el(netgroups[i], SYSDB_ORIG_NETGROUP_MEMBER_USER,
+ &user_found);
+ if (ret != EOK) goto done;
+
+ ret = sysdb_attrs_get_el(netgroups[i], SYSDB_ORIG_NETGROUP_MEMBER_HOST,
+ &host_found);
+ if (ret != EOK) goto done;
+
+ ret = sysdb_attrs_get_string(netgroups[i], SYSDB_ORIG_DN, &orig_dn);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ key.type = HASH_KEY_STRING;
+ value.type = HASH_VALUE_PTR;
+ key.str = discard_const(orig_dn);
+ value.ptr = netgroups[i];
+ ret = hash_enter(state->new_netgroups, &key, &value);
+ if (ret != HASH_SUCCESS) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (ng_found->num_values) state->entities_found |= ENTITY_NG;
+ if (user_found->num_values) state->entities_found |= ENTITY_USER;
+ if (host_found->num_values) state->entities_found |= ENTITY_HOST;
+
+ if (state->entities_found == 0) {
+ continue;
+ }
+
+ ret = sss_filter_sanitize(state, orig_dn, &dn);
+ if (ret != EOK) {
+ goto done;
+ }
+ /* Add this to the filter */
+ filter = talloc_asprintf_append(filter, "(%s=%s)",
+ state->opts->netgroup_map[IPA_AT_NETGROUP_MEMBER_OF].name,
+ dn);
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ fetch_members = true;
+ }
+
+ if (!fetch_members) {
+ ret = ipa_netgr_process_all(state);
+ if (ret != EOK) {
+ tevent_req_error(req, ret);
+ } else {
+ tevent_req_done(req);
+ }
+ return;
+ }
+
+ state->filter = talloc_asprintf_append(filter, ")");
+ if (filter == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (state->entities_found & ENTITY_NG) {
+ state->netgr_base_iter = 0;
+ ret = ipa_netgr_fetch_netgroups(state, req);
+ if (ret != EOK) goto done;
+ } else if (state->entities_found & ENTITY_USER) {
+ ret = ipa_netgr_fetch_users(state, req);
+ if (ret != EOK) goto done;
+ } else if (state->entities_found & ENTITY_HOST) {
+ ret = ipa_netgr_fetch_hosts(state, req);
+ if (ret != EOK) goto done;
+ }
+
+ return;
+done:
+ tevent_req_error(req, ret);
+ return;
+}
+
+static int ipa_netgr_fetch_netgroups(struct ipa_get_netgroups_state *state,
+ struct tevent_req *req)
+{
+ char *filter;
+ const char *base_filter;
+ struct tevent_req *subreq;
+ struct sdap_search_base **bases;
+
+ bases = state->ipa_opts->id->netgroup_search_bases;
+ if (bases[state->netgr_base_iter] == NULL) {
+ /* No more bases to try */
+ return ENOENT;
+ }
+ base_filter = bases[state->netgr_base_iter]->filter;
+
+ filter = talloc_asprintf(state, "(&%s%s(objectclass=%s))",
+ state->filter,
+ base_filter?base_filter:"",
+ state->opts->netgroup_map[SDAP_OC_NETGROUP].name);
+ if (filter == NULL)
+ return ENOMEM;
+
+ subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh,
+ bases[state->netgr_base_iter]->basedn,
+ bases[state->netgr_base_iter]->scope,
+ filter, state->attrs, state->opts->netgroup_map,
+ IPA_OPTS_NETGROUP, state->timeout);
+
+ state->current_entity = ENTITY_NG;
+ if (subreq == NULL) {
+ return ENOMEM;
+ }
+
+ tevent_req_set_callback(subreq, ipa_netgr_members_process, req);
+
+ return EOK;
+}
+
+static int ipa_netgr_fetch_users(struct ipa_get_netgroups_state *state,
+ struct tevent_req *req)
+{
+ const char *attrs[] = { state->opts->user_map[SDAP_AT_USER_NAME].name,
+ state->opts->user_map[SDAP_AT_USER_MEMBEROF].name,
+ "objectclass", NULL };
+ char *filter;
+ const char *base_filter;
+ struct tevent_req *subreq;
+ struct sdap_search_base **bases;
+
+ bases = state->ipa_opts->id->user_search_bases;
+ if (bases[state->user_base_iter] == NULL) {
+ return ENOENT;
+ }
+ base_filter = bases[state->user_base_iter]->filter;
+
+ filter = talloc_asprintf(state, "(&%s%s(objectclass=%s))",
+ state->filter,
+ base_filter?base_filter:"",
+ state->opts->user_map[SDAP_OC_USER].name);
+ if (filter == NULL)
+ return ENOMEM;
+
+ subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh,
+ dp_opt_get_string(state->opts->basic,
+ SDAP_USER_SEARCH_BASE),
+ LDAP_SCOPE_SUBTREE,
+ filter, attrs,
+ state->opts->user_map,
+ SDAP_OPTS_USER, state->timeout);
+
+ state->current_entity = ENTITY_USER;
+ if (subreq == NULL) {
+ talloc_free(attrs);
+ return ENOMEM;
+ }
+
+ tevent_req_set_callback(subreq, ipa_netgr_members_process, req);
+
+ return EOK;
+}
+
+static int ipa_netgr_fetch_hosts(struct ipa_get_netgroups_state *state,
+ struct tevent_req *req)
+{
+ const char **attrs;
+ char *filter;
+ const char *base_filter;
+ struct tevent_req *subreq;
+ int ret;
+ struct sdap_search_base **bases;
+
+ bases = state->ipa_opts->host_search_bases;
+ if (bases[state->host_base_iter] == NULL) {
+ return ENOENT;
+ }
+ base_filter = bases[state->host_base_iter]->filter;
+
+ filter = talloc_asprintf(state, "(&%s%s(objectclass=%s))",
+ state->filter,
+ base_filter?base_filter:"",
+ state->opts->host_map[IPA_OC_HOST].name);
+ if (filter == NULL)
+ return ENOMEM;
+
+ ret = build_attrs_from_map(state, state->opts->host_map,
+ IPA_OPTS_HOST, &attrs);
+ if (ret != EOK) {
+ talloc_free(filter);
+ return ret;
+ }
+
+ subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh,
+ bases[state->host_base_iter]->basedn,
+ bases[state->host_base_iter]->scope,
+ filter, attrs,
+ state->opts->host_map,
+ IPA_OPTS_HOST, state->timeout);
+
+ state->current_entity = ENTITY_HOST;
+ if (subreq == NULL) {
+ talloc_free(filter);
+ return ENOMEM;
+ }
+
+ tevent_req_set_callback(subreq, ipa_netgr_members_process, req);
+
+ return EOK;
+}
+
+static void ipa_netgr_members_process(struct tevent_req *subreq)
+{
+ struct tevent_req *req = tevent_req_callback_data(subreq,
+ struct tevent_req);
+ struct ipa_get_netgroups_state *state = tevent_req_data(req,
+ struct ipa_get_netgroups_state);
+ struct sysdb_attrs **entities;
+ size_t count;
+ int ret, i;
+ const char *orig_dn;
+ char *orig_dn_lower;
+ hash_table_t *table;
+ hash_key_t key;
+ hash_value_t value;
+ int (* next_call)(struct ipa_get_netgroups_state *,
+ struct tevent_req *);
+ bool next_batch_scheduled = false;
+
+ ret = sdap_get_generic_recv(subreq, state, &count, &entities);
+ talloc_zfree(subreq);
+ if (ret) {
+ goto fail;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, ("Found %u members in current search base\n", count));
+
+ next_call = NULL;
+ /* While processing a batch of entities from one search base,
+ * schedule query for another search base if there is one
+ *
+ * If there is no other search base, another class of entities
+ * will be scheduled for lookup after processing of current
+ * batch. The order of lookup is: netgroups -> users -> hosts
+ */
+ if (state->current_entity == ENTITY_NG) {
+ /* We just received a batch of netgroups */
+ state->netgr_base_iter++;
+ ret = ipa_netgr_fetch_netgroups(state, req);
+ table = state->new_netgroups;
+ /* If there is a member netgroup, we always have to
+ * ask for both member users and hosts
+ * -> now schedule users
+ */
+ next_call = ipa_netgr_fetch_users;
+ } else if (state->current_entity == ENTITY_USER) {
+ /* We just received a batch of users */
+ state->user_base_iter++;
+ ret = ipa_netgr_fetch_users(state, req);
+ table = state->new_users;
+ if (state->entities_found & ENTITY_HOST ||
+ state->entities_found & ENTITY_NG) {
+ next_call = ipa_netgr_fetch_hosts;
+ }
+ } else if (state->current_entity == ENTITY_HOST) {
+ /* We just received a batch of hosts */
+ state->host_base_iter++;
+ ret = ipa_netgr_fetch_hosts(state, req);
+ table = state->new_hosts;
+ }
+
+ if (ret == EOK) {
+ /* Next search base has been scheduled for inspection,
+ * don't try to look for other type of entities
+ */
+ next_batch_scheduled = true;
+ } else if (ret != ENOENT) {
+ goto fail;
+ }
+
+ /* Process all member entites and store them in the designated hash table */
+ key.type = HASH_KEY_STRING;
+ value.type = HASH_VALUE_PTR;
+ for (i = 0; i < count; i++) {
+ ret = sysdb_attrs_get_string(entities[i], SYSDB_ORIG_DN, &orig_dn);
+ if (ret != EOK) {
+ goto fail;
+ }
+
+ orig_dn_lower = talloc_strdup(table, orig_dn);
+ if (orig_dn_lower == NULL) {
+ ret = ENOMEM;
+ goto fail;
+ }
+ /* Transform the DN to lower case.
+ * this is important, as the member/memberof attributes
+ * have the value also in lower-case
+ */
+ key.str = orig_dn_lower;
+ while (*orig_dn_lower != '\0') {
+ *orig_dn_lower = tolower(*orig_dn_lower);
+ orig_dn_lower++;
+ }
+ value.ptr = entities[i];
+ ret = hash_enter(table, &key, &value);
+ if (ret != HASH_SUCCESS) {
+ goto fail;
+ }
+ }
+
+ if (next_batch_scheduled) {
+ /* The next search base is already scheduled to be searched */
+ return;
+ }
+
+ if (next_call) {
+ /* There is another class of members that has to be retrieved
+ * - schedule the lookup
+ */
+ ret = next_call(state, req);
+ if (ret != EOK) goto fail;
+ } else {
+ /* All members, that could have been fetched, were fetched */
+ ret = ipa_netgr_process_all(state);
+ if (ret != EOK) goto fail;
+
+ tevent_req_done(req);
+ }
+
+ return;
+
+fail:
+ tevent_req_error(req, ret);
+ return;
+}
+
+static bool extract_netgroups(hash_entry_t *entry, void *pvt)
+{
+ struct ipa_get_netgroups_state *state;
+ state = talloc_get_type(pvt, struct ipa_get_netgroups_state);
+
+ state->netgroups[state->netgroups_count] = talloc_get_type(entry->value.ptr,
+ struct sysdb_attrs);
+ state->netgroups_count++;
+
+ return true;
+}
+
+struct extract_state {
+ const char *group;
+
+ const char **entries;
+ int entries_count;
+};
+
+static bool extract_users(hash_entry_t *entry, void *pvt)
+{
+ int i, ret;
+ struct extract_state *state;
+ struct sysdb_attrs *member;
+ struct ldb_message_element *el;
+ struct ldb_message_element *name_el;
+
+ state = talloc_get_type(pvt, struct extract_state);
+ member = talloc_get_type(entry->value.ptr, struct sysdb_attrs);
+
+ ret = sysdb_attrs_get_el(member, SYSDB_MEMBEROF, &el);
+ if (ret != EOK) return false;
+
+ ret = sysdb_attrs_get_el(member, SYSDB_NAME, &name_el);
+ if (ret != EOK || name_el == NULL || name_el->num_values == 0) {
+ return false;
+ }
+
+ for (i = 0; i < el->num_values; i++) {
+ if (strcmp((char *)el->values[i].data, state->group) == 0) {
+
+ state->entries = talloc_realloc(state, state->entries, const char *,
+ state->entries_count + 1);
+ if (state->entries == NULL) {
+ return false;
+ }
+ state->entries[state->entries_count] = (char *)name_el->values[0].data;
+ state->entries_count++;
+ break;
+ }
+ }
+
+ return true;
+}
+
+static int extract_members(TALLOC_CTX *mem_ctx,
+ struct sysdb_attrs *netgroup,
+ const char *member_type,
+ hash_table_t *lookup_table,
+ const char ***_ret_array,
+ int *_ret_count)
+{
+ struct extract_state *state;
+ struct ldb_message_element *el;
+ struct sysdb_attrs *member;
+ hash_key_t key;
+ hash_value_t value;
+ const char **process = NULL;
+ const char **ret_array = NULL;
+ int process_count = 0;
+ int ret_count = 0;
+ int ret, i;
+
+ key.type = HASH_KEY_STRING;
+ value.type = HASH_VALUE_PTR;
+
+ state = talloc_zero(mem_ctx, struct extract_state);
+ if (state == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_attrs_get_el(netgroup, member_type, &el);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+
+ if (ret == EOK) {
+ for (i = 0; i < el->num_values; i++) {
+ key.str = (char *)el->values[i].data;
+ ret = hash_lookup(lookup_table, &key, &value);
+ if (ret != HASH_SUCCESS && ret != HASH_ERROR_KEY_NOT_FOUND) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ if (ret == HASH_ERROR_KEY_NOT_FOUND) {
+ process = talloc_realloc(mem_ctx, process, const char *, process_count + 1);
+ if (process == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ process[process_count] = (char *)el->values[i].data;
+ process_count++;
+ } else {
+ ret_array = talloc_realloc(mem_ctx, ret_array, const char *, ret_count + 1);
+ if (ret_array == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ member = talloc_get_type(value.ptr, struct sysdb_attrs);
+ ret = sysdb_attrs_get_string(member, SYSDB_NAME, &ret_array[ret_count]);
+ if (ret != EOK) {
+ goto done;
+ }
+ ret_count++;
+ }
+
+ for (i = 0; i < process_count; i++) {
+ state->group = process[i];
+ hash_iterate(lookup_table, extract_users, state);
+ if (state->entries_count > 0) {
+ ret_array = talloc_realloc(mem_ctx, ret_array, const char *,
+ ret_count + state->entries_count);
+ if (ret_array == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ memcpy(&ret_array[ret_count], state->entries,
+ state->entries_count*sizeof(const char *));
+ ret_count += state->entries_count;
+ }
+ state->entries_count = 0;
+ talloc_zfree(state->entries);
+ }
+ }
+ } else {
+ ret_array = NULL;
+ }
+
+ *_ret_array = ret_array;
+ *_ret_count = ret_count;
+ ret = EOK;
+
+done:
+ return ret;
+}
+
+static int ipa_netgr_process_all(struct ipa_get_netgroups_state *state)
+{
+ int i, j, k, ret;
+ const char **members;
+ struct sysdb_attrs *member;
+ const char *member_name;
+ struct extract_state *extract_state;
+ struct ldb_message_element *external_hosts;
+ const char *dash[] = {"-"};
+ const char **uids = NULL;
+ const char **hosts = NULL;
+ int uids_count = 0;
+ int hosts_count = 0;
+ hash_key_t key;
+ hash_value_t value;
+ const char *domain;
+ char *triple;
+
+ state->netgroups = talloc_zero_array(state, struct sysdb_attrs *,
+ hash_count(state->new_netgroups));
+ if (state->netgroups == NULL) {
+ return ENOMEM;
+ }
+
+ extract_state = talloc_zero(state, struct extract_state);
+ if (extract_state == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ key.type = HASH_KEY_STRING;
+ value.type = HASH_VALUE_PTR;
+
+ hash_iterate(state->new_netgroups, extract_netgroups, state);
+ for (i = 0; i < state->netgroups_count; i++) {
+ /* load all its member netgroups, translate */
+ DEBUG(SSSDBG_TRACE_INTERNAL, ("Extracting netgroup members of netgroup %d\n", i));
+ ret = sysdb_attrs_get_string_array(state->netgroups[i],
+ SYSDB_ORIG_NETGROUP_MEMBER,
+ state, &members);
+ if (ret != EOK && ret != ENOENT) {
+ goto done;
+ }
+
+ if (ret == EOK) {
+ for (j = 0; members[j]; j++) {
+ key.str = discard_const(members[j]);
+ ret = hash_lookup(state->new_netgroups, &key, &value);
+ if (ret != HASH_SUCCESS) {
+ ret = ENOENT;
+ goto done;
+ }
+
+ member = talloc_get_type(value.ptr, struct sysdb_attrs);
+ ret = sysdb_attrs_get_string(member, SYSDB_NAME, &member_name);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_string(state->netgroups[i],
+ SYSDB_NETGROUP_MEMBER,
+ member_name);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+ talloc_zfree(members);
+ }
+
+ /* Load all UIDs */
+ DEBUG(SSSDBG_TRACE_INTERNAL, ("Extracting user members of netgroup %d\n", i));
+ ret = extract_members(state, state->netgroups[i],
+ SYSDB_ORIG_NETGROUP_MEMBER_USER,
+ state->new_users,
+ &uids, &uids_count);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, ("Extracting host members of netgroup %d\n", i));
+ ret = extract_members(state, state->netgroups[i],
+ SYSDB_ORIG_NETGROUP_MEMBER_HOST,
+ state->new_hosts,
+ &hosts, &hosts_count);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ ret = sysdb_attrs_get_el(state->netgroups[i],
+ SYSDB_ORIG_NETGROUP_EXTERNAL_HOST,
+ &external_hosts);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (external_hosts->num_values > 0) {
+ hosts = talloc_realloc(state, hosts, const char *,
+ hosts_count + external_hosts->num_values);
+ if (hosts == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ for (j = 0; j < external_hosts->num_values; j++) {
+ hosts[hosts_count] = talloc_strdup(hosts, (char *)external_hosts->values[j].data);
+ if (hosts[hosts_count] == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+ hosts_count++;
+ }
+ }
+
+ ret = sysdb_attrs_get_string(state->netgroups[i], SYSDB_NETGROUP_DOMAIN,
+ &domain);
+ if (ret != EOK) {
+ goto done;
+ }
+
+ if (uids_count == 0) {
+ uids_count = 1;
+ uids = dash;
+ }
+
+ if (hosts_count == 0) {
+ hosts_count = 1;
+ hosts = dash;
+ }
+
+ DEBUG(SSSDBG_TRACE_INTERNAL, ("Putting together triples of netgroup %d\n", i));
+ for (j = 0; j < uids_count; j++) {
+ for (k = 0; k < hosts_count; k++) {
+ triple = talloc_asprintf(state, "(%s,%s,%s)",
+ hosts[k], uids[j],
+ domain);
+ if (triple == NULL) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ ret = sysdb_attrs_add_string(state->netgroups[i], SYSDB_NETGROUP_TRIPLE, triple);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+ }
+ ret = ipa_save_netgroup(state, state->sysdb, state->opts,
+ state->netgroups[i]);
+ if (ret != EOK) {
+ goto done;
+ }
+ }
+
+done:
+ return ret;
+}
+
+int ipa_get_netgroups_recv(struct tevent_req *req,
+ TALLOC_CTX *mem_ctx,
+ size_t *reply_count,
+ struct sysdb_attrs ***reply)
+{
+ struct ipa_get_netgroups_state *state = tevent_req_data(req,
+ struct ipa_get_netgroups_state);
+
+ TEVENT_REQ_RETURN_ON_ERROR(req);
+
+ if (reply_count) {
+ *reply_count = state->netgroups_count;
+ }
+
+ if (reply) {
+ *reply = talloc_steal(mem_ctx, state->netgroups);
+ }
+
+ return EOK;
+}