From 2de04221a7de6e9e26d49cac51e5c7ba66eda665 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Pavel=20B=C5=99ezina?= <pbrezina@redhat.com>
Date: Tue, 12 Feb 2013 13:57:02 +0100
Subject: [PATCH] refactor nested group processing

---
 src/providers/ldap/sdap_async_nested_groups.c | 513 ++++++++++++++++++++++++++
 1 file changed, 513 insertions(+)
 create mode 100644 src/providers/ldap/sdap_async_nested_groups.c

diff --git a/src/providers/ldap/sdap_async_nested_groups.c b/src/providers/ldap/sdap_async_nested_groups.c
new file mode 100644
index 0000000000000000000000000000000000000000..6b2b988c6c310384a5549d3552e60e9e652d8a56
--- /dev/null
+++ b/src/providers/ldap/sdap_async_nested_groups.c
@@ -0,0 +1,513 @@
+/*
+    SSSD
+
+    Authors:
+        Pavel Březina <pbrezina@redhat.com>
+
+    Copyright (C) 2013 Red Hat
+
+    This program is free software; you can redistribute it and/or modify
+    it under the terms of the GNU General Public License as published by
+    the Free Software Foundation; either version 3 of the License, or
+    (at your option) any later version.
+
+    This program is distributed in the hope that it will be useful,
+    but WITHOUT ANY WARRANTY; without even the implied warranty of
+    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+    GNU General Public License for more details.
+
+    You should have received a copy of the GNU General Public License
+    along with this program.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+#include <errno.h>
+#include <string.h>
+#include <tevent.h>
+#include <talloc.h>
+#include <dhash.h>
+
+#include "util/util.h"
+#include "db/sysdb.h"
+
+struct sdap_nested_group_ctx {
+    struct sysdb_ctx *sysdb;
+    struct sdap_options *opts;
+    struct sdap_handle *sh;
+    hash_table_t *users;
+    hash_table_t *groups;
+    bool try_deref;
+    int deref_treshold;
+    int nesting_level;
+    int max_nesting_level;
+};
+
+static errno_t sdap_nested_group_save_entry(struct sysdb_ctx *sysdb,
+                                            hash_table_t *table,
+                                            const char *key_attr,
+                                            struct sysdb_attrs *entry)
+{
+    TALLOC_CTX *tmp_ctx = NULL;
+    hash_key_t key;
+    hash_value_t value;
+    const char *name = NULL;
+    errno_t ret;
+    int hret;
+
+    ret = sysdb_attrs_primary_name(sysdb, entry, key_attr, &name);
+    if (ret != EOK) {
+        return ret;
+    }
+
+    key.type = HASH_KEY_STRING;
+    key.str = talloc_strdup(NULL, name);
+    if (key.str == NULL) {
+        return ENOMEM;
+    }
+
+    if (hash_has_key(table, &key)) {
+        talloc_free(key.str);
+        return EEXIST;
+    }
+
+    value.type = HASH_VALUE_PTR;
+    value.ptr = entry;
+
+    hret = hash_enter(table, &key, &value);
+    if (hret != HASH_SUCCESS) {
+        talloc_free(key.str);
+        return EIO;
+    }
+
+    talloc_steal(table, key.str);
+    talloc_steal(table, value.ptr);
+
+    return EOK;
+}
+
+static errno_t sdap_nested_group_save_user(struct sdap_nested_group_ctx *group_ctx,
+                                           struct sysdb_attrs *user)
+{
+    const char *attr = group_ctx->opts->user_map[SDAP_AT_USER_NAME].sys_name;
+
+    return sdap_nested_group_save_entry(group_ctx->sysdb, group_ctx->users,
+                                        attr, user);
+}
+
+static errno_t sdap_nested_group_save_group(struct sdap_nested_group_ctx *group_ctx,
+                                            struct sysdb_attrs *group)
+{
+    struct sdap_attr_map *map = group_ctx->opts->group_map;
+    const char *attr = map[SDAP_AT_GROUP_NAME].sys_name;
+    gid_t gid;
+    errno_t ret;
+
+    ret = sysdb_attrs_get_uint32_t(group, map[SDAP_AT_GROUP_GID].sys_name,
+                                   &gid);
+    if (ret == ENOENT || (ret == EOK && gid == 0)) {
+        DEBUG(SSSDBG_TRACE_ALL,
+             ("The group's gid was %s\n", ret == ENOENT ? "missing" : "zero"));
+        DEBUG(SSSDBG_TRACE_INTERNAL,
+             ("Marking group as non-posix and setting GID=0!\n"));
+        if (ret == ENOENT) {
+            ret = sysdb_attrs_add_uint32(group,
+                                         map[SDAP_AT_GROUP_GID].sys_name, 0);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_CRIT_FAILURE,
+                      ("Failed to add a GID to non-posix group!\n"));
+                return ret;
+            }
+        }
+
+        ret = sysdb_attrs_add_bool(group, SYSDB_POSIX, false);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_OP_FAILURE,
+                  ("Error: Failed to mark group as non-posix!\n"));
+            return ret;
+        }
+    } else if (ret) {
+        return ret;
+    }
+
+    return sdap_nested_group_save_entry(group_ctx->sysdb, group_ctx->groups,
+                                        attr, group);
+}
+
+struct sdap_nested_group_state {
+    struct sdap_nested_group_ctx *group_ctx;
+};
+
+static void sdap_nested_group_done(struct tevent_req *subreq);
+
+struct tevent_req *sdap_nested_group_send(TALLOC_CTX *mem_ctx,
+                                          struct tevent_context *ev,
+                                          struct sysdb_ctx *sysdb,
+                                          struct sdap_options *opts,
+                                          struct sdap_handle *sh,
+                                          struct sysdb_attrs *group)
+{
+    struct sdap_nested_group_state *state = NULL;
+    struct tevent_req *req = NULL;
+    struct tevent_req *subreq = NULL;
+    errno_t ret;
+    int i;
+
+    req = tevent_req_create(mem_ctx, &state, struct sdap_nested_group_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
+        return NULL;
+    }
+
+    /* create main nested group context */
+    state->group_ctx = talloc_zero(state, struct sdap_nested_group_ctx);
+    if (state->group_ctx == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    ret = sss_hash_create(state->group_ctx, 32, &state->group_ctx->users);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to create hash table [%d]: %s\n",
+                                    ret, strerror(ret)));
+        goto immediately;
+    }
+
+    ret = sss_hash_create(state->group_ctx, 32, &state->group_ctx->groups);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to create hash table [%d]: %s\n",
+                                    ret, strerror(ret)));
+        goto immediately;
+    }
+
+    state->group_ctx->try_deref = true;
+    state->group_ctx->deref_treshold = dp_opt_get_int(opts->basic,
+                                                      SDAP_DEREF_THRESHOLD);
+    state->group_ctx->nesting_level = 0;
+    state->group_ctx->max_nesting_level = dp_opt_get_int(opts->basic,
+                                                         SDAP_NESTING_LEVEL);
+
+    state->group_ctx->sysdb = sysdb;
+    state->group_ctx->opts = opts;
+    state->group_ctx->sh = sh;
+
+    /* if any search base contains filter, disable dereference. */
+    for (i = 0; opts->user_search_bases[i] != NULL; i++) {
+        if (opts->user_search_bases[i]->filter != NULL) {
+            DEBUG(SSSDBG_TRACE_FUNC, ("User search base contains filter, "
+                                      "dereference will be disabled\n"));
+            state->group_ctx->try_deref = false;
+            break;
+        }
+    }
+
+    if (state->group_ctx->try_deref) {
+        for (i = 0; opts->group_search_bases[i] != NULL; i++) {
+            if (opts->group_search_bases[i]->filter != NULL) {
+                DEBUG(SSSDBG_TRACE_FUNC, ("Group search base contains filter, "
+                                          "dereference will be disabled\n"));
+                state->group_ctx->try_deref = false;
+                break;
+            }
+        }
+    }
+
+    /* insert initial group into hash table */
+    ret = sdap_nested_group_save_group(state->group_ctx, group);
+    if (ret == EEXIST) {
+        ret = EOK;
+        goto immediately;
+    } else if (ret != EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Unable to insert group into hash table "
+                                    "[%d]: %s\n", ret, strerror(ret)));
+        goto immediately;
+    }
+
+    /* resolve group */
+    subreq = sdap_nested_group_process_send(state, ev, state->group_ctx, group);
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    tevent_req_set_callback(subreq, sdap_nested_group_done, req);
+
+    return req;
+
+immediately:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+
+    return req;
+}
+
+static void sdap_nested_group_done(struct tevent_req *subreq)
+{
+    struct sdap_nested_group_state *state = NULL;
+    struct tevent_req *req = NULL;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sdap_nested_group_state);
+
+    ret = sdap_nested_group_process_recv(subreq);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    tevent_req_done(req);
+}
+
+errno_t sdap_nested_group_recv(TALLOC_CTX *mem_ctx,
+                               struct tevent_req *req,
+                               hash_table_t **_users,
+                               hash_table_t **_groups)
+{
+    struct sdap_nested_group_state *state = NULL;
+    state = tevent_req_data(req, struct sdap_nested_group_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    *_users = talloc_steal(mem_ctx, state->group_ctx->users);
+    *_groups = talloc_steal(mem_ctx, state->group_ctx->groups);
+
+    return EOK;
+}
+
+struct sdap_nested_group_process_state {
+    struct sdap_nested_group_ctx *group_ctx;
+};
+
+static void sdap_nested_group_process_done(struct tevent_req *subreq);
+
+static struct tevent_req *sdap_nested_group_process_send(TALLOC_CTX *mem_ctx,
+                                                         struct tevent_context *ev,
+                                                         struct sdap_nested_group_ctx *group_ctx,
+                                                         struct sysdb_attrs *group)
+{
+    struct sdap_nested_group_process_state *state = NULL;
+    struct tevent_req *req = NULL;
+    struct tevent_req *subreq = NULL;
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state,
+                            struct sdap_nested_group_process_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
+        return NULL;
+    }
+
+    state->group_ctx = group_ctx;
+
+    /* todo get member list */
+
+    if (group_ctx->try_deref) {
+        subreq = sdap_nested_group_deref_send(state, ev);
+    } else {
+        subreq = sdap_nested_group_single_send(state, ev);
+    }
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    tevent_req_set_callback(subreq, sdap_nested_group_process_done, req);
+
+    return req;
+
+immediately:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+
+    return req;
+}
+
+static void sdap_nested_group_process_done(struct tevent_req *subreq)
+{
+    struct sdap_nested_group_process_state *state = NULL;
+    struct tevent_req *req = NULL;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sdap_nested_group_process_state);
+
+    if (state->group_ctx->try_deref) {
+        ret = sdap_nested_group_deref_recv(subreq);
+    } else {
+        ret = sdap_nested_group_single_recv(subreq);
+    }
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    tevent_req_done(req);
+}
+
+static errno_t sdap_nested_group_process_recv(struct tevent_req *req)
+{
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
+
+struct sdap_nested_group_single_state {
+
+};
+
+static void sdap_nested_group_single_done(struct tevent_req *subreq);
+
+struct tevent_req *sdap_nested_group_single_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev)
+{
+    struct sdap_nested_group_single_state *state = NULL;
+    struct tevent_req *req = NULL;
+    struct tevent_req *subreq = NULL;
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state,
+                            struct sdap_nested_group_single_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
+        return NULL;
+    }
+
+    /* todo */
+    subreq = subreq_send(state);
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    tevent_req_set_callback(subreq, sdap_nested_group_single_done, req);
+
+    return req;
+
+immediately:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+
+    return req;
+}
+
+static void sdap_nested_group_single_done(struct tevent_req *subreq)
+{
+    struct sdap_nested_group_single_state *state = NULL;
+    struct tevent_req *req = NULL;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sdap_nested_group_single_state);
+
+    ret = subreq_recv(subreq);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        goto done;
+    }
+
+done:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    tevent_req_done(req);
+}
+
+int sdap_nested_group_single_recv(struct tevent_req *req)
+{
+    struct sdap_nested_group_single_state *state = NULL;
+    state = tevent_req_data(req, struct sdap_nested_group_single_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
+
+struct sdap_nested_group_deref_state {
+
+};
+
+static void sdap_nested_group_deref_done(struct tevent_req *subreq);
+
+struct tevent_req *sdap_nested_group_deref_send(TALLOC_CTX *mem_ctx, struct tevent_context *ev)
+{
+    struct sdap_nested_group_deref_state *state = NULL;
+    struct tevent_req *req = NULL;
+    struct tevent_req *subreq = NULL;
+    errno_t ret;
+
+    req = tevent_req_create(mem_ctx, &state,
+                            struct sdap_nested_group_deref_state);
+    if (req == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("tevent_req_create() failed\n"));
+        return NULL;
+    }
+
+    /* todo */
+    subreq = subreq_send(state);
+    if (subreq == NULL) {
+        ret = ENOMEM;
+        goto immediately;
+    }
+
+    tevent_req_set_callback(subreq, sdap_nested_group_deref_done, req);
+
+    return req;
+
+immediately:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+
+    return req;
+}
+
+static void sdap_nested_group_deref_done(struct tevent_req *subreq)
+{
+    struct sdap_nested_group_deref_state *state = NULL;
+    struct tevent_req *req = NULL;
+    errno_t ret;
+
+    req = tevent_req_callback_data(subreq, struct tevent_req);
+    state = tevent_req_data(req, struct sdap_nested_group_deref_state);
+
+    ret = subreq_recv(subreq);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        goto done;
+    }
+
+done:
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    tevent_req_done(req);
+}
+
+int sdap_nested_group_deref_recv(struct tevent_req *req)
+{
+    struct sdap_nested_group_deref_state *state = NULL;
+    state = tevent_req_data(req, struct sdap_nested_group_deref_state);
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    return EOK;
+}
-- 
1.7.11.7

