From e526b608657f229f7486b3aa8c53b0f2c53b42b1 Mon Sep 17 00:00:00 2001 From: Jan Zeleny Date: Wed, 16 Nov 2011 04:19:57 -0500 Subject: Added support for fetching netgroups in IPA provider --- src/providers/ipa/ipa_netgroups.c | 992 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 992 insertions(+) create mode 100644 src/providers/ipa/ipa_netgroups.c (limited to 'src/providers') diff --git a/src/providers/ipa/ipa_netgroups.c b/src/providers/ipa/ipa_netgroups.c new file mode 100644 index 000000000..ddebe61bd --- /dev/null +++ b/src/providers/ipa/ipa_netgroups.c @@ -0,0 +1,992 @@ +/* + SSSD + + Async IPA Helper routines for netgroups + + Authors: + Jan Zeleny + + 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 . +*/ + +#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 + +#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; +} -- cgit