[SSSD] [PATCH] Process SELinux mapping while offline

Jakub Hrozek jhrozek at redhat.com
Sun Jan 6 23:29:14 UTC 2013


This patchset fixes the SELinux processing so that it works also offline
for cases described in #1626 for example.

The code was architected in an extremely strange way where every request
would store a per-request/per-user score attribute in the selinux mapping
objects themselves and the responder would just pick the highest score.

At the same time, the scores were only calculated when the mappings were
downloaded and only for rules that matched, which pretty much broke offline
support. I intend to fix the architecture by only making the provider only
download and store the rules and let the responder pick the context. But I'm
not sure if I can do that right during the current time constraints. I filed
#1743 to track that effort and I'll be sending some patches for master so
far, but I'd like to include this patchset to fix the functionality at last.

I also noticed that the rules are downloaded on *every login*, without
any timeout in between, so I filed #1744 to either reuse
IPA_HBAC_REFRESH or introduce a similar timeout for performance reasons.

[PATCH 1/4] SYSDB: Remove duplicate selinux defines
Some constants were defined in both sysdb.h and sysdb_selinux.h. This
patch removes the duplicates.

[PATCH 2/4] SYSDB: Split a function to read all SELinux maps
This function will be reused in the DP to read maps from cache when
offline.

[PATCH 3/4] SELINUX: Process maps even when offline
The patch is quite big, but I couldn't split it in a meaningful way,
sorry. The patch changes the ipa_get_selinux{send,recv} to only provide
data independently on the offline stat and moves all the processing to
the ipa_selinux_handler. The scores are calculated

[PATCH 4/4] IPA: Rename IPA_CONFIG_SELINUX_DEFAULT_MAP
The option IPA_CONFIG_SELINUX_DEFAULT_MAP was misnamed. It doesn't
describe any map, but the default context the user should obtain when
nothing else matches.
-------------- next part --------------
>From 339318193c58ccc83d0402aeb64c1da0289f94b4 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Sun, 6 Jan 2013 11:34:46 -0500
Subject: [PATCH 1/4] SYSDB: Remove duplicate selinux defines

---
 src/db/sysdb.h         | 5 -----
 src/db/sysdb_selinux.h | 1 +
 2 files changed, 1 insertion(+), 5 deletions(-)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index ed00c165952aa80102b88c9e857355f2ef27d570..abe0ac33bae1d03382728a74c2c5e0d0f58b99fa 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -101,11 +101,6 @@
 #define SYSDB_FQDN "fqdn"
 #define SYSDB_SERVERHOSTNAME "serverHostname"
 
-#define SYSDB_SELINUX_SEEALSO "seeAlso"
-#define SYSDB_SELINUX_USER "selinuxUser"
-#define SYSDB_SELINUX_ENABLED "enabled"
-#define SYSDB_SELINUX_HOST_PRIORITY "hostPriority"
-
 #define SYSDB_CACHEDPWD "cachedPassword"
 
 #define SYSDB_UUID "uniqueID"
diff --git a/src/db/sysdb_selinux.h b/src/db/sysdb_selinux.h
index 39bb67a1921d8e22d49802b6dd09e51b102f18a5..c7dbbe98c2e76a072a79443ad68322f437e6597a 100644
--- a/src/db/sysdb_selinux.h
+++ b/src/db/sysdb_selinux.h
@@ -34,6 +34,7 @@
 #define SYSDB_SELINUX_ENABLED "enabled"
 #define SYSDB_SELINUX_DEFAULT_USER "user"
 #define SYSDB_SELINUX_DEFAULT_ORDER "order"
+#define SYSDB_SELINUX_HOST_PRIORITY "hostPriority"
 
 enum selinux_entity_type {
     SELINUX_CONFIG,
-- 
1.8.0.2

-------------- next part --------------
>From ad0110d674358c0d26d0c13b1ce976f46d454fda Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Sun, 6 Jan 2013 23:44:30 +0100
Subject: [PATCH 2/4] SYSDB: Split a function to read all SELinux maps

---
 src/db/sysdb_selinux.c | 66 ++++++++++++++++++++++++++++++++------------------
 src/db/sysdb_selinux.h |  6 +++++
 2 files changed, 49 insertions(+), 23 deletions(-)

diff --git a/src/db/sysdb_selinux.c b/src/db/sysdb_selinux.c
index 857b17d956babc07670901d06f7219ce8a6f72a5..a6b4809f67f43cbb8d71ad66a39a8b6776a2e0d2 100644
--- a/src/db/sysdb_selinux.c
+++ b/src/db/sysdb_selinux.c
@@ -292,12 +292,53 @@ done:
     return ret;
 }
 
