[SSSD] [PATCH] Process SELinux mapping while offline

Jakub Hrozek jhrozek at redhat.com
Tue Jan 8 13:51:07 UTC 2013


On Tue, Jan 08, 2013 at 01:37:50PM +0100, Sumit Bose wrote:
> On Tue, Jan 08, 2013 at 12:44:54PM +0100, Sumit Bose wrote:
> > On Mon, Jan 07, 2013 at 12:29:14AM +0100, Jakub Hrozek wrote:
> > > 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.
> > 
> > ACK
> 
> sorry, ACK was too early, this patch breaks make check. Following patch
> fixes it:
> 
> diff --git a/src/providers/ipa/ipa_opts.h b/src/providers/ipa/ipa_opts.h
> index 01c32f4..66ca8ba 100644
> --- a/src/providers/ipa/ipa_opts.h
> +++ b/src/providers/ipa/ipa_opts.h
> @@ -28,6 +28,7 @@
>  #include "db/sysdb_sudo.h"
>  #include "db/sysdb_autofs.h"
>  #include "db/sysdb_services.h"
> +#include "db/sysdb_selinux.h"
>  
>  struct dp_option ipa_basic_opts[] = {
>      { "ipa_domain", DP_OPT_STRING, NULL_STRING, NULL_STRING },
> 
> 
> 
> bye,
> Sumit
> 
> > 
> > > 
> > > [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.
> > 
> > ACK
> > 
> > > 
> > > [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
> > 
> > see comments below
> > > 
> > > [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.
> > 
> > ACK
> > 
> > I'll run some test now. Is there anything I should test explicitly?
> > 
> > bye,
> > Sumit
> > 

Thank you for the review. A new patchset is attached.
-------------- next part --------------
>From d416a056f03bb927bf0e8c5e23a99369c22d6253 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 +
 src/providers/ipa/ipa_opts.h | 1 +
 3 files changed, 2 insertions(+), 5 deletions(-)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 35888977f28b205da51d332d76517908fa364f8a..9db831456d8e228107872bd732f80493ff09b03b 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -100,11 +100,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,
diff --git a/src/providers/ipa/ipa_opts.h b/src/providers/ipa/ipa_opts.h
index 01c32f462919b2b63c6e888b0cd221eab9e344b6..66ca8ba7d1308b9e91e77f01fc309a0ba6a0927d 100644
--- a/src/providers/ipa/ipa_opts.h
+++ b/src/providers/ipa/ipa_opts.h
@@ -28,6 +28,7 @@
 #include "db/sysdb_sudo.h"
 #include "db/sysdb_autofs.h"
 #include "db/sysdb_services.h"
+#include "db/sysdb_selinux.h"
 
 struct dp_option ipa_basic_opts[] = {
     { "ipa_domain", DP_OPT_STRING, NULL_STRING, NULL_STRING },
-- 
1.8.0.2

-------------- next part --------------
>From 68a8e26e0646c00eab8e634c5401055ca3de4b96 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 9c8250d8626178c397fd38a765e27570d2c5497b 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 | 655 ++++++++++++++++++++++++++--------------
 1 file changed, 429 insertions(+), 226 deletions(-)

diff --git a/src/providers/ipa/ipa_selinux.c b/src/providers/ipa/ipa_selinux.c
index b64de90b1236b7828753d79b8402a4fab0dfeeae..909ef056cbccca80bcc512e3fca858b1f4577c51 100644
--- a/src/providers/ipa/ipa_selinux.c
+++ b/src/providers/ipa/ipa_selinux.c
@@ -37,52 +37,53 @@
 #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,
+                          struct be_req *be_req, 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 +91,96 @@ 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, be_req,
+                                       pd->user, hostname);
+    if (op_ctx == NULL) {
+        DEBUG(SSSDBG_OP_FAILURE, ("Cannot create op context\n"));
+        goto fail;
+    }
+
+    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,
+                          struct be_req *be_req, 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;
+    }
+    op_ctx->be_req = be_req;
+
+    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;
+        return op_ctx;
+    } 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 +189,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 = 0;
+    struct sysdb_attrs **hbac_rules = 0;
 
     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 +237,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 +257,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 +461,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 +487,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 +516,45 @@ 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);
+        if (ret == EOK) {
+            tevent_req_done(req);
+            return;
+        }
+        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 +564,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 +679,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 +686,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 +745,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 +771,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 +823,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 +838,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 +893,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 3e133f3a23e328df1e8d01b65a9cb648643c1e93 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 909ef056cbccca80bcc512e3fca858b1f4577c51..d1dbe576589d311dbaf2b589cb88eb61083e882a 100644
--- a/src/providers/ipa/ipa_selinux.c
+++ b/src/providers/ipa/ipa_selinux.c
@@ -612,7 +612,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;
     }
@@ -856,7 +857,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