From c429d0726f6f988435450f53e016236a5c823355 Mon Sep 17 00:00:00 2001
From: Jan Zeleny <jzeleny@redhat.com>
Date: Thu, 12 Jan 2012 11:10:23 -0500
Subject: [PATCH 2/2] Support multiple search bases in HBAC

---
 src/providers/ipa/ipa_access.c       |    2 +-
 src/providers/ipa/ipa_access.h       |    2 +
 src/providers/ipa/ipa_hbac_hosts.c   |  228 +++++++++++++++++++++++++++------
 src/providers/ipa/ipa_hbac_private.h |    2 +-
 src/providers/ipa/ipa_init.c         |    1 +
 5 files changed, 191 insertions(+), 44 deletions(-)

diff --git a/src/providers/ipa/ipa_access.c b/src/providers/ipa/ipa_access.c
index bb496e594bbd81554b3eb374880dca966de1a9ff..4ee3d82a3fcbfe3fa8507d685e1b9086db389841 100644
--- a/src/providers/ipa/ipa_access.c
+++ b/src/providers/ipa/ipa_access.c
@@ -302,7 +302,7 @@ static int hbac_get_host_info_step(struct hbac_ctx *hbac_ctx)
                                                     IPA_HBAC_SUPPORT_SRCHOST),
                                     dp_opt_get_string(hbac_ctx->ipa_options,
                                                       IPA_HOSTNAME),
