From 48670fb1fbb2a65529c9fe72a583cf34224b99b6 Mon Sep 17 00:00:00 2001
From: Jan Zeleny <jzeleny@redhat.com>
Date: Wed, 18 Jan 2012 11:08:06 -0500
Subject: [PATCH 01/10] Implemented support for multiple search bases in HBAC
 rules and services

---
 src/providers/ipa/ipa_access.c        |   34 +-----
 src/providers/ipa/ipa_access.h        |    3 +-
 src/providers/ipa/ipa_common.c        |   23 ++++
 src/providers/ipa/ipa_common.h        |    1 +
 src/providers/ipa/ipa_hbac_private.h  |   18 +---
 src/providers/ipa/ipa_hbac_rules.c    |  157 ++++++++++++++++++++-----
 src/providers/ipa/ipa_hbac_rules.h    |   42 +++++++
 src/providers/ipa/ipa_hbac_services.c |  205 ++++++++++++++++++++++++++-------
 src/providers/ipa/ipa_init.c          |    1 +
 9 files changed, 366 insertions(+), 118 deletions(-)
 create mode 100644 src/providers/ipa/ipa_hbac_rules.h

diff --git a/src/providers/ipa/ipa_access.c b/src/providers/ipa/ipa_access.c
index 4ee3d82a3fcbfe3fa8507d685e1b9086db389841..7d42e8b4ef32e33196d3762b47d2aeb1e2f7ffbd 100644
--- a/src/providers/ipa/ipa_access.c
+++ b/src/providers/ipa/ipa_access.c
@@ -31,30 +31,7 @@
 #include "providers/ipa/ipa_access.h"
 #include "providers/ipa/ipa_hbac.h"
 #include "providers/ipa/ipa_hbac_private.h"
