From 8c616e19a799971c738e5ff33d24cd7164259c37 Mon Sep 17 00:00:00 2001 From: Sumit Bose Date: Tue, 5 Oct 2010 13:51:05 +0200 Subject: [PATCH 8/9] Implement netgroup support for LDAP provider --- Makefile.am | 4 + src/db/sysdb.h | 3 + src/providers/ldap/ldap_common.h | 6 + src/providers/ldap/ldap_id.c | 26 + src/providers/ldap/ldap_id_netgroup.c | 224 +++++++++ src/providers/ldap/sdap_async.h | 14 +- src/providers/ldap/sdap_async_netgroups.c | 713 +++++++++++++++++++++++++++++ 7 files changed, 989 insertions(+), 1 deletions(-) create mode 100644 src/providers/ldap/ldap_id_netgroup.c create mode 100644 src/providers/ldap/sdap_async_netgroups.c diff --git a/Makefile.am b/Makefile.am index 0dc53d3..436ddd5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -765,6 +765,7 @@ libsss_ldap_la_SOURCES = \ src/providers/ldap/ldap_id.c \ src/providers/ldap/ldap_id_enum.c \ src/providers/ldap/ldap_id_cleanup.c \ + src/providers/ldap/ldap_id_netgroup.c \ src/providers/ldap/sdap_access.c \ src/providers/ldap/ldap_auth.c \ src/providers/ldap/ldap_init.c \ @@ -772,6 +773,7 @@ libsss_ldap_la_SOURCES = \ src/providers/ldap/sdap_async.c \ src/providers/ldap/sdap_async_accounts.c \ src/providers/ldap/sdap_async_connection.c \ + src/providers/ldap/sdap_async_netgroups.c \ src/providers/ldap/sdap_child_helpers.c \ src/providers/ldap/sdap_fd_events.c \ src/providers/ldap/sdap_id_op.c \ @@ -848,11 +850,13 @@ libsss_ipa_la_SOURCES = \ src/providers/ldap/ldap_id.c \ src/providers/ldap/ldap_id_enum.c \ src/providers/ldap/ldap_id_cleanup.c \ + src/providers/ldap/ldap_id_netgroup.c \ src/providers/ldap/ldap_auth.c \ src/providers/ldap/ldap_common.c \ src/providers/ldap/sdap_async.c \ src/providers/ldap/sdap_async_accounts.c \ src/providers/ldap/sdap_async_connection.c \ + src/providers/ldap/sdap_async_netgroups.c \ src/providers/ldap/sdap_child_helpers.c \ src/providers/ldap/sdap_fd_events.c \ src/providers/ldap/sdap_id_op.c \ diff --git a/src/db/sysdb.h b/src/db/sysdb.h index a1d6c91..60c0ae8 100644 --- a/src/db/sysdb.h +++ b/src/db/sysdb.h @@ -79,6 +79,8 @@ #define SYSDB_INITGR_EXPIRE "initgrExpireTimestamp" #define SYSDB_NETGROUP_TRIPLE "netgroupTriple" +#define SYSDB_ORIG_NETGROUP_MEMBER "originalMemberNisNetgroup" +#define SYSDB_NETGROUP_MEMBER "memberNisNetgroup" #define SYSDB_DESCRIPTION "description" #define SYSDB_CACHEDPWD "cachedPassword" @@ -142,6 +144,7 @@ NULL} #define SYSDB_NETGR_ATTRS {SYSDB_NAME, SYSDB_NETGROUP_TRIPLE, \ + SYSDB_NETGROUP_MEMBER, \ SYSDB_DEFAULT_ATTRS, \ NULL} diff --git a/src/providers/ldap/ldap_common.h b/src/providers/ldap/ldap_common.h index 2a0e976..3ebd481 100644 --- a/src/providers/ldap/ldap_common.h +++ b/src/providers/ldap/ldap_common.h @@ -113,6 +113,12 @@ struct tevent_req *groups_get_send(TALLOC_CTX *memctx, int attrs_type); int groups_get_recv(struct tevent_req *req, int *dp_error_out); + +struct tevent_req *netgroup_get_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_id_ctx *ctx, + const char *name); +int netgroup_get_recv(struct tevent_req *req, int *dp_error_out); /* setup child logging */ int setup_child(struct sdap_id_ctx *ctx); diff --git a/src/providers/ldap/ldap_id.c b/src/providers/ldap/ldap_id.c index 0c90773..be4642a 100644 --- a/src/providers/ldap/ldap_id.c +++ b/src/providers/ldap/ldap_id.c @@ -647,6 +647,7 @@ int groups_by_user_recv(struct tevent_req *req, int *dp_error_out) static void sdap_account_info_users_done(struct tevent_req *req); static void sdap_account_info_groups_done(struct tevent_req *req); static void sdap_account_info_initgr_done(struct tevent_req *req); +static void sdap_account_info_netgroups_done(struct tevent_req *req); void sdap_account_info_handler(struct be_req *breq) { @@ -728,6 +729,21 @@ void sdap_account_info_handler(struct be_req *breq) break; + case BE_REQ_NETGROUP: + if (ar->filter_type != BE_FILTER_NAME) { + ret = EINVAL; + err = "Invalid filter type"; + break; + } + + req = netgroup_get_send(breq, breq->be_ctx->ev, ctx, ar->filter_value); + if (!req) { + return sdap_handler_done(breq, DP_ERR_FATAL, ENOMEM, "Out of memory"); + } + + tevent_req_set_callback(req, sdap_account_info_netgroups_done, breq); + break; + default: /*fail*/ ret = EINVAL; err = "Invalid request type"; @@ -793,3 +809,13 @@ static void sdap_account_info_initgr_done(struct tevent_req *req) sdap_account_info_complete(breq, dp_error, ret, "Init Groups Failed"); } +static void sdap_account_info_netgroups_done(struct tevent_req *req) +{ + struct be_req *breq = tevent_req_callback_data(req, struct be_req); + int ret, dp_error; + + ret = netgroup_get_recv(req, &dp_error); + talloc_zfree(req); + + sdap_account_info_complete(breq, dp_error, ret, "Netgroup lookup failed"); +} diff --git a/src/providers/ldap/ldap_id_netgroup.c b/src/providers/ldap/ldap_id_netgroup.c new file mode 100644 index 0000000..c82ccc4 --- /dev/null +++ b/src/providers/ldap/ldap_id_netgroup.c @@ -0,0 +1,224 @@ +/* + SSSD + + LDAP Identity Backend Module - Netgroup support + + 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 + +#include "util/util.h" +#include "db/sysdb.h" +#include "providers/ldap/ldap_common.h" +#include "providers/ldap/sdap_async.h" + + +struct netgroup_get_state { + struct tevent_context *ev; + struct sdap_id_ctx *ctx; + struct sdap_id_op *op; + struct sysdb_ctx *sysdb; + struct sss_domain_info *domain; + + const char *name; + + char *filter; + const char **attrs; + + size_t count; + struct sysdb_attrs **netgroups; + + int dp_error; +}; + +static int netgroup_get_retry(struct tevent_req *req); +static void netgroup_get_connect_done(struct tevent_req *subreq); +static void netgroup_get_done(struct tevent_req *subreq); + +struct tevent_req *netgroup_get_send(TALLOC_CTX *memctx, + struct tevent_context *ev, + struct sdap_id_ctx *ctx, + const char *name) +{ + struct tevent_req *req; + struct netgroup_get_state *state; + int ret; + + req = tevent_req_create(memctx, &state, struct netgroup_get_state); + if (!req) return NULL; + + state->ev = ev; + state->ctx = ctx; + state->dp_error = DP_ERR_FATAL; + + state->op = sdap_id_op_create(state, state->ctx->conn_cache); + if (!state->op) { + DEBUG(2, ("sdap_id_op_create failed\n")); + ret = ENOMEM; + goto fail; + } + + state->sysdb = ctx->be->sysdb; + state->domain = state->ctx->be->domain; + state->name = name; + + state->filter = talloc_asprintf(state, "(&(%s=%s)(objectclass=%s))", + ctx->opts->netgroup_map[SDAP_AT_NETGROUP_NAME].name, + name, + ctx->opts->netgroup_map[SDAP_OC_NETGROUP].name); + if (!state->filter) { + DEBUG(2, ("Failed to build filter\n")); + ret = ENOMEM; + goto fail; + } + + ret = build_attrs_from_map(state, ctx->opts->netgroup_map, + SDAP_OPTS_NETGROUP, &state->attrs); + if (ret != EOK) goto fail; + + ret = netgroup_get_retry(req); + if (ret != EOK) { + goto fail; + } + + return req; + +fail: + tevent_req_error(req, ret); + tevent_req_post(req, ev); + return req; +} + +static int netgroup_get_retry(struct tevent_req *req) +{ + struct netgroup_get_state *state = tevent_req_data(req, + struct netgroup_get_state); + struct tevent_req *subreq; + int ret = EOK; + + subreq = sdap_id_op_connect_send(state->op, state, &ret); + if (!subreq) { + return ret; + } + + tevent_req_set_callback(subreq, netgroup_get_connect_done, req); + return EOK; +} + +static void netgroup_get_connect_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct netgroup_get_state *state = tevent_req_data(req, + struct netgroup_get_state); + int dp_error = DP_ERR_FATAL; + int ret; + + ret = sdap_id_op_connect_recv(subreq, &dp_error); + talloc_zfree(subreq); + + if (ret != EOK) { + state->dp_error = dp_error; + tevent_req_error(req, ret); + return; + } + + subreq = sdap_get_netgroups_send(state, state->ev, + state->domain, state->sysdb, + state->ctx->opts, + sdap_id_op_handle(state->op), + state->attrs, state->filter); + if (!subreq) { + tevent_req_error(req, ENOMEM); + return; + } + tevent_req_set_callback(subreq, netgroup_get_done, req); + + return; +} + +static void netgroup_get_done(struct tevent_req *subreq) +{ + struct tevent_req *req = tevent_req_callback_data(subreq, + struct tevent_req); + struct netgroup_get_state *state = tevent_req_data(req, + struct netgroup_get_state); + int dp_error = DP_ERR_FATAL; + int ret; + + ret = sdap_get_netgroups_recv(subreq, state, NULL, &state->count, + &state->netgroups); + talloc_zfree(subreq); + ret = sdap_id_op_done(state->op, ret, &dp_error); + + if (dp_error == DP_ERR_OK && ret != EOK) { + /* retry */ + ret = netgroup_get_retry(req); + if (ret != EOK) { + tevent_req_error(req, ret); + return; + } + + return; + } + + if (ret && ret != ENOENT) { + state->dp_error = dp_error; + tevent_req_error(req, ret); + return; + } + + if (ret == EOK && state->count > 1) { + DEBUG(1, ("Found more than one netgroup with the name [%s].\n", + state->name)); + tevent_req_error(req, EINVAL); + return; + } + + if (ret == ENOENT) { + ret = sysdb_delete_netgroup(state->sysdb, state->domain, state->name); + if (ret) { + tevent_req_error(req, ret); + return; + } + + state->dp_error = DP_ERR_OK; + tevent_req_error(req, ENOENT); + return; + } + + state->dp_error = DP_ERR_OK; + tevent_req_done(req); + return; +} + +int netgroup_get_recv(struct tevent_req *req, int *dp_error_out) +{ + struct netgroup_get_state *state = tevent_req_data(req, + struct netgroup_get_state); + + if (dp_error_out) { + *dp_error_out = state->dp_error; + } + + TEVENT_REQ_RETURN_ON_ERROR(req); + + return EOK; +} diff --git a/src/providers/ldap/sdap_async.h b/src/providers/ldap/sdap_async.h index e5a60da..182042a 100644 --- a/src/providers/ldap/sdap_async.h +++ b/src/providers/ldap/sdap_async.h @@ -59,6 +59,19 @@ struct tevent_req *sdap_get_groups_send(TALLOC_CTX *memctx, int sdap_get_groups_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, char **timestamp); +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_handle *sh, + const char **attrs, + const char *wildcard); +int sdap_get_netgroups_recv(struct tevent_req *req, + TALLOC_CTX *mem_ctx, char **timestamp, + size_t *reply_count, + struct sysdb_attrs ***reply); + struct tevent_req *sdap_kinit_send(TALLOC_CTX *memctx, struct tevent_context *ev, struct sdap_handle *sh, @@ -132,5 +145,4 @@ struct tevent_req *sdap_get_generic_send(TALLOC_CTX *memctx, int sdap_get_generic_recv(struct tevent_req *req, TALLOC_CTX *mem_ctx, size_t *reply_count, struct sysdb_attrs ***reply_list); - #endif /* _SDAP_ASYNC_H_ */ diff --git a/src/providers/ldap/sdap_async_netgroups.c b/src/providers/ldap/sdap_async_netgroups.c new file mode 100644 index 0000000..5f6ad05 --- /dev/null +++ b/src/providers/ldap/sdap_async_netgroups.c @@ -0,0 +1,713 @@ +/* + 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" + +static bool is_dn(const char *str) +{ + int ret; + LDAPDN dn; + + ret = ldap_str2dn(str, &dn, LDAP_DN_FORMAT_LDAPV3); + return (ret == LDAP_SUCCESS ? true : false); +} + +static errno_t sdap_save_netgroup(TALLOC_CTX *memctx, + struct sysdb_ctx *ctx, + struct sdap_options *opts, + struct sss_domain_info *dom, + struct sysdb_attrs *attrs, + char **_timestamp) +{ + struct ldb_message_element *el; + struct sysdb_attrs *netgroup_attrs; + const char *name = NULL; + int ret; + char *timestamp = NULL; + size_t c; + + 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 = 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, + 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 = sysdb_attrs_get_el(attrs, + opts->netgroup_map[SDAP_AT_NETGROUP_TRIPLE].sys_name, + &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, + opts->netgroup_map[SDAP_AT_NETGROUP_TRIPLE].sys_name, + (const char*)el->values[c].data); + if (ret) { + goto fail; + } + } + } + + ret = sysdb_attrs_get_el(attrs, + opts->netgroup_map[SDAP_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[SDAP_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, dom, name, NULL, netgroup_attrs, + dp_opt_get_int(opts->basic, + SDAP_ENTRY_CACHE_TIMEOUT)); + if (ret) goto fail; + + if (_timestamp) { + *_timestamp = timestamp; + } + + return EOK; + +fail: + DEBUG(2, ("Failed to save netgroup %s\n", name)); + return ret; +} + +struct dn_item { + const char *dn; + struct sysdb_attrs *netgroup; + char *cn; + struct dn_item *next; + struct dn_item *prev; +}; + +static 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; + } + } + + 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 sss_domain_info *dom; + 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->dom = dom; + 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, dom->name); + 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; + 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; + } + + 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, NULL, + cn_attr, state->opts->netgroup_map, + SDAP_OPTS_NETGROUP); + 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 *filter; + + char *higher_timestamp; + struct sysdb_attrs **netgroups; + size_t count; +}; + +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_handle *sh, + const char **attrs, + const char *filter) +{ + struct tevent_req *req, *subreq; + 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->filter = filter; + state->attrs = attrs; + state->higher_timestamp = NULL; + state->netgroups = NULL; + state->count = 0; + + subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh, + dp_opt_get_string(state->opts->basic, + SDAP_SEARCH_BASE), + LDAP_SCOPE_SUBTREE, + state->filter, state->attrs, + state->opts->netgroup_map, + SDAP_OPTS_NETGROUP); + if (!subreq) { + talloc_zfree(req); + return NULL; + } + tevent_req_set_callback(subreq, sdap_get_netgroups_process, req); + + return req; +} + +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) { + 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; + + ret = netgroup_translate_ldap_members_recv(subreq, state, &state->count, + &state->netgroups); + talloc_zfree(subreq); + if (ret) { + tevent_req_error(req, ret); + return; + } + + for (c = 0; c < state->count; c++) { + ret = sdap_save_netgroup(state, state->sysdb, + state->opts, state->dom, + state->netgroups[c], + &state->higher_timestamp); + 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; +} -- 1.7.2.3