/* SSSD Async LDAP Helper routines for netgroups Authors: Sumit Bose Copyright (C) 2010 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/ldap/ldap_common.h" bool is_dn(const char *str) { int ret; LDAPDN dn; ret = ldap_str2dn(str, &dn, LDAP_DN_FORMAT_LDAPV3); ldap_dnfree(dn); return (ret == LDAP_SUCCESS ? true : false); } static errno_t sdap_save_netgroup(TALLOC_CTX *memctx, struct sysdb_ctx *ctx, struct sss_domain_info *dom, struct sdap_options *opts, struct sysdb_attrs *attrs, char **_timestamp, time_t now) { struct ldb_message_element *el; struct sysdb_attrs *netgroup_attrs; const char *name = NULL; int ret; char *timestamp = NULL; char **missing = NULL; ret = sysdb_attrs_get_el(attrs, opts->netgroup_map[SDAP_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; netgroup_attrs = sysdb_new_attrs(memctx); if (!netgroup_attrs) { ret = ENOMEM; goto fail; } ret = sdap_attrs_add_string(attrs, SYSDB_ORIG_DN, "original DN", name, netgroup_attrs); if (ret != EOK) { goto fail; } ret = sysdb_attrs_get_el(attrs, opts->netgroup_map[SDAP_AT_NETGROUP_MODSTAMP].sys_name, &el); if (ret) { goto fail; } if (el->num_values == 0) { DEBUG(7, ("Original mod-Timestamp is not available for [%s].\n", name)); } else { ret = sysdb_attrs_add_string(netgroup_attrs, opts->netgroup_map[SDAP_AT_NETGROUP_MODSTAMP].sys_name, (const char*)el->values[0].data); if (ret) { goto fail; } timestamp = talloc_strdup(memctx, (const char*)el->values[0].data); if (!timestamp) { ret = ENOMEM; goto fail; } } ret = sdap_attrs_add_list(attrs, opts->netgroup_map[SDAP_AT_NETGROUP_TRIPLE].sys_name, "netgroup triple", name, netgroup_attrs); if (ret != EOK) { goto fail; } ret = sdap_attrs_add_list(attrs, opts->netgroup_map[SDAP_AT_NETGROUP_MEMBER].sys_name, "original members", name, netgroup_attrs); if (ret != EOK) { goto fail; } ret = sdap_attrs_add_list(attrs, SYSDB_NETGROUP_MEMBER, "members", name, netgroup_attrs); if (ret != EOK) { goto fail; } DEBUG(6, ("Storing info for netgroup %s\n", name)); ret = sdap_save_all_names(name, attrs, !dom->case_sensitive, netgroup_attrs); if (ret != EOK) { DEBUG(1, ("Failed to save netgroup names\n")); goto fail; } /* Make sure that any attributes we requested from LDAP that we * did not receive are also removed from the sysdb */ ret = list_missing_attrs(attrs, opts->netgroup_map, SDAP_OPTS_NETGROUP, attrs, &missing); if (ret != EOK) { DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to list missing attributes\n")); goto fail; } ret = sysdb_add_netgroup(ctx, name, NULL, netgroup_attrs, missing, dom->netgroup_timeout, now); if (ret) goto fail; if (_timestamp) { *_timestamp = timestamp; } return EOK; fail: DEBUG(2, ("Failed to save netgroup %s\n", name)); return ret; } errno_t update_dn_list(struct dn_item *dn_list, const size_t count, struct ldb_message **res, bool *all_resolved) { struct dn_item *dn_item; size_t c; const char *dn; const char *cn; bool not_resolved = false; *all_resolved = false; DLIST_FOR_EACH(dn_item, dn_list) { if (dn_item->cn != NULL) { continue; } for(c = 0; c < count; c++) { dn = ldb_msg_find_attr_as_string(res[c], SYSDB_ORIG_DN, NULL); if (dn == NULL) { DEBUG(1, ("Missing original DN.\n")); return EINVAL; } if (strcmp(dn, dn_item->dn) == 0) { DEBUG(9, ("Found matching entry for [%s].\n", dn_item->dn)); cn = ldb_msg_find_attr_as_string(res[c], SYSDB_NAME, NULL); if (cn == NULL) { DEBUG(1, ("Missing name.\n")); return EINVAL; } dn_item->cn = talloc_strdup(dn_item, cn); break; } } if (dn_item->cn == NULL) { not_resolved = true; } } *all_resolved = !not_resolved; return EOK; } struct netgr_translate_members_state { struct tevent_context *ev; struct sdap_options *opts; struct sdap_handle *sh; struct sysdb_ctx *sysdb; struct sysdb_attrs **netgroups; size_t count; struct dn_item *dn_list; struct dn_item *dn_item; struct dn_item *dn_idx; }; static errno_t netgr_translate_members_ldap_step(struct tevent_req *req); static void netgr_translate_members_ldap_done(struct tevent_req *subreq); struct tevent_req *netgr_translate_members_send(TALLOC_CTX *memctx, struct tevent_context *ev, struct sdap_options *opts, struct sdap_handle *sh, struct sss_domain_info *dom, struct sysdb_ctx *sysdb, const size_t count, struct sysdb_attrs **netgroups) { struct tevent_req *req; struct netgr_translate_members_state *state; size_t c; size_t mc; const char **member_list; size_t sysdb_count; int ret; struct ldb_message **sysdb_res; struct dn_item *dn_item; char *dn_filter; char *sysdb_filter; struct ldb_dn *netgr_basedn; bool all_resolved; const char *cn_attr[] = { SYSDB_NAME, SYSDB_ORIG_DN, NULL }; req = tevent_req_create(memctx, &state, struct netgr_translate_members_state); if (req == NULL) { return NULL; } state->ev = ev; state->opts = opts; state->sh = sh; state->sysdb = sysdb; state->netgroups = netgroups; state->count = count; state->dn_list = NULL; state->dn_item = NULL; state->dn_idx = NULL; for (c = 0; c < count; c++) { ret = sysdb_attrs_get_string_array(netgroups[c], SYSDB_ORIG_NETGROUP_MEMBER, state, &member_list); if (ret != EOK) { DEBUG(7, ("Missing netgroup members.\n")); continue; } for (mc = 0; member_list[mc] != NULL; mc++) { if (is_dn(member_list[mc])) { dn_item = talloc_zero(state, struct dn_item); if (dn_item == NULL) { DEBUG(1, ("talloc failed.\n")); ret = ENOMEM; goto fail; } DEBUG(9, ("Adding [%s] to DN list.\n", member_list[mc])); dn_item->netgroup = netgroups[c]; dn_item->dn = member_list[mc]; DLIST_ADD(state->dn_list, dn_item); } else { ret = sysdb_attrs_add_string(netgroups[c], SYSDB_NETGROUP_MEMBER, member_list[mc]); if (ret != EOK) { DEBUG(1, ("sysdb_attrs_add_string failed.\n")); goto fail; } } } } if (state->dn_list == NULL) { DEBUG(9, ("No DNs found among netgroup members.\n")); tevent_req_done(req); tevent_req_post(req, ev); return req; } dn_filter = talloc_strdup(state, "(|"); if (dn_filter == NULL) { DEBUG(1, ("talloc_strdup failed.\n")); ret = ENOMEM;; goto fail; } DLIST_FOR_EACH(dn_item, state->dn_list) { dn_filter = talloc_asprintf_append(dn_filter, "(%s=%s)", SYSDB_ORIG_DN, dn_item->dn); if (dn_filter == NULL) { DEBUG(1, ("talloc_asprintf_append failed.\n")); ret = ENOMEM; goto fail; } } dn_filter = talloc_asprintf_append(dn_filter, ")"); if (dn_filter == NULL) { DEBUG(1, ("talloc_asprintf_append failed.\n")); ret = ENOMEM; goto fail; } sysdb_filter = talloc_asprintf(state, "(&(%s)%s)", SYSDB_NC, dn_filter); if (sysdb_filter == NULL) { DEBUG(1, ("talloc_asprintf failed.\n")); ret = ENOMEM; goto fail; } netgr_basedn = sysdb_netgroup_base_dn(sysdb, state); if (netgr_basedn == NULL) { ret = ENOMEM; goto fail; } ret = sysdb_search_entry(state, sysdb, netgr_basedn, LDB_SCOPE_BASE, sysdb_filter, cn_attr, &sysdb_count, &sysdb_res); talloc_zfree(netgr_basedn); talloc_zfree(sysdb_filter); if (ret != EOK && ret != ENOENT) { DEBUG(1, ("sysdb_search_entry failed.\n")); goto fail; } if (ret == EOK) { ret = update_dn_list(state->dn_list, sysdb_count, sysdb_res, &all_resolved); if (ret != EOK) { DEBUG(1, ("update_dn_list failed.\n")); goto fail; } if (all_resolved) { DLIST_FOR_EACH(dn_item, state->dn_list) { ret = sysdb_attrs_add_string(dn_item->netgroup, SYSDB_NETGROUP_MEMBER, dn_item->cn); if (ret != EOK) { DEBUG(1, ("sysdb_attrs_add_string failed.\n")); goto fail; } } tevent_req_done(req); tevent_req_post(req, ev); return req; } } state->dn_idx = state->dn_list; ret = netgr_translate_members_ldap_step(req); if (ret != EOK && ret != EAGAIN) { DEBUG(1, ("netgr_translate_members_ldap_step failed.\n")); goto fail; } if (ret == EOK) { tevent_req_done(req); tevent_req_post(req, ev); } return req; fail: tevent_req_error(req, ret); tevent_req_post(req, ev); return req; } /* netgr_translate_members_ldap_step() returns * EOK: if everthing is translated, the caller can call tevent_req_done * EAGAIN: if there are still members waiting to be translated, the caller * should return to the mainloop * Exyz: every other return code indicates an error and tevent_req_error * should be called */ static errno_t netgr_translate_members_ldap_step(struct tevent_req *req) { struct netgr_translate_members_state *state = tevent_req_data(req, struct netgr_translate_members_state); const char **cn_attr; char *filter = NULL; struct tevent_req *subreq; int ret; DLIST_FOR_EACH(state->dn_item, state->dn_idx) { if (state->dn_item->cn == NULL) { break; } } if (state->dn_item == NULL) { DLIST_FOR_EACH(state->dn_item, state->dn_list) { ret = sysdb_attrs_add_string(state->dn_item->netgroup, SYSDB_NETGROUP_MEMBER, state->dn_item->cn); if (ret != EOK) { DEBUG(1, ("sysdb_attrs_add_string failed.\n")); tevent_req_error(req, ret); return ret; } } return EOK; } if (!sss_ldap_dn_in_search_bases(state, state->dn_item->dn, state->opts->netgroup_search_bases, &filter)) { /* not in search base, skip it */ state->dn_idx = state->dn_item->next; DLIST_REMOVE(state->dn_list, state->dn_item); return netgr_translate_members_ldap_step(req); } cn_attr = talloc_array(state, const char *, 3); if (cn_attr == NULL) { DEBUG(1, ("talloc_array failed.\n")); return ENOMEM; } cn_attr[0] = state->opts->netgroup_map[SDAP_AT_NETGROUP_NAME].name; cn_attr[1] = "objectclass"; cn_attr[2] = NULL; DEBUG(9, ("LDAP base search for [%s].\n", state->dn_item->dn)); subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh, state->dn_item->dn, LDAP_SCOPE_BASE, filter, cn_attr, state->opts->netgroup_map, SDAP_OPTS_NETGROUP, dp_opt_get_int(state->opts->basic, SDAP_SEARCH_TIMEOUT), false); if (!subreq) { DEBUG(1, ("sdap_get_generic_send failed.\n")); return ENOMEM; } talloc_steal(subreq, cn_attr); tevent_req_set_callback(subreq, netgr_translate_members_ldap_done, req); return EAGAIN; } static void netgr_translate_members_ldap_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct netgr_translate_members_state *state = tevent_req_data(req, struct netgr_translate_members_state); int ret; size_t count; struct sysdb_attrs **netgroups; const char *str; ret = sdap_get_generic_recv(subreq, state, &count, &netgroups); talloc_zfree(subreq); if (ret != EOK) { DEBUG(1, ("sdap_get_generic request failed.\n")); goto fail; } switch (count) { case 0: DEBUG(0, ("sdap_get_generic_recv found no entry for [%s].\n", state->dn_item->dn)); break; case 1: ret = sysdb_attrs_get_string(netgroups[0], SYSDB_NAME, &str); if (ret != EOK) { DEBUG(1, ("sysdb_attrs_add_string failed.\n")); break; } state->dn_item->cn = talloc_strdup(state->dn_item, str); if (state->dn_item->cn == NULL) { DEBUG(1, ("talloc_strdup failed.\n")); } break; default: DEBUG(1, ("Unexpected number of results [%d] for base search.\n", count)); } if (state->dn_item->cn == NULL) { DEBUG(1, ("Failed to resolve netgroup name for DN [%s], using DN.\n", state->dn_item->dn)); state->dn_item->cn = talloc_strdup(state->dn_item, state->dn_item->dn); } state->dn_idx = state->dn_item->next; ret = netgr_translate_members_ldap_step(req); if (ret != EOK && ret != EAGAIN) { DEBUG(1, ("netgr_translate_members_ldap_step failed.\n")); goto fail; } if (ret == EOK) { tevent_req_done(req); } return; fail: tevent_req_error(req, ret); return; } static errno_t netgroup_translate_ldap_members_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, size_t *count, struct sysdb_attrs ***netgroups) { struct netgr_translate_members_state *state = tevent_req_data(req, struct netgr_translate_members_state); TEVENT_REQ_RETURN_ON_ERROR(req); *count = state->count; *netgroups = talloc_steal(mem_ctx, state->netgroups); return EOK; } /* ==Search-Netgroups-with-filter============================================ */ struct sdap_get_netgroups_state { struct tevent_context *ev; struct sdap_options *opts; struct sdap_handle *sh; struct sss_domain_info *dom; struct sysdb_ctx *sysdb; const char **attrs; const char *base_filter; char *filter; int timeout; char *higher_timestamp; struct sysdb_attrs **netgroups; size_t count; size_t base_iter; struct sdap_search_base **search_bases; }; static errno_t sdap_get_netgroups_next_base(struct tevent_req *req); static void sdap_get_netgroups_process(struct tevent_req *subreq); static void netgr_translate_members_done(struct tevent_req *subreq); struct tevent_req *sdap_get_netgroups_send(TALLOC_CTX *memctx, struct tevent_context *ev, struct sss_domain_info *dom, struct sysdb_ctx *sysdb, struct sdap_options *opts, struct sdap_search_base **search_bases, struct sdap_handle *sh, const char **attrs, const char *filter, int timeout) { errno_t ret; struct tevent_req *req; struct sdap_get_netgroups_state *state; req = tevent_req_create(memctx, &state, struct sdap_get_netgroups_state); if (!req) return NULL; state->ev = ev; state->opts = opts; state->dom = dom; state->sh = sh; state->sysdb = sysdb; state->attrs = attrs; state->higher_timestamp = NULL; state->netgroups = NULL; state->count = 0; state->timeout = timeout; state->base_filter = filter; state->base_iter = 0; state->search_bases = search_bases; if (!state->search_bases) { DEBUG(SSSDBG_CRIT_FAILURE, ("Netgroup lookup request without a netgroup search base\n")); ret = EINVAL; goto done; } ret = sdap_get_netgroups_next_base(req); done: if (ret != EOK) { tevent_req_error(req, ret); tevent_req_post(req, state->ev); } return req; } static errno_t sdap_get_netgroups_next_base(struct tevent_req *req) { struct tevent_req *subreq; struct sdap_get_netgroups_state *state; state = tevent_req_data(req, struct sdap_get_netgroups_state); talloc_zfree(state->filter); state->filter = sdap_get_id_specific_filter(state, state->base_filter, state->search_bases[state->base_iter]->filter); if (!state->filter) { return ENOMEM; } DEBUG(SSSDBG_TRACE_FUNC, ("Searching for netgroups with base [%s]\n", state->search_bases[state->base_iter]->basedn)); subreq = sdap_get_generic_send( state, state->ev, state->opts, state->sh, state->search_bases[state->base_iter]->basedn, state->search_bases[state->base_iter]->scope, state->filter, state->attrs, state->opts->netgroup_map, SDAP_OPTS_NETGROUP, state->timeout, false); if (!subreq) { return ENOMEM; } tevent_req_set_callback(subreq, sdap_get_netgroups_process, req); return EOK; } static void sdap_get_netgroups_process(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sdap_get_netgroups_state *state = tevent_req_data(req, struct sdap_get_netgroups_state); int ret; ret = sdap_get_generic_recv(subreq, state, &state->count, &state->netgroups); talloc_zfree(subreq); if (ret) { tevent_req_error(req, ret); return; } DEBUG(6, ("Search for netgroups, returned %d results.\n", state->count)); if (state->count == 0) { /* No netgroups found in this search */ state->base_iter++; if (state->search_bases[state->base_iter]) { /* There are more search bases to try */ ret = sdap_get_netgroups_next_base(req); if (ret != EOK) { tevent_req_error(req, ENOENT); } return; } tevent_req_error(req, ENOENT); return; } subreq = netgr_translate_members_send(state, state->ev, state->opts, state->sh, state->dom, state->sysdb, state->count, state->netgroups); if (!subreq) { tevent_req_error(req, ENOMEM); return; } tevent_req_set_callback(subreq, netgr_translate_members_done, req); return; } static void netgr_translate_members_done(struct tevent_req *subreq) { struct tevent_req *req = tevent_req_callback_data(subreq, struct tevent_req); struct sdap_get_netgroups_state *state = tevent_req_data(req, struct sdap_get_netgroups_state); int ret; size_t c; time_t now; ret = netgroup_translate_ldap_members_recv(subreq, state, &state->count, &state->netgroups); talloc_zfree(subreq); if (ret) { tevent_req_error(req, ret); return; } now = time(NULL); for (c = 0; c < state->count; c++) { ret = sdap_save_netgroup(state, state->sysdb, state->dom, state->opts, state->netgroups[c], &state->higher_timestamp, now); if (ret) { DEBUG(2, ("Failed to store netgroups.\n")); tevent_req_error(req, ret); return; } } DEBUG(9, ("Saving %d Netgroups - Done\n", state->count)); tevent_req_done(req); } int sdap_get_netgroups_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, char **timestamp, size_t *reply_count, struct sysdb_attrs ***reply) { struct sdap_get_netgroups_state *state = tevent_req_data(req, struct sdap_get_netgroups_state); TEVENT_REQ_RETURN_ON_ERROR(req); if (timestamp) { *timestamp = talloc_steal(mem_ctx, state->higher_timestamp); } if (reply_count) { *reply_count = state->count; } if (reply) { *reply = talloc_steal(mem_ctx, state->netgroups); } return EOK; }