>From 3332232d8bdd676e2a59451caa96ebe1e098f6b0 Mon Sep 17 00:00:00 2001
From: Pavel Reichl <preichl@redhat.com>
Date: Thu, 20 Nov 2014 18:27:04 +0000
Subject: [PATCH] LDAP: retain external members

When processing group membership check sysdb for group members from
extern domain and include them in newly processed group membership as
extern members are curently found only when initgroups() is called.

Resolves:
https://fedorahosted.org/sssd/ticket/2492
---
 src/db/sysdb.h                         |   7 +++
 src/db/sysdb_ops.c                     |  84 ++++++++++++++++++++++++++
 src/providers/ldap/sdap_async_groups.c | 104 +++++++++++++++++++++++++++++++++
 3 files changed, 195 insertions(+)

diff --git a/src/db/sysdb.h b/src/db/sysdb.h
index aad641dca7fda3191f2b59982b0445e079585562..510687495c26d8b560c1616dee231ec9e5c99785 100644
--- a/src/db/sysdb.h
+++ b/src/db/sysdb.h
@@ -906,4 +906,11 @@ errno_t sysdb_search_object_by_sid(TALLOC_CTX *mem_ctx,
                                    const char *sid_str,
                                    const char **attrs,
                                    struct ldb_result **msg);
+
+errno_t sysdb_get_sids_of_members(TALLOC_CTX *mem_ctx,
+                                  struct sss_domain_info *dom,
+                                  const char *group_name,
+                                  const char ***_sids,
+                                  const char ***_dns,
+                                  size_t *_n);
 #endif /* __SYS_DB_H__ */
diff --git a/src/db/sysdb_ops.c b/src/db/sysdb_ops.c
index b6b732d8d7eccab12610a9fe54645db4a09a2e11..355967311c7a54e9f86bd3cae0b34a9e42b8023f 100644
--- a/src/db/sysdb_ops.c
+++ b/src/db/sysdb_ops.c
@@ -3517,3 +3517,87 @@ done:
     talloc_zfree(tmp_ctx);
     return ret;
 }
+
+errno_t sysdb_get_sids_of_members(TALLOC_CTX *mem_ctx,
+                                  struct sss_domain_info *dom,
+                                  const char *group_name,
+                                  const char ***_sids,
+                                  const char ***_dns,
+                                  size_t *_n)
+{
+    errno_t ret;
+    size_t i, m_count;
+    TALLOC_CTX *tmp_ctx;
+    struct ldb_message *msg;
+    struct ldb_message **members;
+    const char *attrs[] = { SYSDB_SID_STR, NULL };
+    const char **sids = NULL, **dns = NULL;
+    size_t n = 0;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    ret = sysdb_search_group_by_name(tmp_ctx, dom->sysdb, dom, group_name,
+                                     NULL, &msg);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    /* Get sid_str attribute of all elemets pointed to by group members */
+    ret = sysdb_asq_search(tmp_ctx, dom->sysdb, msg->dn, NULL, SYSDB_MEMBER, attrs,
+                           &m_count, &members);
+    if (ret != EOK) {
+        goto done;
+    }
+
+    sids = talloc_array(tmp_ctx, const char*, m_count);
+    if (sids == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    dns = talloc_array(tmp_ctx, const char*, m_count);
+    if (dns == NULL) {
+        ret = ENOMEM;
+        goto done;
+    }
+
+    for (i=0; i < m_count; i++) {
+        const char *sidstr;
+
+        sidstr = ldb_msg_find_attr_as_string(members[i], SYSDB_SID_STR, NULL);
+
+        if (sidstr != NULL) {
+            sids[n] = talloc_steal(sids, sidstr);
+
+            dns[n] = talloc_steal(dns, ldb_dn_get_linearized(members[i]->dn));
+            if (dns[n] == NULL) {
+                ret = ENOMEM;
+                goto done;
+            }
+            n++;
+        }
+    }
+
+    if (n == 0) {
+        ret = ENOENT;
+        goto done;
+    }
+
+    *_n = n;
+    *_sids = talloc_steal(mem_ctx, sids);
+    *_dns = talloc_steal(mem_ctx, dns);
+
+    ret = EOK;
+
+done:
+    if (ret == ENOENT) {
+        DEBUG(SSSDBG_TRACE_FUNC, "No such entry\n");
+    } else if (ret) {
+        DEBUG(SSSDBG_OP_FAILURE, "Error: %d (%s)\n", ret, strerror(ret));
+    }
+    talloc_free(tmp_ctx);
+    return ret;
+}
diff --git a/src/providers/ldap/sdap_async_groups.c b/src/providers/ldap/sdap_async_groups.c
index 274b07e6dcc551e10e408991e3925241197ed5f5..91124bc82103c044258a76170a71657409b39d66 100644
--- a/src/providers/ldap/sdap_async_groups.c
+++ b/src/providers/ldap/sdap_async_groups.c
@@ -784,6 +784,87 @@ done:
     return ret;
 }
 