-
-static char *get_hbac_search_base(TALLOC_CTX *mem_ctx,
-                                  struct dp_option *ipa_options)
-{
-    char *base;
-    int ret;
-
-    base = dp_opt_get_string(ipa_options, IPA_HBAC_SEARCH_BASE);
-    if (base != NULL) {
-        return talloc_strdup(mem_ctx, base);
-    }
-
-    DEBUG(9, ("ipa_hbac_search_base not available, trying base DN.\n"));
-
-    ret = domain_to_basedn(mem_ctx,
-                           dp_opt_get_string(ipa_options, IPA_KRB5_REALM),
-                           &base);
-    if (ret != EOK) {
-        DEBUG(1, ("domain_to_basedn failed.\n"));
-        return NULL;
-    }
-
-    return base;
-}
+#include "providers/ipa/ipa_hbac_rules.h"
 
 static void ipa_access_reply(struct hbac_ctx *hbac_ctx, int pam_status)
 {
@@ -119,9 +96,8 @@ void ipa_access_handler(struct be_req *be_req)
     hbac_ctx->sdap_ctx = ipa_access_ctx->sdap_ctx;
     hbac_ctx->ipa_options = ipa_access_ctx->ipa_options;
     hbac_ctx->tr_ctx = ipa_access_ctx->tr_ctx;
-    hbac_ctx->hbac_search_base = get_hbac_search_base(hbac_ctx,
-                                                      hbac_ctx->ipa_options);
-    if (hbac_ctx->hbac_search_base == NULL) {
+    hbac_ctx->search_bases = ipa_access_ctx->hbac_search_bases;
+    if (hbac_ctx->search_bases == NULL) {
         DEBUG(1, ("No HBAC search base found.\n"));
         goto fail;
     }
@@ -334,7 +310,7 @@ static void hbac_get_service_info_step(struct tevent_req *req)
                                     hbac_ctx_sysdb(hbac_ctx),
                                     sdap_id_op_handle(hbac_ctx->sdap_op),
                                     hbac_ctx_sdap_id_ctx(hbac_ctx)->opts,
-                                    hbac_ctx->hbac_search_base);
+                                    hbac_ctx->search_bases);
     if (req == NULL) {
         DEBUG(1,("Could not get service info\n"));
         goto fail;
@@ -399,7 +375,7 @@ static void hbac_get_rule_info_step(struct tevent_req *req)
                                   hbac_ctx_ev(hbac_ctx),
                                   sdap_id_op_handle(hbac_ctx->sdap_op),
                                   hbac_ctx_sdap_id_ctx(hbac_ctx)->opts,
-                                  hbac_ctx->hbac_search_base,
+                                  hbac_ctx->search_bases,
                                   hbac_ctx->ipa_host);
     if (req == NULL) {
         DEBUG(1, ("Could not get rules\n"));
diff --git a/src/providers/ipa/ipa_access.h b/src/providers/ipa/ipa_access.h
index e695acd8f0bff230af7c59b489f7fa800137dcc2..655710132fe5e5db5094b8720eb90bedc91535a4 100644
--- a/src/providers/ipa/ipa_access.h
+++ b/src/providers/ipa/ipa_access.h
@@ -46,6 +46,7 @@ struct ipa_access_ctx {
     time_t last_update;
 
     struct sdap_search_base **host_search_bases;
+    struct sdap_search_base **hbac_search_bases;
 };
 
 struct hbac_ctx {
@@ -57,7 +58,7 @@ struct hbac_ctx {
     struct be_req *be_req;
     struct pam_data *pd;
 
-    char *hbac_search_base;
+    struct sdap_search_base **search_bases;
 
     /* Hosts */
     size_t host_count;
diff --git a/src/providers/ipa/ipa_common.c b/src/providers/ipa/ipa_common.c
index 07e87bbbadc911c46c27a952a4b595b87ec1495e..d321e2a4e216ef4c6a48625146af34e02705e9b4 100644
--- a/src/providers/ipa/ipa_common.c
+++ b/src/providers/ipa/ipa_common.c
@@ -558,6 +558,29 @@ int ipa_get_id_options(struct ipa_options *ipa_opts,
                                  &ipa_opts->host_search_bases);
     if (ret != EOK) goto done;
 
+    if (NULL == dp_opt_get_string(ipa_opts->basic,
+                                  IPA_HBAC_SEARCH_BASE)) {
+        value = talloc_asprintf(tmpctx, "cn=hbac,%s", basedn);
+        if (!value) {
+            ret = ENOMEM;
+            goto done;
+        }
+
+        ret = dp_opt_set_string(ipa_opts->basic, IPA_HBAC_SEARCH_BASE, value);
+        if (ret != EOK) {
+            goto done;
+        }
+
+        DEBUG(6, ("Option %s set to %s\n",
+                  ipa_opts->basic[IPA_HBAC_SEARCH_BASE].opt_name,
+                  dp_opt_get_string(ipa_opts->basic,
+                                    IPA_HBAC_SEARCH_BASE)));
+    }
+    ret = sdap_parse_search_base(ipa_opts->basic, ipa_opts->basic,
+                                 IPA_HBAC_SEARCH_BASE,
+                                 &ipa_opts->hbac_search_bases);
+    if (ret != EOK) goto done;
+
     value = dp_opt_get_string(ipa_opts->id->basic, SDAP_DEREF);
     if (value != NULL) {
         ret = deref_string_to_val(value, &i);
diff --git a/src/providers/ipa/ipa_common.h b/src/providers/ipa/ipa_common.h
index 9cbd993f547e76d53e6d46747c7181fe6592061a..49afe74db4b7b61a8662c7113e4540618cc1876b 100644
--- a/src/providers/ipa/ipa_common.h
+++ b/src/providers/ipa/ipa_common.h
@@ -98,6 +98,7 @@ struct ipa_options {
     struct dp_option *basic;
 
     struct sdap_search_base **host_search_bases;
+    struct sdap_search_base **hbac_search_bases;
     struct ipa_service *service;
 
     /* id provider */
diff --git a/src/providers/ipa/ipa_hbac_private.h b/src/providers/ipa/ipa_hbac_private.h
index 6920f9e19ba7db341b8c8fd69f238a5b992e2db2..8d735b8bd06d5f98ea71b25945f9f37ab7dfcac4 100644
--- a/src/providers/ipa/ipa_hbac_private.h
+++ b/src/providers/ipa/ipa_hbac_private.h
@@ -144,7 +144,7 @@ ipa_hbac_service_info_send(TALLOC_CTX *mem_ctx,
                            struct sysdb_ctx *sysdb,
                            struct sdap_handle *sh,
                            struct sdap_options *opts,
-                           const char *search_base);
+                           struct sdap_search_base **search_bases);
 
 errno_t
 ipa_hbac_service_info_recv(struct tevent_req *req,
@@ -166,22 +166,6 @@ get_ipa_servicegroupname(TALLOC_CTX *mem_ctx,
                          const char *service_dn,
                          char **servicename);
 
-/* From ipa_hbac_rules.c */
-struct tevent_req *
-ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx,
-                        bool get_deny_rules,
-                        struct tevent_context *ev,
-                        struct sdap_handle *sh,
-                        struct sdap_options *opts,
-                        const char *search_base,
-                        struct sysdb_attrs *ipa_host);
-
-errno_t
-ipa_hbac_rule_info_recv(struct tevent_req *req,
-                        TALLOC_CTX *mem_ctx,
-                        size_t *rule_count,
-                        struct sysdb_attrs ***rules);
-
 /* From ipa_hbac_users.c */
 errno_t
 hbac_user_attrs_to_rule(TALLOC_CTX *mem_ctx,
diff --git a/src/providers/ipa/ipa_hbac_rules.c b/src/providers/ipa/ipa_hbac_rules.c
index 0b567dab869487dc827784143052ae244ff7e51b..c07cf332b3a509fcf3c142253db269d815268e79 100644
--- a/src/providers/ipa/ipa_hbac_rules.c
+++ b/src/providers/ipa/ipa_hbac_rules.c
@@ -22,15 +22,28 @@
 
 #include "util/util.h"
 #include "providers/ipa/ipa_hbac_private.h"
+#include "providers/ipa/ipa_hbac_rules.h"
 #include "providers/ldap/sdap_async.h"
 
 struct ipa_hbac_rule_state {
+    struct tevent_context *ev;
+    struct sdap_handle *sh;
     struct sdap_options *opts;
 
+    int search_base_iter;
+    struct sdap_search_base **search_bases;
+
+    const char **attrs;
+    char *rules_filter;
+    char *cur_filter;
+
     size_t rule_count;
     struct sysdb_attrs **rules;
 };
 
+static errno_t
+ipa_hbac_rule_info_next(struct tevent_req *req,
+                        struct ipa_hbac_rule_state *state);
 static void
 ipa_hbac_rule_info_done(struct tevent_req *subreq);
 
@@ -40,13 +53,12 @@ ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx,
                         struct tevent_context *ev,
                         struct sdap_handle *sh,
                         struct sdap_options *opts,
-                        const char *search_base,
+                        struct sdap_search_base **search_bases,
                         struct sysdb_attrs *ipa_host)
 {
     errno_t ret;
     size_t i;
     struct tevent_req *req = NULL;
-    struct tevent_req *subreq;
     struct ipa_hbac_rule_state *state;
     TALLOC_CTX *tmp_ctx;
     const char *host_dn;
@@ -54,21 +66,6 @@ ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx,
     char *host_group_clean;
     char *rule_filter;
     const char **memberof_list;
-    const char *rule_attrs[] = { OBJECTCLASS,
-                                 IPA_CN,
-                                 IPA_UNIQUE_ID,
-                                 IPA_ENABLED_FLAG,
-                                 IPA_ACCESS_RULE_TYPE,
-                                 IPA_MEMBER_USER,
-                                 IPA_USER_CATEGORY,
-                                 IPA_MEMBER_SERVICE,
-                                 IPA_SERVICE_CATEGORY,
-                                 IPA_SOURCE_HOST,
-                                 IPA_SOURCE_HOST_CATEGORY,
-                                 IPA_EXTERNAL_HOST,
-                                 IPA_MEMBER_HOST,
-                                 IPA_HOST_CATEGORY,
-                                 NULL };
 
     if (ipa_host == NULL) {
         DEBUG(1, ("Missing host\n"));
@@ -93,7 +90,31 @@ ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx,
         return NULL;
     }
 
+    state->ev = ev;
+    state->sh = sh;
     state->opts = opts;
+    state->search_bases = search_bases;
+    state->search_base_iter = 0;
+    state->attrs = talloc_zero_array(state, const char *, 15);
+    if (state->attrs == NULL) {
+        ret = ENOMEM;
+        goto immediate;
+    }
+    state->attrs[0] = OBJECTCLASS;
+    state->attrs[1] = IPA_CN;
+    state->attrs[2] = IPA_UNIQUE_ID;
+    state->attrs[3] = IPA_ENABLED_FLAG;
+    state->attrs[4] = IPA_ACCESS_RULE_TYPE;
+    state->attrs[5] = IPA_MEMBER_USER;
+    state->attrs[6] = IPA_USER_CATEGORY;
+    state->attrs[7] = IPA_MEMBER_SERVICE;
+    state->attrs[8] = IPA_SERVICE_CATEGORY;
+    state->attrs[9] = IPA_SOURCE_HOST;
+    state->attrs[10] = IPA_SOURCE_HOST_CATEGORY;
+    state->attrs[11] = IPA_EXTERNAL_HOST;
+    state->attrs[12] = IPA_MEMBER_HOST;
+    state->attrs[13] = IPA_HOST_CATEGORY;
+    state->attrs[14] = NULL;
 
     if (get_deny_rules) {
         rule_filter = talloc_asprintf(tmp_ctx,
@@ -154,19 +175,16 @@ ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx,
         ret = ENOMEM;
         goto immediate;
     }
-    talloc_steal(state, rule_filter);
+    state->rules_filter = talloc_steal(state, rule_filter);
 
-    subreq = sdap_get_generic_send(state, ev, opts, sh, search_base,
-                                   LDAP_SCOPE_SUB, rule_filter, rule_attrs,
-                                   NULL, 0,
-                                   dp_opt_get_int(state->opts->basic,
-                                                  SDAP_ENUM_SEARCH_TIMEOUT));
-    if (subreq == NULL) {
-        DEBUG(1, ("sdap_get_generic_send failed.\n"));
-        ret = ENOMEM;
+    ret = ipa_hbac_rule_info_next(req, state);
+    if (ret == EOK) {
+        ret = EINVAL;
+    }
+
+    if (ret != EAGAIN) {
         goto immediate;
     }
-    tevent_req_set_callback(subreq, ipa_hbac_rule_info_done, req);
 
     talloc_free(tmp_ctx);
     return req;
@@ -186,6 +204,45 @@ error:
     return NULL;
 }
 
+static errno_t
+ipa_hbac_rule_info_next(struct tevent_req *req,
+                        struct ipa_hbac_rule_state *state)
+{
+    struct tevent_req *subreq;
+    struct sdap_search_base *base;
+
+    base = state->search_bases[state->search_base_iter];
+    if (base  == NULL) {
+        return EOK;
+    }
+
+    talloc_zfree(state->cur_filter);
+    state->cur_filter = sdap_get_id_specific_filter(state,
+                                                    state->rules_filter,
+                                                    base->filter);
+    if (state->cur_filter == NULL) {
+        return ENOMEM;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, ("Sending request for next search base: "
+                              "[%s][%d][%s]\n", base->basedn, base->scope,
+                              base->filter));
+
+    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(state->opts->basic,
+                                                  SDAP_ENUM_SEARCH_TIMEOUT));
+    if (subreq == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("sdap_get_generic_send failed.\n"));
+        return ENOMEM;
+    }
+    tevent_req_set_callback(subreq, ipa_hbac_rule_info_done, req);
+
+    return EAGAIN;
+}
+
 static void
 ipa_hbac_rule_info_done(struct tevent_req *subreq)
 {
@@ -194,21 +251,59 @@ ipa_hbac_rule_info_done(struct tevent_req *subreq)
             tevent_req_callback_data(subreq, struct tevent_req);
     struct ipa_hbac_rule_state *state =
             tevent_req_data(req, struct ipa_hbac_rule_state);
+    int i;
+    size_t rule_count;
+    size_t total_count;
+    struct sysdb_attrs **rules;
+    struct sysdb_attrs **target;
 
     ret = sdap_get_generic_recv(subreq, state,
-                                &state->rule_count,
-                                &state->rules);
+                                &rule_count,
+                                &rules);
     if (ret != EOK) {
         DEBUG(3, ("Could not retrieve HBAC rules\n"));
-        tevent_req_error(req, ret);
+        goto fail;
+    }
+
+    if (rule_count > 0) {
+        total_count = rule_count + state->rule_count;
+        state->rules = talloc_realloc(state, state->rules,
+                                      struct sysdb_attrs *,
+                                      total_count);
+        if (state->rules == NULL) {
+            ret = ENOMEM;
+            goto fail;
+        }
+
+        i = 0;
+        while (state->rule_count < total_count) {
+            target = &state->rules[state->rule_count];
+            *target = talloc_steal(state->rules, rules[i]);
+
+            state->rule_count++;
+            i++;
+        }
+    }
+
+    state->search_base_iter++;
+    ret = ipa_hbac_rule_info_next(req, state);
+    if (ret == EAGAIN) {
         return;
-    } else if (state->rule_count == 0) {
+    } else if (ret != EOK) {
+        goto fail;
+    } else if (ret == EOK && state->rule_count == 0) {
         DEBUG(3, ("No rules apply to this host\n"));
         tevent_req_error(req, ENOENT);
         return;
     }
 
+    /* We went through all search bases and we have some results */
     tevent_req_done(req);
+
+    return;
+
+fail:
+    tevent_req_error(req, ret);
 }
 
 errno_t
diff --git a/src/providers/ipa/ipa_hbac_rules.h b/src/providers/ipa/ipa_hbac_rules.h
new file mode 100644
index 0000000000000000000000000000000000000000..31ff304804668fd7c3b204eac00370da1248a2ad
--- /dev/null
+++ b/src/providers/ipa/ipa_hbac_rules.h
@@ -0,0 +1,42 @@
+/*
+    SSSD
+
+    Authors:
+        Jan Zeleny <jzeleny@redhat.com>
+
+    Copyright (C) 2012 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/>.
+*/
+
+#ifndef IPA_HBAC_RULES_H_
+#define IPA_HBAC_RULES_H_
+
+/* From ipa_hbac_rules.c */
+struct tevent_req *
+ipa_hbac_rule_info_send(TALLOC_CTX *mem_ctx,
+                        bool get_deny_rules,
+                        struct tevent_context *ev,
+                        struct sdap_handle *sh,
+                        struct sdap_options *opts,
+                        struct sdap_search_base **search_bases,
+                        struct sysdb_attrs *ipa_host);
+
+errno_t
+ipa_hbac_rule_info_recv(struct tevent_req *req,
+                        TALLOC_CTX *mem_ctx,
+                        size_t *rule_count,
+                        struct sysdb_attrs ***rules);
+
+#endif /* IPA_HBAC_RULES_H_ */
diff --git a/src/providers/ipa/ipa_hbac_services.c b/src/providers/ipa/ipa_hbac_services.c
index cc4e6dea5fc78e3c4b3b8689b9984bb833622315..8d4240ce61a77e6565c40c4190be22e9300fe9ff 100644
--- a/src/providers/ipa/ipa_hbac_services.c
+++ b/src/providers/ipa/ipa_hbac_services.c
@@ -29,9 +29,14 @@ struct ipa_hbac_service_state {
     struct sysdb_ctx *sysdb;
     struct sdap_handle *sh;
     struct sdap_options *opts;
-    const char *search_base;
     const char **attrs;
 
+    char *service_filter;
+    char *cur_filter;
+
+    struct sdap_search_base **search_bases;
+    int search_base_iter;
+
     /* Return values */
     size_t service_count;
     struct sysdb_attrs **services;
@@ -40,8 +45,14 @@ struct ipa_hbac_service_state {
     struct sysdb_attrs **servicegroups;
 };
 
+static errno_t
+ipa_hbac_service_info_next(struct tevent_req *req,
+                           struct ipa_hbac_service_state *state);
 static void
 ipa_hbac_service_info_done(struct tevent_req *subreq);
+static errno_t
+ipa_hbac_servicegroup_info_next(struct tevent_req *req,
+                                struct ipa_hbac_service_state *state);
 static void
 ipa_hbac_servicegroup_info_done(struct tevent_req *subreq);
 
@@ -51,12 +62,11 @@ ipa_hbac_service_info_send(TALLOC_CTX *mem_ctx,
                            struct sysdb_ctx *sysdb,
                            struct sdap_handle *sh,
                            struct sdap_options *opts,
-                           const char *search_base)
+                           struct sdap_search_base **search_bases)
 {
     errno_t ret;
     struct ipa_hbac_service_state *state;
     struct tevent_req *req;
-    struct tevent_req *subreq;
     char *service_filter;
 
     req = tevent_req_create(mem_ctx, &state, struct ipa_hbac_service_state);
@@ -69,7 +79,9 @@ ipa_hbac_service_info_send(TALLOC_CTX *mem_ctx,
     state->sysdb = sysdb;
     state->sh = sh;
     state->opts = opts;
-    state->search_base = search_base;
+
+    state->search_bases = search_bases;
+    state->search_base_iter = 0;
 
     service_filter = talloc_asprintf(state, "(objectClass=%s)",
                                      IPA_HBAC_SERVICE);
@@ -78,6 +90,9 @@ ipa_hbac_service_info_send(TALLOC_CTX *mem_ctx,
         goto immediate;
     }
 
+    state->service_filter = service_filter;
+    state->cur_filter = NULL;
+
     state->attrs = talloc_array(state, const char *, 6);
     if (state->attrs == NULL) {
         DEBUG(1, ("Failed to allocate service attribute list.\n"));
@@ -91,17 +106,14 @@ ipa_hbac_service_info_send(TALLOC_CTX *mem_ctx,
     state->attrs[4] = IPA_MEMBEROF;
     state->attrs[5] = NULL;
 
-    subreq = sdap_get_generic_send(state, ev, opts, sh, search_base,
-                                   LDAP_SCOPE_SUB, service_filter,
-                                   state->attrs, NULL, 0,
-                                   dp_opt_get_int(opts->basic,
-                                                  SDAP_ENUM_SEARCH_TIMEOUT));
-    if (subreq == NULL) {
-        DEBUG(1, ("Error requesting service info\n"));
-        ret = EIO;
+    ret = ipa_hbac_service_info_next(req, state);
+    if (ret == EOK) {
+        ret = EINVAL;
+    }
+
+    if (ret != EAGAIN) {
         goto immediate;
     }
-    tevent_req_set_callback(subreq, ipa_hbac_service_info_done, req);
 
     return req;
 
@@ -115,6 +127,43 @@ immediate:
     return req;
 }
 
+static errno_t ipa_hbac_service_info_next(struct tevent_req *req,
+                                          struct ipa_hbac_service_state *state)
+{
+    struct tevent_req *subreq;
+    struct sdap_search_base *base;
+
+    base = state->search_bases[state->search_base_iter];
+    if (base  == NULL) {
+        return EOK;
+    }
+
+    talloc_zfree(state->cur_filter);
+    state->cur_filter = sdap_get_id_specific_filter(state,
+                                                    state->service_filter,
+                                                    base->filter);
+    if (state->cur_filter == NULL) {
+        return ENOMEM;
+    }
+
+    DEBUG(SSSDBG_TRACE_FUNC, ("Sending request for next search base: "
+                              "[%s][%d][%s]\n", base->basedn, base->scope,
+                              base->filter));
+    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(state->opts->basic,
+                                                  SDAP_ENUM_SEARCH_TIMEOUT));
+    if (subreq == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Error requesting service info\n"));
+        return EIO;
+    }
+    tevent_req_set_callback(subreq, ipa_hbac_service_info_done, req);
+
+    return EAGAIN;
+}
+
 static void
 ipa_hbac_service_info_done(struct tevent_req *subreq)
 {
@@ -141,9 +190,14 @@ ipa_hbac_service_info_done(struct tevent_req *subreq)
          * There's no reason to try to process groups
          */
 
+        state->search_base_iter++;
+        ret = ipa_hbac_service_info_next(req, state);
+        if (ret == EAGAIN) {
+            return;
+        }
+
         state->service_count = 0;
         state->services = NULL;
-        ret = EOK;
         goto done;
     }
 
@@ -162,27 +216,65 @@ ipa_hbac_service_info_done(struct tevent_req *subreq)
         goto done;
     }
 
+    talloc_zfree(state->service_filter);
+    state->service_filter = servicegroup_filter;
+
+    state->search_base_iter = 0;
+    ret = ipa_hbac_servicegroup_info_next(req, state);
+    if (ret == EOK) {
+        ret = EINVAL;
+    }
+
+    if (ret != EAGAIN) {
+        goto done;
+    }
+
+    return;
+
+done:
+    if (ret == EOK) {
+        tevent_req_done(req);
+    } else {
+        tevent_req_error(req, ret);
+    }
+}
+
+static errno_t
+ipa_hbac_servicegroup_info_next(struct tevent_req *req,
+                                struct ipa_hbac_service_state *state)
+{
+    struct tevent_req *subreq;
+    struct sdap_search_base *base;
+
+    base = state->search_bases[state->search_base_iter];
+    if (base  == NULL) {
+        return EOK;
+    }
+
+    talloc_zfree(state->cur_filter);
+    state->cur_filter = sdap_get_id_specific_filter(state,
+                                                    state->service_filter,
+                                                    base->filter);
+    if (state->cur_filter == NULL) {
+        return ENOMEM;
+    }
+
     /* Look up service groups */
+    DEBUG(SSSDBG_TRACE_FUNC, ("Sending request for next search base: "
+                              "[%s][%d][%s]\n", base->basedn, base->scope,
+                              base->filter));
     subreq = sdap_get_generic_send(state, state->ev, state->opts, state->sh,
-                                   state->search_base, LDAP_SCOPE_SUB,
-                                   servicegroup_filter, state->attrs, NULL, 0,
+                                   base->basedn, base->scope,
+                                   state->cur_filter, state->attrs, NULL, 0,
                                    dp_opt_get_int(state->opts->basic,
                                                   SDAP_ENUM_SEARCH_TIMEOUT));
     if (subreq == NULL) {
-        DEBUG(1, ("Error requesting host info\n"));
-        ret = EIO;
-        goto done;
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Error requesting servicegroup info\n"));
+        return EIO;
     }
     tevent_req_set_callback(subreq, ipa_hbac_servicegroup_info_done, req);
 
-    return;
-
-done:
-    if (ret == EOK) {
-        tevent_req_done(req);
-    } else {
-        tevent_req_error(req, ret);
-    }
+    return EAGAIN;
 }
 
 static void
@@ -193,28 +285,61 @@ ipa_hbac_servicegroup_info_done(struct tevent_req *subreq)
             tevent_req_callback_data(subreq, struct tevent_req);
     struct ipa_hbac_service_state *state =
             tevent_req_data(req, struct ipa_hbac_service_state);
+    size_t total_count;
+    size_t group_count;
+    struct sysdb_attrs **groups;
+    struct sysdb_attrs **target;
+    int i;
 
     ret = sdap_get_generic_recv(subreq, state,
-                                &state->servicegroup_count,
-                                &state->servicegroups);
+                                &group_count,
+                                &groups);
     talloc_zfree(subreq);
     if (ret != EOK) {
         goto done;
     }
 
-    ret = replace_attribute_name(IPA_MEMBER, SYSDB_ORIG_MEMBER,
-                                 state->servicegroup_count,
-                                 state->servicegroups);
-    if (ret != EOK) {
-        DEBUG(1, ("Could not replace attribute names\n"));
-        goto done;
+    if (group_count > 0) {
+        ret = replace_attribute_name(IPA_MEMBER, SYSDB_ORIG_MEMBER,
+                                     group_count,
+                                     groups);
+        if (ret != EOK) {
+            DEBUG(1, ("Could not replace attribute names\n"));
+            goto done;
+        }
+
+        ret = replace_attribute_name(IPA_MEMBEROF, SYSDB_ORIG_MEMBEROF,
+                                     state->servicegroup_count,
+                                     state->servicegroups);
+        if (ret != EOK) {
+            DEBUG(1, ("Could not replace attribute names\n"));
+            goto done;
+        }
+
+        total_count = state->servicegroup_count + group_count;
+        state->servicegroups = talloc_realloc(state, state->servicegroups,
+                                              struct sysdb_attrs *,
+                                              total_count);
+        if (state->servicegroups == NULL) {
+            ret = ENOMEM;
+            goto done;
+        }
+
+        i = 0;
+        while (state->servicegroup_count < total_count) {
+            target = &state->servicegroups[state->servicegroup_count];
+            *target = talloc_steal(state->servicegroups, groups[i]);
+
+            state->servicegroup_count++;
+            i++;
+        }
     }
 
-    ret = replace_attribute_name(IPA_MEMBEROF, SYSDB_ORIG_MEMBEROF,
-                                 state->servicegroup_count,
-                                 state->servicegroups);
-    if (ret != EOK) {
-        DEBUG(1, ("Could not replace attribute names\n"));
+    state->search_base_iter++;
+    ret = ipa_hbac_servicegroup_info_next(req, state);
+    if (ret == EAGAIN) {
+        return;
+    } else if (ret != EOK) {
         goto done;
     }
 
diff --git a/src/providers/ipa/ipa_init.c b/src/providers/ipa/ipa_init.c
index 78a102ae98b1cc4547552392152daeee3f74bdd1..b3544f749d9b13ff8fef1ca02e11417f22116950 100644
--- a/src/providers/ipa/ipa_init.c
+++ b/src/providers/ipa/ipa_init.c
@@ -375,6 +375,7 @@ int sssm_ipa_access_init(struct be_ctx *bectx,
     }
     ipa_access_ctx->sdap_ctx = id_ctx->sdap_id_ctx;
     ipa_access_ctx->host_search_bases = id_ctx->ipa_options->host_search_bases;
+    ipa_access_ctx->hbac_search_bases = id_ctx->ipa_options->hbac_search_bases;
 
     ret = dp_copy_options(ipa_access_ctx, ipa_options->basic,
                           IPA_OPTS_BASIC, &ipa_access_ctx->ipa_options);
-- 
1.7.6.4

