>From e61d6743fe7754a4aadf3ed9f883696968fede07 Mon Sep 17 00:00:00 2001
From: Jan Zeleny <jzeleny@redhat.com>
Date: Mon, 16 Jul 2012 13:54:21 -0400
Subject: [PATCH 2/2] Modify priority evaluation in SELinux user maps

The functionality now is following:

When rule is being matched, its priority is determined as a combination
of user and host specificity (host taking preference).

After the rule is matched in provider, only its host priority is stored
in sysdb for later usage.

When rules are matched in the responder, their user priority is
determined. After that their host priority is retrieved directly from
sysdb and sum of both priorities is user to determine whether to use
that rule or not. If more rules have the same priority, the order given
in IPA config is used.
---
 src/db/sysdb.h                  |    2 +
 src/db/sysdb_selinux.c          |   34 ++++++++++++++++++++++++++++++++-
 src/providers/ipa/ipa_session.c |   36 +++++++++++++++++++++++++++++++++-
 src/util/sss_selinux.c          |   40 ++++++++++++++++++++++++++++++++++----
 src/util/sss_selinux.h          |   11 +++++++++-
 5 files changed, 114 insertions(+), 9 deletions(-)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index 5baac98b3f6df02ff7c2a41f531dbf1ba84208ff..d10885dbcb4e66d6a628886d3d8ad43389d3499b 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -120,6 +120,8 @@
 #define SYSDB_SELINUX_SEEALSO "seeAlso"
 #define SYSDB_SELINUX_USER "selinuxUser"
 #define SYSDB_SELINUX_ENABLED "enabled"
+#define SYSDB_SELINUX_HOST_PRIORITY "hostPriority"
+#define SYSDB_SELINUX_USER_PRIORITY "userPriority"
 
 #define SYSDB_CACHEDPWD "cachedPassword"
 
diff --git a/src/db/sysdb_selinux.c b/src/db/sysdb_selinux.c
index e9f2da11ba476f7bd081935dcd4041104daff981..8e69cd3e276cc92681410cf7f9fb4921dab17ea7 100644
--- a/src/db/sysdb_selinux.c
+++ b/src/db/sysdb_selinux.c
@@ -341,6 +341,7 @@ errno_t sysdb_search_selinux_usermap_by_username(TALLOC_CTX *mem_ctx,
                             SYSDB_HOST_CATEGORY,
                             SYSDB_ORIG_MEMBER_USER,
                             SYSDB_ORIG_MEMBER_HOST,
+                            SYSDB_SELINUX_HOST_PRIORITY,
                             SYSDB_SELINUX_USER,
                             NULL };
     struct ldb_message **msgs = NULL;