+static errno_t
+are_sids_from_same_dom(const char *sid1, const char *sid2, bool *_result)
+{
+    size_t len_prefix_sid1;
+    size_t len_prefix_sid2;
+    char *rid1, *rid2;
+    bool result;
+
+    rid1 = strrchr(sid1, '-');
+    if (rid1 == NULL) {
+        return EINVAL;
+    }
+
+    rid2 = strrchr(sid2, '-');
+    if (rid2 == NULL) {
+        return EINVAL;
+    }
+
+    len_prefix_sid1 = rid1 - sid1;
+    len_prefix_sid2 = rid2 - sid2;
+
+    result = (len_prefix_sid1 == len_prefix_sid2) &&
+        (strncmp(sid1, sid2, len_prefix_sid1) == 0);
+
+    *_result = result;
+
+    return EOK;
+}
+
+static errno_t
+retain_extern_members(TALLOC_CTX *mem_ctx,
+                      struct sss_domain_info *dom,
+                      const char *group_name,
+                      const char *group_sid,
+                      char ***_userdns,
+                      size_t *_nuserdns)
+{
+    TALLOC_CTX *tmp_ctx;
+    const char **sids, **dns;
+    bool same_domain;
+    errno_t ret;
+    size_t i, n;
+    size_t nuserdns = 0;
+    const char **userdns = NULL;
+
+    tmp_ctx = talloc_new(NULL);
+    if (tmp_ctx == NULL) {
+        return ENOMEM;
+    }
+
+    ret = sysdb_get_sids_of_members(tmp_ctx, dom, group_name, &sids, &dns, &n);
+    if (ret != EOK) {
+        if (ret != ENOENT) {
+            DEBUG(SSSDBG_TRACE_ALL,
+                  "get_sids_of_members failed: %d [%s]\n",
+                  ret, sss_strerror(ret));
+        }
+        goto done;
+    }
+
+    for (i=0; i < n; i++) {
+        ret = are_sids_from_same_dom(group_sid, sids[i], &same_domain);
+        if (ret == EOK && !same_domain) {
+            DEBUG(SSSDBG_TRACE_ALL, "extern member: %s\n", dns[i]);
+            nuserdns++;
+            userdns = talloc_realloc(tmp_ctx, userdns, const char*, nuserdns);
+            if (userdns == NULL) {
+                ret = ENOMEM;
+                goto done;
+            }
+            userdns[nuserdns-1] = talloc_steal(userdns, dns[i]);
+        }
+    }
+    *_nuserdns = nuserdns;
+    *_userdns = discard_const(talloc_steal(mem_ctx, userdns));
+    ret = EOK;
+
+done:
+    talloc_free(tmp_ctx);
+    return ret;
+}
 
 /* ==Save-Group-Memebrs=================================================== */
 
@@ -800,6 +881,7 @@ static int sdap_save_grpmem(TALLOC_CTX *memctx,
 {
     struct ldb_message_element *el;
     struct sysdb_attrs *group_attrs = NULL;
+    const char *group_sid;
     const char *group_name;
     char **userdns = NULL;
     size_t nuserdns = 0;
@@ -826,6 +908,28 @@ static int sdap_save_grpmem(TALLOC_CTX *memctx,
         }
     }
 
+    /* This is a temporal solution until the IPA provider is able to
+     * resolve external group membership.
+     * https://fedorahosted.org/sssd/ticket/2522
+     */
+    if (opts->schema_type == SDAP_SCHEMA_IPA_V1) {
+        ret = sysdb_attrs_get_string(attrs, SYSDB_SID_STR, &group_sid);
+        if (ret != EOK) {
+            DEBUG(SSSDBG_TRACE_FUNC, "Failed to get group sid\n");
+            group_sid = NULL;
+        }
+
+        if (group_sid != NULL) {
+            ret = retain_extern_members(memctx, dom, group_name, group_sid,
+                                        &userdns, &nuserdns);
+            if (ret != EOK) {
+                DEBUG(SSSDBG_TRACE_INTERNAL,
+                      "retain_extern_members failed: %d:[%s].\n",
+                      ret, sss_strerror(ret));
+            }
+        }
+    }
+
     ret = sysdb_attrs_get_el(attrs,
                     opts->group_map[SDAP_AT_GROUP_MEMBER].sys_name, &el);
     if (ret != EOK) {
-- 
1.9.3

