[SSSD] [PATCH] Use new schema for HBAC service checks

Sumit Bose sbose at redhat.com
Fri May 21 18:46:29 UTC 2010


Hi,

here are two patches for the IPA access provider. The first one checks
if the rule is enable or not the second should fix #475 by using the new
service attribute and object in the IPA schema. Although the code should
handle service groups it currently does not work, because the IPA server
does not set the memberOf attribute to the service objects. If this is
fixed on the server side service groups should work, too.

There are some areas of the code which are begging for refactoring, but
I think this is a post-1.2 task.

bye,
Sumit
-------------- next part --------------
From db96a61891529bd43115bb01be1ad887f232acc2 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Fri, 21 May 2010 15:16:15 +0200
Subject: [PATCH 1/2] Check ipaEnabledFlag

---
 src/providers/ipa/ipa_access.c |   28 +++++++++++++++++++++++-----
 1 files changed, 23 insertions(+), 5 deletions(-)

diff --git a/src/providers/ipa/ipa_access.c b/src/providers/ipa/ipa_access.c
index 2a498b1..b8a928a 100644
--- a/src/providers/ipa/ipa_access.c
+++ b/src/providers/ipa/ipa_access.c
@@ -1502,9 +1502,9 @@ enum check_result check_remote_hosts(const char *rhost,
     return RULE_ERROR;
 }
 