@@ -351,6 +352,9 @@ errno_t sysdb_search_selinux_usermap_by_username(TALLOC_CTX *mem_ctx,
     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;
@@ -405,7 +409,35 @@ errno_t sysdb_search_selinux_usermap_by_username(TALLOC_CTX *mem_ctx,
         tmp_attrs->a = msgs[i]->elements;
         tmp_attrs->num = msgs[i]->num_elements;
 
-        if (sss_selinux_match(tmp_attrs, user, NULL)) {
+        if (sss_selinux_match(tmp_attrs, user, NULL, &priority)) {
+            priority &= ~(SELINUX_PRIORITY_HOST_NAME |
+                          SELINUX_PRIORITY_HOST_GROUP |
+                          SELINUX_PRIORITY_HOST_CAT);
+
+            /* Now figure out host priority */
+            ret = sysdb_attrs_get_uint32_t(tmp_attrs,
+                                           SYSDB_SELINUX_HOST_PRIORITY,
+                                           &host_priority);
+            if (ret != EOK) {
+                continue;
+            }
+
+            priority += host_priority;
+            if (priority < top_priority) {
+                /* This rule has lower priority than what we already have,
+                 * skip it */
+                continue;
+            } else if (priority > top_priority) {
+                /* If the rule has higher priority, drop what we already
+                 * have */
+                while (usermaps_cnt > 0) {
+                    usermaps_cnt--;
+                    talloc_zfree(usermaps[usermaps_cnt]);
+                }
+                top_priority = priority;
+            }
+
+
             usermaps[usermaps_cnt] = talloc_steal(usermaps, msgs[i]);
             usermaps_cnt++;
         } else {
diff --git a/src/providers/ipa/ipa_session.c b/src/providers/ipa/ipa_session.c
index 238acdde776520dbb69046b8d45ecac4569e5cbc..12e80729770b9550dd04cf4b3c7cb39efd325672 100644
--- a/src/providers/ipa/ipa_session.c
+++ b/src/providers/ipa/ipa_session.c
@@ -326,6 +326,7 @@ static void ipa_get_selinux_maps_done(struct tevent_req *subreq)
     const char *tmp_str;
     size_t conf_cnt = 0;
     size_t pos_cnt = 0;
+    uint32_t priority = 0;
     errno_t ret;
     int i;
 
@@ -358,7 +359,17 @@ static void ipa_get_selinux_maps_done(struct tevent_req *subreq)
     }
 
     for (i = 0; i < count; i++) {
-        if (sss_selinux_match(results[i], state->user, state->host)) {
+        if (sss_selinux_match(results[i], state->user, state->host, &priority)) {
+            priority &= ~(SELINUX_PRIORITY_USER_NAME |
+                          SELINUX_PRIORITY_USER_GROUP |
+                          SELINUX_PRIORITY_USER_CAT);
+            ret = sysdb_attrs_add_uint32(results[i],
+                                         SYSDB_SELINUX_HOST_PRIORITY,
+                                         priority);
+            if (ret != EOK) {
+                goto done;
+            }
+
             state->confirmed_match[conf_cnt] = talloc_steal(state->confirmed_match,
                                                             results[i]);
             conf_cnt++;
@@ -445,6 +456,7 @@ static void ipa_get_selinux_hbac_done(struct tevent_req *subreq)
     size_t rule_count;
     size_t conf_cnt;
     size_t pos_cnt;
+    uint32_t priority = 0;
     errno_t ret;
     int i, j;
 
@@ -458,7 +470,7 @@ static void ipa_get_selinux_hbac_done(struct tevent_req *subreq)
     for (pos_cnt = 0 ; state->possible_match[pos_cnt]; pos_cnt++) ;
 
     for (i = 0; i < rule_count; i++) {
-        if (!sss_selinux_match(rules[i], state->user, state->host)) {
+        if (!sss_selinux_match(rules[i], state->user, state->host, &priority)) {
             continue;
         }
 
@@ -480,6 +492,26 @@ static void ipa_get_selinux_hbac_done(struct tevent_req *subreq)
             }
 
             if (strcasecmp(hbac_dn, seealso_dn) == 0) {
+                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;
+                }
+
                 state->confirmed_match[conf_cnt++] = talloc_steal(
                                                         state->confirmed_match,
                                                         usermap);
diff --git a/src/util/sss_selinux.c b/src/util/sss_selinux.c
index bdb117951adc9738d2a2bbf90602ea8db5b65ee3..7b2417bbead70b110df7ee4a0974d1eaa2525b3f 100644
--- a/src/util/sss_selinux.c
+++ b/src/util/sss_selinux.c
@@ -49,7 +49,8 @@ static bool match_entity(struct ldb_message_element *values,
 
 bool sss_selinux_match(struct sysdb_attrs *usermap,
                        struct sysdb_attrs *user,
-                       struct sysdb_attrs *host)
+                       struct sysdb_attrs *host,
+                       uint32_t *_priority)
 {
     struct ldb_message_element *users_el = NULL;
     struct ldb_message_element *usercat = NULL;
@@ -58,6 +59,9 @@ bool sss_selinux_match(struct sysdb_attrs *usermap,
     struct ldb_message_element *dn;
     struct ldb_message_element *memberof;
     int i;
+    uint32_t priority = 0;
+    bool matched_name;
+    bool matched_group;
     errno_t ret;
 
     if (usermap == NULL) {
@@ -90,10 +94,21 @@ bool sss_selinux_match(struct sysdb_attrs *usermap,
          */
         if (usercat == NULL || usercat->num_values == 0 ||
             strcasecmp((char *)usercat->values[0].data, "all") != 0) {
-            if (users_el == NULL || (!match_entity(users_el, dn) &&
-                !match_entity(users_el, memberof))) {
+            if (users_el == NULL) {
                 return false;
+            } else {
+                matched_name = match_entity(users_el, dn);
+                matched_group = match_entity(users_el, memberof);
+                if (matched_name) {
+                    priority |= SELINUX_PRIORITY_USER_NAME;
+                } else if (matched_group) {
+                    priority |= SELINUX_PRIORITY_USER_GROUP;
+                } else {
+                    return false;
+                }
             }
+        } else {
+            priority |= SELINUX_PRIORITY_USER_CAT;
         }
     }
 
@@ -109,11 +124,26 @@ bool sss_selinux_match(struct sysdb_attrs *usermap,
          */
         if (hostcat == NULL || hostcat->num_values == 0 ||
             strcasecmp((char *)hostcat->values[0].data, "all") != 0) {
-            if (hosts_el == NULL || (!match_entity(hosts_el, dn) &&
-                !match_entity(hosts_el, memberof))) {
+            if (hosts_el == NULL) {
                 return false;
+            } else {
+                matched_name = match_entity(hosts_el, dn);
+                matched_group = match_entity(hosts_el, memberof);
+                if (matched_name) {
+                    priority |= SELINUX_PRIORITY_HOST_NAME;
+                } else if (matched_group) {
+                    priority |= SELINUX_PRIORITY_HOST_GROUP;
+                } else {
+                    return false;
+                }
             }
         }
+    } else {
+        priority |= SELINUX_PRIORITY_HOST_CAT;
+    }
+
+    if (_priority != NULL) {
+        *_priority = priority;
     }
 
     return true;
diff --git a/src/util/sss_selinux.h b/src/util/sss_selinux.h
index 11a5445e65bc1c65c88dec583aa1f0c90b7e6789..def389400fb92defede3a4d61504fa3432d68479 100644
--- a/src/util/sss_selinux.h
+++ b/src/util/sss_selinux.h
@@ -30,6 +30,14 @@
 
 #include <db/sysdb.h>
 
+#define SELINUX_PRIORITY_USER_CAT 1
+#define SELINUX_PRIORITY_USER_GROUP 2
+#define SELINUX_PRIORITY_USER_NAME 4
+/* According to specification, host has higher priority */
+#define SELINUX_PRIORITY_HOST_CAT 8
+#define SELINUX_PRIORITY_HOST_GROUP 16
+#define SELINUX_PRIORITY_HOST_NAME 32
+
 errno_t
 sss_selinux_extract_user(TALLOC_CTX *mem_ctx,
                          struct sysdb_ctx *sysdb,
@@ -38,7 +46,8 @@ sss_selinux_extract_user(TALLOC_CTX *mem_ctx,
 
 bool sss_selinux_match(struct sysdb_attrs *usermap,
                        struct sysdb_attrs *user,
-                       struct sysdb_attrs *host);
+                       struct sysdb_attrs *host,
+                       uint32_t *_priority);
 
 const char *sss_selinux_map_get_seuser(struct ldb_message *usermap);
 
-- 
1.7.7.6

