/* 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; struct sss_domain_info *dom; 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 sss_domain_info *dom, 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(SSSDBG_TRACE_INTERNAL, ("No netgroup triples for netgroup [%s].\n", name)); ret = sysdb_attrs_get_el(netgroup_attrs, SYSDB_NETGROUP_TRIPLE, &el); if (ret != EOK) { goto fail; } } 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, NULL, dom->netgroup_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 sss_domain_info *dom, 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; state->dom = dom; if (!ipa_options->id->netgroup_search_bases) { DEBUG(SSSDBG_CRIT_FAILURE, ("Netgroup lookup request without a search base\n")); ret = EINVAL; goto done; } 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, true); 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_MEMBER_USER, &user_found); if (ret != EOK) goto done; ret = sysdb_attrs_get_el(netgroups[i], SYSDB_ORIG_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 (state->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, true); 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, true); 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->ipa_opts->host_map[IPA_OC_HOST].name); if (filter == NULL) return ENOMEM; ret = build_attrs_from_map(state, state->ipa_opts->host_map, IPA_OPTS_HOST, NULL, &attrs, NULL); 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->ipa_opts->host_map, IPA_OPTS_HOST, state->timeout, true); 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; } else { DEBUG(SSSDBG_CRIT_FAILURE, ("Invalid entity type given for processing: %d\n", state->current_entity)); ret = EINVAL; goto fail; } 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_entities(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, pi; 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 (pi = 0; pi < process_count; pi++) { state->group = process[pi]; hash_iterate(lookup_table, extract_entities, 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; } j = 0; 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); } DEBUG(SSSDBG_TRACE_INTERNAL, ("Extracted %d netgroup members\n", j)); /* Load all UIDs */ DEBUG(SSSDBG_TRACE_ALL, ("Extracting user members of netgroup %d\n", i)); ret = extract_members(state, state->netgroups[i], SYSDB_ORIG_MEMBER_USER, state->new_users, &uids, &uids_count); if (ret != EOK) { goto done; } DEBUG(SSSDBG_TRACE_INTERNAL, ("Extracted %d user members\n", uids_count)); DEBUG(SSSDBG_TRACE_ALL, ("Extracting host members of netgroup %d\n", i)); ret = extract_members(state, state->netgroups[i], SYSDB_ORIG_MEMBER_HOST, state->new_hosts, &hosts, &hosts_count); if (ret != EOK) { goto done; } DEBUG(SSSDBG_TRACE_INTERNAL, ("Extracted %d host members\n", hosts_count)); 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 || hosts_count > 0) { 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->dom, state->opts, state->netgroups[i]); if (ret != EOK) { goto done; } } ret = EOK; 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; }