summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlexander Bokovoy <abokovoy@redhat.com>2015-12-23 15:04:40 +0200
committerAlexander Bokovoy <abokovoy@redhat.com>2016-01-26 13:38:32 +0200
commitd1b87904462e890a855ac9d3b68ed02e089450d8 (patch)
tree3ebe7c7b1b3dd8a3e2507004101e652412c3bf46
parent52beb5e79905712a8aaabf19e52e654fc4648a94 (diff)
downloadslapi-nis-d1b87904462e890a855ac9d3b68ed02e089450d8.tar.gz
slapi-nis-d1b87904462e890a855ac9d3b68ed02e089450d8.tar.xz
slapi-nis-d1b87904462e890a855ac9d3b68ed02e089450d8.zip
slapi-nis: add support to resolve external members of IPA groups
FreeIPA allows to include external (non-LDAP) members into POSIX groups. To define external members, an attribute ipaExternalMember is set to the list of references to external members. Currently both FreeIPA and SSSD support only references done with SIDs (Security Identifiers) from the forests trusted by FreeIPA. Resolving external members of FreeIPA groups requires resolving SIDs to user and group names. However, since this resolution is already implemented by SSSD for the group in question, slapi-nis can use the fact that there is non-empty ipaExternalMember attribute's value to trigger lookup of the FreeIPA group via SSSD and then copy over memberUid attribute value set. This logic requires that ipaExternalMember attribute value is present in the entry to be put into the map cache. Thus, an additional configuration is needed for the groups container: schema-compat-entry-attribute: ipaexternalmember=%deref_r("member","ipaexternalmember") Note that resolving external members of IPA groups requires to use version of slapi-nis that populates the map cache after LDAP server startup, as SSSD needs to talk back to the LDAP server in the process of resolving external group members and that is not possible at the time when slapi-nis plugin starts up as the LDAP server is not yet listenting for incoming connections at that point.
-rw-r--r--doc/ipa/sch-ipa.txt15
-rw-r--r--src/back-sch.c112
2 files changed, 127 insertions, 0 deletions
diff --git a/doc/ipa/sch-ipa.txt b/doc/ipa/sch-ipa.txt
index 106e6cc..eb6238a 100644
--- a/doc/ipa/sch-ipa.txt
+++ b/doc/ipa/sch-ipa.txt
@@ -81,6 +81,21 @@ and groups from trusted domains. No additional configuration is needed.
ipa-adtrust-install, however, will not set the minimal numeric id for user or
group.
+Additionally, group area configuration should include following two attributes to
+allow resolving external members of IPA groups:
+
+schema-compat-entry-attribute: ipaexternalmember=%deref_r("member","ipaexternalmember")
+schema-compat-entry-attribute: objectclass=ipaexternalgroup
+
+When 'ipaExternalMember' attribute is present in the group entry generated by
+Schema Compatibility plugin, the plugin will attempt to retrieve the group
+members from SSSD daemon. If group has non-empty list of members, these new
+members will replace the original ones as they will include both IPA and external
+group members.
+
+SSSD greater than 1.13.3 is required to produce correct behavior due to bug
+https://fedorahosted.org/sssd/ticket/2522
+
== Authentication of the trusted domains' users ==
When the Schema Compatibility Plugin is configured to expose users from trusted
diff --git a/src/back-sch.c b/src/back-sch.c
index 98542c5..04fe667 100644
--- a/src/back-sch.c
+++ b/src/back-sch.c
@@ -419,6 +419,115 @@ backend_set_operational_attributes(Slapi_Entry *e,
}
}
+#ifdef USE_NSSWITCH
+#define IPA_ATTR_EXTERNAL_MEMBER "ipaExternalMember"
+#define IPA_ATTR_MEMBERUID "memberUid"
+static void
+backend_set_process_external_members(Slapi_PBlock *pb,
+ Slapi_Entry *e,
+ struct plugin_state *state,
+ struct backend_set_data *data)
+{
+ Slapi_Attr *attr = NULL;
+ Slapi_ValueSet *valueset = NULL;
+ bool_t is_attr_exists, is_group_exists;
+ struct backend_staged_search staged = {0, };
+ struct backend_search_cbdata cbdata = {0, };
+ char *plugin_id = state->plugin_desc->spd_id;
+
+ is_attr_exists = slapi_entry_attr_find(e, IPA_ATTR_EXTERNAL_MEMBER, &attr) == 0;
+
+ if (!is_attr_exists || attr == NULL) {
+ return;
+ }
+
+ /* There are external members in this entry, do group lookup via SSSD
+ * and update entry's memberUid attribute */
+
+ staged.name = slapi_entry_attr_get_charptr(e, "cn");
+ staged.type = SCH_NSSWITCH_GROUP;
+ staged.search_members = FALSE;
+ staged.is_id = FALSE;
+ staged.is_sid = FALSE;
+ staged.container_sdn = (char*) slapi_sdn_get_dn(data->container_sdn);
+ staged.entries = NULL;
+ staged.count = 0;
+ cbdata.nsswitch_buffer_len = MAX(16384, MAX(sysconf(_SC_GETPW_R_SIZE_MAX), sysconf(_SC_GETGR_R_SIZE_MAX)));
+ cbdata.nsswitch_buffer = malloc(cbdata.nsswitch_buffer_len);
+ cbdata.state = state;
+ cbdata.staged = &staged;
+
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_id,
+ "refreshing group membership for group \"%s\"\n", staged.name);
+
+ do {
+ /* This group must exist because it exists in the original tree
+ * but as dirsrv was restarted, SSSD might still consider its domain offline. */
+ is_group_exists = backend_retrieve_from_nsswitch(&staged, &cbdata);
+ if (!is_group_exists) {
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_id,
+ "group \"%s\" does not exist because SSSD is offline.",
+ staged.name);
+ if (state->ready_to_serve == 0) {
+ /* Only wait for SSSD when we populate the original set */
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_id,
+ "waiting for SSSD to become online...");
+ DS_Sleep(PR_SecondsToInterval(35));
+ } else {
+ break;
+ }
+ }
+ } while (!is_group_exists);
+
+ if (staged.entries != NULL && staged.entries[0] != NULL) {
+ attr = NULL;
+ if (slapi_entry_attr_find(staged.entries[0], IPA_ATTR_MEMBERUID, &attr) == 0) {
+#if 0
+ /* Debug output of original and updated memberUid values */
+ char **ary1, **ary2;
+ ary1 = slapi_entry_attr_get_charray(e, "memberUid");
+ ary2 = slapi_entry_attr_get_charray(staged.entries[0], "memberUid");
+
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_id,
+ "original group \"%s\":\n", staged.name);
+ for (int i = 0; ary1 && ary1[i] != NULL; ++i) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_id,
+ "\t> %s\n", ary1[i]);
+ }
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_id,
+ "new group \"%s\":\n", staged.name);
+ for (int i = 0; ary2 && ary2[i] != NULL; ++i) {
+ slapi_log_error(SLAPI_LOG_FATAL, plugin_id,
+ "\t> %s\n", ary2[i]);
+ }
+ slapi_ch_array_free(ary2);
+ slapi_ch_array_free(ary1);
+#endif
+
+ (void)slapi_attr_get_valueset(attr, &valueset);
+
+ if (slapi_entry_attr_find(e, IPA_ATTR_MEMBERUID, &attr) == 0) {
+ (void) slapi_entry_attr_delete(e, IPA_ATTR_MEMBERUID);
+ }
+ (void) slapi_entry_add_valueset(e, IPA_ATTR_MEMBERUID, valueset);
+ slapi_valueset_free(valueset);
+ } else {
+ slapi_log_error(SLAPI_LOG_PLUGIN, plugin_id,
+ "group \"%s\" doesn't have memberUid attribute\n", staged.name);
+ }
+ slapi_entry_free(staged.entries[0]);
+ }
+
+ if (staged.entries != NULL) {
+ free(staged.entries);
+ }
+
+ (void)slapi_entry_attr_delete(e, IPA_ATTR_EXTERNAL_MEMBER);
+ free(cbdata.nsswitch_buffer);
+ slapi_ch_free_string(&staged.name);
+}
+#endif
+
/* Given a map-entry directory entry, determine a key, a value, and extra data
* to be stored in the map cache, and add them to the map cache. */
static void
@@ -613,6 +722,9 @@ backend_set_entry_from(Slapi_PBlock *pb, enum backend_entry_source source,
slapi_entry_add_string(entry,
"objectClass", "extensibleObject");
}
+#ifdef USE_NSSWITCH
+ backend_set_process_external_members(pb, entry, data->common.state, data);
+#endif
/* Clean up the entry by doing a round trip through the LDIF parser. */
ldif = slapi_entry2str(entry, &len);
slapi_entry_free(entry);