+errno_t
+sysdb_get_selinux_usermaps(TALLOC_CTX *mem_ctx,
+                           struct sysdb_ctx *sysdb,
+                           const char **attrs,
+                           size_t *count,
+                           struct ldb_message ***messages)
+{
+    errno_t ret;
+    char *filter;
+    struct ldb_dn *basedn;
+    struct sss_domain_info *domain;
+
+    domain = sysdb_ctx_get_domain(sysdb);
+    basedn = ldb_dn_new_fmt(mem_ctx, sysdb_ctx_get_ldb(sysdb),
+                            SYSDB_TMPL_SELINUX_BASE, domain->name);
+    if (!basedn) {
+        return ENOMEM;
+    }
+
+    filter = talloc_asprintf(mem_ctx, "(%s=%s)",
+                             SYSDB_OBJECTCLASS, SYSDB_SELINUX_USERMAP_CLASS);
+    if (filter == NULL) {
+        talloc_free(basedn);
+        return ENOMEM;
+    }
+
+    ret = sysdb_search_entry(mem_ctx, sysdb, basedn, LDB_SCOPE_SUBTREE, filter,
+                             attrs, count, messages);
+    talloc_free(basedn);
+    talloc_free(filter);
+    if (ret == ENOENT) {
+        *count = 0;
+        *messages = NULL;
+    } else if (ret) {
+        return ret;
+    }
+
+    return EOK;
+}
+
 errno_t sysdb_search_selinux_usermap_by_username(TALLOC_CTX *mem_ctx,
                                                  struct sysdb_ctx *sysdb,
                                                  const char *username,
                                                  struct ldb_message ***_usermaps)
 {
     TALLOC_CTX *tmp_ctx;
+    struct ldb_message **msgs = NULL;
     const char *attrs[] = { SYSDB_NAME,
                             SYSDB_USER_CATEGORY,
                             SYSDB_HOST_CATEGORY,
@@ -306,18 +347,14 @@ errno_t sysdb_search_selinux_usermap_by_username(TALLOC_CTX *mem_ctx,
                             SYSDB_SELINUX_HOST_PRIORITY,
                             SYSDB_SELINUX_USER,
                             NULL };
-    struct ldb_message **msgs = NULL;
     struct sysdb_attrs *user;
     struct sysdb_attrs *tmp_attrs;
     struct ldb_message **usermaps = NULL;
-    struct sss_domain_info *domain;
-    struct ldb_dn *basedn;
     size_t msgs_count = 0;
     size_t usermaps_cnt;
     uint32_t priority = 0;
     uint32_t host_priority = 0;
     uint32_t top_priority = 0;
-    char *filter;
     errno_t ret;
     int i;
 
@@ -333,25 +370,8 @@ errno_t sysdb_search_selinux_usermap_by_username(TALLOC_CTX *mem_ctx,
     }
 
     /* Now extract all SELinux user maps */
-    domain = sysdb_ctx_get_domain(sysdb);
-    basedn = ldb_dn_new_fmt(tmp_ctx, sysdb_ctx_get_ldb(sysdb),
-                            SYSDB_TMPL_SELINUX_BASE, domain->name);
-    if (!basedn) {
-        ret = ENOMEM;
-        goto done;
-    }
-
-    filter = talloc_asprintf(tmp_ctx, "(objectclass=%s)", SYSDB_SELINUX_USERMAP_CLASS);
-    if (filter == NULL) {
-        ret = ENOMEM;
-        goto done;
-    }
-
-    ret = sysdb_search_entry(tmp_ctx, sysdb, basedn, LDB_SCOPE_SUBTREE, filter,
-                             attrs, &msgs_count, &msgs);
-    if (ret == ENOENT) {
-        msgs_count = 0;
-    } else if (ret) {
+    ret = sysdb_get_selinux_usermaps(tmp_ctx, sysdb, attrs, &msgs_count, &msgs);
+    if (ret) {
         goto done;
     }
 
diff --git a/src/db/sysdb_selinux.h b/src/db/sysdb_selinux.h
index c7dbbe98c2e76a072a79443ad68322f437e6597a..5fcbb56ef20f1ee11b979fc6e0b4ea87adfa939e 100644
--- a/src/db/sysdb_selinux.h
+++ b/src/db/sysdb_selinux.h
@@ -48,6 +48,12 @@ errno_t sysdb_store_selinux_config(struct sysdb_ctx *sysdb,
                                    const char *default_map,
                                    const char *order);
 
+errno_t sysdb_get_selinux_usermaps(TALLOC_CTX *mem_ctx,
+                                   struct sysdb_ctx *sysdb,
+                                   const char **attrs,
+                                   size_t *count,
+                                   struct ldb_message ***messages);
+
 errno_t sysdb_search_selinux_usermap_by_mapname(TALLOC_CTX *mem_ctx,
                                                 struct sysdb_ctx *sysdb,
                                                 const char *name,
-- 
1.8.0.2

-------------- next part --------------
>From 99a38176212580a938a92a8fee9d46444f99e660 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Sun, 6 Jan 2013 23:52:19 +0100
Subject: [PATCH 3/4] SELINUX: Process maps even when offline

Changes the ipa_get_selinux{send,recv} request so that it only delivers
data and moves processing to the IPA selinux handler.
---
 src/providers/ipa/ipa_selinux.c | 647 ++++++++++++++++++++++++++--------------
 1 file changed, 421 insertions(+), 226 deletions(-)

diff --git a/src/providers/ipa/ipa_selinux.c b/src/providers/ipa/ipa_selinux.c
index b64de90b1236b7828753d79b8402a4fab0dfeeae..c9c803f91b5e2371a2778994378e0b8b5688023d 100644
--- a/src/providers/ipa/ipa_selinux.c
+++ b/src/providers/ipa/ipa_selinux.c
@@ -37,52 +37,52 @@
 #include "providers/ipa/ipa_selinux_common.h"
 #include "providers/ipa/ipa_selinux_maps.h"
 
-struct ipa_get_selinux_state {
-    struct be_req *be_req;
-    struct pam_data *pd;
-    struct ipa_selinux_ctx *selinux_ctx;
-    struct sdap_id_op *op;
-
-    const char *hostname;
-    struct sysdb_attrs *host;
-    struct sysdb_attrs *user;
-
-    struct sysdb_attrs *defaults;
-    struct sysdb_attrs **selinuxmaps;
-    size_t nmaps;
-
-    struct sysdb_attrs **possible_match;
-    size_t possible_matches;
-};
-
-static struct
-tevent_req *ipa_get_selinux_send(struct be_req *breq,
-                                 struct pam_data *pd,
-                                 struct ipa_selinux_ctx *selinux_ctx);
-static void ipa_selinux_handler_done(struct tevent_req *subreq);
+static struct tevent_req *
+ipa_get_selinux_send(struct be_req *breq,
+                     struct sysdb_attrs *user,
+                     struct sysdb_attrs *host,
+                     struct ipa_selinux_ctx *selinux_ctx);
 static errno_t ipa_get_selinux_recv(struct tevent_req *req,
                                     TALLOC_CTX *mem_ctx,
                                     size_t *count,
                                     struct sysdb_attrs ***maps,
+                                    size_t *hbac_count,
+                                    struct sysdb_attrs ***hbac_rules,
                                     char **default_user,
                                     char **map_order);
 
+static struct ipa_selinux_op_ctx *
+ipa_selinux_create_op_ctx(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
+                          const char *username, const char *hostname);
+static void ipa_selinux_handler_done(struct tevent_req *subreq);
+
 static void ipa_get_selinux_connect_done(struct tevent_req *subreq);
 static void ipa_get_selinux_hosts_done(struct tevent_req *subreq);
 static void ipa_get_config_step(struct tevent_req *req);
 static void ipa_get_selinux_config_done(struct tevent_req *subreq);
 static void ipa_get_selinux_maps_done(struct tevent_req *subreq);
 static void ipa_get_selinux_hbac_done(struct tevent_req *subreq);
-static int
-ipa_get_selinux_hbac_process(struct ipa_get_selinux_state *state,
-                             struct sysdb_attrs **rules,
-                             size_t rule_count);
+static errno_t ipa_selinux_process_maps(struct sysdb_attrs *user,
+                                        struct sysdb_attrs *host,
+                                        struct sysdb_attrs **selinux_maps,
+                                        size_t selinux_map_count,
+                                        struct sysdb_attrs **hbac_rules,
+                                        size_t hbac_rule_count);
+
+struct ipa_selinux_op_ctx {
+    struct be_req *be_req;
+
+    struct sysdb_attrs *user;
+    struct sysdb_attrs *host;
+};
 
 void ipa_selinux_handler(struct be_req *be_req)
 {
     struct ipa_selinux_ctx *selinux_ctx;
+    struct ipa_selinux_op_ctx *op_ctx;
     struct tevent_req *req;
     struct pam_data *pd;
+    const char *hostname;
 
     pd = talloc_get_type(be_req->req_data, struct pam_data);
 
@@ -90,23 +90,93 @@ void ipa_selinux_handler(struct be_req *be_req)
                              be_req->be_ctx->bet_info[BET_SELINUX].pvt_bet_data,
                              struct ipa_selinux_ctx);
 
+    hostname = dp_opt_get_string(selinux_ctx->id_ctx->ipa_options->basic,
+                                 IPA_HOSTNAME);
+    if (!hostname) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Cannot determine this machine's host name\n"));
+        goto fail;
+    }
 
-    req = ipa_get_selinux_send(be_req, pd, selinux_ctx);
+    op_ctx = ipa_selinux_create_op_ctx(be_req, be_req->sysdb, pd->user, hostname);
+    if (op_ctx == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Cannot create op context\n"));
+        goto fail;
+    }
+    op_ctx->be_req = be_req;
+
+    req = ipa_get_selinux_send(be_req, op_ctx->user, op_ctx->host, selinux_ctx);
     if (req == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Cannot initiate the search\n"));
         goto fail;
     }
 
-    tevent_req_set_callback(req, ipa_selinux_handler_done, be_req);
-
+    tevent_req_set_callback(req, ipa_selinux_handler_done, op_ctx);
     return;
 
 fail:
     be_req->fn(be_req, DP_ERR_FATAL, PAM_SYSTEM_ERR, NULL);
 }
 
+static struct ipa_selinux_op_ctx *
+ipa_selinux_create_op_ctx(TALLOC_CTX *mem_ctx, struct sysdb_ctx *sysdb,
+                          const char *username, const char *hostname)
+{
+    struct ipa_selinux_op_ctx *op_ctx;
+    struct ldb_dn *host_dn;
+    const char *attrs[] = { SYSDB_ORIG_DN,
+                            SYSDB_ORIG_MEMBEROF,
+                            NULL };
+    size_t count;
+    struct ldb_message **msgs;
+    struct sysdb_attrs **hosts;
+    errno_t ret;
+
+    op_ctx = talloc_zero(mem_ctx, struct ipa_selinux_op_ctx);
+    if (op_ctx == NULL) {
+        return NULL;
+    }
+
+    ret = sss_selinux_extract_user(op_ctx, sysdb, username, &op_ctx->user);
+    if (ret != EOK) {
+        goto fail;
+    }
+
+    host_dn = sysdb_custom_dn(sysdb, op_ctx, hostname, HBAC_HOSTS_SUBDIR);
+    if (host_dn == NULL) {
+        goto fail;
+    }
+
+    /* Look up the host to get its originalMemberOf entries */
+    ret = sysdb_search_entry(op_ctx, sysdb, host_dn,
+                             LDB_SCOPE_BASE, NULL,
+                             attrs, &count, &msgs);
+    if (ret == ENOENT || count == 0) {
+        op_ctx->host = NULL;
+    } else if (ret != EOK) {
+        goto fail;
+    } else if (count > 1) {
+        DEBUG(SSSDBG_OP_FAILURE, ("More than one result for a BASE search!\n"));
+        goto fail;
+    }
+
+    ret = sysdb_msg2attrs(op_ctx, count, msgs, &hosts);
+    talloc_free(msgs);
+    if (ret != EOK) {
+        goto fail;
+    }
+
+    op_ctx->host = hosts[0];
+    return op_ctx;
+
+fail:
+    talloc_free(op_ctx);
+    return NULL;
+}
+
 static void ipa_selinux_handler_done(struct tevent_req *req)
 {
-    struct be_req *breq = tevent_req_callback_data(req, struct be_req);
+    struct ipa_selinux_op_ctx *op_ctx = tevent_req_callback_data(req, struct ipa_selinux_op_ctx);
+    struct be_req *breq = op_ctx->be_req;
     struct sysdb_ctx *sysdb = breq->be_ctx->sysdb;
     errno_t ret, sret;
     size_t map_count = 0;
@@ -115,13 +185,23 @@ static void ipa_selinux_handler_done(struct tevent_req *req)
     char *default_user = NULL;
     struct pam_data *pd = talloc_get_type(breq->req_data, struct pam_data);
     char *map_order = NULL;
+    size_t hbac_count;
+    struct sysdb_attrs **hbac_rules;
 
     ret = ipa_get_selinux_recv(req, breq, &map_count, &maps,
+                               &hbac_count, &hbac_rules,
                                &default_user, &map_order);
     if (ret != EOK) {
         goto fail;
     }
 
+    ret = ipa_selinux_process_maps(op_ctx->user, op_ctx->host,
+                                   maps, map_count,
+                                   hbac_rules, hbac_count);
+    if (ret != EOK) {
+        goto fail;
+    }
+
     ret = sysdb_transaction_start(sysdb);
     if (ret != EOK) {
         DEBUG(SSSDBG_CRIT_FAILURE, ("Failed to start transaction\n"));
@@ -153,11 +233,8 @@ static void ipa_selinux_handler_done(struct tevent_req *req)
         DEBUG(SSSDBG_OP_FAILURE, ("Could not commit transaction\n"));
         goto fail;
     }
-
-    /* Just in case more code will follow after this place in the future */
     in_transaction = false;
 
-
     pd->pam_status = PAM_SUCCESS;
     breq->fn(breq, DP_ERR_OK, EOK, "Success");
     return;
@@ -176,9 +253,195 @@ fail:
     }
 }
 
-static struct tevent_req *ipa_get_selinux_send(struct be_req *breq,
-                                               struct pam_data *pd,
-                                               struct ipa_selinux_ctx *selinux_ctx)
+static errno_t
+ipa_selinux_process_seealso_maps(struct sysdb_attrs *user,
+                                 struct sysdb_attrs *host,
+                                 struct sysdb_attrs **seealso_rules,
+                                 size_t seealso_rules_count,
+                                 struct sysdb_attrs **hbac_rules,
+                                 size_t hbac_rule_count);
+static errno_t
+ipa_selinux_process_maps(struct sysdb_attrs *user,
+                         struct sysdb_attrs *host,
+                         struct sysdb_attrs **selinux_maps,
+                         size_t selinux_map_count,
+                         struct sysdb_attrs **hbac_rules,
+                         size_t hbac_rule_count)
+{
+    TALLOC_CTX *tmp_ctx;
+    int i;
+    errno_t ret;
+    uint32_t priority = 0;
+    struct sysdb_attrs **seealso_rules;
+    size_t num_seealso_rules;
+    const char *seealso_str;
+
+    tmp_ctx = talloc_new(NULL);
+    if (!tmp_ctx) {
+        return ENOMEM;
+    }
+
+    seealso_rules = talloc_zero_array(tmp_ctx, struct sysdb_attrs *,
+                                      selinux_map_count + 1);
+    if (seealso_rules == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+    num_seealso_rules = 0;
+
+    for (i = 0; i < selinux_map_count; i++) {
+        if (sss_selinux_match(selinux_maps[i], user,
+                              host, &priority)) {
+            priority &= ~(SELINUX_PRIORITY_USER_NAME |
+                          SELINUX_PRIORITY_USER_GROUP |
+                          SELINUX_PRIORITY_USER_CAT);
+            ret = sysdb_attrs_add_uint32(selinux_maps[i],
+                                         SYSDB_SELINUX_HOST_PRIORITY,
+                                         priority);
+            if (ret != EOK) {
+                goto done;
+            }
+            continue;
+        }
+
+        ret = sysdb_attrs_get_string(selinux_maps[i],
+                                     SYSDB_SELINUX_SEEALSO, &seealso_str);
+        if (ret == ENOENT) {
+            continue;
+        } else if (ret != EOK) {
+            goto done;
+        }
+
+        seealso_rules[num_seealso_rules] = selinux_maps[i];
+        num_seealso_rules++;
+    }
+
+    ret = ipa_selinux_process_seealso_maps(user, host,
+                                           seealso_rules, num_seealso_rules,
+                                           hbac_rules, hbac_rule_count);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    ret = EOK;
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
+
+static errno_t
+ipa_selinux_process_seealso_maps(struct sysdb_attrs *user,
+                                 struct sysdb_attrs *host,
+                                 struct sysdb_attrs **seealso_rules,
+                                 size_t seealso_rules_count,
+                                 struct sysdb_attrs **hbac_rules,
+                                 size_t hbac_rule_count)
+{
+    int i, j;
+    errno_t ret;
+    struct ldb_message_element *el;
+    uint32_t priority = 0;
+    struct sysdb_attrs *usermap;
+    const char *seealso_dn;
+    const char *hbac_dn;
+
+    for (i = 0; i < hbac_rule_count; i++) {
+        ret = sysdb_attrs_get_string(hbac_rules[i], SYSDB_ORIG_DN, &hbac_dn);
+        if (ret != EOK) {
+            return ret;
+        }
+
+        /* We need to do this translation for further processing. We have to
+         * do it manually because no map was used to retrieve HBAC rules.
+         */
+        ret = sysdb_attrs_get_el(hbac_rules[i], IPA_MEMBER_HOST, &el);
+        if (ret != EOK) return ret;
+        el->name = SYSDB_ORIG_MEMBER_HOST;
+
+        ret = sysdb_attrs_get_el(hbac_rules[i], IPA_MEMBER_USER, &el);
+        if (ret != EOK) return ret;
+        el->name = SYSDB_ORIG_MEMBER_USER;
+
+        DEBUG(SSSDBG_TRACE_ALL,
+              ("Matching HBAC rule %s with SELinux mappings\n", hbac_dn));
+
+        if (!sss_selinux_match(hbac_rules[i], user, host, &priority)) {
+            DEBUG(SSSDBG_TRACE_ALL, ("Rule did not match\n"));
+            continue;
+        }
+
+        /* HBAC rule matched, find if it is in the "possible" list */
+        for (j = 0; j < seealso_rules_count; j++) {
+            usermap = seealso_rules[j];
+            if (usermap == NULL) {
+                continue;
+            }
+
+            ret = sysdb_attrs_get_string(usermap, SYSDB_SELINUX_SEEALSO, &seealso_dn);
+            if (ret != EOK) {
+                return ret;
+            }
+
+            if (strcasecmp(hbac_dn, seealso_dn) == 0) {
+                DEBUG(SSSDBG_TRACE_FUNC, ("HBAC rule [%s] matched, copying its"
+                                          "attributes to SELinux user map [%s]\n",
+                                          hbac_dn, seealso_dn));
+                priority &= ~(SELINUX_PRIORITY_USER_NAME |
+                              SELINUX_PRIORITY_USER_GROUP |
+                              SELINUX_PRIORITY_USER_CAT);
+                ret = sysdb_attrs_add_uint32(usermap,
+                                             SYSDB_SELINUX_HOST_PRIORITY,
+                                             priority);
+                if (ret != EOK) {
+                    return ret;
+                }
+
+                ret = sysdb_attrs_copy_values(hbac_rules[i], usermap, SYSDB_ORIG_MEMBER_USER);
+                if (ret != EOK) {
+                    return ret;
+                }
+
+                ret = sysdb_attrs_copy_values(hbac_rules[i], usermap, SYSDB_USER_CATEGORY);
+                if (ret != EOK) {
+                    return ret;
+                }
+
+                /* Speed up the next iteration */
+                seealso_rules[j] = NULL;
+            }
+        }
+    }
+
+    return EOK;
+}
+
+/* A more generic request to gather all SELinux and HBAC rules. Updates
+ * cache if necessary
+ */
+struct ipa_get_selinux_state {
+    struct be_req *be_req;
+    struct ipa_selinux_ctx *selinux_ctx;
+    struct sdap_id_op *op;
+
+    struct sysdb_attrs *host;
+    struct sysdb_attrs *user;
+
+    struct sysdb_attrs *defaults;
+    struct sysdb_attrs **selinuxmaps;
+    size_t nmaps;
+
+    struct sysdb_attrs **hbac_rules;
+    size_t hbac_rule_count;
+};
+
+static errno_t
+ipa_get_selinux_maps_offline(struct tevent_req *req);
+
+static struct tevent_req *
+ipa_get_selinux_send(struct be_req *breq,
+                     struct sysdb_attrs *user,
+                     struct sysdb_attrs *host,
+                     struct ipa_selinux_ctx *selinux_ctx)
 {
     struct tevent_req *req;
     struct tevent_req *subreq;
@@ -194,8 +457,9 @@ static struct tevent_req *ipa_get_selinux_send(struct be_req *breq,
     }
 
     state->be_req = breq;
-    state->pd = pd;
     state->selinux_ctx = selinux_ctx;
+    state->user = user;
+    state->host = host;
 
     offline = be_is_offline(bctx);
     DEBUG(SSSDBG_TRACE_INTERNAL, ("Connection status is [%s].\n",
@@ -219,7 +483,7 @@ static struct tevent_req *ipa_get_selinux_send(struct be_req *breq,
 
         tevent_req_set_callback(subreq, ipa_get_selinux_connect_done, req);
     } else {
-        ret = EAGAIN;
+        ret = ipa_get_selinux_maps_offline(req);
         goto immediate;
     }
 
@@ -248,67 +512,41 @@ static void ipa_get_selinux_connect_done(struct tevent_req *subreq)
 
     const char *access_name;
     const char *selinux_name;
-    struct ldb_dn *host_dn;
-    const char *attrs[] = { SYSDB_ORIG_DN,
-                            SYSDB_ORIG_MEMBEROF,
-                            NULL };
-    size_t count;
-    struct ldb_message **msgs;
-    struct sysdb_attrs **hosts;
+    const char *hostname;
 
     ret = sdap_id_op_connect_recv(subreq, &dp_error);
     talloc_zfree(subreq);
 
     if (dp_error == DP_ERR_OFFLINE) {
         talloc_zfree(state->op);
-        ret = EAGAIN;
+        ret = ipa_get_selinux_maps_offline(req);
+        goto fail;
     }
 
     if (ret != EOK) {
         goto fail;
     }
 
-    state->hostname = dp_opt_get_string(state->selinux_ctx->id_ctx->ipa_options->basic,
-                                        IPA_HOSTNAME);
-
     access_name = state->be_req->be_ctx->bet_info[BET_ACCESS].mod_name;
     selinux_name = state->be_req->be_ctx->bet_info[BET_SELINUX].mod_name;
-    if (strcasecmp(access_name, selinux_name) == 0) {
-        host_dn = sysdb_custom_dn(bctx->sysdb, state, state->hostname, HBAC_HOSTS_SUBDIR);
-        if (host_dn == NULL) {
-            ret = ENOMEM;
-            goto fail;
-        }
-
-        /* Look up the host to get its originalMemberOf entries */
-        ret = sysdb_search_entry(state, bctx->sysdb, host_dn,
-                                 LDB_SCOPE_BASE, NULL,
-                                 attrs, &count, &msgs);
-        if (ret == ENOENT || count == 0) {
-            /* We need to query the server */
-            goto server;
-        } else if (ret != EOK) {
-            goto fail;
-        } else if (count > 1) {
-            DEBUG(SSSDBG_OP_FAILURE, ("More than one result for a BASE search!\n"));
-            ret = EIO;
-            goto fail;
-        }
-
-        ret = sysdb_msg2attrs(state, count, msgs, &hosts);
-        if (ret != EOK) {
-            goto fail;
-        }
-
-        state->host = hosts[0];
+    if (strcasecmp(access_name, selinux_name) == 0 && state->host != NULL) {
+        /* If the access control module is the same as the selinux module
+         * and the access control had already discovered the host
+         */
         return ipa_get_config_step(req);
     }
 
-server:
+    hostname = dp_opt_get_string(state->selinux_ctx->id_ctx->ipa_options->basic,
+                                        IPA_HOSTNAME);
+    if (hostname == NULL) {
+        DEBUG(SSSDBG_CRIT_FAILURE, ("Cannot determine the host name\n"));
+        goto fail;
+    }
+
     subreq = ipa_host_info_send(state, bctx->ev, bctx->sysdb,
                                 sdap_id_op_handle(state->op),
                                 id_ctx->sdap_id_ctx->opts,
-                                state->hostname,
+                                hostname,
                                 id_ctx->ipa_options->host_map,
                                 NULL,
                                 state->selinux_ctx->host_search_bases);
@@ -318,13 +556,92 @@ server:
     }
 
     tevent_req_set_callback(subreq, ipa_get_selinux_hosts_done, req);
-
     return;
 
 fail:
     tevent_req_error(req, ret);
 }
 
+static errno_t
+ipa_get_selinux_maps_offline(struct tevent_req *req)
+{
+    errno_t ret;
+    size_t nmaps;
+    struct ldb_message **maps;
+    struct ldb_message *defaults;
+    const char *attrs[] = { SYSDB_NAME,
+                            SYSDB_USER_CATEGORY,
+                            SYSDB_HOST_CATEGORY,
+                            SYSDB_ORIG_MEMBER_USER,
+                            SYSDB_ORIG_MEMBER_HOST,
+                            SYSDB_SELINUX_SEEALSO,
+                            SYSDB_SELINUX_USER,
+                            NULL };
+    const char *default_user;
+    const char *order;
+
+    struct ipa_get_selinux_state *state = tevent_req_data(req,
+                                                  struct ipa_get_selinux_state);
+
+    /* read the config entry */
+    ret = sysdb_search_selinux_config(state, state->be_req->be_ctx->sysdb,
+                                      NULL, &defaults);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sysdb_search_selinux_config failed [%d]: %s\n",
+                                  ret, strerror(ret)));
+        return ret;
+    }
+
+    default_user = ldb_msg_find_attr_as_string(defaults,
+                                               SYSDB_SELINUX_DEFAULT_USER,
+                                               NULL);
+    order = ldb_msg_find_attr_as_string(defaults, SYSDB_SELINUX_DEFAULT_ORDER,
+                                        NULL);
+
+    state->defaults = sysdb_new_attrs(state);
+    if (state->defaults == NULL) {
+        return ENOMEM;
+    }
+
+    ret = sysdb_attrs_add_string(state->defaults,
+                                 IPA_CONFIG_SELINUX_DEFAULT_MAP, default_user);
+    if (ret != EOK) {
+        return ret;
+    }
+
+    ret = sysdb_attrs_add_string(state->defaults,
+                                 IPA_CONFIG_SELINUX_MAP_ORDER, order);
+    if (ret != EOK) {
+        return ret;
+    }
+
+    /* read all the SELinux rules */
+    ret = sysdb_get_selinux_usermaps(state, state->be_req->be_ctx->sysdb,
+                                     attrs, &nmaps, &maps);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("sysdb_get_selinux_usermaps failed [%d]: %s\n",
+                                  ret, strerror(ret)));
+        return ret;
+    }
+
+    ret = sysdb_msg2attrs(state, nmaps, maps, &state->selinuxmaps);
+    if (ret != EOK) {
+        return ret;
+    }
+    state->nmaps = nmaps;
+
+    /* read all the HBAC rules */
+    ret = hbac_get_cached_rules(state, state->be_req->be_ctx->sysdb,
+                                &state->hbac_rule_count, &state->hbac_rules);
+    if (ret != EOK) {
+        DEBUG(SSSDBG_OP_FAILURE, ("hbac_get_cached_rules failed [%d]: %s\n",
+                                  ret, strerror(ret)));
+        return ret;
+    }
+
+    return EOK;
+}
+
 static void ipa_get_selinux_hosts_done(struct tevent_req *subreq)
 {
     errno_t ret;
@@ -354,7 +671,6 @@ done:
 
 static void ipa_get_config_step(struct tevent_req *req)
 {
-    errno_t ret;
     const char *domain;
     struct tevent_req *subreq;
     struct ipa_get_selinux_state *state = tevent_req_data(req,
@@ -362,13 +678,6 @@ static void ipa_get_config_step(struct tevent_req *req)
     struct be_ctx *bctx = state->be_req->be_ctx;
     struct ipa_id_ctx *id_ctx = state->selinux_ctx->id_ctx;
 
-    ret = sss_selinux_extract_user(state, bctx->sysdb,
-                                   state->pd->user, &state->user);
-    if (ret != EOK) {
-        tevent_req_error(req, ret);
-        return;
-    }
-
     domain = dp_opt_get_string(state->selinux_ctx->id_ctx->ipa_options->basic,
                                IPA_KRB5_REALM);
     subreq = ipa_get_config_send(state, bctx->ev,
@@ -428,11 +737,9 @@ static void ipa_get_selinux_maps_done(struct tevent_req *subreq)
 
     char *selinux_name;
     char *access_name;
-    struct sysdb_attrs **rules;
-    size_t rule_count;
 
     const char *tmp_str;
-    uint32_t priority = 0;
+    bool check_hbac;
     errno_t ret;
     int i;
 
@@ -456,54 +763,29 @@ static void ipa_get_selinux_maps_done(struct tevent_req *subreq)
 
     DEBUG(SSSDBG_TRACE_FUNC,
          ("Found %d SELinux user maps\n", state->nmaps));
-    state->possible_match = talloc_zero_array(state, struct sysdb_attrs *,
-                                              state->nmaps + 1);
-    if (state->possible_match == NULL) {
-        ret = ENOMEM;
-        goto done;
-    }
 
+    check_hbac = false;
     for (i = 0; i < state->nmaps; i++) {
-        if (sss_selinux_match(state->selinuxmaps[i], state->user,
-                              state->host, &priority)) {
-            priority &= ~(SELINUX_PRIORITY_USER_NAME |
-                          SELINUX_PRIORITY_USER_GROUP |
-                          SELINUX_PRIORITY_USER_CAT);
-            ret = sysdb_attrs_add_uint32(state->selinuxmaps[i],
-                                         SYSDB_SELINUX_HOST_PRIORITY,
-                                         priority);
-            if (ret != EOK) {
-                goto done;
-            }
-            continue;
-        }
-
         ret = sysdb_attrs_get_string(state->selinuxmaps[i],
                                      SYSDB_SELINUX_SEEALSO, &tmp_str);
-        if (ret == ENOENT) {
-            continue;
+        if (ret == EOK) {
+            check_hbac = true;
+            break;
         }
-
-        state->possible_match[state->possible_matches] = state->selinuxmaps[i];
-        state->possible_matches++;
     }
 
-    if (state->possible_matches) {
+    if (check_hbac) {
         access_name = state->be_req->be_ctx->bet_info[BET_ACCESS].mod_name;
         selinux_name = state->be_req->be_ctx->bet_info[BET_SELINUX].mod_name;
         if (strcasecmp(access_name, selinux_name) == 0) {
             ret = hbac_get_cached_rules(state, state->be_req->be_ctx->sysdb,
-                                        &rule_count, &rules);
-            if (ret != EOK) {
-                goto done;
-            }
-
-            ret = ipa_get_selinux_hbac_process(state, rules, rule_count);
+                                        &state->hbac_rule_count, &state->hbac_rules);
+            /* Terminates the request */
             goto done;
         }
 
-        DEBUG(SSSDBG_TRACE_FUNC, ("%d SELinux maps referenced an HBAC rule. "
-              "Need to refresh HBAC rules\n", state->possible_matches));
+        DEBUG(SSSDBG_TRACE_FUNC, ("SELinux maps referenced an HBAC rule. "
+              "Need to refresh HBAC rules\n"));
         subreq = ipa_hbac_rule_info_send(state, false, bctx->ev,
                                          sdap_id_op_handle(state->op),
                                          id_ctx->sdap_id_ctx->opts,
@@ -533,22 +815,14 @@ static void ipa_get_selinux_hbac_done(struct tevent_req *subreq)
                                                       struct tevent_req);
     struct ipa_get_selinux_state *state = tevent_req_data(req,
                                                   struct ipa_get_selinux_state);
-    struct sysdb_attrs **rules;
-    size_t rule_count;
     errno_t ret;
 
-    ret = ipa_hbac_rule_info_recv(subreq, state, &rule_count,
-                                  &rules);
+    ret = ipa_hbac_rule_info_recv(subreq, state, &state->hbac_rule_count,
+                                  &state->hbac_rules);
     DEBUG(SSSDBG_TRACE_INTERNAL,
-          ("Received %d HBAC rules\n", rule_count));
+          ("Received %d HBAC rules\n", state->hbac_rule_count));
     talloc_free(subreq);
-    if (ret != EOK) {
-        goto done;
-    }
 
-    ret = ipa_get_selinux_hbac_process(state, rules, rule_count);
-
-done:
     if (ret != EOK) {
         tevent_req_error(req, ret);
     } else {
@@ -556,100 +830,13 @@ done:
     }
 }
 
-static int
-ipa_get_selinux_hbac_process(struct ipa_get_selinux_state *state,
-                             struct sysdb_attrs **rules,
-                             size_t rule_count)
-{
-    int i, j;
-    errno_t ret;
-    uint32_t priority = 0;
-    const char *hbac_dn;
-    const char *seealso_dn;
-    struct sysdb_attrs *usermap;
-    struct ldb_message_element *el;
-
-    for (i = 0; i < rule_count; i++) {
-        ret = sysdb_attrs_get_string(rules[i], SYSDB_ORIG_DN, &hbac_dn);
-        if (ret != EOK) {
-            goto done;
-        }
-
-        /* We need to do this translation for further processing. We have to
-         * do it manually because no map was used to retrieve HBAC rules.
-         */
-        ret = sysdb_attrs_get_el(rules[i], IPA_MEMBER_HOST, &el);
-        if (ret != EOK) goto done;
-        el->name = SYSDB_ORIG_MEMBER_HOST;
-
-        ret = sysdb_attrs_get_el(rules[i], IPA_MEMBER_USER, &el);
-        if (ret != EOK) goto done;
-        el->name = SYSDB_ORIG_MEMBER_USER;
-
-        DEBUG(SSSDBG_TRACE_ALL,
-              ("Matching HBAC rule %s with SELinux mappings\n", hbac_dn));
-
-        if (!sss_selinux_match(rules[i], state->user, state->host, &priority)) {
-            DEBUG(SSSDBG_TRACE_ALL, ("Rule did not match\n"));
-            continue;
-        }
-
-
-        /* HBAC rule matched, find if it is in the "possible" list */
-        for (j = 0; j < state->possible_matches; j++) {
-            usermap = state->possible_match[j];
-            if (usermap == NULL) {
-                continue;
-            }
-
-            ret = sysdb_attrs_get_string(usermap, SYSDB_SELINUX_SEEALSO, &seealso_dn);
-            if (ret != EOK) {
-                goto done;
-            }
-
-            if (strcasecmp(hbac_dn, seealso_dn) == 0) {
-                DEBUG(SSSDBG_TRACE_FUNC, ("HBAC rule [%s] matched, copying its"
-                                          "attributes to SELinux user map [%s]\n",
-                                          hbac_dn, seealso_dn));
-                priority &= ~(SELINUX_PRIORITY_USER_NAME |
-                              SELINUX_PRIORITY_USER_GROUP |
-                              SELINUX_PRIORITY_USER_CAT);
-                ret = sysdb_attrs_add_uint32(usermap,
-                                             SYSDB_SELINUX_HOST_PRIORITY,
-                                             priority);
-                if (ret != EOK) {
-                    goto done;
-                }
-
-                ret = sysdb_attrs_copy_values(rules[i], usermap, SYSDB_ORIG_MEMBER_USER);
-                if (ret != EOK) {
-                    goto done;
-                }
-
-                ret = sysdb_attrs_copy_values(rules[i], usermap, SYSDB_USER_CATEGORY);
-                if (ret != EOK) {
-                    goto done;
-                }
-
-                /* Just to boost the following lookup */
-                state->possible_match[j] = NULL;
-            }
-        }
-    }
-
-    /* Now we can dispose all possible rules, since they aren't possible any more */
-    talloc_zfree(state->possible_match);
-
-    ret = EOK;
-done:
-    return ret;
-}
-
 static errno_t
 ipa_get_selinux_recv(struct tevent_req *req,
                      TALLOC_CTX *mem_ctx,
                      size_t *count,
                      struct sysdb_attrs ***maps,
+                     size_t *hbac_count,
+                     struct sysdb_attrs ***hbac_rules,
                      char **default_user,
                      char **map_order)
 {
@@ -698,5 +885,13 @@ ipa_get_selinux_recv(struct tevent_req *req,
         *maps = NULL;
     }
 
+    if (state->hbac_rules != NULL) {
+        *hbac_count = state->hbac_rule_count;
+        *hbac_rules = talloc_steal(mem_ctx, state->hbac_rules);
+    } else {
+        *hbac_count = 0;
+        *hbac_rules = NULL;
+    }
+
     return EOK;
 }
-- 
1.8.0.2

-------------- next part --------------
>From cf74ff8e6bd5140cadeb5902baa7a9f3cb139037 Mon Sep 17 00:00:00 2001
From: Jakub Hrozek <jhrozek at redhat.com>
Date: Sun, 6 Jan 2013 23:58:04 +0100
Subject: [PATCH 4/4] IPA: Rename IPA_CONFIG_SELINUX_DEFAULT_MAP

It is not a map, but a default context. The name should reflect that.
---
 src/providers/ipa/ipa_config.c  | 2 +-
 src/providers/ipa/ipa_config.h  | 2 +-
 src/providers/ipa/ipa_selinux.c | 6 ++++--
 3 files changed, 6 insertions(+), 4 deletions(-)

diff --git a/src/providers/ipa/ipa_config.c b/src/providers/ipa/ipa_config.c
index 25f1e37bc2b7ea030bd429a1e7125f86b548ad27..05231cc763c28c2867688530a6d4c17d2fd30933 100644
--- a/src/providers/ipa/ipa_config.c
+++ b/src/providers/ipa/ipa_config.c
@@ -61,7 +61,7 @@ ipa_get_config_send(TALLOC_CTX *mem_ctx,
             goto done;
         }
         state->attrs[0] = IPA_CONFIG_MIGRATION_ENABLED;
-        state->attrs[1] = IPA_CONFIG_SELINUX_DEFAULT_MAP;
+        state->attrs[1] = IPA_CONFIG_SELINUX_DEFAULT_USER_CTX;
         state->attrs[2] = IPA_CONFIG_SELINUX_MAP_ORDER;
         state->attrs[3] = NULL;
     } else {
diff --git a/src/providers/ipa/ipa_config.h b/src/providers/ipa/ipa_config.h
index 9a25984b6e3736a1d2b977b882dea2fc45f08606..2f1e147d7edab0aca2a16269c6a73bc607b21bd5 100644
--- a/src/providers/ipa/ipa_config.h
+++ b/src/providers/ipa/ipa_config.h
@@ -31,7 +31,7 @@
 #include "providers/ldap/ldap_common.h"
 #include "db/sysdb.h"
 
-#define IPA_CONFIG_SELINUX_DEFAULT_MAP "ipaSELinuxUserMapDefault"
+#define IPA_CONFIG_SELINUX_DEFAULT_USER_CTX "ipaSELinuxUserMapDefault"
 #define IPA_CONFIG_SELINUX_MAP_ORDER "ipaSELinuxUserMapOrder"
 #define IPA_CONFIG_MIGRATION_ENABLED "ipaMigrationEnabled"
 #define IPA_CONFIG_SEARCH_BASE_TEMPLATE "cn=etc,%s"
diff --git a/src/providers/ipa/ipa_selinux.c b/src/providers/ipa/ipa_selinux.c
index c9c803f91b5e2371a2778994378e0b8b5688023d..8d0e0f77e4278588d6de9a83c0cf461b107f1a5a 100644
--- a/src/providers/ipa/ipa_selinux.c
+++ b/src/providers/ipa/ipa_selinux.c
@@ -604,7 +604,8 @@ ipa_get_selinux_maps_offline(struct tevent_req *req)
     }
 
     ret = sysdb_attrs_add_string(state->defaults,
-                                 IPA_CONFIG_SELINUX_DEFAULT_MAP, default_user);
+                                 IPA_CONFIG_SELINUX_DEFAULT_USER_CTX,
+                                 default_user);
     if (ret != EOK) {
         return ret;
     }
@@ -848,7 +849,8 @@ ipa_get_selinux_recv(struct tevent_req *req,
     TEVENT_REQ_RETURN_ON_ERROR(req);
 
     if (state->defaults != NULL) {
-        ret = sysdb_attrs_get_string(state->defaults, IPA_CONFIG_SELINUX_DEFAULT_MAP,
+        ret = sysdb_attrs_get_string(state->defaults,
+                                     IPA_CONFIG_SELINUX_DEFAULT_USER_CTX,
                                      &tmp_str);
         if (ret != EOK && ret != ENOENT) {
             return ret;
-- 
1.8.0.2



More information about the sssd-devel mailing list