-                                    hbac_ctx->hbac_search_base);
+                                    hbac_ctx->access_ctx->host_search_bases);
     if (req == NULL) {
         DEBUG(1, ("Could not get host info\n"));
         return ENOMEM;
diff --git a/src/providers/ipa/ipa_access.h b/src/providers/ipa/ipa_access.h
index 2a6bdad5063852025a183770e5dbae1b64021d03..e695acd8f0bff230af7c59b489f7fa800137dcc2 100644
--- a/src/providers/ipa/ipa_access.h
+++ b/src/providers/ipa/ipa_access.h
@@ -44,6 +44,8 @@ struct ipa_access_ctx {
     struct dp_option *ipa_options;
     struct time_rules_ctx *tr_ctx;
     time_t last_update;
+
+    struct sdap_search_base **host_search_bases;
 };
 
 struct hbac_ctx {
diff --git a/src/providers/ipa/ipa_hbac_hosts.c b/src/providers/ipa/ipa_hbac_hosts.c
index 3a00a220143be66bd9bbbfb9de83c5bf9502dd6c..e263132ee0a59513fd52bb1c96b6a04eb897ca80 100644
--- a/src/providers/ipa/ipa_hbac_hosts.c
+++ b/src/providers/ipa/ipa_hbac_hosts.c
@@ -30,9 +30,14 @@ struct ipa_hbac_host_state {
     struct sysdb_ctx *sysdb;
     struct sdap_handle *sh;
     struct sdap_options *opts;
-    const char *search_base;
     const char **attrs;
 
+    struct sdap_search_base **search_bases;
+    int search_base_iter;
+
+    char *cur_filter;
+    char *host_filter;
+
     bool support_srchost;
     const char *hostname;
 
@@ -60,6 +65,13 @@ ipa_hbac_host_info_done(struct tevent_req *subreq);
 static void
 ipa_hbac_hostgroup_info_done(struct tevent_req *subreq);
 
+static errno_t
+ipa_hbac_host_info_next(struct tevent_req *req,
+                        struct ipa_hbac_host_state *state);
+static errno_t
+ipa_hbac_hostgroup_info_next(struct tevent_req *req,
+                             struct ipa_hbac_host_state *state);
+
 struct tevent_req *
 ipa_hbac_host_info_send(TALLOC_CTX *mem_ctx,
                         struct tevent_context *ev,
@@ -68,13 +80,11 @@ ipa_hbac_host_info_send(TALLOC_CTX *mem_ctx,
                         struct sdap_options *opts,
                         bool support_srchost,
                         const char *hostname,
-                        const char *search_base)
+                        struct sdap_search_base **search_bases)
 {
     errno_t ret;
     struct ipa_hbac_host_state *state;
     struct tevent_req *req;
-    struct tevent_req *subreq;
-    char *host_filter;
 
     req = tevent_req_create(mem_ctx, &state, struct ipa_hbac_host_state);
     if (req == NULL) {
@@ -87,19 +97,22 @@ ipa_hbac_host_info_send(TALLOC_CTX *mem_ctx,
     state->opts = opts;
     state->support_srchost = support_srchost;
     state->hostname = hostname;
-    state->search_base = search_base;
+    state->search_bases = search_bases;
+    state->search_base_iter = 0;
+    state->cur_filter = NULL;
 
     if (support_srchost) {
-        host_filter = talloc_asprintf(state, "(objectClass=%s)", IPA_HOST);
+        state->host_filter = talloc_asprintf(state, "(objectClass=%s)",
+                                             IPA_HOST);
     } else {
         if (hostname == NULL) {
             ret = EINVAL;
             goto immediate;
         }
-        host_filter = talloc_asprintf(state, "(&(objectClass=%s)(%s=%s))",
-                                      IPA_HOST, IPA_HOST_FQDN, hostname);
+        state->host_filter = talloc_asprintf(state, "(&(objectClass=%s)(%s=%s))",
+                                             IPA_HOST, IPA_HOST_FQDN, hostname);
     }
-    if (host_filter == NULL) {
+    if (state->host_filter == NULL) {
         ret = ENOMEM;
         goto immediate;
     }
@@ -119,28 +132,65 @@ ipa_hbac_host_info_send(TALLOC_CTX *mem_ctx,
     state->attrs[6] = IPA_CN;
     state->attrs[7] = NULL;
 
-    subreq = sdap_get_generic_send(state, ev, opts, sh, search_base,
-                                   LDAP_SCOPE_SUB, host_filter,
+    ret = ipa_hbac_host_info_next(req, state);
+    if (ret == EOK) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("No host search base configured?\n"));
+        ret = EINVAL;
+    }
+
+    if (ret != EAGAIN) {
+        goto immediate;
+    }
+
+    return req;
+
+immediate:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static errno_t ipa_hbac_host_info_next(struct tevent_req *req,
+                                       struct ipa_hbac_host_state *state)
+{
+    struct sdap_search_base *base;
+    struct tevent_req *subreq;
+
+    base = state->search_bases[state->search_base_iter];
+    if (base == NULL) {
+        return EOK;
+    }
+
+    talloc_zfree(state->cur_filter);
+    if (base->filter != NULL) {
+        state->cur_filter = talloc_asprintf(state, "(&%s%s)",
+                                            state->host_filter,
+                                            base->filter);
+    } else {
+        state->cur_filter = talloc_strdup(state, state->host_filter);
+    }
+    if (state->cur_filter == NULL) {
+        return ENOMEM;
+    }
+
+    subreq = sdap_get_generic_send(state, state->ev, state->opts,
+                                   state->sh, base->basedn,
+                                   base->scope, state->cur_filter,
                                    state->attrs, NULL, 0,
-                                   dp_opt_get_int(opts->basic,
+                                   dp_opt_get_int(state->opts->basic,
                                                   SDAP_ENUM_SEARCH_TIMEOUT));
     if (subreq == NULL) {
         DEBUG(1, ("Error requesting host info\n"));
-        ret = EIO;
-        goto immediate;
+        talloc_zfree(state->cur_filter);
+        return EIO;
     }
     tevent_req_set_callback(subreq, ipa_hbac_host_info_done, req);
 
-    return req;
-
-immediate:
-    if (ret == EOK) {
-        tevent_req_done(req);
-    } else {
-        tevent_req_error(req, ret);
-    }
-    tevent_req_post(req, ev);
-    return req;
+    return EAGAIN;
 }
 
 static void
@@ -151,7 +201,6 @@ ipa_hbac_host_info_done(struct tevent_req *subreq)
             tevent_req_callback_data(subreq, struct tevent_req);
     struct ipa_hbac_host_state *state =
             tevent_req_data(req, struct ipa_hbac_host_state);
-    char *hostgroup_filter;
     const char *host_dn;
     int i;
 
@@ -165,7 +214,14 @@ ipa_hbac_host_info_done(struct tevent_req *subreq)
     }
 
     if (state->host_count == 0) {
-        tevent_req_error(req, ENOENT);
+        state->search_base_iter++;
+        ret = ipa_hbac_host_info_next(req, state);
+        if (ret == EOK) {
+            /* No more search bases to try */
+            tevent_req_error(req, ENOENT);
+        } else if (ret != EAGAIN) {
+            tevent_req_error(req, ret);
+        }
         return;
     }
 
@@ -193,22 +249,22 @@ ipa_hbac_host_info_done(struct tevent_req *subreq)
 
     /* Look up host groups */
     if (state->support_srchost) {
-        hostgroup_filter = talloc_asprintf(state, "(objectClass=%s)",
-                                                  IPA_HOSTGROUP);
-        if (hostgroup_filter == NULL) {
+        talloc_zfree(state->host_filter);
+        state->host_filter = talloc_asprintf(state, "(objectClass=%s)",
+                                             IPA_HOSTGROUP);
+        if (state->host_filter == NULL) {
             tevent_req_error(req, ENOMEM);
             return;
         }
+        state->search_base_iter = 0;
 
-        subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh,
-                                       state->search_base, LDAP_SCOPE_SUB,
-                                       hostgroup_filter, state->attrs, hostgroup_map,
-                                       HOSTGROUP_MAP_ATTRS_COUNT,
-                                       dp_opt_get_int(state->opts->basic,
-                                                      SDAP_ENUM_SEARCH_TIMEOUT));
-        if (subreq == NULL) {
-            DEBUG(1, ("Error requesting host info\n"));
-            tevent_req_error(req, EIO);
+        ret = ipa_hbac_hostgroup_info_next(req, state);
+        if (ret == EOK) {
+            DEBUG(SSSDBG_CRIT_FAILURE, ("No host search base configured?\n"));
+            tevent_req_error(req, EINVAL);
+            return;
+        } else if (ret != EAGAIN) {
+            tevent_req_error(req, ret);
             return;
         }
     } else {
@@ -240,6 +296,45 @@ ipa_hbac_host_info_done(struct tevent_req *subreq)
     tevent_req_set_callback(subreq, ipa_hbac_hostgroup_info_done, req);
 }
 
+static errno_t ipa_hbac_hostgroup_info_next(struct tevent_req *req,
+                                            struct ipa_hbac_host_state *state)
+{
+    struct sdap_search_base *base;
+    struct tevent_req *subreq;
+
+    base = state->search_bases[state->search_base_iter];
+    if (base == NULL) {
+        return EOK;
+    }
+
+    talloc_zfree(state->cur_filter);
+    if (base->filter != NULL) {
+        state->cur_filter = talloc_asprintf(state, "(&%s%s)",
+                                            state->host_filter,
+                                            base->filter);
+    } else {
+        state->cur_filter = talloc_strdup(state, state->host_filter);
+    }
+    if (state->cur_filter == NULL) {
+        return ENOMEM;
+    }
+
+    subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh,
+                                   base->basedn, base->scope,
+                                   state->cur_filter, state->attrs, hostgroup_map,
+                                   HOSTGROUP_MAP_ATTRS_COUNT,
+                                   dp_opt_get_int(state->opts->basic,
+                                                  SDAP_ENUM_SEARCH_TIMEOUT));
+    if (subreq == NULL) {
+        DEBUG(1, ("Error requesting hostgroup info\n"));
+        talloc_zfree(state->cur_filter);
+        return EIO;
+    }
+    tevent_req_set_callback(subreq, ipa_hbac_hostgroup_info_done, req);
+
+    return EAGAIN;
+}
+
 static void
 ipa_hbac_hostgroup_info_done(struct tevent_req *subreq)
 {
@@ -249,15 +344,51 @@ ipa_hbac_hostgroup_info_done(struct tevent_req *subreq)
     struct ipa_hbac_host_state *state =
             tevent_req_data(req, struct ipa_hbac_host_state);
 
+    size_t hostgroups_total;
+    size_t hostgroup_count;
+    struct sysdb_attrs **hostgroups;
     struct sdap_deref_attrs **deref_result;
     const char *hostgroup_name;
-    int i;
+    const char *hostgroup_dn;
+    int i, j;
 
     if (state->support_srchost) {
         ret = sdap_get_generic_recv(subreq, state,
-                                    &state->hostgroup_count,
-                                    &state->hostgroups);
+                                    &hostgroup_count,
+                                    &hostgroups);
         talloc_zfree(subreq);
+
+        /* Merge the two arrays */
+        if (hostgroup_count > 0) {
+            hostgroups_total = hostgroup_count + state->hostgroup_count;
+            state->hostgroups = talloc_realloc(state, state->hostgroups,
+                                               struct sysdb_attrs *,
+                                               hostgroups_total);
+            if (state->hostgroups == NULL) {
+                tevent_req_error(req, ENOMEM);
+                return;
+            }
+
+            i = 0;
+            while(state->hostgroup_count < hostgroups_total) {
+                state->hostgroups[state->hostgroup_count] = hostgroups[i];
+                state->hostgroup_count++;
+                i++;
+            }
+        }
+
+        /* No look in next base */
+        state->search_base_iter++;
+        ret = ipa_hbac_hostgroup_info_next(req, state);
+        if (ret != EOK && ret != EAGAIN) {
+            tevent_req_error(req, ret);
+        }
+
+        if (ret != EOK) {
+            /* Only continue if no error occurred
+             * and no req was created */
+            return;
+        }
     } else {
         ret = sdap_deref_search_recv(subreq, state,
                                      &state->hostgroup_count,
@@ -275,16 +406,29 @@ ipa_hbac_hostgroup_info_done(struct tevent_req *subreq)
                 goto done;
             }
 
+            j = 0;
             for (i = 0; i < state->hostgroup_count; i++) {
                 ret = sysdb_attrs_get_string(deref_result[i]->attrs,
+                                             SYSDB_ORIG_DN, &hostgroup_dn);
+                if (ret != EOK) goto done;
+
+                if (!sss_ldap_dn_in_search_bases(state, hostgroup_dn,
+                                                 state->search_bases,
+                                                 NULL)) {
+                    continue;
+                }
+
+                ret = sysdb_attrs_get_string(deref_result[i]->attrs,
                                              IPA_CN, &hostgroup_name);
                 if (ret != EOK) goto done;
 
                 DEBUG(SSSDBG_FUNC_DATA, ("Dereferenced host group: %s\n",
                                         hostgroup_name));
-                state->hostgroups[i] = talloc_steal(state->hostgroups,
+                state->hostgroups[j] = talloc_steal(state->hostgroups,
                                                     deref_result[i]->attrs);
+                j++;
             }
+            state->hostgroup_count = j;
         }
     }
 
diff --git a/src/providers/ipa/ipa_hbac_private.h b/src/providers/ipa/ipa_hbac_private.h
index 05256058d9c9ac34dd820ac2777527887c74e840..6920f9e19ba7db341b8c8fd69f238a5b992e2db2 100644
--- a/src/providers/ipa/ipa_hbac_private.h
+++ b/src/providers/ipa/ipa_hbac_private.h
@@ -107,7 +107,7 @@ ipa_hbac_host_info_send(TALLOC_CTX *mem_ctx,
                         struct sdap_options *opts,
                         bool support_srchost,
                         const char *hostname,
-                        const char *search_base);
+                        struct sdap_search_base **search_bases);
 
 errno_t
 ipa_hbac_host_info_recv(struct tevent_req *req,
diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
index e16a95336a50a97833033d65e0be5c92b9d850b4..78a102ae98b1cc4547552392152daeee3f74bdd1 100644
--- a/src/providers/ipa/ipa_init.c
+++ b/src/providers/ipa/ipa_init.c
@@ -374,6 +374,7 @@ int sssm_ipa_access_init(struct be_ctx *bectx,
         goto done;
     }
     ipa_access_ctx->sdap_ctx = id_ctx->sdap_id_ctx;
+    ipa_access_ctx->host_search_bases = id_ctx->ipa_options->host_search_bases;
 
     ret = dp_copy_options(ipa_access_ctx, ipa_options->basic,
                           IPA_OPTS_BASIC, &ipa_access_ctx->ipa_options);
-- 
1.7.6.4