-static errno_t check_if_rule_applies(enum hbac_result *result,
-                                     struct hbac_ctx *hbac_ctx,
-                                     struct sysdb_attrs *rule_attrs) {
+static errno_t check_if_rule_applies(struct hbac_ctx *hbac_ctx,
+                                     struct sysdb_attrs *rule_attrs,
+                                     enum hbac_result *result) {
     int ret;
     struct ldb_message_element *el;
     enum hbac_result rule_type;
@@ -1529,6 +1529,24 @@ static errno_t check_if_rule_applies(enum hbac_result *result,
     }
     DEBUG(9, ("Processsing rule [%s].\n", rule_name));
 
+    ret = sysdb_attrs_get_el(rule_attrs, IPA_ENABLED_FLAG, &el);
+    if (ret != EOK) {
+        DEBUG(1, ("Failed to find out if rule is enabled or not, "
+                  "assuming it is enabled.\n"));
+    } else {
+        if (el->num_values == 0) {
+            DEBUG(1, ("Failed to find out if rule is enabled or not, "
+                      "assuming it is enabled.\n"));
+        } else {
+            if (strncasecmp("false", (const char*) el->values[0].data,
+                            el->values[0].length) == 0) {
+                DEBUG(7, ("Rule is disabled.\n"));
+                *result = HBAC_NOT_APPLICABLE;
+                return EOK;
+            }
+        }
+    }
+
     /* rule type */
     ret = sysdb_attrs_get_el(rule_attrs, IPA_ACCESS_RULE_TYPE, &el);
     if (ret != EOK) {
@@ -1596,8 +1614,8 @@ static int evaluate_ipa_hbac_rules(struct hbac_ctx *hbac_ctx,
 
     for (i = 0; i < hbac_ctx->hbac_rule_count ; i++) {
 
-        ret = check_if_rule_applies(&result, hbac_ctx,
-                                    hbac_ctx->hbac_rule_list[i]);
+        ret = check_if_rule_applies(hbac_ctx, hbac_ctx->hbac_rule_list[i],
+                                    &result);
         if (ret != EOK) {
             DEBUG(1, ("check_if_rule_applies failed.\n"));
             return ret;
-- 
1.6.6.1

-------------- next part --------------
From b88c1a4a6658f1693098b8cdb911f31b3c0f7c7e Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Wed, 19 May 2010 15:23:34 +0200
Subject: [PATCH 2/2] Use new schema for HBAC service checks

---
 src/providers/ipa/ipa_access.c |  830 ++++++++++++++++++++++++++++++++++++++-
 src/providers/ipa/ipa_access.h |    4 +
 2 files changed, 812 insertions(+), 22 deletions(-)

diff --git a/src/providers/ipa/ipa_access.c b/src/providers/ipa/ipa_access.c
index b8a928a..9d639e6 100644
--- a/src/providers/ipa/ipa_access.c
+++ b/src/providers/ipa/ipa_access.c
@@ -31,7 +31,8 @@
 #include "providers/ipa/ipa_access.h"
 #include "providers/ipa/ipa_timerules.h"
 
-#define IPA_HOST_MEMBEROF "memberOf"
+#define OBJECTCLASS "objectclass"
+#define IPA_MEMBEROF "memberOf"
 #define IPA_HOST_SERVERHOSTNAME "serverHostName"
 #define IPA_HOST_FQDN "fqdn"
 #define IPA_ACCESS_RULE_TYPE "accessRuleType"
@@ -47,14 +48,21 @@
 #define IPA_MEMBER_HOST "memberHost"
 #define IPA_HOST_CATEGORY "hostCategory"
 #define IPA_CN "cn"
+#define IPA_MEMBER_SERVICE "memberService"
+#define IPA_SERVICE_CATEGORY "serviceCategory"
+#define IPA_SERVICEGROUP_MEMBER "member"
 
 #define IPA_HOST_BASE_TMPL "cn=computers,cn=accounts,%s"
 #define IPA_HBAC_BASE_TMPL "cn=hbac,%s"
+#define IPA_SERVICES_BASE_TMPL "cn=hbacservices,cn=accounts,%s"
+#define IPA_SERVICEGROUPS_BASE_TMPL "cn=hbacservicegroups,cn=accounts,%s"
 
 #define SYSDB_HBAC_BASE_TMPL "cn=hbac,"SYSDB_TMPL_CUSTOM_BASE
 
 #define HBAC_RULES_SUBDIR "hbac_rules"
 #define HBAC_HOSTS_SUBDIR "hbac_hosts"
+#define HBAC_SERVICES_SUBDIR "hbac_services"
+#define HBAC_SERVICEGROUPS_SUBDIR "hbac_servicegroups"
 
 static errno_t msgs2attrs_array(TALLOC_CTX *mem_ctx, size_t count,
                                 struct ldb_message **msgs,
@@ -98,6 +106,660 @@ static void ipa_access_reply(struct be_req *be_req, int pam_status)
     }
 }
 
+struct hbac_get_service_data_state {
+    struct tevent_context *ev;
+    struct sdap_id_ctx *sdap_ctx;
+    struct sysdb_ctx *sysdb;
+    struct sysdb_handle *handle;
+    const char *basedn;
+    bool offline;
+
+    char *services_filter;
+    char *services_search_base;
+    const char **services_attrs;
+    struct sysdb_attrs **services_reply_list;
+    size_t services_reply_count;
+
+    char *servicegroups_filter;
+    char *servicegroups_search_base;
+    const char **servicegroups_attrs;
+    struct sysdb_attrs **servicegroups_reply_list;
+    size_t servicegroups_reply_count;
+
+    size_t current_item;
+};
+
+static void hbac_get_services_connect_done(struct tevent_req *subreq);
+static void hbac_services_get_done(struct tevent_req *subreq);
+static void hbac_servicegroups_get_done(struct tevent_req *subreq);
+static void hbac_service_data_sysdb_transaction_started(struct tevent_req *subreq);
+static void hbac_services_delete_done(struct tevent_req *subreq);
+static void hbac_servicegroups_delete_done(struct tevent_req *subreq);
+static void hbac_service_store_prepare(struct tevent_req *req);
+static void hbac_service_store_done(struct tevent_req *subreq);
+static void hbac_servicegroup_store_prepare(struct tevent_req *req);
+static void hbac_servicegroup_store_done(struct tevent_req *subreq);
+
+struct tevent_req *hbac_get_service_data_send(TALLOC_CTX *memctx,
+                                              struct tevent_context *ev,
+                                               bool offline,
+                                               struct sdap_id_ctx *sdap_ctx,
+                                               struct sysdb_ctx *sysdb,
+                                               const char *basedn)
+{
+    struct tevent_req *req = NULL;
+    struct tevent_req *subreq = NULL;
+    struct hbac_get_service_data_state *state;
+    int ret;
+
+    req = tevent_req_create(memctx, &state, struct hbac_get_service_data_state);
+    if (req == NULL) {
+        DEBUG(1, ("tevent_req_create failed.\n"));
+        return NULL;
+    }
+
+    state->ev = ev;
+    state->offline = offline;
+    state->sdap_ctx = sdap_ctx;
+    state->sysdb = sysdb;
+    state->handle = NULL;
+    state->basedn = basedn;
+
+    state->services_reply_list = NULL;
+    state->services_reply_count = 0;
+
+    state->servicegroups_filter = NULL;
+    state->servicegroups_search_base = NULL;
+    state->servicegroups_attrs = NULL;
+    state->servicegroups_reply_list = NULL;
+    state->servicegroups_reply_count = 0;
+
+    state->current_item = 0;
+
+    state->services_search_base = talloc_asprintf(state, IPA_SERVICES_BASE_TMPL,
+                                              basedn);
+    if (state->services_search_base == NULL) {
+        DEBUG(1, ("Failed to create service search base.\n"));
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    state->services_attrs = talloc_array(state, const char *, 7);
+    if (state->services_attrs == NULL) {
+        DEBUG(1, ("Failed to allocate service attribute list.\n"));
+        ret = ENOMEM;
+        goto fail;
+    }
+    state->services_attrs[0] = IPA_CN;
+    state->services_attrs[1] = SYSDB_ORIG_DN;
+    state->services_attrs[2] = IPA_UNIQUE_ID;
+    state->services_attrs[3] = IPA_MEMBEROF;
+    state->services_attrs[4] = SYSDB_ORIG_MEMBEROF;
+    state->services_attrs[5] = OBJECTCLASS;
+    state->services_attrs[6] = NULL;
+
+    state->services_filter = talloc_asprintf(state,
+                                            "(objectclass=ipaHBACService)");
+    if (state->services_filter == NULL) {
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    DEBUG(9, ("Services filter: [%s].\n", state->services_filter));
+
+    if (offline) {
+        subreq = sysdb_search_custom_send(state, state->ev, state->sysdb, NULL,
+                                          state->sdap_ctx->be->domain,
+                                          state->services_filter,
+                                          HBAC_SERVICES_SUBDIR,
+                                          state->services_attrs);
+        if (subreq == NULL) {
+            DEBUG(1, ("sysdb_search_custom_send failed.\n"));
+            ret = ENOMEM;
+            goto fail;
+        }
+
+        tevent_req_set_callback(subreq, hbac_services_get_done, req);
+
+        return req;
+    }
+
+    if (sdap_ctx->gsh == NULL || ! sdap_ctx->gsh->connected) {
+        subreq = sdap_cli_connect_send(state, ev, sdap_ctx->opts,
+                                       sdap_ctx->be, sdap_ctx->service, NULL);
+        if (!subreq) {
+            DEBUG(1, ("sdap_cli_connect_send failed.\n"));
+            ret = ENOMEM;
+            goto fail;
+        }
+
+        tevent_req_set_callback(subreq, hbac_get_services_connect_done, req);
+
+        return req;
+    }
+
+    subreq = sdap_get_generic_send(state, state->ev,
+                                   state->sdap_ctx->opts,
+                                   state->sdap_ctx->gsh,
+                                   state->services_search_base,
+                                   LDAP_SCOPE_SUB,
+                                   state->services_filter,
+                                   state->services_attrs,
+                                   NULL, 0);
+
+    if (subreq == NULL) {
+        DEBUG(1, ("sdap_get_generic_send failed.\n"));
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    tevent_req_set_callback(subreq, hbac_services_get_done, req);
+
+    return req;
+
+fail:
+    tevent_req_error(req, ret);
+    tevent_req_post(req, ev);
+    return req;
+}
+
+static void hbac_get_services_connect_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct hbac_get_service_data_state *state = tevent_req_data(req,
+                                            struct hbac_get_service_data_state);
+    int ret;
+
+    ret = sdap_cli_connect_recv(subreq, state->sdap_ctx, &state->sdap_ctx->gsh,
+                                NULL);
+    talloc_zfree(subreq);
+    if (ret) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    subreq = sdap_get_generic_send(state, state->ev,
+                                   state->sdap_ctx->opts,
+                                   state->sdap_ctx->gsh,
+                                   state->services_search_base,
+                                   LDAP_SCOPE_SUB,
+                                   state->services_filter,
+                                   state->services_attrs,
+                                   NULL, 0);
+
+    if (subreq == NULL) {
+        DEBUG(1, ("sdap_get_generic_send failed.\n"));
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    tevent_req_set_callback(subreq, hbac_services_get_done, req);
+    return;
+
+fail:
+    tevent_req_error(req, ret);
+    return;
+}
+
+static void hbac_services_get_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct hbac_get_service_data_state *state = tevent_req_data(req,
+                                            struct hbac_get_service_data_state);
+    int ret;
+    struct ldb_message **msgs;
+
+    if (state->offline) {
+        ret = sysdb_search_custom_recv(subreq, state,
+                                       &state->services_reply_count, &msgs);
+    } else {
+        ret = sdap_get_generic_recv(subreq, state, &state->services_reply_count,
+                                    &state->services_reply_list);
+    }
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    if (state->offline) {
+        ret = msgs2attrs_array(state, state->services_reply_count, msgs,
+                               &state->services_reply_list);
+        talloc_zfree(msgs);
+        if (ret != EOK) {
+            DEBUG(1, ("msgs2attrs_array failed.\n"));
+            goto fail;
+        }
+    }
+
+    state->servicegroups_search_base = talloc_asprintf(state,
+                                                    IPA_SERVICEGROUPS_BASE_TMPL,
+                                                    state->basedn);
+    if (state->servicegroups_search_base == NULL) {
+        DEBUG(1, ("Failed to create service groups search base.\n"));
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    state->servicegroups_attrs = talloc_array(state, const char *, 8);
+    if (state->servicegroups_attrs == NULL) {
+        DEBUG(1, ("Failed to allocate servicegroup attribute list.\n"));
+        ret = ENOMEM;
+        goto fail;
+    }
+    state->servicegroups_attrs[0] = IPA_CN;
+    state->servicegroups_attrs[1] = IPA_SERVICEGROUP_MEMBER;
+    state->servicegroups_attrs[2] = SYSDB_ORIG_DN;
+    state->servicegroups_attrs[3] = IPA_UNIQUE_ID;
+    state->servicegroups_attrs[4] = IPA_MEMBEROF;
+    state->servicegroups_attrs[5] = SYSDB_ORIG_MEMBEROF;
+    state->servicegroups_attrs[6] = OBJECTCLASS;
+    state->servicegroups_attrs[7] = NULL;
+
+    state->servicegroups_filter = talloc_asprintf(state,
+                                           "(objectclass=ipaHBACServiceGroup)");
+    if (state->servicegroups_filter == NULL) {
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    DEBUG(9, ("Services filter: [%s].\n", state->servicegroups_filter));
+
+    if (state->offline) {
+        subreq = sysdb_search_custom_send(state, state->ev, state->sysdb, NULL,
+                                          state->sdap_ctx->be->domain,
+                                          state->servicegroups_filter,
+                                          HBAC_SERVICEGROUPS_SUBDIR,
+                                          state->servicegroups_attrs);
+        if (subreq == NULL) {
+            DEBUG(1, ("sysdb_search_custom_send failed.\n"));
+            ret = ENOMEM;
+            goto fail;
+        }
+
+        tevent_req_set_callback(subreq, hbac_servicegroups_get_done, req);
+
+        return;
+    }
+
+    subreq = sdap_get_generic_send(state, state->ev,
+                                   state->sdap_ctx->opts,
+                                   state->sdap_ctx->gsh,
+                                   state->servicegroups_search_base,
+                                   LDAP_SCOPE_SUB,
+                                   state->servicegroups_filter,
+                                   state->servicegroups_attrs,
+                                   NULL, 0);
+
+    if (subreq == NULL) {
+        DEBUG(1, ("sdap_get_generic_send failed.\n"));
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    tevent_req_set_callback(subreq, hbac_servicegroups_get_done, req);
+    return;
+
+fail:
+    tevent_req_error(req, ret);
+    return;
+}
+
+static void hbac_servicegroups_get_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct hbac_get_service_data_state *state = tevent_req_data(req,
+                                            struct hbac_get_service_data_state);
+    int ret;
+    struct ldb_message **msgs;
+
+    if (state->offline) {
+        ret = sysdb_search_custom_recv(subreq, state,
+                                       &state->servicegroups_reply_count, &msgs);
+    } else {
+        ret = sdap_get_generic_recv(subreq, state,
+                                    &state->servicegroups_reply_count,
+                                    &state->servicegroups_reply_list);
+    }
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    if (state->offline) {
+        ret = msgs2attrs_array(state, state->servicegroups_reply_count, msgs,
+                               &state->servicegroups_reply_list);
+        talloc_zfree(msgs);
+        if (ret != EOK) {
+            DEBUG(1, ("msgs2attrs_array failed.\n"));
+            goto fail;
+        }
+    }
+
+    if ((state->services_reply_count == 0 &&
+         state->servicegroups_reply_count == 0)|| state->offline) {
+        tevent_req_done(req);
+        return;
+    }
+
+    subreq = sysdb_transaction_send(state, state->ev, state->sysdb);
+    if (subreq == NULL) {
+        DEBUG(1, ("sysdb_transaction_send failed.\n"));
+        ret = ENOMEM;
+        goto fail;
+    }
+    tevent_req_set_callback(subreq, hbac_service_data_sysdb_transaction_started,
+                            req);
+    return;
+fail:
+    tevent_req_error(req, ret);
+    return;
+}
+
+static void hbac_service_data_sysdb_transaction_started(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct hbac_get_service_data_state *state = tevent_req_data(req,
+                                            struct hbac_get_service_data_state);
+    int ret;
+    struct ldb_dn *services_base_dn;
+
+    ret = sysdb_transaction_recv(subreq, state, &state->handle);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    services_base_dn = sysdb_custom_subtree_dn(state->sysdb, state,
+                                           state->sdap_ctx->be->domain->name,
+                                           HBAC_SERVICES_SUBDIR);
+    if (services_base_dn == NULL) {
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    DEBUG(9, ("Trying to delete [%s].\n", ldb_dn_get_linearized(services_base_dn)));
+    subreq = sysdb_delete_recursive_send(state, state->ev, state->handle,
+                                         services_base_dn, true);
+    if (subreq == NULL) {
+        DEBUG(1, ("sysdb_delete_recursive_send failed.\n"));
+        ret = ENOMEM;
+        goto fail;
+    }
+    tevent_req_set_callback(subreq, hbac_services_delete_done, req);
+    return;
+
+fail:
+    tevent_req_error(req, ret);
+    return;
+}
+
+static void hbac_services_delete_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct hbac_get_service_data_state *state = tevent_req_data(req,
+                                            struct hbac_get_service_data_state);
+    int ret;
+    struct ldb_dn *servicegroups_base_dn;
+
+    ret = sysdb_delete_recursive_recv(subreq);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    servicegroups_base_dn = sysdb_custom_subtree_dn(state->sysdb, state,
+                                           state->sdap_ctx->be->domain->name,
+                                           HBAC_SERVICEGROUPS_SUBDIR);
+    if (servicegroups_base_dn == NULL) {
+        ret = ENOMEM;
+        goto fail;
+    }
+
+    DEBUG(9, ("Trying to delete [%s].\n", ldb_dn_get_linearized(servicegroups_base_dn)));
+    subreq = sysdb_delete_recursive_send(state, state->ev, state->handle,
+                                         servicegroups_base_dn, true);
+    if (subreq == NULL) {
+        DEBUG(1, ("sysdb_delete_recursive_send failed.\n"));
+        ret = ENOMEM;
+        goto fail;
+    }
+    tevent_req_set_callback(subreq, hbac_servicegroups_delete_done, req);
+    return;
+
+fail:
+    tevent_req_error(req, ret);
+    return;
+}
+
+static void hbac_servicegroups_delete_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct hbac_get_service_data_state *state = tevent_req_data(req,
+                                            struct hbac_get_service_data_state);
+    int ret;
+
+    ret = sysdb_delete_recursive_recv(subreq);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    state->current_item = 0;
+    hbac_service_store_prepare(req);
+}
+
+static void hbac_service_store_prepare(struct tevent_req *req)
+{
+    struct hbac_get_service_data_state *state = tevent_req_data(req,
+                                            struct hbac_get_service_data_state);
+    int ret;
+    struct ldb_message_element *el;
+    struct tevent_req *subreq;
+    char *object_name;
+
+    if (state->current_item < state->services_reply_count) {
+
+/* TODO: I would prefer IPA_UNIQUE_ID but currently it is not available */
+        ret = sysdb_attrs_get_el(state->services_reply_list[state->current_item],
+                                 IPA_CN, &el);
+        if (ret != EOK) {
+            DEBUG(1, ("sysdb_attrs_get_el failed.\n"));
+            goto fail;
+        }
+        if (el->num_values == 0) {
+            ret = EINVAL;
+            goto fail;
+        }
+        object_name = talloc_strndup(state, (const char *)el->values[0].data,
+                                     el->values[0].length);
+        if (object_name == NULL) {
+            ret = ENOMEM;
+            goto fail;
+        }
+        DEBUG(9, ("Object name: [%s].\n", object_name));
+
+        ret = sysdb_attrs_replace_name(
+                                state->services_reply_list[state->current_item],
+                                IPA_MEMBEROF, SYSDB_ORIG_MEMBEROF);
+        if (ret != EOK) {
+            DEBUG(1, ("sysdb_attrs_replace_name failed.\n"));
+            goto fail;
+        }
+
+        subreq = sysdb_store_custom_send(state, state->ev,
+                                         state->handle,
+                                         state->sdap_ctx->be->domain,
+                                         object_name,
+                                         HBAC_SERVICES_SUBDIR,
+                                         state->services_reply_list[state->current_item]);
+
+        if (subreq == NULL) {
+            DEBUG(1, ("sysdb_store_custom_send failed.\n"));
+            ret = ENOMEM;
+            goto fail;
+        }
+
+        tevent_req_set_callback(subreq, hbac_service_store_done, req);
+        return;
+    }
+
+    state->current_item = 0;
+    hbac_servicegroup_store_prepare(req);
+
+    return;
+
+fail:
+    tevent_req_error(req, ret);
+    return;
+}
+
+static void hbac_service_store_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct hbac_get_service_data_state *state = tevent_req_data(req,
+                                            struct hbac_get_service_data_state);
+    int ret;
+
+    ret = sysdb_store_custom_recv(subreq);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    state->current_item++;
+    hbac_service_store_prepare(req);
+}
+
+static void hbac_servicegroup_store_prepare(struct tevent_req *req)
+{
+    struct hbac_get_service_data_state *state = tevent_req_data(req,
+                                            struct hbac_get_service_data_state);
+    int ret;
+    struct ldb_message_element *el;
+    struct tevent_req *subreq;
+    char *object_name;
+
+    if (state->current_item < state->servicegroups_reply_count) {
+
+/* TODO: I would prefer IPA_UNIQUE_ID but currently it is not available */
+        ret = sysdb_attrs_get_el(state->servicegroups_reply_list[state->current_item],
+                                 IPA_CN, &el);
+        if (ret != EOK) {
+            DEBUG(1, ("sysdb_attrs_get_el failed.\n"));
+            goto fail;
+        }
+        if (el->num_values == 0) {
+            ret = EINVAL;
+            goto fail;
+        }
+        object_name = talloc_strndup(state, (const char *)el->values[0].data,
+                                     el->values[0].length);
+        if (object_name == NULL) {
+            ret = ENOMEM;
+            goto fail;
+        }
+        DEBUG(9, ("Object name: [%s].\n", object_name));
+
+        ret = sysdb_attrs_replace_name(
+                           state->servicegroups_reply_list[state->current_item],
+                           IPA_MEMBEROF, SYSDB_ORIG_MEMBEROF);
+        if (ret != EOK) {
+            DEBUG(1, ("sysdb_attrs_replace_name failed.\n"));
+            goto fail;
+        }
+
+        subreq = sysdb_store_custom_send(state, state->ev,
+                                         state->handle,
+                                         state->sdap_ctx->be->domain,
+                                         object_name,
+                                         HBAC_SERVICEGROUPS_SUBDIR,
+                                         state->servicegroups_reply_list[state->current_item]);
+
+        if (subreq == NULL) {
+            DEBUG(1, ("sysdb_store_custom_send failed.\n"));
+            ret = ENOMEM;
+            goto fail;
+        }
+
+        tevent_req_set_callback(subreq, hbac_servicegroup_store_done, req);
+        return;
+    }
+
+    subreq = sysdb_transaction_commit_send(state, state->ev, state->handle);
+    if (subreq == NULL) {
+        DEBUG(1, ("sysdb_transaction_commit_send failed.\n"));
+        ret = ENOMEM;
+        goto fail;
+    }
+    tevent_req_set_callback(subreq, sysdb_transaction_complete, req);
+
+    return;
+
+fail:
+    tevent_req_error(req, ret);
+    return;
+}
+
+static void hbac_servicegroup_store_done(struct tevent_req *subreq)
+{
+    struct tevent_req *req = tevent_req_callback_data(subreq,
+                                                      struct tevent_req);
+    struct hbac_get_service_data_state *state = tevent_req_data(req,
+                                            struct hbac_get_service_data_state);
+    int ret;
+
+    ret = sysdb_store_custom_recv(subreq);
+    talloc_zfree(subreq);
+    if (ret != EOK) {
+        tevent_req_error(req, ret);
+        return;
+    }
+
+    state->current_item++;
+    hbac_servicegroup_store_prepare(req);
+}
+
+static int hbac_get_service_data_recv(struct tevent_req *req,
+                                  TALLOC_CTX *memctx,
+                                  size_t *hbac_services_count,
+                                  struct sysdb_attrs ***hbac_services_list,
+                                  size_t *hbac_servicegroups_count,
+                                  struct sysdb_attrs ***hbac_servicegroups_list)
+{
+    struct hbac_get_service_data_state *state = tevent_req_data(req,
+                                            struct hbac_get_service_data_state);
+    int i;
+
+    TEVENT_REQ_RETURN_ON_ERROR(req);
+
+    *hbac_services_count = state->services_reply_count;
+    *hbac_services_list = talloc_steal(memctx, state->services_reply_list);
+    for (i = 0; i < state->services_reply_count; i++) {
+        talloc_steal(memctx, state->services_reply_list[i]);
+    }
+
+    *hbac_servicegroups_count = state->servicegroups_reply_count;
+    *hbac_servicegroups_list = talloc_steal(memctx,
+                                               state->servicegroups_reply_list);
+    for (i = 0; i < state->servicegroups_reply_count; i++) {
+        talloc_steal(memctx, state->servicegroups_reply_list[i]);
+    }
+    return EOK;
+}
+
 struct hbac_get_user_info_state {
     struct tevent_context *ev;
     struct be_ctx *be_ctx;;
@@ -407,7 +1069,7 @@ static struct tevent_req *hbac_get_host_info_send(TALLOC_CTX *memctx,
         ret = ENOMEM;
         goto fail;
     }
-    state->host_attrs[0] = IPA_HOST_MEMBEROF;
+    state->host_attrs[0] = IPA_MEMBEROF;
     state->host_attrs[1] = IPA_HOST_SERVERHOSTNAME;
     state->host_attrs[2] = IPA_HOST_FQDN;
     state->host_attrs[3] = "objectClass";
@@ -626,7 +1288,7 @@ static void hbac_get_host_memberof_done(struct tevent_req *subreq)
 
         ret = sysdb_attrs_get_el(state->host_reply_list[i],
                                  state->offline ? SYSDB_ORIG_MEMBEROF :
-                                                  IPA_HOST_MEMBEROF,
+                                                  IPA_MEMBEROF,
                                  &el);
         if (ret != EOK) {
             DEBUG(1, ("sysdb_attrs_get_el failed.\n"));
@@ -642,7 +1304,7 @@ static void hbac_get_host_memberof_done(struct tevent_req *subreq)
                sizeof(const char *) * (el->num_values + 1));
 
         for(v = 0; v < el->num_values; v++) {
-            DEBUG(9, ("%s: [%.*s].\n", IPA_HOST_MEMBEROF, el->values[v].length,
+            DEBUG(9, ("%s: [%.*s].\n", IPA_MEMBEROF, el->values[v].length,
                                      (const char *)el->values[v].data));
             hhi[i]->memberof[v] = talloc_strndup(hhi,
                                                (const char *)el->values[v].data,
@@ -727,7 +1389,7 @@ static void hbac_get_host_info_store_prepare(struct tevent_req *req)
 
         ret = sysdb_attrs_replace_name(
                                     state->host_reply_list[state->current_item],
-                                    IPA_HOST_MEMBEROF, SYSDB_ORIG_MEMBEROF);
+                                    IPA_MEMBEROF, SYSDB_ORIG_MEMBEROF);
         if (ret != EOK) {
             DEBUG(1, ("sysdb_attrs_replace_name failed.\n"));
             goto fail;
@@ -870,7 +1532,7 @@ static struct tevent_req *hbac_get_rules_send(TALLOC_CTX *memctx,
         goto fail;
     }
 
-    state->hbac_attrs = talloc_array(state, const char *, 16);
+    state->hbac_attrs = talloc_array(state, const char *, 18);
     if (state->hbac_attrs == NULL) {
         DEBUG(1, ("Failed to allocate HBAC attribute list.\n"));
         ret = ENOMEM;
@@ -887,11 +1549,13 @@ static struct tevent_req *hbac_get_rules_send(TALLOC_CTX *memctx,
     state->hbac_attrs[8] = IPA_UNIQUE_ID;
     state->hbac_attrs[9] = IPA_ENABLED_FLAG;
     state->hbac_attrs[10] = IPA_CN;
-    state->hbac_attrs[11] = "objectclass";
+    state->hbac_attrs[11] = OBJECTCLASS;
     state->hbac_attrs[12] = IPA_MEMBER_HOST;
     state->hbac_attrs[13] = IPA_HOST_CATEGORY;
-    state->hbac_attrs[14] = SYSDB_ORIG_DN;
-    state->hbac_attrs[15] = NULL;
+    state->hbac_attrs[14] = IPA_MEMBER_SERVICE;
+    state->hbac_attrs[15] = IPA_SERVICE_CATEGORY;
+    state->hbac_attrs[16] = SYSDB_ORIG_DN;
+    state->hbac_attrs[17] = NULL;
 
     state->hbac_filter = talloc_asprintf(state,
                                          "(&(objectclass=ipaHBACRule)"
@@ -1248,41 +1912,129 @@ enum check_result {
     RULE_ERROR
 };
 
-enum check_result check_service(struct pam_data *pd,
+static errno_t get_service_data(const char *cn, size_t count,
+                                struct sysdb_attrs **list, const char **dn,
+                                struct ldb_message_element **mof)
+{
+    int ret;
+    int i;
+    int j;
+    struct ldb_message_element *el;
+
+    for (i = 0; i < count; i++) {
+        ret = sysdb_attrs_get_el(list[i], IPA_CN, &el);
+        if (ret != EOK) {
+            DEBUG(1, ("sysdb_attrs_get_el failed.\n"));
+            return ENOENT;
+        }
+        if (el->num_values == 0) {
+            DEBUG(9, ("No cn found.\n"));
+            return ENOENT;
+        } else {
+            for (j = 0; j < el->num_values; j++) {
+                if (strncmp(cn, (const char *) el->values[j].data,
+                            el->values[j].length) == 0) {
+
+                    ret = sysdb_attrs_get_string(list[i], SYSDB_ORIG_DN, dn);
+                    if (ret != EOK) {
+                        DEBUG(1, ("sysdb_attrs_get_string failed.\n"));
+                        return ret;
+                    }
+
+                    ret = sysdb_attrs_get_el(list[i], SYSDB_ORIG_MEMBEROF, mof);
+                    if (ret != EOK) {
+                        DEBUG(1, ("sysdb_attrs_get_el failed.\n"));
+                        return ret;
+                    }
+
+                    return EOK;
+                }
+            }
+        }
+    }
+
+    return ENOENT;
+}
+
+enum check_result check_service(struct hbac_ctx *hbac_ctx,
                                 struct sysdb_attrs *rule_attrs)
 {
     int ret;
     int i;
+    int g;
     struct ldb_message_element *el;
+    const char *service_dn;
+    struct ldb_message_element *service_memberof;
 
-    if (pd->service == NULL) {
+    if (hbac_ctx->pd->service == NULL) {
         DEBUG(1, ("No service in pam data, assuming error.\n"));
         return RULE_ERROR;
     }
 
-    ret = sysdb_attrs_get_el(rule_attrs, IPA_SERVICE_NAME, &el);
+    ret = sysdb_attrs_get_el(rule_attrs, IPA_SERVICE_CATEGORY, &el);
     if (ret != EOK) {
         DEBUG(1, ("sysdb_attrs_get_el failed.\n"));
         return RULE_ERROR;
     }
     if (el->num_values == 0) {
-        DEBUG(9, ("No services in rule specified, assuming rule applies.\n"));
-        return RULE_APPLICABLE;
+        DEBUG(9, ("Service category is not set.\n"));
     } else {
         for (i = 0; i < el->num_values; i++) {
-            if (strlen(pd->service) == el->values[i].length &&
-                strncasecmp(pd->service, (const char *) el->values[i].data,
+            if (strncasecmp("all", (const char *) el->values[i].data,
                             el->values[i].length) == 0) {
-                DEBUG(9, ("Service [%s] found, rule applies.\n",
-                          pd->service));
+                DEBUG(9, ("Service category is set to 'all', rule applies.\n"));
                 return RULE_APPLICABLE;
             }
+            DEBUG(9, ("Unsupported service category [%.*s].\n",
+                      el->values[i].length,
+                      (char *) el->values[i].data));
         }
-        DEBUG(9, ("No matching service found, rule does not apply.\n"));
+    }
+
+    ret = get_service_data(hbac_ctx->pd->service, hbac_ctx->hbac_services_count,
+                           hbac_ctx->hbac_services_list, &service_dn,
+                           &service_memberof);
+    if (ret != EOK) {
+        DEBUG(1, ("Cannot find original DN for service [%s].\n",
+                  hbac_ctx->pd->service));
+        return RULE_ERROR;
+    }
+    DEBUG(9, ("OriginalDN for service [%s]: [%s].\n", hbac_ctx->pd->service,
+              service_dn));
+
+    ret = sysdb_attrs_get_el(rule_attrs, IPA_MEMBER_SERVICE, &el);
+    if (ret != EOK) {
+        DEBUG(1, ("sysdb_attrs_get_el failed.\n"));
+        return RULE_ERROR;
+    }
+    if (el->num_values == 0) {
+        DEBUG(9, ("No service or service group specified, rule does not apply.\n"));
         return RULE_NOT_APPLICABLE;
     }
 
-    return RULE_ERROR;
+    for (i = 0; i < el->num_values; i++) {
+        if (strncmp(service_dn, (const char *) el->values[i].data,
+                    el->values[i].length) == 0) {
+            DEBUG(9, ("Service [%s] found in the list of allowed "
+                      "services.\n", hbac_ctx->pd->service));
+            return RULE_APPLICABLE;
+        }
+
+        for (g = 0; g < service_memberof->num_values; g++) {
+            if (service_memberof->values[g].length == el->values[i].length &&
+                strncmp((const char *) service_memberof->values[g].data,
+                        (const char *) el->values[i].data,
+                        el->values[i].length) == 0) {
+                DEBUG(9, ("Service [%s] is a member of a group in the list of "
+                          "allowed service groups.\n", hbac_ctx->pd->service));
+                return RULE_APPLICABLE;
+            }
+        }
+    }
+
+    DEBUG(9, ("Service [%s] was not found in the list of allowed services and "
+              "service groups.\n", hbac_ctx->pd->service));
+    return RULE_NOT_APPLICABLE;
 }
 
 enum check_result check_access_time(struct time_rules_ctx *tr_ctx,
@@ -1569,7 +2321,7 @@ static errno_t check_if_rule_applies(struct hbac_ctx *hbac_ctx,
         return EINVAL;
     }
 
-    ret = check_service(pd, rule_attrs);
+    ret = check_service(hbac_ctx, rule_attrs);
     if (ret != RULE_APPLICABLE) {
         goto not_applicable;
     }
@@ -1643,6 +2395,7 @@ static int evaluate_ipa_hbac_rules(struct hbac_ctx *hbac_ctx,
 
 static void hbac_get_host_info_done(struct tevent_req *req);
 static void hbac_get_rules_done(struct tevent_req *req);
+static void hbac_get_service_data_done(struct tevent_req *req);
 static void hbac_get_user_info_done(struct tevent_req *req);
 
 void ipa_access_handler(struct be_req *be_req)
@@ -1778,7 +2531,6 @@ fail:
 static void hbac_get_rules_done(struct tevent_req *req)
 {
     struct hbac_ctx *hbac_ctx = tevent_req_callback_data(req, struct hbac_ctx);
-    struct pam_data *pd = hbac_ctx->pd;
     struct be_req *be_req = hbac_ctx->be_req;
     int ret;
     int pam_status = PAM_SYSTEM_ERR;
@@ -1790,6 +2542,40 @@ static void hbac_get_rules_done(struct tevent_req *req)
         goto fail;
     }
 
+    req = hbac_get_service_data_send(hbac_ctx, be_req->be_ctx->ev,
+                                     hbac_ctx->offline, hbac_ctx->sdap_ctx,
+                                     be_req->be_ctx->sysdb,
+                                     hbac_ctx->ldap_basedn);
+    if (req == NULL) {
+        DEBUG(1, ("hbac_get_service_data_send failed.\n"));
+        goto fail;
+    }
+
+    tevent_req_set_callback(req, hbac_get_service_data_done, hbac_ctx);
+    return;
+
+fail:
+    ipa_access_reply(be_req, pam_status);
+}
+
+static void hbac_get_service_data_done(struct tevent_req *req)
+{
+    struct hbac_ctx *hbac_ctx = tevent_req_callback_data(req, struct hbac_ctx);
+    struct pam_data *pd = hbac_ctx->pd;
+    struct be_req *be_req = hbac_ctx->be_req;
+    int ret;
+    int pam_status = PAM_SYSTEM_ERR;
+
+    ret = hbac_get_service_data_recv(req, hbac_ctx,
+                                     &hbac_ctx->hbac_services_count,
+                                     &hbac_ctx->hbac_services_list,
+                                     &hbac_ctx->hbac_servicegroups_count,
+                                     &hbac_ctx->hbac_servicegroups_list);
+    talloc_zfree(req);
+    if (ret != EOK) {
+        goto fail;
+    }
+
     req = hbac_get_user_info_send(hbac_ctx, be_req->be_ctx->ev, be_req->be_ctx,
                                   pd->user);
     if (req == NULL) {
diff --git a/src/providers/ipa/ipa_access.h b/src/providers/ipa/ipa_access.h
index e3ba5c3..e3e0c4f 100644
--- a/src/providers/ipa/ipa_access.h
+++ b/src/providers/ipa/ipa_access.h
@@ -60,6 +60,10 @@ struct hbac_ctx {
     const char **groups;
     bool offline;
     char *ldap_basedn;
+    struct sysdb_attrs **hbac_services_list;
+    size_t hbac_services_count;
+    struct sysdb_attrs **hbac_servicegroups_list;
+    size_t hbac_servicegroups_count;
 };
 
 void ipa_access_handler(struct be_req *be_req);
-- 
1.6.6.1



More information about the sssd-devel mailing list